Skip to content

Commit 0dd874f

Browse files
authored
campaigns: move validation code to campaignutils (#353)
Fixes sourcegraph/sourcegraph#14249.
1 parent 729e9a1 commit 0dd874f

File tree

6 files changed

+45
-83
lines changed

6 files changed

+45
-83
lines changed

cmd/src/campaigns_common.go

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -193,12 +193,8 @@ func campaignsExecute(ctx context.Context, out *output.Output, svc *campaigns.Se
193193
}
194194

195195
pending := campaignsCreatePending(out, "Parsing campaign spec")
196-
campaignSpec, rawSpec, err := svc.ParseCampaignSpec(specFile)
196+
campaignSpec, rawSpec, err := campaignsParseSpec(out, svc, specFile)
197197
if err != nil {
198-
return "", "", errors.Wrap(err, "parsing campaign spec")
199-
}
200-
201-
if err := campaignsValidateSpec(out, campaignSpec); err != nil {
202198
return "", "", err
203199
}
204200
campaignsCompletePending(pending, "Parsing campaign spec")
@@ -290,6 +286,34 @@ func campaignsExecute(ctx context.Context, out *output.Output, svc *campaigns.Se
290286
return id, url, nil
291287
}
292288

289+
// campaignsParseSpec parses and validates the given campaign spec. If the spec
290+
// has validation errors, the errors are output in a human readable form and an
291+
// exitCodeError is returned.
292+
func campaignsParseSpec(out *output.Output, svc *campaigns.Service, input io.ReadCloser) (*campaigns.CampaignSpec, string, error) {
293+
spec, raw, err := svc.ParseCampaignSpec(input)
294+
if err != nil {
295+
if merr, ok := err.(*multierror.Error); ok {
296+
block := out.Block(output.Line("\u274c", output.StyleWarning, "Campaign spec failed validation."))
297+
defer block.Close()
298+
299+
for i, err := range merr.Errors {
300+
block.Writef("%d. %s", i+1, err)
301+
}
302+
303+
return nil, "", &exitCodeError{
304+
error: nil,
305+
exitCode: 2,
306+
}
307+
} else {
308+
// This shouldn't happen; let's just punt and let the normal
309+
// rendering occur.
310+
return nil, "", err
311+
}
312+
}
313+
314+
return spec, raw, nil
315+
}
316+
293317
// printExecutionError is used to print the possible error returned by
294318
// campaignsExecute.
295319
func printExecutionError(out *output.Output, err error) {

cmd/src/campaigns_repositories.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,9 @@ Examples:
5454
return err
5555
}
5656

57-
spec, _, err := svc.ParseCampaignSpec(specFile)
58-
if err != nil {
59-
return errors.Wrap(err, "parsing campaign spec")
60-
}
61-
6257
out := output.NewOutput(flagSet.Output(), output.OutputOpts{Verbose: *verbose})
63-
if err := campaignsValidateSpec(out, spec); err != nil {
58+
spec, _, err := campaignsParseSpec(out, svc, specFile)
59+
if err != nil {
6460
return err
6561
}
6662

cmd/src/campaigns_validate.go

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import (
44
"flag"
55
"fmt"
66

7-
"github.com/hashicorp/go-multierror"
8-
"github.com/pkg/errors"
97
"github.com/sourcegraph/src-cli/internal/campaigns"
108
"github.com/sourcegraph/src-cli/internal/output"
119
)
@@ -40,13 +38,8 @@ Examples:
4038

4139
svc := campaigns.NewService(&campaigns.ServiceOpts{})
4240

43-
spec, _, err := svc.ParseCampaignSpec(specFile)
44-
if err != nil {
45-
return errors.Wrap(err, "parsing campaign spec")
46-
}
47-
4841
out := output.NewOutput(flagSet.Output(), output.OutputOpts{Verbose: *verbose})
49-
if err := campaignsValidateSpec(out, spec); err != nil {
42+
if _, _, err := campaignsParseSpec(out, svc, specFile); err != nil {
5043
return err
5144
}
5245

@@ -64,30 +57,3 @@ Examples:
6457
},
6558
})
6659
}
67-
68-
// campaignsValidateSpec validates the given campaign spec. If the spec has
69-
// validation errors, they are output in a human readable form and an
70-
// exitCodeError is returned.
71-
func campaignsValidateSpec(out *output.Output, spec *campaigns.CampaignSpec) error {
72-
if err := spec.Validate(); err != nil {
73-
if merr, ok := err.(*multierror.Error); ok {
74-
block := out.Block(output.Line("\u274c", output.StyleWarning, "Campaign spec failed validation."))
75-
defer block.Close()
76-
77-
for i, err := range merr.Errors {
78-
block.Writef("%d. %s", i+1, err)
79-
}
80-
81-
return &exitCodeError{
82-
error: nil,
83-
exitCode: 2,
84-
}
85-
} else {
86-
// This shouldn't happen; let's just punt and let the normal
87-
// rendering occur.
88-
return err
89-
}
90-
}
91-
92-
return nil
93-
}

