Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions internal/sparsefieldsgen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@
// that key from the entity's own marked *ListResponse, so a get and its
// list can never disagree.
//
// A fourth marker applies to individual fields of a marked response:
// Two more markers apply to individual fields of a marked response:
//
// - sparsefields:skip
// Excludes the field from slot generation. Used when a response's main
// slice holds relationships rather than a sparse-fields-capable entity
// (e.g. SearchResponse.Items).
//
// - sparsefields:key=entityName
// Overrides the fields[...] entity key for that slot. For list slots the
Expand Down Expand Up @@ -71,6 +76,7 @@ const (
markerList = "sparsefields:list"
markerGet = "sparsefields:get"
markerKey = "sparsefields:key"
markerSkip = "sparsefields:skip"

rootImportPath = "github.com/teamwork/twapi-go-sdk"
rootImportName = "twapi"
Expand Down Expand Up @@ -721,7 +727,7 @@ func extractFields(
func extractSlots(st *ast.StructType, ownerName string, fieldTypeOf map[string]string) ([]slot, error) {
var slots []slot
for _, field := range st.Fields.List {
if len(field.Names) == 0 || field.Tag == nil {
if len(field.Names) == 0 || field.Tag == nil || hasSkipMarker(field) {
continue
}
tagText, err := strconv.Unquote(field.Tag.Value)
Expand Down Expand Up @@ -789,7 +795,7 @@ func extractSlots(st *ast.StructType, ownerName string, fieldTypeOf map[string]s
func extractGetSlots(st *ast.StructType, ownerName string, fieldTypeOf, entityKeyOf map[string]string) ([]slot, error) {
var slots []slot
for _, field := range st.Fields.List {
if len(field.Names) == 0 || field.Tag == nil {
if len(field.Names) == 0 || field.Tag == nil || hasSkipMarker(field) {
continue
}
tagText, err := strconv.Unquote(field.Tag.Value)
Expand Down Expand Up @@ -847,7 +853,7 @@ func extractGetSlots(st *ast.StructType, ownerName string, fieldTypeOf, entityKe
func extractIncludedSlots(st *ast.StructType, ownerName string, fieldTypeOf map[string]string) ([]slot, error) {
var slots []slot
for _, field := range st.Fields.List {
if len(field.Names) == 0 || field.Tag == nil {
if len(field.Names) == 0 || field.Tag == nil || hasSkipMarker(field) {
continue
}
mt, ok := field.Type.(*ast.MapType)
Expand Down Expand Up @@ -886,6 +892,17 @@ func extractIncludedSlots(st *ast.StructType, ownerName string, fieldTypeOf map[
return slots, nil
}

// hasSkipMarker reports whether a field opts out of slot generation via
// `sparsefields:skip` on its doc or trailing comment.
func hasSkipMarker(field *ast.Field) bool {
for _, doc := range []*ast.CommentGroup{field.Doc, field.Comment} {
if _, ok := markerOverride(doc, markerSkip, ""); ok {
return true
}
}
return false
}

// elementIdent returns an unqualified ident for a same-package type reference,
// stripping pointer wrappers. Returns false for qualified types (e.g.
// twapi.Relationship) and structural types (slice-of-slice, etc.).
Expand Down
2 changes: 2 additions & 0 deletions projects/comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ type Comment struct {

// CommentSideload contains minimal information about a comment, used for
// sideloading in other API responses.
//
// sparsefields:gen
type CommentSideload struct {
// ID is the unique identifier of the comment.
ID int64 `json:"id"`
Expand Down
9 changes: 9 additions & 0 deletions projects/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ type SearchRequestFilters struct {
// Include contains additional related information to include in the response
// as a sideload.
Include []SearchRequestSideload

// Fields selects sparse fieldsets for the sideloaded entities. Leave a slot
// empty to receive the API default for that entity.
Fields SearchFields
}

func (s SearchRequestFilters) apply(req *http.Request) {
Expand Down Expand Up @@ -156,6 +160,7 @@ func (s SearchRequestFilters) apply(req *http.Request) {
}
query.Set("include", strings.Join(include, ","))
}
s.Fields.apply(query)

// By default the API returns results ordered by updated date. To ensure we
// get the most relevant results, we set the orderBy parameter to relevance.
Expand Down Expand Up @@ -201,6 +206,8 @@ func (s SearchRequest) HTTPRequest(ctx context.Context, server string) (*http.Re
// SearchResponse contains search results matching the request filters.
//
// https://apidocs.teamwork.com/docs/teamwork/v3/search/get-projects-api-v3-search-json
//
// sparsefields:list
type SearchResponse struct {
request SearchRequest

Expand All @@ -213,6 +220,8 @@ type SearchResponse struct {
// Items is the list of search results matching the request filters. Each item
// contains a relationship to the actual item, which can be found as a
// sideload.
//
// sparsefields:skip
Items []SearchItem `json:"search"`

// Included contains related objects included in the response.
Expand Down
58 changes: 58 additions & 0 deletions projects/sparse_fields_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

53 changes: 53 additions & 0 deletions projects/sparse_fields_gen_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.