[ROX-12622] : Add create collection service#3600
Conversation
keyallis
left a comment
There was a problem hiding this comment.
The service layer should be pretty slim, left some review comments of things you can trim down on since the functionality will be in the datastore layer
| nameQuery := search.NewQueryBuilder().AddExactMatches(search.CollectionName, request.GetName()).ProtoQuery() | ||
| c, err := s.datastore.Count(ctx, nameQuery) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| if c != 0 { | ||
| return nil, errors.Wrap(errox.AlreadyExists, "A collection with that name already exists") | ||
| } |
There was a problem hiding this comment.
Datastore level will certify that the name is not already in use
| } | ||
|
|
||
| collection := &storage.ResourceCollection{ | ||
| Id: uuid.NewV4().String(), |
There was a problem hiding this comment.
Datastore level will assign the ID
There was a problem hiding this comment.
Don't see that in datastore's AddCollection implementation. Is that something currently in review? Or I can add this line to datastore level.
| CreatedAt: protoconv.ConvertTimeToTimestamp(time.Now()), | ||
| CreatedBy: creator, |
There was a problem hiding this comment.
We should also be setting the UpdatedAt and UpdatedBy fields
| collection := &storage.ResourceCollection{ | ||
| Id: uuid.NewV4().String(), | ||
| Name: request.GetName(), | ||
| Description: request.GetDescription(), | ||
| CreatedAt: protoconv.ConvertTimeToTimestamp(time.Now()), | ||
| CreatedBy: creator, | ||
| ResourceSelectors: request.GetResourceSelectors(), | ||
| } |
There was a problem hiding this comment.
This creation could be abstracted out into standalone function that takes a request object and just fills out the fields since that operation will need to be done for Add, DryRun, Update
|
|
||
| return &storage.SlimUser{ | ||
| Id: ctxIdentity.UID(), | ||
| Name: ctxIdentity.FullName(), |
There was a problem hiding this comment.
There is func for that UserFromContext(ctx context.Context)
|
Images are ready for the commit at d1cf445. To use with deploy scripts, first |
997d790 to
a866a3e
Compare
keyallis
left a comment
There was a problem hiding this comment.
Just one comment on future work but otherwise LGTM
| func collectionRequestToCollection(ctx context.Context, request collectionRequest, isCreate bool) (*storage.ResourceCollection, error) { | ||
| user := utils.UserFromContext(ctx) | ||
| if user == nil { | ||
| return nil, errors.New("User identity not provided") | ||
| } | ||
|
|
||
| timeNow := protoconv.ConvertTimeToTimestamp(time.Now()) | ||
|
|
||
| collection := &storage.ResourceCollection{ | ||
| Name: request.GetName(), | ||
| Description: request.GetDescription(), | ||
| LastUpdated: timeNow, | ||
| UpdatedBy: user, | ||
| ResourceSelectors: request.GetResourceSelectors(), | ||
| } |
There was a problem hiding this comment.
You might have to do some digging to figure out a clean way of doing this (maybe generics!) but for future work we need to also include the Id field population in the object creation for Update requests
There was a problem hiding this comment.
Ah yes, I will add an idGetter func() string to the arguments, and update can then pass func() {return request.GetId() } as that func.
About generics, I thought to do it using generics, but since we are calling functions GetName, GetDescription, etc on the passed obj, if I call them on generic type R, compilation would fail. The solution for that is to constrain the generic type using an interface. In this case the constraining interface would look like
type collectionRequest interface {
*v1.CreateCollectionRequest | *v1.DryRunCollectionRequest | *v1.UpdateCollectionRequest
}
func [R collectionRequest] collectionRequestToCollection(ctx context, request R, isCreate bool) {...}
However, this fails to compile too since go 1.18 doesn't have a good support for union of structs within a constraining interface. Since *v1.CreateCollectionRequest and others are structs and not interfaces themselves, GetName, etc are still not accessible from collectionRequest.
The workaround for this is to then add those methods to the constraining interface as well, which makes the interface look like
type collectionRequest interface {
*v1.CreateCollectionRequest
GetName() string
GetDescription() string
GetResourceSelectors() []*storage.ResourceSelector
GetEmbeddedCollectionIds() []string
}
This works, but then I still had to put those methods in the interface. so I just avoided the generics altogether for this case.
md2119
left a comment
There was a problem hiding this comment.
Some minor comments, otherwise lgtm.
| // CreateCollection creates a new collection from the given request | ||
| func (s *serviceImpl) CreateCollection(ctx context.Context, request *v1.CreateCollectionRequest) (*v1.CreateCollectionResponse, error) { | ||
| if !features.ObjectCollections.Enabled() { | ||
| return nil, nil |
| func collectionRequestToCollection(ctx context.Context, request collectionRequest, isCreate bool) (*storage.ResourceCollection, error) { | ||
| user := utils.UserFromContext(ctx) | ||
| if user == nil { | ||
| return nil, errors.New("User identity not provided") |
There was a problem hiding this comment.
because it implied from the context could not determine user identity seems more appropriate
| Description: request.GetDescription(), | ||
| LastUpdated: timeNow, | ||
| UpdatedBy: user, | ||
| ResourceSelectors: request.GetResourceSelectors(), |
There was a problem hiding this comment.
valid resource selectors are non-empty
There was a problem hiding this comment.
Added an error on empty resource selectors.
| if request.GetName() == "" { | ||
| return nil, errors.Wrap(errox.InvalidArgs, "Collection name should not be empty") | ||
| } |
There was a problem hiding this comment.
move this into collectionRequestToCollection(...)
a866a3e to
d1cf445
Compare
| } else if getID != nil { | ||
| collection.Id = getID() | ||
| } |
There was a problem hiding this comment.
implying getID wrt isCreate is confusing. Strongly recommend dropping this check and explicitly basing the condition on create and update workflow.
There was a problem hiding this comment.
Ah, I merged this PR before noticing the comment. I will add it in my Delete collections service PR. Should I change to something like below?
if isCreate {...}
else {
if getID == nil {
return nil, err
}
collection.Id = getID()
}
Description
Added REST endpoint for creating a new collection.
Checklist
[ ] Evaluated and added CHANGELOG entry if required[ ] Determined and documented upgrade stepsIf any of these don't apply, please comment below.
Testing Performed
Unit tests added.
In addition to reviewing your code, reviewers must also review your testing
instructions and make sure they are sufficient.