Files
genqlient/generate/description.go
T
Ben Kraft e88305ecbd Add support for abstract-typed named fragments (#79)
## Summary:
In previous commits I added support to genqlient for interfaces,
inline fragments, and, most recently, named fragments of concrete
(object) type.  This leaves only named fragments of interface type!
Like other named fragments, these are useful for code-sharing,
especially if you want some code that can handle the same fields of
several different types.

As seems to be inevitable with genqlient, this was mostly pretty
straightforward, although there turned out to be surprisingly many
places we needed to add some handling; almost anywhere that touches
interfaces *or* named fragments needed some updates.  But it's all
hopefully fairly clear code.

As a part of this change I made three semi-related improvements:
1. I refactored the handling of descriptions (i.e. GoDoc), because it
   was getting more and more confusing and duplicative.  I'm still not
   sure how much of it it makes sense to inline vs. separate, but I
   think this is better than it was.  This resulted in some minor
   changes to descriptions, generally in the direction of making things
   more consistent.
2. I bumped the minimum Go version to 1.14 so we can guarantee support
   for duplicate interface methods.  These are useful for
   abstract-in-absstract spreads; we generate an interface for the
   fragment, and (if the fragment-type implements the scope-type) we
   embed it into the interface we generate for its spread-context, and
   if the two have a duplicated field we thus duplicate the method.  It
   wouldn't be impossible to support this on 1.13 (maybe just by
   omitting said embed) but it didn't seem worth it.  This also removes
   a few special-cases in tests.
3. I added a bunch of code to better format syntax errors in the
   generated code (which we see from `gofmt`).  This is mostly just an
   internal improvement; I wrote it because I got annoyed while hunting
   down a few such errors..

Fixes, at last, #8.

Issue: https://github.com/Khan/genqlient/issues/8

## Test plan:
make check


Author: benjaminjkraft

Reviewers: dnerdy, benjaminjkraft, aberkan, MiguelCastillo

Required Reviewers: 

Approved By: dnerdy

Checks:  Lint,  Test (1.17),  Test (1.16),  Test (1.15),  Test (1.14),  Test (1.17),  Test (1.16),  Test (1.15),  Test (1.14),  Lint

Pull Request URL: https://github.com/Khan/genqlient/pull/79
2021-09-09 09:48:18 -07:00

82 lines
2.7 KiB
Go

package generate
// Code relating to generating GoDoc from GraphQL descriptions.
//
// For fields, and types where we just copy the "whole" type (enum and
// input-object), this is easy: we just use the GraphQL description. But for
// struct types, there are often more useful things we can say.
import (
"fmt"
"strings"
)
// descriptionInfo is embedded in types whose descriptions may be more complex
// than just a copy of the GraphQL doc.
type descriptionInfo struct {
// user-specified comment for this type
CommentOverride string
// name of the corresponding GraphQL type
GraphQLName string
// GraphQL description of the type .GraphQLName, if any
GraphQLDescription string
// name of the corresponding GraphQL fragment (on .GraphQLName), if any
FragmentName string
}
func maybeAddTypeDescription(info descriptionInfo, description string) string {
if info.GraphQLDescription == "" {
return description
}
return fmt.Sprintf(
"%v\nThe GraphQL type's documentation follows.\n\n%v",
description, info.GraphQLDescription)
}
func fragmentDescription(info descriptionInfo) string {
return maybeAddTypeDescription(info, fmt.Sprintf(
"%v includes the GraphQL fields of %v requested by the fragment %v.",
info.FragmentName, info.GraphQLName, info.FragmentName))
}
func structDescription(typ *goStructType) string {
switch {
case typ.CommentOverride != "":
return typ.CommentOverride
case typ.IsInput:
// Input types have all their fields, just use the GraphQL description.
return typ.GraphQLDescription
case typ.FragmentName != "":
return fragmentDescription(typ.descriptionInfo)
default:
// For types where we only have some fields, note that, along with
// the GraphQL documentation (if any). We don't want to just use
// the GraphQL documentation, since it may refer to fields we
// haven't selected, say.
return maybeAddTypeDescription(typ.descriptionInfo, fmt.Sprintf(
"%v includes the requested fields of the GraphQL type %v.",
typ.GoName, typ.GraphQLName))
}
}
func interfaceDescription(typ *goInterfaceType) string {
goImplNames := make([]string, len(typ.Implementations))
for i, impl := range typ.Implementations {
goImplNames[i] = impl.Reference()
}
implementationList := fmt.Sprintf(
"\n\n%v is implemented by the following types:\n\t%v",
typ.GoName, strings.Join(goImplNames, "\n\t"))
switch {
case typ.CommentOverride != "":
return typ.CommentOverride + implementationList
case typ.FragmentName != "":
return fragmentDescription(typ.descriptionInfo) + implementationList
default:
return maybeAddTypeDescription(typ.descriptionInfo, fmt.Sprintf(
"%v includes the requested fields of the GraphQL interface %v.%v",
typ.GoName, typ.GraphQLName, implementationList))
}
}