Skip to content

Commit

Permalink
knollfear/bcda-710 Adding 410 (gone) response code to old jobs (#151)
Browse files Browse the repository at this point in the history
* Adding 410 (gone) response code to old jobs, archived jobs and expired jobs.

* Using GetJobTimeout() instead of constant to allow for setting this via an environment variable.

* Using GetJobTimeout() instead of constant to allow for setting this via an environment variable.

* fixing new method signature, misplaced ; and bad casing on ACOID

* Added operation outcome to archived/expired result
Added "deleted" issuetype which was on the reference page, but not in the code.

* adding operation outcome to another case I missed.
  • Loading branch information
knollfear authored Jan 30, 2019
1 parent 8d95410 commit 684865f
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 3 deletions.
14 changes: 14 additions & 0 deletions bcda/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ func bulkRequest(t string, w http.ResponseWriter, r *http.Request) {
200:bulkResponseBody
400:ErrorModel
404:ErrorModel
410:ErrorModel
500:FHIRResponse
*/
func jobStatus(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -275,6 +276,13 @@ func jobStatus(w http.ResponseWriter, r *http.Request) {
case "Failed":
responseutils.WriteError(&fhirmodels.OperationOutcome{}, w, http.StatusInternalServerError)
case "Completed":
// If the job should be expired, but the cleanup job hasn't run for some reason, still respond with 410
if job.CreatedAt.Add(GetJobTimeout()).Before(time.Now()) {
w.Header().Set("Expires", job.CreatedAt.Add(GetJobTimeout()).String())
oo := responseutils.CreateOpOutcome(responseutils.Error, responseutils.Exception, "", responseutils.Deleted)
responseutils.WriteError(oo, w, http.StatusGone)
return
}
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Expires", job.CreatedAt.Add(GetJobTimeout()).String())
scheme := "http"
Expand Down Expand Up @@ -331,6 +339,12 @@ func jobStatus(w http.ResponseWriter, r *http.Request) {
}

w.WriteHeader(http.StatusOK)
case "Archived":
fallthrough
case "Expired":
w.Header().Set("Expires", job.CreatedAt.Add(GetJobTimeout()).String())
oo := responseutils.CreateOpOutcome(responseutils.Error, responseutils.Exception, "", responseutils.Deleted)
responseutils.WriteError(oo, w, http.StatusGone)
}
}

Expand Down
93 changes: 90 additions & 3 deletions bcda/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import (
"crypto/tls"
"encoding/json"
"fmt"
"github.com/CMSgov/bcda-app/bcda/auth"
"github.com/CMSgov/bcda-app/bcda/encryption"
"net/http"
"net/http/httptest"
"os"
"testing"

"github.com/CMSgov/bcda-app/bcda/auth"
"github.com/CMSgov/bcda-app/bcda/encryption"
"time"

"github.com/CMSgov/bcda-app/bcda/database"
"github.com/CMSgov/bcda-app/bcda/models"
Expand Down Expand Up @@ -573,6 +573,93 @@ func (s *APITestSuite) TestJobStatusCompletedErrorFileExists() {
os.Remove(errFilePath)
}

func (s *APITestSuite) TestJobStatusExpired() {
j := models.Job{
ACOID: uuid.Parse("DBBD1CE1-AE24-435C-807D-ED45953077D3"),
UserID: uuid.Parse("82503A18-BF3B-436D-BA7B-BAE09B7FFD2F"),
RequestURL: "/api/v1/ExplanationOfBenefit/$export",
Status: "Expired",
}

s.db.Save(&j)

req := httptest.NewRequest("GET", fmt.Sprintf("/api/v1/jobs/%d", j.ID), nil)

handler := http.HandlerFunc(jobStatus)

rctx := chi.NewRouteContext()
rctx.URLParams.Add("jobId", fmt.Sprint(j.ID))
req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx))
token := makeJWT("DBBD1CE1-AE24-435C-807D-ED45953077D3", "82503A18-BF3B-436D-BA7B-BAE09B7FFD2F")
req = req.WithContext(context.WithValue(req.Context(), "token", token))

handler.ServeHTTP(s.rr, req)

assert.Equal(s.T(), http.StatusGone, s.rr.Code)
// There seems to be some slight difference in precision here. Match on first 20 chars sb fine.
assert.Equal(s.T(), j.CreatedAt.Add(GetJobTimeout()).String()[:20], s.rr.Header().Get("Expires")[:20])
s.db.Delete(&j)
}

// THis job is old, but has not yet been marked as expired.
func (s *APITestSuite) TestJobStatusNotExpired() {
j := models.Job{
ACOID: uuid.Parse("DBBD1CE1-AE24-435C-807D-ED45953077D3"),
UserID: uuid.Parse("82503A18-BF3B-436D-BA7B-BAE09B7FFD2F"),
RequestURL: "/api/v1/ExplanationOfBenefit/$export",
Status: "Completed",
}

//s.db.Save(&j)
j.CreatedAt = time.Now().Add(-GetJobTimeout()).Add(-GetJobTimeout())
s.db.Save(&j)

req := httptest.NewRequest("GET", fmt.Sprintf("/api/v1/jobs/%d", j.ID), nil)

handler := http.HandlerFunc(jobStatus)

rctx := chi.NewRouteContext()
rctx.URLParams.Add("jobId", fmt.Sprint(j.ID))
req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx))
token := makeJWT("DBBD1CE1-AE24-435C-807D-ED45953077D3", "82503A18-BF3B-436D-BA7B-BAE09B7FFD2F")
req = req.WithContext(context.WithValue(req.Context(), "token", token))

