-
Notifications
You must be signed in to change notification settings - Fork 127
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
EVG-13846 EVG-13946 Mainline commits query and graphql resolver. #4663
Conversation
rest/data/task.go
Outdated
@@ -242,8 +242,8 @@ func (tc *DBTaskConnector) GetManifestByTask(taskId string) (*manifest.Manifest, | |||
|
|||
// FindTasksByVersion gets all tasks for a specific version | |||
// Results can be filtered by task name, variant name and status in addition to being paginated and limited | |||
func (tc *DBTaskConnector) FindTasksByVersion(versionID string, statuses []string, baseStatuses []string, variant string, taskName string, page, limit int, fieldsToProject []string, sorts []task.TasksSortOrder) ([]task.Task, int, error) { | |||
tasks, total, err := task.GetTasksByVersion(versionID, sorts, statuses, baseStatuses, variant, taskName, page, limit, fieldsToProject) | |||
func (tc *DBTaskConnector) FindTasksByVersion(versionID string, statuses []string, baseStatuses []string, variants []string, taskNames []string, page, limit int, fieldsToProject []string, sorts []task.TasksSortOrder) ([]task.Task, int, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry to ask this of you because it probably should've been done before, but this is getting really unwieldy; having this many inputs of the same type so close together introduces a lot of room for programmer error, and makes code very difficult to read. Would you mind refactoring this to take in a filterOptions struct or something, that contains the fields between statuses -> sort? (This also means you should just be able to pass in a blank filterOptions struct when you don't want to filter, which would be nice)
graphql/util.go
Outdated
ets = append(ets, &at) | ||
} | ||
result.ExecutionTasksFull = ets | ||
func GenerateBuildVariants(ctx context.Context, sc data.Connector, versionId string, searchVariants []string, searchTasks []string, statuses []string) ([]*GroupedBuildVariant, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this function can just be private (otherwise I'd probably ask that input is given with a struct rather than a collection of string maps, and a function header is provided)
graphql/util.go
Outdated
} | ||
tasks, _, err := sc.FindTasksByVersion(versionId, statuses, []string{}, searchVariants, searchTasks, 0, 0, []string{}, defaultSort) | ||
if err != nil { | ||
return nil, InternalServerError.Send(ctx, fmt.Sprintf("Error getting tasks for patch `%s`: %s", versionId, err)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want this to be err.Error() explicitly?
graphql/util.go
Outdated
if err != nil { | ||
return nil, InternalServerError.Send(ctx, fmt.Sprintf("Error getting tasks for patch `%s`: %s", versionId, err)) | ||
} | ||
for _, task := range tasks { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: avoid package names as variable names (in case we ever need to use the task collection in this function)
graphql/models_gen.go
Outdated
Tasks []*model.APITask `json:"tasks"` | ||
type MainlineCommit struct { | ||
Version *model.APIVersion `json:"version"` | ||
VersionMeta *VersionMeta `json:"versionMeta"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we call this versionMetadata instead? "meta" doesn't really mean anything and I'm not sure there's a reason to shorten it. Also, I'm wondering why you want a separate struct for metadata, as opposed to just filling out the necessary fields in the APIVersion. Is this to differentiate activated from not activated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes this is so the buildVariant resolver isn't used on non activated versions. Since the types are different we're able to be explicit about what fields activated and non activated versions accept. I may try to build out a better type for this though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to implement something like this.
type MainlineCommit {
version: Version
}
union Version = UnactivatedVersion | ActivatedVersion
type UnactivatedVersion {
id: String!
createTime: Time!
message: String!
revision: String!
author: String!
status: String!
order: Int!
repo: String!
project: String!
branch: String!
requester: String!
activated: Boolean
}
type ActivatedVersion {
startTime: Time!
finishTime: Time!
buildVariants(options: BuildVariantOptions): [GroupedBuildVariant]
}
But a bug in gqlgen does not allow me to generate the types for union types and interfaces. So i think this is the best we can do for now.
model/version.go
Outdated
if err := db.Aggregate(VersionCollection, pipeline, &res); err != nil { | ||
return nil, errors.Wrapf(err, "error aggregating versions and builds") | ||
} | ||
if len(res) == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This if statement seems unnecessary
model/version.go
Outdated
res := []Version{} | ||
|
||
if err := db.Aggregate(VersionCollection, pipeline, &res); err != nil { | ||
return nil, errors.Wrapf(err, "error aggregating versions and builds") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't get builds, right? Since it's only aggregating on the VersionCollection. Do you want it to also be getting builds?
@@ -824,7 +824,17 @@ func (r *patchResolver) TaskStatuses(ctx context.Context, obj *restModel.APIPatc | |||
defaultSort := []task.TasksSortOrder{ | |||
{Key: task.DisplayNameKey, Order: 1}, | |||
} | |||
tasks, _, err := r.sc.FindTasksByVersion(*obj.Id, []string{}, []string{}, "", "", 0, 0, []string{task.DisplayStatusKey}, defaultSort) | |||
opts := data.TaskFilterOptions{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So you don't need to specify any of the fields you don't want to use; the defaults are the same as what you're specifying.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So what I meant here was that you should just say:
opts := data.TaskFilterOptions{Sorts: defaultSort}
rather than specifying the fields that you don't need (same below).
model/task/task.go
Outdated
var match bson.M = bson.M{} | ||
|
||
// Allow searching by either variant name or variant display | ||
if variant != "" { | ||
if len(variants) > 0 { | ||
variantsAsRegex := strings.Join(variants[:], "|") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this [:] for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^
graphql/resolvers.go
Outdated
var mainlineCommits MainlineCommits | ||
activatedVersionCount := 0 | ||
for _, v := range versions { | ||
if activatedVersionCount == *options.Limit { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
preference for FromIntPointer since it's safer
Do we set options.Limit to the default anywhere here or is this potentially just 0?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This hasn't been answered/updated ^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right i assumed gqlgen would have automatically applied a default for this field based off of us supplying a default value in the schema definition. But that doesn't appear to be the case.
model/version.go
Outdated
Activated bool `json:"activated"` | ||
} | ||
|
||
func GetMainlineCommitVersionsWithOptions(projectName string, opts MainlineCommitVersionOptions) ([]Version, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we write a unit test for this? I'd like to know if you have a Version inserted with Activated not set, if it gets included in the Activated: false match; otherwise if Activated is false we likely want to query both "false and unset"
@@ -824,7 +824,17 @@ func (r *patchResolver) TaskStatuses(ctx context.Context, obj *restModel.APIPatc | |||
defaultSort := []task.TasksSortOrder{ | |||
{Key: task.DisplayNameKey, Order: 1}, | |||
} | |||
tasks, _, err := r.sc.FindTasksByVersion(*obj.Id, []string{}, []string{}, "", "", 0, 0, []string{task.DisplayStatusKey}, defaultSort) | |||
opts := data.TaskFilterOptions{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So what I meant here was that you should just say:
opts := data.TaskFilterOptions{Sorts: defaultSort}
rather than specifying the fields that you don't need (same below).
graphql/resolvers.go
Outdated
|
||
activatedVersions, err := model.GetMainlineCommitVersionsWithOptions(projectId, opts) | ||
if err != nil { | ||
return nil, ResourceNotFound.Send(ctx, fmt.Sprintf("Error getting activated versions %s", err.Error())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be versions: %s
I think (same below)
graphql/resolvers.go
Outdated
var mainlineCommits MainlineCommits | ||
activatedVersionCount := 0 | ||
for _, v := range versions { | ||
if activatedVersionCount == *options.Limit { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This hasn't been answered/updated ^
} else { | ||
if len(mainlineCommits.Versions) > 0 { | ||
lastMainlineCommit := mainlineCommits.Versions[len(mainlineCommits.Versions)-1] | ||
if lastMainlineCommit.RolledUpVersions != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can check this in the go playground, but I believe appending to a nil list does work. Otherwise if there aren't any rolled up versions yet, you're just omitting the version completely at this step.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I added some comments and moved the logic a little bit to make it more clear what this is doing. I also updated the graphql tests to capture this scenario.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for these comments!
defaultSort := []task.TasksSortOrder{ | ||
{Key: task.DisplayNameKey, Order: 1}, | ||
} | ||
opts := data.TaskFilterOptions{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here: only need to specify Statuses, Variants, TaskNames, Sorts
graphql/resolvers.go
Outdated
if options.Statuses != nil { | ||
statuses = options.Statuses | ||
} | ||
return generateBuildVariants(ctx, r.sc, *v.Id, variants, tasks, statuses) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can just pass in each option directly; there's no reason we can't pass in a nil list.
model/task/task.go
Outdated
var match bson.M = bson.M{} | ||
|
||
// Allow searching by either variant name or variant display | ||
if variant != "" { | ||
if len(variants) > 0 { | ||
variantsAsRegex := strings.Join(variants[:], "|") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a couple last thoughts from me!
} else { | ||
if len(mainlineCommits.Versions) > 0 { | ||
lastMainlineCommit := mainlineCommits.Versions[len(mainlineCommits.Versions)-1] | ||
if lastMainlineCommit.RolledUpVersions != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for these comments!
model/version.go
Outdated
@@ -138,7 +139,7 @@ func (self *Version) SetActivated() error { | |||
} | |||
|
|||
func (self *Version) SetNotActivated() error { | |||
if utility.FromBoolPtr(self.Activated) { | |||
if self.Activated != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's possible we'll want to use this function in the future to set something from activated to not activated (not sure why but it does seem like something that should work), so I think we only really want to return early if self.Activated is set explicitly to False already (you can use !utility.FromBoolTPtr to this, because the nil case defaults to true)
|
||
// Returns grouped build variants for a version. Will not return build variants for unactivated versions | ||
func (r *versionResolver) BuildVariants(ctx context.Context, v *restModel.APIVersion, options *BuildVariantOptions) ([]*GroupedBuildVariant, error) { | ||
if !utility.FromBoolPtr(v.Activated) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to consider if v.Activated is nil, or would the MainlineCommits resolver have been guaranteed to hit first?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case it is guaranteed to have been hit first. But if we want to use this for another field that returns a version it is not guaranteed. There will be a super low likely hood of it being nil unless someone decides to view a really old version that was introduced before this field (v.Activated) was introduced
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added handling for that edge case just in case.
model/version.go
Outdated
@@ -139,7 +139,7 @@ func (self *Version) SetActivated() error { | |||
} | |||
|
|||
func (self *Version) SetNotActivated() error { | |||
if self.Activated != nil { | |||
if !utility.FromBoolPtr(self.Activated) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I suggested FromBoolTPtr here because right now, nil will default to false, which means that if self.Activated is false we'll exit early. FromBoolTPtr defaults nil to true, meaning we'll continue on to the update, which is what we want to happen (sorry about all the semantics, bool pointers are weird to reason about)
This is the main query for the mainline commits page.
This supports most of the core features of the mainline commits flow.
$limit
and$skip
This notably does not include the ability to filter by test results. After speaking with @julianedwards it seems like it might be considerably more difficult to implement than expected so that is being held off for a later discussion.