project: add remote repository config type

RemoteRepositoryConfigType defines how a remote repository is configured and
managed. Currently only "remotesource" is supported.

In future other config types (like a fully manual config) could be supported.
This commit is contained in:
Simone Gotti 2019-05-03 12:21:44 +02:00
parent ea02eed2d9
commit 1f09eea949
4 changed files with 61 additions and 29 deletions

View File

@ -135,6 +135,9 @@ func (s *CommandHandler) CreateProject(ctx context.Context, project *types.Proje
if !types.IsValidVisibility(project.Visibility) { if !types.IsValidVisibility(project.Visibility) {
return nil, util.NewErrBadRequest(errors.Errorf("invalid project visibility")) return nil, util.NewErrBadRequest(errors.Errorf("invalid project visibility"))
} }
if !types.IsValidRemoteRepositoryConfigType(project.RemoteRepositoryConfigType) {
return nil, util.NewErrBadRequest(errors.Errorf("invalid project remote repository config type %q", project.RemoteRepositoryConfigType))
}
var cgt *datamanager.ChangeGroupsUpdateToken var cgt *datamanager.ChangeGroupsUpdateToken
@ -181,11 +184,15 @@ func (s *CommandHandler) CreateProject(ctx context.Context, project *types.Proje
return util.NewErrBadRequest(errors.Errorf("project group with name %q, path %q already exists", pg.Name, pp)) return util.NewErrBadRequest(errors.Errorf("project group with name %q, path %q already exists", pg.Name, pp))
} }
if project.RemoteRepositoryConfigType == types.RemoteRepositoryConfigTypeRemoteSource {
// check that the linked account matches the remote source // check that the linked account matches the remote source
user, err := s.readDB.GetUserByLinkedAccount(tx, project.LinkedAccountID) user, err := s.readDB.GetUserByLinkedAccount(tx, project.LinkedAccountID)
if err != nil { if err != nil {
return errors.Wrapf(err, "failed to get user with linked account id %q", project.LinkedAccountID) return errors.Wrapf(err, "failed to get user with linked account id %q", project.LinkedAccountID)
} }
if user == nil {
return util.NewErrBadRequest(errors.Errorf("user for linked account %q doesn't exist", project.LinkedAccountID))
}
la, ok := user.LinkedAccounts[project.LinkedAccountID] la, ok := user.LinkedAccounts[project.LinkedAccountID]
if !ok { if !ok {
return util.NewErrBadRequest(errors.Errorf("linked account id %q for user %q doesn't exist", project.LinkedAccountID, user.Name)) return util.NewErrBadRequest(errors.Errorf("linked account id %q for user %q doesn't exist", project.LinkedAccountID, user.Name))
@ -193,6 +200,7 @@ func (s *CommandHandler) CreateProject(ctx context.Context, project *types.Proje
if la.RemoteSourceID != project.RemoteSourceID { if la.RemoteSourceID != project.RemoteSourceID {
return util.NewErrBadRequest(errors.Errorf("linked account id %q remote source %q different than project remote source %q", project.LinkedAccountID, la.RemoteSourceID, project.RemoteSourceID)) return util.NewErrBadRequest(errors.Errorf("linked account id %q remote source %q different than project remote source %q", project.LinkedAccountID, la.RemoteSourceID, project.RemoteSourceID))
} }
}
return nil return nil
}) })

View File

