diff --git a/pkger/http_server_template.go b/pkger/http_server_template.go index e5f6004a77..8bf0c1b694 100644 --- a/pkger/http_server_template.go +++ b/pkger/http_server_template.go @@ -316,6 +316,14 @@ type RespApply struct { Errors []ValidationErr `json:"errors,omitempty" yaml:"errors,omitempty"` } +// RespApplyErr is the response body for a dry-run parse error in the apply template endpoint. +type RespApplyErr struct { + RespApply + + Code string `json:"code" yaml:"code"` + Message string `json:"message" yaml:"message"` +} + func (s *HTTPServerTemplates) apply(w http.ResponseWriter, r *http.Request) { var reqBody ReqApply encoding, err := decodeWithEncoding(r, &reqBody) @@ -415,7 +423,11 @@ func (s *HTTPServerTemplates) apply(w http.ResponseWriter, r *http.Request) { if reqBody.DryRun { impact, err := s.svc.DryRun(r.Context(), *orgID, userID, applyOpts...) if IsParseErr(err) { - s.api.Respond(w, r, http.StatusUnprocessableEntity, impactToRespApply(impact, err)) + s.api.Respond(w, r, http.StatusUnprocessableEntity, RespApplyErr{ + RespApply: impactToRespApply(impact, err), + Code: errors.EUnprocessableEntity, + Message: "unprocessable entity", + }) return } if err != nil { diff --git a/pkger/http_server_template_test.go b/pkger/http_server_template_test.go index 28533dd8f6..a7b78186c9 100644 --- a/pkger/http_server_template_test.go +++ b/pkger/http_server_template_test.go @@ -606,6 +606,76 @@ func TestPkgerHTTPServerTemplate(t *testing.T) { t.Run(tt.name, fn) } }) + + t.Run("resp apply err response", func(t *testing.T) { + tests := []struct { + name string + contentType string + reqBody pkger.ReqApply + }{ + { + name: "invalid json", + contentType: "application/json", + reqBody: pkger.ReqApply{ + DryRun: true, + OrgID: platform.ID(9000).String(), + RawTemplate: simpleInvalidBody(t, pkger.EncodingJSON), + }, + }, + } + for _, tt := range tests { + fn := func(t *testing.T) { + svc := &fakeSVC{ + dryRunFn: func(ctx context.Context, orgID, userID platform.ID, opts ...pkger.ApplyOptFn) (pkger.ImpactSummary, error) { + var opt pkger.ApplyOpt + for _, o := range opts { + o(&opt) + } + pkg, err := pkger.Combine(opt.Templates) + if err != nil { + return pkger.ImpactSummary{}, err + } + + if err := pkg.Validate(); err != nil { + return pkger.ImpactSummary{}, err + } + sum := pkg.Summary() + var diff pkger.Diff + for _, b := range sum.Buckets { + diff.Buckets = append(diff.Buckets, pkger.DiffBucket{ + DiffIdentifier: pkger.DiffIdentifier{ + MetaName: b.Name, + }, + }) + } + return pkger.ImpactSummary{ + Summary: sum, + Diff: diff, + }, nil + }, + } + + pkgHandler := pkger.NewHTTPServerTemplates(zap.NewNop(), svc) + svr := newMountedHandler(pkgHandler, 1) + + testttp. + PostJSON(t, "/api/v2/templates/apply", tt.reqBody). + Headers("Content-Type", tt.contentType). + Do(svr). + ExpectStatus(http.StatusUnprocessableEntity). + ExpectBody(func(buf *bytes.Buffer) { + var resp pkger.RespApplyErr + decodeBody(t, buf, &resp) + require.Equal(t, "unprocessable entity", resp.Code) + require.Greater(t, len(resp.Message), 0) + require.NotNil(t, resp.Summary) + require.NotNil(t, resp.Diff) + require.Greater(t, len(resp.Errors), 0) + }) + } + t.Run(tt.name, fn) + } + }) }) t.Run("apply a pkg", func(t *testing.T) { @@ -850,6 +920,17 @@ func bucketPkgJsonWithJsonnetRemote(t *testing.T) pkger.ReqRawTemplate { } } +func simpleInvalidBody(t *testing.T, encoding pkger.Encoding) pkger.ReqRawTemplate { + t.Helper() + b := bytes.Buffer{} + b.WriteString("[ {}, {} ]") + return pkger.ReqRawTemplate{ + ContentType: encoding.String(), + Sources: []string{"test1.json"}, + Template: b.Bytes(), + } +} + func newReqApplyYMLBody(t *testing.T, orgID platform.ID, dryRun bool) *bytes.Buffer { t.Helper()