handler.ServeHTTP(s.rr, req)

assert.Equal(s.T(), http.StatusGone, s.rr.Code)
// There seems to be some slight difference in precision here. Match on first 20 chars sb fine.
assert.Equal(s.T(), j.CreatedAt.Add(GetJobTimeout()).String()[:20], s.rr.Header().Get("Expires")[:20])
s.db.Delete(&j)
}

func (s *APITestSuite) TestJobStatusArchived() {
j := models.Job{
ACOID: uuid.Parse("DBBD1CE1-AE24-435C-807D-ED45953077D3"),
UserID: uuid.Parse("82503A18-BF3B-436D-BA7B-BAE09B7FFD2F"),
RequestURL: "/api/v1/ExplanationOfBenefit/$export",
Status: "Archived",
}

s.db.Save(&j)

req := httptest.NewRequest("GET", fmt.Sprintf("/api/v1/jobs/%d", j.ID), nil)

handler := http.HandlerFunc(jobStatus)

rctx := chi.NewRouteContext()
rctx.URLParams.Add("jobId", fmt.Sprint(j.ID))
req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx))
token := makeJWT("DBBD1CE1-AE24-435C-807D-ED45953077D3", "82503A18-BF3B-436D-BA7B-BAE09B7FFD2F")
req = req.WithContext(context.WithValue(req.Context(), "token", token))

handler.ServeHTTP(s.rr, req)

assert.Equal(s.T(), http.StatusGone, s.rr.Code)
// There seems to be some slight difference in precision here. Match on first 20 chars sb fine.
assert.Equal(s.T(), j.CreatedAt.Add(GetJobTimeout()).String()[:20], s.rr.Header().Get("Expires")[:20])
s.db.Delete(&j)
}

func (s *APITestSuite) TestServeData() {
os.Setenv("FHIR_PAYLOAD_DIR", "../bcdaworker/data/test")
req := httptest.NewRequest("GET", "/data/test.ndjson", nil)
Expand Down
1 change: 1 addition & 0 deletions bcda/responseutils/issuetype.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const (
Not_supported = "Content not supported"
Duplicate = "Duplicate"
Not_found = "Not Found"
Deleted = "Deleted"
Too_long = "Content Too Long"
Code_invalid = "Invalid Code"
Extension = "Unacceptable Extension"
Expand Down

0 comments on commit 684865f

Please sign in to comment.