@ -394,13 +394,13 @@ func TestProjectGroupsAndProjects(t *testing.T) {
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
t.Run("create a project in user root project group", func(t *testing.T) { t.Run("create a project in user root project group", func(t *testing.T) {
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("user", user.Name)}, Visibility: types.VisibilityPublic}) _, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("user", user.Name)}, Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
if err != nil { if err != nil {
t.Fatalf("unexpected err: %v", err) t.Fatalf("unexpected err: %v", err)
} }
}) })
t.Run("create a project in org root project group", func(t *testing.T) { t.Run("create a project in org root project group", func(t *testing.T) {
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("org", org.Name)}, Visibility: types.VisibilityPublic}) _, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("org", org.Name)}, Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
if err != nil { if err != nil {
t.Fatalf("unexpected err: %v", err) t.Fatalf("unexpected err: %v", err)
} }
@ -418,13 +418,13 @@ func TestProjectGroupsAndProjects(t *testing.T) {
} }
}) })
t.Run("create a project in user non root project group with same name as a root project", func(t *testing.T) { t.Run("create a project in user non root project group with same name as a root project", func(t *testing.T) {
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("user", user.Name, "projectgroup01")}, Visibility: types.VisibilityPublic}) _, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("user", user.Name, "projectgroup01")}, Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
if err != nil { if err != nil {
t.Fatalf("unexpected err: %+#v", err) t.Fatalf("unexpected err: %+#v", err)
} }
}) })
t.Run("create a project in org non root project group with same name as a root project", func(t *testing.T) { t.Run("create a project in org non root project group with same name as a root project", func(t *testing.T) {
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("org", org.Name, "projectgroup01")}, Visibility: types.VisibilityPublic}) _, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("org", org.Name, "projectgroup01")}, Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
if err != nil { if err != nil {
t.Fatalf("unexpected err: %v", err) t.Fatalf("unexpected err: %v", err)
} }
@ -433,7 +433,7 @@ func TestProjectGroupsAndProjects(t *testing.T) {
t.Run("create duplicated project in user root project group", func(t *testing.T) { t.Run("create duplicated project in user root project group", func(t *testing.T) {
projectName := "project01" projectName := "project01"
expectedErr := fmt.Sprintf("project with name %q, path %q already exists", projectName, path.Join("user", user.Name, projectName)) expectedErr := fmt.Sprintf("project with name %q, path %q already exists", projectName, path.Join("user", user.Name, projectName))
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: projectName, Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("user", user.Name)}, Visibility: types.VisibilityPublic}) _, err := cs.ch.CreateProject(ctx, &types.Project{Name: projectName, Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("user", user.Name)}, Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
if err.Error() != expectedErr { if err.Error() != expectedErr {
t.Fatalf("expected err %v, got err: %v", expectedErr, err) t.Fatalf("expected err %v, got err: %v", expectedErr, err)
} }
@ -441,7 +441,7 @@ func TestProjectGroupsAndProjects(t *testing.T) {
t.Run("create duplicated project in org root project group", func(t *testing.T) { t.Run("create duplicated project in org root project group", func(t *testing.T) {
projectName := "project01" projectName := "project01"
expectedErr := fmt.Sprintf("project with name %q, path %q already exists", projectName, path.Join("org", org.Name, projectName)) expectedErr := fmt.Sprintf("project with name %q, path %q already exists", projectName, path.Join("org", org.Name, projectName))
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: projectName, Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("org", org.Name)}, Visibility: types.VisibilityPublic}) _, err := cs.ch.CreateProject(ctx, &types.Project{Name: projectName, Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("org", org.Name)}, Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
if err.Error() != expectedErr { if err.Error() != expectedErr {
t.Fatalf("expected err %v, got err: %v", expectedErr, err) t.Fatalf("expected err %v, got err: %v", expectedErr, err)
} }
@ -450,7 +450,7 @@ func TestProjectGroupsAndProjects(t *testing.T) {
t.Run("create duplicated project in user non root project group", func(t *testing.T) { t.Run("create duplicated project in user non root project group", func(t *testing.T) {
projectName := "project01" projectName := "project01"
expectedErr := fmt.Sprintf("project with name %q, path %q already exists", projectName, path.Join("user", user.Name, "projectgroup01", projectName)) expectedErr := fmt.Sprintf("project with name %q, path %q already exists", projectName, path.Join("user", user.Name, "projectgroup01", projectName))
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: projectName, Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("user", user.Name, "projectgroup01")}, Visibility: types.VisibilityPublic}) _, err := cs.ch.CreateProject(ctx, &types.Project{Name: projectName, Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("user", user.Name, "projectgroup01")}, Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
if err.Error() != expectedErr { if err.Error() != expectedErr {
t.Fatalf("expected err %v, got err: %v", expectedErr, err) t.Fatalf("expected err %v, got err: %v", expectedErr, err)
} }
@ -458,7 +458,7 @@ func TestProjectGroupsAndProjects(t *testing.T) {
t.Run("create duplicated project in org non root project group", func(t *testing.T) { t.Run("create duplicated project in org non root project group", func(t *testing.T) {
projectName := "project01" projectName := "project01"
expectedErr := fmt.Sprintf("project with name %q, path %q already exists", projectName, path.Join("org", org.Name, "projectgroup01", projectName)) expectedErr := fmt.Sprintf("project with name %q, path %q already exists", projectName, path.Join("org", org.Name, "projectgroup01", projectName))
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: projectName, Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("org", org.Name, "projectgroup01")}, Visibility: types.VisibilityPublic}) _, err := cs.ch.CreateProject(ctx, &types.Project{Name: projectName, Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("org", org.Name, "projectgroup01")}, Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
if err.Error() != expectedErr { if err.Error() != expectedErr {
t.Fatalf("expected err %v, got err: %v", expectedErr, err) t.Fatalf("expected err %v, got err: %v", expectedErr, err)
} }
@ -466,14 +466,14 @@ func TestProjectGroupsAndProjects(t *testing.T) {
t.Run("create project in unexistent project group", func(t *testing.T) { t.Run("create project in unexistent project group", func(t *testing.T) {
expectedErr := `project group with id "unexistentid" doesn't exist` expectedErr := `project group with id "unexistentid" doesn't exist`
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: "unexistentid"}, Visibility: types.VisibilityPublic}) _, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: "unexistentid"}, Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
if err.Error() != expectedErr { if err.Error() != expectedErr {
t.Fatalf("expected err %v, got err: %v", expectedErr, err) t.Fatalf("expected err %v, got err: %v", expectedErr, err)
} }
}) })
t.Run("create project without parent id specified", func(t *testing.T) { t.Run("create project without parent id specified", func(t *testing.T) {
expectedErr := "project parent id required" expectedErr := "project parent id required"
_, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Visibility: types.VisibilityPublic}) _, err := cs.ch.CreateProject(ctx, &types.Project{Name: "project01", Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
if err.Error() != expectedErr { if err.Error() != expectedErr {
t.Fatalf("expected err %v, got err: %v", expectedErr, err) t.Fatalf("expected err %v, got err: %v", expectedErr, err)
} }
@ -488,7 +488,7 @@ func TestProjectGroupsAndProjects(t *testing.T) {
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
wg.Add(1) wg.Add(1)
go cs.ch.CreateProject(ctx, &types.Project{Name: "project02", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("user", user.Name)}, Visibility: types.VisibilityPublic}) go cs.ch.CreateProject(ctx, &types.Project{Name: "project02", Parent: types.Parent{Type: types.ConfigTypeProjectGroup, ID: path.Join("user", user.Name)}, Visibility: types.VisibilityPublic, RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeManual})
wg.Done() wg.Done()
} }
wg.Wait() wg.Wait()

View File

@ -99,6 +99,7 @@ func (c *CommandHandler) CreateProject(ctx context.Context, req *CreateProjectRe
ID: parentID, ID: parentID,
}, },
Visibility: req.Visibility, Visibility: req.Visibility,
RemoteRepositoryConfigType: types.RemoteRepositoryConfigTypeRemoteSource,
RemoteSourceID: rs.ID, RemoteSourceID: rs.ID,
LinkedAccountID: la.ID, LinkedAccountID: la.ID,
RepositoryID: repo.ID, RepositoryID: repo.ID,

View File

@ -148,6 +148,27 @@ type LinkedAccount struct {
Oauth2AccessTokenExpiresAt time.Time `json:"oauth_2_access_token_expires_at,omitempty"` Oauth2AccessTokenExpiresAt time.Time `json:"oauth_2_access_token_expires_at,omitempty"`
} }
// RemoteRepositoryConfigType defines how a remote repository is configured and
// managed. Currently only "remotesource" is supported.
// In future other config types (like a fully manual config) could be supported.
type RemoteRepositoryConfigType string
const (
// RemoteRepositoryConfigTypeManual is currently only used for tests and not available for direct usage
RemoteRepositoryConfigTypeManual RemoteRepositoryConfigType = "manual"
RemoteRepositoryConfigTypeRemoteSource RemoteRepositoryConfigType = "remotesource"
)
func IsValidRemoteRepositoryConfigType(t RemoteRepositoryConfigType) bool {
switch t {
case RemoteRepositoryConfigTypeManual:
case RemoteRepositoryConfigTypeRemoteSource:
default:
return false
}
return true
}
type Project struct { type Project struct {
// The type version. Increase when a breaking change is done. Usually not // The type version. Increase when a breaking change is done. Usually not
// needed when adding fields. // needed when adding fields.
@ -161,6 +182,8 @@ type Project struct {
Visibility Visibility `json:"visibility,omitempty"` Visibility Visibility `json:"visibility,omitempty"`
// Remote Repository fields // Remote Repository fields
RemoteRepositoryConfigType RemoteRepositoryConfigType `json:"remote_repository_config_type,omitempty"`
RemoteSourceID string `json:"remote_source_id,omitempty"` RemoteSourceID string `json:"remote_source_id,omitempty"`
LinkedAccountID string `json:"linked_account_id,omitempty"` LinkedAccountID string `json:"linked_account_id,omitempty"`