graphqlbackend: Support perforce changelists in repo and commits API#51291
Conversation
bd8c3c6 to
b90ebe8
Compare
b90ebe8 to
529c8d0
Compare
| // | ||
| // For depots converted with git-p4, this special handling is NOT required. | ||
| if r.repoResolver.IsPerforceDepot() && strings.HasPrefix(commit.Message.Body(), "[p4-fusion") { | ||
| subject, err := parseP4FusionCommitSubject(commit.Message.Subject()) |
There was a problem hiding this comment.
the graphqlbackend package is quite a behemoth already. I recommend new code which contains business logic to go somewhere else and then the resolver code is very very lightweight. Can you update this PR to put the P4 parsing coding and the other non-graphql specific p4 code somewhere else?
It also feels a bit weird to update just this graphql resolver to massage the subject. But I don't have a better suggestion, so I guess lets do this. But it almost feels like in some other layer you want to translate git commit messages into a different format. To me it feels like that should just be done in the p4-fusion layer? IE all you are doing is translating the message here, why not just write the mssage you want in p4-fusion? That does sound a bit more hectic to do though.
There was a problem hiding this comment.
Both git p4 and p4-fusion add Perforce information to the git commit message when they convert Perforce changelists to git commits. That information has traditionally simply been displayed to the user in the UI, but now we want to hide that information and show the user only the original commit message from Perforce. However, the extra information is important because it contains the Changelist Id, which we want to display in the UI instead of commit hashes, and also use in URLs as we go forward with these changes. We are hesitant to change p4-fusion and git p4 (we don't own those tools, we currently need that changelist info, and there are clients with already-synced depots, so they have this commit message format), so we need to parse the commit message (and subject, for ones generated by p4-fusion) at some point along the way to the UI; there might be a better place to do it, but I'm not sure we can push it any deeper than this.
There was a problem hiding this comment.
Ok I agree with that point. I still think p4 specific business logic like this should not live inside the graphql resolver for a git commit subject. It feels alright that we inject something here though that looks like this. IE I am mostly taking issue with strings.HasPrefix(commit.Message.Body(), "[p4-fusion") { living here. It feels like the impact on this function should be mostly a single function call which works something like
if subject, ok = maybeTransformP4Subject(ctx, r.repoResolver, commit.Message); ok {
return subject
}
Or alternatively based on slack discussions the design of this is gonna change a bit and the impl of subject resolver will live in something p4 specific.
There was a problem hiding this comment.
Yep, pushing that code you mentioned into a function would be prettier and more portable.
sashaostrikov
left a comment
There was a problem hiding this comment.
LGTM! Left a couple of comments.
| """ | ||
| Returns true if this git-repo was converted from a perforce depot. | ||
| """ | ||
| isPerforceDepot: Boolean! |
There was a problem hiding this comment.
I remember this being something @mrnugget was worried about happening (similarly to adding additional isX isY stuff to the Repository and other types)
There was a problem hiding this comment.
One idea we were thinking of having as an adopted pattern in the packages side of things was moving to union types across the graphql API, but thatd be a much bigger endeavor (for which we had a staffing proposal under the packages project, unfortunately not going ahead this quarter :/ )
There was a problem hiding this comment.
I agree with this concern. Should probably use the same suggestion I used here https://github.com/sourcegraph/sourcegraph/pull/51291#discussion_r1182834582
Is your suggestion something along the lines of my second suggestion? IE rely on ... on PerforceRepo {
There was a problem hiding this comment.
Is your suggestion something along the lines of my second suggestion? IE rely on
... on PerforceRepo {
I think along that line of thought, yea. In the client code you can switch on __typename and render differently (or different components entirely) based on that value to e.g. include or exclude stuff based on if its e.g. a PerforceRepo, PackageRepo, GitRepo etc
There was a problem hiding this comment.
That was originally the idea we had come up with in a chat between Philipp Spiess, Ryan Slade, Peter and myself. I had planned for it to be a subproject under this quarters packages project and for the idea to be picked up then by other non-git ecosystems, but alas no staffing.
There was a problem hiding this comment.
In our case, this might not be a trivial change though, since you'd probably need to change the "main case" (git repository) in all providers. But maybe there's a way around that and make it gradual, i.e. define a interface PerforceDepot and then implement ToPerforceDepot() on the type you have here and return new type when it's a perforce depot.
My only concern is if this is the right time to be implementing this as an interface, since at the root of it all its still a git repo that we're presenting as a perforce depot by only tweaking the commit message and the commit ref.
It would have made sense if we were to introduce perforce depots as first class citizens and then this change might have been a better time to do it then. What do you think?
There was a problem hiding this comment.
I think it's the same thing as what you already have: an enum and a field that's only valid if the enum has a certain value. Except that the interfaces hide that from you and are more GraphQL-native. With your solution you'd need to query this:
query {
repo(name: "foo/bar") {
# [...]
sourceType
perforceChangelist
}
}Then in your client-side application code you'd have to do this:
if (repo.sourceType === sourceType.PERFORCE_DEPOT) {
// use changelist
}With an interface you'd do this (I think, again, you'd need to confirm this):
query {
repo(name: "foo/bar") {
# [...]
... on PerforceDepot {
perforceChangelist
}
}
}And you'd get a PerforceDepot type in your client-side code.
What I'm unsure about is whether repo needs to be an interface for that or not. If it has to be an interface, then it's a lot of work to change, I think.
There was a problem hiding this comment.
Okay - let me give this a try and come back with what I find out.
There was a problem hiding this comment.
Yes, I think in order to do that it would have to become an interface. I think further down the line it would be very nice to have a interface Repository and then two types type Depot and type GitRepository that will host non-common fields to have a proper abstraction. I don't know when we want to go more first class here, as long as it's only the 2 or 3 fields here that are depot specific, I think we can live with that for now, but we should account for a more complex migration of the API further down the line.
There was a problem hiding this comment.
Summary based on quick chat on Slack
Here's the ideal scenario:
- Define a new interface (say
Source) with the common fields between a perforce depot and a git repo - Update repo to implement this
Sourceinterface and add fields like stars , fork etc - Create a new type that also implements
Sourceand adds theperforceChangeliston top of it
But this is likely going to be a big change (especially in the client) and out of scope of this PR but should be on our minds to get around to eventually.
For the time being - the approach of enum should be good to go ahead with.
Bundle size report 📦
Look at the Statoscope report for a full comparison between the commits ba38eea and e08c73f or learn more. Open explanation
|
| // If parsing this commit message fails for some reason, log the reason and fall-through | ||
| // to return the the original git-commit's subject instead of a hard failure or an empty | ||
| // subject. | ||
| r.logger.Error("failed to parse p4 fusio commit subject", log.Error(err)) |
There was a problem hiding this comment.
| r.logger.Error("failed to parse p4 fusio commit subject", log.Error(err)) | |
| r.logger.Error("failed to parse p4 fusion commit subject", log.Error(err)) |
keegancsmith
left a comment
There was a problem hiding this comment.
backend LGTM. some comments inline, I'll leave it to the people who had the further slack discussion on API design to approve the graphql schema changes.
| // | ||
| // For depots converted with git-p4, this special handling is NOT required. | ||
| if r.repoResolver.IsPerforceDepot() && strings.HasPrefix(commit.Message.Body(), "[p4-fusion") { | ||
| subject, err := parseP4FusionCommitSubject(commit.Message.Subject()) |
There was a problem hiding this comment.
Ok I agree with that point. I still think p4 specific business logic like this should not live inside the graphql resolver for a git commit subject. It feels alright that we inject something here though that looks like this. IE I am mostly taking issue with strings.HasPrefix(commit.Message.Body(), "[p4-fusion") { living here. It feels like the impact on this function should be mostly a single function call which works something like
if subject, ok = maybeTransformP4Subject(ctx, r.repoResolver, commit.Message); ok {
return subject
}
Or alternatively based on slack discussions the design of this is gonna change a bit and the impl of subject resolver will live in something p4 specific.
| return nil, errors.Wrap(err, "failed to retrieve innerRepo") | ||
| } | ||
|
|
||
| if r.innerRepo.ExternalRepo.ServiceType == extsvc.TypePerforce { |
There was a problem hiding this comment.
use the returned repo above, don't directly access innerRepo
The changes in this PR were extracted from #51195 and then iterated upon once it was clear they need to be in a separate PR.
Why
We want to show changelist IDs for perforce depots in the UI instead of commit SHAs which is meaningless for a perforce user.
What
In this PR, we introduce some growing changes to the repository and commits APIs. They are:
booleanenumisPerforceDepotsourceTypeto the Repositories API to differentiate between a Perforce depot converted to a git repo vs a native git repoperforceChangelistto the Commits APIBodyof of the commit object to return the original changelist message as theSubjectBodyAPI will now returnnilbecause we do not want to show the metadata that was added to the commit's body during the conversion of the perforce changelist to a git commitTest plan