go.mod

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,19 @@ require (
1818
github.com/olekukonko/tablewriter v0.0.4 // indirect
1919
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4
2020
github.com/pkg/errors v0.9.1
21-
github.com/sourcegraph/campaignutils v0.0.0-20201014140011-721315691938
21+
github.com/sourcegraph/campaignutils v0.0.0-20201016010611-63eb2bca27ad
2222
github.com/sourcegraph/codeintelutils v0.0.0-20200824140252-1db3aed5cf58
2323
github.com/sourcegraph/go-diff v0.6.0
2424
github.com/sourcegraph/jsonx v0.0.0-20200629203448-1a936bd500cf
2525
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
2626
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
27-
github.com/xeipuuv/gojsonschema v1.2.0
2827
golang.org/x/net v0.0.0-20200625001655-4c5254603344
2928
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4
3029
gopkg.in/yaml.v2 v2.3.0
3130
jaytaylor.com/html2text v0.0.0-20200412013138-3577fbdbcff7
3231
)
3332

3433
replace github.com/gosuri/uilive v0.0.4 => github.com/mrnugget/uilive v0.0.4-fix-escape
34+
35+
// See: https://github.com/ghodss/yaml/pull/65
36+
replace github.com/ghodss/yaml => github.com/sourcegraph/yaml v1.0.1-0.20200714132230-56936252f152

