From 87bf3711e1d4aac790f122ecc6796a96b90bb0b5 Mon Sep 17 00:00:00 2001 From: Xavier Coulon Date: Thu, 24 May 2018 22:43:07 +0200 Subject: [PATCH] Validate space name with a regexp in the controller (OSIO#3580) This will prevent creating spaces with a name that cannot be used as a value in pod labels (matching max length and pattern). The regexp and length checks in the controller replace the validation function that was previously used at the design level, which allows for returning proper JSON-API errors to the clients. Fixes openshiftio/openshift.io#3580 Signed-off-by: Xavier Coulon --- controller/space.go | 23 ++ controller/space_blackbox_test.go | 432 +++++++++++++++--------------- design/resources.go | 4 + design/spaces.go | 2 +- errors/errors.go | 4 +- 5 files changed, 242 insertions(+), 223 deletions(-) diff --git a/controller/space.go b/controller/space.go index 9d908a3948..caefef2751 100644 --- a/controller/space.go +++ b/controller/space.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" "net/url" + "regexp" "github.com/fabric8-services/fabric8-wit/app" "github.com/fabric8-services/fabric8-wit/application" @@ -546,6 +547,18 @@ func (c *SpaceController) Update(ctx *app.UpdateSpaceContext) error { return ctx.OK(&response) } +const ( + // see https://github.com/kubernetes/community/blob/master/contributors/design-proposals/architecture/identifiers.md + spaceNameMaxLength int = 63 + spaceNamePattern string = `^([A-Za-z0-9][-A-Za-z0-9]*)?[A-Za-z0-9]$` +) + +var nameRegex *regexp.Regexp + +func init() { + nameRegex = regexp.MustCompile(spaceNamePattern) // will panic if the pattern is invalid +} + func validateCreateSpace(ctx *app.CreateSpaceContext) error { if ctx.Payload.Data == nil { return errors.NewBadParameterError("data", nil).Expected("not nil") @@ -556,6 +569,16 @@ func validateCreateSpace(ctx *app.CreateSpaceContext) error { if ctx.Payload.Data.Attributes.Name == nil { return errors.NewBadParameterError("data.attributes.name", nil).Expected("not nil") } + name := *ctx.Payload.Data.Attributes.Name + // now, verify the length and pattern for the space name + if len(name) > spaceNameMaxLength { + return errors.NewBadParameterError("data.attributes.name", name).Expected(fmt.Sprintf("max length: %d (was %d)", spaceNameMaxLength, len(name))) + } + matched := nameRegex.MatchString(name) + if !matched { + return errors.NewBadParameterError("data.attributes.name", name).Expected(fmt.Sprintf("matching '%s' pattern", spaceNamePattern)) + } + // // TODO(kwk): Comment back in once space template is official // if ctx.Payload.Data.Relationships == nil { // return errors.NewBadParameterError("data.relationships", nil).Expected("not nil") diff --git a/controller/space_blackbox_test.go b/controller/space_blackbox_test.go index d0bf72b340..6e3f7926e5 100644 --- a/controller/space_blackbox_test.go +++ b/controller/space_blackbox_test.go @@ -154,241 +154,233 @@ func (s *SpaceControllerTestSuite) SecuredSpaceIterationController(identity acco return svc, NewSpaceIterationsController(svc, s.db, s.Configuration) } -func (s *SpaceControllerTestSuite) TestValidateSpaceName() { +func (s *SpaceControllerTestSuite) TestCreateSpace() { s.T().Run("ok", func(t *testing.T) { - // given - p := newCreateSpacePayload(&testsupport.TestMaxsizedNameObj, nil) - // when - err := p.Validate() - // Validate payload function returns no error - assert.Nil(t, err) - }) - - s.T().Run("Fail - length", func(t *testing.T) { - // given - p := newCreateSpacePayload(&testsupport.TestOversizedNameObj, nil) - // when - err := p.Validate() - // Validate payload function returns an error - assert.NotNil(t, err) - assert.Contains(t, err.Error(), "length of type.name must be less than or equal to 63 but got") - }) - s.T().Run("Fail - prefix", func(t *testing.T) { - // given - invalidSpaceName := "_TestSpace" - p := newCreateSpacePayload(&invalidSpaceName, nil) - // when - err := p.Validate() - // Validate payload function returns an error - assert.NotNil(t, err) - assert.Contains(t, err.Error(), "type.name must match the regexp") - }) -} + t.Run("valid name", func(t *testing.T) { + // given + name := testsupport.CreateRandomValidTestName("TestSuccessCreateSpace-") + p := newCreateSpacePayload(&name, nil) + svc, ctrl := s.SecuredController(testsupport.TestIdentity) + // when + compareWithGoldenAgnostic(t, filepath.Join(s.testDir, "create", "ok.payload.req.golden.json"), p) + res, created := test.CreateSpaceCreated(t, svc.Context, svc, ctrl, p) + // then + require.NotNil(t, created.Data) + require.NotNil(t, created.Data.Attributes) + assert.NotNil(t, created.Data.Attributes.CreatedAt) + assert.NotNil(t, created.Data.Attributes.UpdatedAt) + require.NotNil(t, created.Data.Attributes.Name) + assert.Equal(t, name, *created.Data.Attributes.Name) + require.NotNil(t, created.Data.Links) + assert.NotNil(t, created.Data.Links.Self) + compareWithGoldenAgnostic(t, filepath.Join(s.testDir, "create", "ok.payload.res.golden.json"), created) + compareWithGoldenAgnostic(t, filepath.Join(s.testDir, "create", "ok.headers.res.golden.json"), res.Header()) + }) -func (s *SpaceControllerTestSuite) TestCreateSpace() { - s.T().Run("Fail - unsecure", func(t *testing.T) { - // given - p := newCreateSpacePayload(nil, nil) - svc, ctrl := s.UnSecuredController() - // when/then - test.CreateSpaceUnauthorized(t, svc.Context, svc, ctrl, p) - }) + t.Run("with explicit template", func(t *testing.T) { + // given + fxt := tf.NewTestFixture(t, s.DB, tf.SpaceTemplates(1)) + name := testsupport.CreateRandomValidTestName("TestSuccessCreateSpace-") + p := newCreateSpacePayload(&name, nil) - s.T().Run("Fail - auth returned 400", func(t *testing.T) { - // given - spaceName := uuid.NewV4().String() - p := newCreateSpacePayload(&spaceName, nil) - r := DummyResourceManager{ - httpResponseCode: 400, - } - svc, ctrl := s.SecuredControllerWithDummyResourceManager(testsupport.TestIdentity, r) - // when/then - test.CreateSpaceBadRequest(t, svc.Context, svc, ctrl, p) - }) - s.T().Run("Fail - auth returned 401", func(t *testing.T) { - // given - spaceName := uuid.NewV4().String() - p := newCreateSpacePayload(&spaceName, nil) - r := DummyResourceManager{ - httpResponseCode: 401, - } - svc, ctrl := s.SecuredControllerWithDummyResourceManager(testsupport.TestIdentity, r) - // when/then - test.CreateSpaceUnauthorized(t, svc.Context, svc, ctrl, p) - }) - s.T().Run("Fail - auth returned 500", func(t *testing.T) { - // given - spaceName := uuid.NewV4().String() - p := newCreateSpacePayload(&spaceName, nil) - r := DummyResourceManager{ - httpResponseCode: 500, - } - svc, ctrl := s.SecuredControllerWithDummyResourceManager(testsupport.TestIdentity, r) - // when/then - test.CreateSpaceInternalServerError(t, svc.Context, svc, ctrl, p) - }) + if p.Data.Relationships == nil { + p.Data.Relationships = &app.SpaceRelationships{} + } + p.Data.Relationships.SpaceTemplate = app.NewSpaceTemplateRelation( + fxt.SpaceTemplates[0].ID, + rest.AbsoluteURL( + &http.Request{Host: "api.service.domain.org"}, + app.SpaceTemplateHref(fxt.SpaceTemplates[0].ID.String()), + ), + ) + svc, ctrl := s.SecuredController(testsupport.TestIdentity) + // when + compareWithGoldenAgnostic(t, filepath.Join(s.testDir, "create", "ok_with_explicit_template.payload.req.golden.json"), p) + res, created := test.CreateSpaceCreated(t, svc.Context, svc, ctrl, p) + // then + require.NotNil(t, created.Data) + require.NotNil(t, created.Data.Attributes) + assert.NotNil(t, created.Data.Attributes.CreatedAt) + assert.NotNil(t, created.Data.Attributes.UpdatedAt) + require.NotNil(t, created.Data.Attributes.Name) + assert.Equal(t, name, *created.Data.Attributes.Name) + require.NotNil(t, created.Data.Links) + assert.NotNil(t, created.Data.Links.Self) + compareWithGoldenAgnostic(t, filepath.Join(s.testDir, "create", "ok_with_explicit_template.payload.res.golden.json"), created) + compareWithGoldenAgnostic(t, filepath.Join(s.testDir, "create", "ok_with_explicit_template.headers.res.golden.json"), res.Header()) + }) - s.T().Run("ok", func(t *testing.T) { - // given - name := testsupport.CreateRandomValidTestName("TestSuccessCreateSpace-") - p := newCreateSpacePayload(&name, nil) - svc, ctrl := s.SecuredController(testsupport.TestIdentity) - // when - compareWithGoldenAgnostic(t, filepath.Join(s.testDir, "create", "ok.payload.req.golden.json"), p) - res, created := test.CreateSpaceCreated(t, svc.Context, svc, ctrl, p) - // then - require.NotNil(t, created.Data) - require.NotNil(t, created.Data.Attributes) - assert.NotNil(t, created.Data.Attributes.CreatedAt) - assert.NotNil(t, created.Data.Attributes.UpdatedAt) - require.NotNil(t, created.Data.Attributes.Name) - assert.Equal(t, name, *created.Data.Attributes.Name) - require.NotNil(t, created.Data.Links) - assert.NotNil(t, created.Data.Links.Self) - compareWithGoldenAgnostic(t, filepath.Join(s.testDir, "create", "ok.payload.res.golden.json"), created) - compareWithGoldenAgnostic(t, filepath.Join(s.testDir, "create", "ok.headers.res.golden.json"), res.Header()) - }) + t.Run("with default area", func(t *testing.T) { + // given + name := testsupport.CreateRandomValidTestName("TestSuccessCreateSpaceAndDefaultArea-") + p := newCreateSpacePayload(&name, nil) + svc, ctrl := s.SecuredController(testsupport.TestIdentity) + // when + _, created := test.CreateSpaceCreated(t, svc.Context, svc, ctrl, p) + require.NotNil(t, created.Data) + spaceAreaSvc, spaceAreaCtrl := s.SecuredSpaceAreaController(testsupport.TestIdentity) + _, areaList := test.ListSpaceAreasOK(t, spaceAreaSvc.Context, spaceAreaSvc, spaceAreaCtrl, *created.Data.ID, nil, nil) + // then + // only 1 default gets created. + assert.Len(t, areaList.Data, 1) + assert.Equal(t, name, *areaList.Data[0].Attributes.Name) + + // verify if root iteration is created or not + spaceIterationSvc, spaceIterationCtrl := s.SecuredSpaceIterationController(testsupport.TestIdentity) + _, iterationList := test.ListSpaceIterationsOK(t, spaceIterationSvc.Context, spaceIterationSvc, spaceIterationCtrl, *created.Data.ID, nil, nil) + require.Len(t, iterationList.Data, 1) + assert.Equal(t, name, *iterationList.Data[0].Attributes.Name) + }) - s.T().Run("ok (with explicit template)", func(t *testing.T) { - // given - fxt := tf.NewTestFixture(t, s.DB, tf.SpaceTemplates(1)) - name := testsupport.CreateRandomValidTestName("TestSuccessCreateSpace-") - p := newCreateSpacePayload(&name, nil) + t.Run("with description", func(t *testing.T) { + // given + name := testsupport.CreateRandomValidTestName("TestSuccessCreateSpaceWithDescription-") + description := "Space for TestSuccessCreateSpaceWithDescription" + p := newCreateSpacePayload(&name, &description) + svc, ctrl := s.SecuredController(testsupport.TestIdentity) + // when + _, created := test.CreateSpaceCreated(t, svc.Context, svc, ctrl, p) + // then + assert.NotNil(t, created.Data) + assert.NotNil(t, created.Data.Attributes) + assert.NotNil(t, created.Data.Attributes.CreatedAt) + assert.NotNil(t, created.Data.Attributes.UpdatedAt) + assert.NotNil(t, created.Data.Attributes.Name) + assert.Equal(t, name, *created.Data.Attributes.Name) + assert.NotNil(t, created.Data.Attributes.Description) + assert.Equal(t, description, *created.Data.Attributes.Description) + assert.NotNil(t, created.Data.Links) + assert.NotNil(t, created.Data.Links.Self) + }) - if p.Data.Relationships == nil { - p.Data.Relationships = &app.SpaceRelationships{} - } - p.Data.Relationships.SpaceTemplate = app.NewSpaceTemplateRelation( - fxt.SpaceTemplates[0].ID, - rest.AbsoluteURL( - &http.Request{Host: "api.service.domain.org"}, - app.SpaceTemplateHref(fxt.SpaceTemplates[0].ID.String()), - ), - ) - svc, ctrl := s.SecuredController(testsupport.TestIdentity) - // when - compareWithGoldenAgnostic(t, filepath.Join(s.testDir, "create", "ok_with_explicit_template.payload.req.golden.json"), p) - res, created := test.CreateSpaceCreated(t, svc.Context, svc, ctrl, p) - // then - require.NotNil(t, created.Data) - require.NotNil(t, created.Data.Attributes) - assert.NotNil(t, created.Data.Attributes.CreatedAt) - assert.NotNil(t, created.Data.Attributes.UpdatedAt) - require.NotNil(t, created.Data.Attributes.Name) - assert.Equal(t, name, *created.Data.Attributes.Name) - require.NotNil(t, created.Data.Links) - assert.NotNil(t, created.Data.Links.Self) - compareWithGoldenAgnostic(t, filepath.Join(s.testDir, "create", "ok_with_explicit_template.payload.res.golden.json"), created) - compareWithGoldenAgnostic(t, filepath.Join(s.testDir, "create", "ok_with_explicit_template.headers.res.golden.json"), res.Header()) - }) + t.Run("same name but different owner", func(t *testing.T) { + // given + name := testsupport.CreateRandomValidTestName("SameName-") + description := "Space for TestSuccessCreateSameSpaceNameDifferentOwners" + newDescription := "Space for TestSuccessCreateSameSpaceNameDifferentOwners2" + a := newCreateSpacePayload(&name, &description) + svc, ctrl := s.SecuredController(testsupport.TestIdentity) + _, created := test.CreateSpaceCreated(t, svc.Context, svc, ctrl, a) + // when + b := newCreateSpacePayload(&name, &newDescription) + svc2, ctrl2 := s.SecuredController(testsupport.TestIdentity2) + _, created2 := test.CreateSpaceCreated(t, svc2.Context, svc2, ctrl2, b) + // then + assert.NotNil(t, created.Data) + assert.NotNil(t, created.Data.Attributes) + assert.NotNil(t, created.Data.Attributes.Name) + assert.Equal(t, name, *created.Data.Attributes.Name) + assert.NotNil(t, created2.Data) + assert.NotNil(t, created2.Data.Attributes) + assert.NotNil(t, created2.Data.Attributes.Name) + assert.Equal(t, name, *created2.Data.Attributes.Name) + assert.NotEqual(t, created.Data.Relationships.OwnedBy.Data.ID, created2.Data.Relationships.OwnedBy.Data.ID) + }) - s.T().Run("ok with default area", func(t *testing.T) { - // given - name := testsupport.CreateRandomValidTestName("TestSuccessCreateSpaceAndDefaultArea-") - p := newCreateSpacePayload(&name, nil) - svc, ctrl := s.SecuredController(testsupport.TestIdentity) - // when - _, created := test.CreateSpaceCreated(t, svc.Context, svc, ctrl, p) - require.NotNil(t, created.Data) - spaceAreaSvc, spaceAreaCtrl := s.SecuredSpaceAreaController(testsupport.TestIdentity) - _, areaList := test.ListSpaceAreasOK(t, spaceAreaSvc.Context, spaceAreaSvc, spaceAreaCtrl, *created.Data.ID, nil, nil) - // then - // only 1 default gets created. - assert.Len(t, areaList.Data, 1) - assert.Equal(t, name, *areaList.Data[0].Attributes.Name) - - // verify if root iteration is created or not - spaceIterationSvc, spaceIterationCtrl := s.SecuredSpaceIterationController(testsupport.TestIdentity) - _, iterationList := test.ListSpaceIterationsOK(t, spaceIterationSvc.Context, spaceIterationSvc, spaceIterationCtrl, *created.Data.ID, nil, nil) - require.Len(t, iterationList.Data, 1) - assert.Equal(t, name, *iterationList.Data[0].Attributes.Name) + t.Run("with max length name", func(t *testing.T) { + // given + name := testsupport.TestMaxsizedNameObj + p := newCreateSpacePayload(&name, nil) + svc, ctrl := s.SecuredController(testsupport.TestIdentity) + // when + _, created := test.CreateSpaceCreated(t, svc.Context, svc, ctrl, p) + // then + require.NotNil(t, created.Data) + require.NotNil(t, created.Data.Attributes) + assert.NotNil(t, created.Data.Attributes.CreatedAt) + assert.NotNil(t, created.Data.Attributes.UpdatedAt) + require.NotNil(t, created.Data.Attributes.Name) + assert.Equal(t, name, *created.Data.Attributes.Name) + require.NotNil(t, created.Data.Links) + assert.NotNil(t, created.Data.Links.Self) + }) }) - s.T().Run("ok with description", func(t *testing.T) { - // given - name := testsupport.CreateRandomValidTestName("TestSuccessCreateSpaceWithDescription-") - description := "Space for TestSuccessCreateSpaceWithDescription" - p := newCreateSpacePayload(&name, &description) - svc, ctrl := s.SecuredController(testsupport.TestIdentity) - // when - _, created := test.CreateSpaceCreated(t, svc.Context, svc, ctrl, p) - // then - assert.NotNil(t, created.Data) - assert.NotNil(t, created.Data.Attributes) - assert.NotNil(t, created.Data.Attributes.CreatedAt) - assert.NotNil(t, created.Data.Attributes.UpdatedAt) - assert.NotNil(t, created.Data.Attributes.Name) - assert.Equal(t, name, *created.Data.Attributes.Name) - assert.NotNil(t, created.Data.Attributes.Description) - assert.Equal(t, description, *created.Data.Attributes.Description) - assert.NotNil(t, created.Data.Links) - assert.NotNil(t, created.Data.Links.Self) - }) + s.T().Run("fail", func(t *testing.T) { - s.T().Run("ok same name but different owner", func(t *testing.T) { - // given - name := testsupport.CreateRandomValidTestName("SameName-") - description := "Space for TestSuccessCreateSameSpaceNameDifferentOwners" - newDescription := "Space for TestSuccessCreateSameSpaceNameDifferentOwners2" - a := newCreateSpacePayload(&name, &description) - svc, ctrl := s.SecuredController(testsupport.TestIdentity) - _, created := test.CreateSpaceCreated(t, svc.Context, svc, ctrl, a) - // when - b := newCreateSpacePayload(&name, &newDescription) - svc2, ctrl2 := s.SecuredController(testsupport.TestIdentity2) - _, created2 := test.CreateSpaceCreated(t, svc2.Context, svc2, ctrl2, b) - // then - assert.NotNil(t, created.Data) - assert.NotNil(t, created.Data.Attributes) - assert.NotNil(t, created.Data.Attributes.Name) - assert.Equal(t, name, *created.Data.Attributes.Name) - assert.NotNil(t, created2.Data) - assert.NotNil(t, created2.Data.Attributes) - assert.NotNil(t, created2.Data.Attributes.Name) - assert.Equal(t, name, *created2.Data.Attributes.Name) - assert.NotEqual(t, created.Data.Relationships.OwnedBy.Data.ID, created2.Data.Relationships.OwnedBy.Data.ID) - }) + t.Run("same name and same owner", func(t *testing.T) { + // given + name := testsupport.CreateRandomValidTestName("SameName-") + description := "Space for TestSuccessCreateSameSpaceNameDifferentOwners" + newDescription := "Space for TestSuccessCreateSameSpaceNameDifferentOwners2" + // when + a := newCreateSpacePayload(&name, &description) + svc, ctrl := s.SecuredController(testsupport.TestIdentity) + _, created := test.CreateSpaceCreated(t, svc.Context, svc, ctrl, a) + // then + assert.NotNil(t, created.Data) + assert.NotNil(t, created.Data.Attributes) + assert.NotNil(t, created.Data.Attributes.Name) + assert.Equal(t, name, *created.Data.Attributes.Name) - s.T().Run("ok with max length name", func(t *testing.T) { - // given - name := testsupport.TestMaxsizedNameObj - p := newCreateSpacePayload(&name, nil) - svc, ctrl := s.SecuredController(testsupport.TestIdentity) - // when - _, created := test.CreateSpaceCreated(t, svc.Context, svc, ctrl, p) - // then - require.NotNil(t, created.Data) - require.NotNil(t, created.Data.Attributes) - assert.NotNil(t, created.Data.Attributes.CreatedAt) - assert.NotNil(t, created.Data.Attributes.UpdatedAt) - require.NotNil(t, created.Data.Attributes.Name) - assert.Equal(t, name, *created.Data.Attributes.Name) - require.NotNil(t, created.Data.Links) - assert.NotNil(t, created.Data.Links.Self) - }) + // when + b := newCreateSpacePayload(&name, &newDescription) + b.Data.Attributes.Name = &name + b.Data.Attributes.Description = &newDescription + test.CreateSpaceConflict(t, svc.Context, svc, ctrl, b) + }) - s.T().Run("fail same name and same owner", func(t *testing.T) { - // given - name := testsupport.CreateRandomValidTestName("SameName-") - description := "Space for TestSuccessCreateSameSpaceNameDifferentOwners" - newDescription := "Space for TestSuccessCreateSameSpaceNameDifferentOwners2" - // when - a := newCreateSpacePayload(&name, &description) - svc, ctrl := s.SecuredController(testsupport.TestIdentity) - _, created := test.CreateSpaceCreated(t, svc.Context, svc, ctrl, a) - // then - assert.NotNil(t, created.Data) - assert.NotNil(t, created.Data.Attributes) - assert.NotNil(t, created.Data.Attributes.Name) - assert.Equal(t, name, *created.Data.Attributes.Name) + t.Run("invalid name", func(t *testing.T) { + t.Run("invalid character", func(t *testing.T) { + // given + name := "foo@bar.com" + description := "Space with invalid name" + // when/then + p := newCreateSpacePayload(&name, &description) + svc, ctrl := s.SecuredController(testsupport.TestIdentity) + test.CreateSpaceBadRequest(t, svc.Context, svc, ctrl, p) + }) + + t.Run("invalid length", func(t *testing.T) { + // given + name := testsupport.TestOversizedNameObj + description := "Space with invalid name" + // when/then + p := newCreateSpacePayload(&name, &description) + svc, ctrl := s.SecuredController(testsupport.TestIdentity) + test.CreateSpaceBadRequest(t, svc.Context, svc, ctrl, p) + }) + }) - // when - b := newCreateSpacePayload(&name, &newDescription) - b.Data.Attributes.Name = &name - b.Data.Attributes.Description = &newDescription - test.CreateSpaceConflict(t, svc.Context, svc, ctrl, b) + t.Run("auth", func(t *testing.T) { + t.Run("400", func(t *testing.T) { + // given + spaceName := uuid.NewV4().String() + p := newCreateSpacePayload(&spaceName, nil) + r := DummyResourceManager{ + httpResponseCode: 400, + } + svc, ctrl := s.SecuredControllerWithDummyResourceManager(testsupport.TestIdentity, r) + // when/then + test.CreateSpaceBadRequest(t, svc.Context, svc, ctrl, p) + }) + + t.Run("returned 401", func(t *testing.T) { + // given + spaceName := uuid.NewV4().String() + p := newCreateSpacePayload(&spaceName, nil) + r := DummyResourceManager{ + httpResponseCode: 401, + } + svc, ctrl := s.SecuredControllerWithDummyResourceManager(testsupport.TestIdentity, r) + // when/then + test.CreateSpaceUnauthorized(t, svc.Context, svc, ctrl, p) + }) + + s.T().Run("500", func(t *testing.T) { + // given + spaceName := uuid.NewV4().String() + p := newCreateSpacePayload(&spaceName, nil) + r := DummyResourceManager{ + httpResponseCode: 500, + } + svc, ctrl := s.SecuredControllerWithDummyResourceManager(testsupport.TestIdentity, r) + // when/then + test.CreateSpaceInternalServerError(t, svc.Context, svc, ctrl, p) + }) + }) }) } diff --git a/design/resources.go b/design/resources.go index 40a215cc08..e40a15f409 100644 --- a/design/resources.go +++ b/design/resources.go @@ -97,6 +97,10 @@ var _ = a.Resource("trackerquery", func() { }) }) +const ( + spaceNamePattern string = `^([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$` +) + var nameValidationFunction = func() { a.MaxLength(63) // maximum name length is 63 characters a.MinLength(1) // minimum name length is 1 characters diff --git a/design/spaces.go b/design/spaces.go index f992731e7d..f3f18afada 100644 --- a/design/spaces.go +++ b/design/spaces.go @@ -63,7 +63,7 @@ var spaceOwnedBy = a.Type("SpaceOwnedBy", func() { }) var spaceAttributes = a.Type("SpaceAttributes", func() { - a.Attribute("name", d.String, "Name for the space", nameValidationFunction) + a.Attribute("name", d.String, "Name for the space") // name validation is performed at the controller level, to return a proper JSON-API response a.Attribute("description", d.String, "Description for the space", func() { a.Example("This is the foobar collaboration space") }) diff --git a/errors/errors.go b/errors/errors.go index 7518667df4..b9a8fa299a 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -179,8 +179,8 @@ func (err BadParameterError) Error() string { } // Expected sets the optional expectedValue parameter on the BadParameterError -func (err BadParameterError) Expected(expexcted interface{}) BadParameterError { - err.expectedValue = expexcted +func (err BadParameterError) Expected(expected interface{}) BadParameterError { + err.expectedValue = expected err.hasExpectedValue = true return err }