go.sum

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,16 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
4848
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
4949
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
5050
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
51-
github.com/sourcegraph/campaignutils v0.0.0-20201014140011-721315691938 h1:h5gOw7sMlYFSQFNdFPXNHJ4dtD0nlgClmL8SGAzlNM4=
52-
github.com/sourcegraph/campaignutils v0.0.0-20201014140011-721315691938/go.mod h1:5P8k8KlKz78RZJ2EFk9k9ln/j/twj28z/+BvO5hHZJ8=
51+
github.com/sourcegraph/campaignutils v0.0.0-20201016010611-63eb2bca27ad h1:HeSWFpxau4Jqk0s4yEhOdep+KYrJDm0497uhb/hnsgU=
52+
github.com/sourcegraph/campaignutils v0.0.0-20201016010611-63eb2bca27ad/go.mod h1:xm6i78Mk2t4DBLQDqEFc/3x6IPf7yYZCgbNaTQGhJHA=
5353
github.com/sourcegraph/codeintelutils v0.0.0-20200824140252-1db3aed5cf58 h1:Ps+U1xoZP+Zoph39YfRB6Q846ghO8pgrAgp0MiObvPs=
5454
github.com/sourcegraph/codeintelutils v0.0.0-20200824140252-1db3aed5cf58/go.mod h1:HplI8gRslTrTUUsSYwu28hSOderix7m5dHNca7xBzeo=
5555
github.com/sourcegraph/go-diff v0.6.0 h1:WbN9e/jD8ujU+o0vd9IFN5AEwtfB0rn/zM/AANaClqQ=
5656
github.com/sourcegraph/go-diff v0.6.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs=
5757
github.com/sourcegraph/jsonx v0.0.0-20200629203448-1a936bd500cf h1:oAdWFqhStsWiiMP/vkkHiMXqFXzl1XfUNOdxKJbd6bI=
5858
github.com/sourcegraph/jsonx v0.0.0-20200629203448-1a936bd500cf/go.mod h1:ppFaPm6kpcHnZGqQTFhUIAQRIEhdQDWP1PCv4/ON354=
59+
github.com/sourcegraph/yaml v1.0.1-0.20200714132230-56936252f152 h1:z/MpntplPaW6QW95pzcAR/72Z5TWDyDnSo0EOcyij9o=
60+
github.com/sourcegraph/yaml v1.0.1-0.20200714132230-56936252f152/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
5961
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
6062
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
6163
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -102,7 +104,10 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IV
102104
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
103105
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
104106
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
107+
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
105108
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
106109
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
110+
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
111+
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
107112
jaytaylor.com/html2text v0.0.0-20200412013138-3577fbdbcff7 h1:mub0MmFLOn8XLikZOAhgLD1kXJq8jgftSrrv7m00xFo=
108113
jaytaylor.com/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:OxvTsCwKosqQ1q7B+8FwXqg4rKZ/UG9dUW+g/VL2xH4=

internal/campaigns/campaign_spec.go

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@ package campaigns
33
import (
44
"fmt"
55

6-
"github.com/hashicorp/go-multierror"
7-
"github.com/pkg/errors"
86
"github.com/sourcegraph/campaignutils/overridable"
7+
"github.com/sourcegraph/campaignutils/yaml"
98
"github.com/sourcegraph/src-cli/schema"
10-
"github.com/xeipuuv/gojsonschema"
11-
"gopkg.in/yaml.v2"
129
)
1310

1411
// Some general notes about the struct definitions below.
@@ -75,41 +72,13 @@ type Step struct {
7572

7673
func ParseCampaignSpec(data []byte) (*CampaignSpec, error) {
7774
var spec CampaignSpec
78-
if err := yaml.Unmarshal(data, &spec); err != nil {
75+
if err := yaml.UnmarshalValidate(schema.CampaignSpecJSON, data, &spec); err != nil {
7976
return nil, err
8077
}
8178

8279
return &spec, nil
8380
}
8481

85-
var campaignSpecSchema *gojsonschema.Schema
86-
87-
func (spec *CampaignSpec) Validate() error {
88-
if campaignSpecSchema == nil {
89-
var err error
90-
campaignSpecSchema, err = gojsonschema.NewSchemaLoader().Compile(gojsonschema.NewStringLoader(schema.CampaignSpecJSON))
91-
if err != nil {
92-
return errors.Wrap(err, "parsing campaign spec schema")
93-
}
94-
}
95-
96-
result, err := campaignSpecSchema.Validate(gojsonschema.NewGoLoader(spec))
97-
if err != nil {
98-
return errors.Wrapf(err, "validating campaign spec")
99-
}
100-
if result.Valid() {
101-
return nil
102-
}
103-
104-
var errs *multierror.Error
105-
for _, verr := range result.Errors() {
106-
// ResultError instances don't actually implement error, so we need to
107-
// wrap them as best we can before adding them to the multierror.
108-
errs = multierror.Append(errs, errors.New(verr.String()))
109-
}
110-
return errs
111-
}
112-
11382
func (on *OnQueryOrRepository) String() string {
11483
if on.RepositoriesMatchingQuery != "" {
11584
return on.RepositoriesMatchingQuery

0 commit comments

Comments
 (0)