fix(forge): correct generated schema aliases
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
dba9852567
commit
d553cbaa2d
14 changed files with 160 additions and 53 deletions
|
|
@ -39,6 +39,9 @@ type SpecInfo struct {
|
|||
// _ = SchemaDefinition{Type: "object"}
|
||||
type SchemaDefinition struct {
|
||||
Description string `json:"description"`
|
||||
Format string `json:"format"`
|
||||
Ref string `json:"$ref"`
|
||||
Items *SchemaProperty `json:"items"`
|
||||
Type string `json:"type"`
|
||||
Properties map[string]SchemaProperty `json:"properties"`
|
||||
Required []string `json:"required"`
|
||||
|
|
@ -140,9 +143,10 @@ func ExtractTypes(spec *Spec) map[string]*GoType {
|
|||
result[name] = gt
|
||||
continue
|
||||
}
|
||||
if len(def.Properties) == 0 && def.AdditionalProperties != nil {
|
||||
|
||||
if aliasType, ok := definitionAliasType(def, spec.Definitions); ok {
|
||||
gt.IsAlias = true
|
||||
gt.AliasType = resolveMapType(*def.AdditionalProperties)
|
||||
gt.AliasType = aliasType
|
||||
result[name] = gt
|
||||
continue
|
||||
}
|
||||
|
|
@ -157,7 +161,7 @@ func ExtractTypes(spec *Spec) map[string]*GoType {
|
|||
}
|
||||
gf := GoField{
|
||||
GoName: goName,
|
||||
GoType: resolveGoType(prop),
|
||||
GoType: resolveGoType(prop, spec.Definitions),
|
||||
JSONName: fieldName,
|
||||
Comment: prop.Description,
|
||||
Required: required[fieldName],
|
||||
|
|
@ -172,6 +176,46 @@ func ExtractTypes(spec *Spec) map[string]*GoType {
|
|||
return result
|
||||
}
|
||||
|
||||
func definitionAliasType(def SchemaDefinition, defs map[string]SchemaDefinition) (string, bool) {
|
||||
if def.Ref != "" {
|
||||
return refName(def.Ref), true
|
||||
}
|
||||
|
||||
switch def.Type {
|
||||
case "string":
|
||||
return "string", true
|
||||
case "integer":
|
||||
switch def.Format {
|
||||
case "int64":
|
||||
return "int64", true
|
||||
case "int32":
|
||||
return "int32", true
|
||||
default:
|
||||
return "int", true
|
||||
}
|
||||
case "number":
|
||||
switch def.Format {
|
||||
case "float":
|
||||
return "float32", true
|
||||
default:
|
||||
return "float64", true
|
||||
}
|
||||
case "boolean":
|
||||
return "bool", true
|
||||
case "array":
|
||||
if def.Items != nil {
|
||||
return "[]" + resolveGoType(*def.Items, defs), true
|
||||
}
|
||||
return "[]any", true
|
||||
case "object":
|
||||
if def.AdditionalProperties != nil {
|
||||
return resolveMapType(*def.AdditionalProperties, defs), true
|
||||
}
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
// DetectCRUDPairs finds Create*Option / Edit*Option pairs in the swagger definitions
|
||||
// and maps them back to the base type name.
|
||||
//
|
||||
|
|
@ -201,10 +245,9 @@ func DetectCRUDPairs(spec *Spec) []CRUDPair {
|
|||
}
|
||||
|
||||
// resolveGoType maps a swagger schema property to a Go type string.
|
||||
func resolveGoType(prop SchemaProperty) string {
|
||||
func resolveGoType(prop SchemaProperty, defs map[string]SchemaDefinition) string {
|
||||
if prop.Ref != "" {
|
||||
parts := core.Split(prop.Ref, "/")
|
||||
return "*" + parts[len(parts)-1]
|
||||
return refGoType(prop.Ref, defs)
|
||||
}
|
||||
switch prop.Type {
|
||||
case "string":
|
||||
|
|
@ -236,25 +279,59 @@ func resolveGoType(prop SchemaProperty) string {
|
|||
return "bool"
|
||||
case "array":
|
||||
if prop.Items != nil {
|
||||
return "[]" + resolveGoType(*prop.Items)
|
||||
return "[]" + resolveGoType(*prop.Items, defs)
|
||||
}
|
||||
return "[]any"
|
||||
case "object":
|
||||
return resolveMapType(prop)
|
||||
return resolveMapType(prop, defs)
|
||||
default:
|
||||
return "any"
|
||||
}
|
||||
}
|
||||
|
||||
// resolveMapType maps a swagger object with additionalProperties to a Go map type.
|
||||
func resolveMapType(prop SchemaProperty) string {
|
||||
func resolveMapType(prop SchemaProperty, defs map[string]SchemaDefinition) string {
|
||||
valueType := "any"
|
||||
if prop.AdditionalProperties != nil {
|
||||
valueType = resolveGoType(*prop.AdditionalProperties)
|
||||
valueType = resolveGoType(*prop.AdditionalProperties, defs)
|
||||
}
|
||||
return "map[string]" + valueType
|
||||
}
|
||||
|
||||
func refName(ref string) string {
|
||||
parts := core.Split(ref, "/")
|
||||
return parts[len(parts)-1]
|
||||
}
|
||||
|
||||
func refGoType(ref string, defs map[string]SchemaDefinition) string {
|
||||
name := refName(ref)
|
||||
def, ok := defs[name]
|
||||
if !ok {
|
||||
return "*" + name
|
||||
}
|
||||
if definitionNeedsPointer(def) {
|
||||
return "*" + name
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func definitionNeedsPointer(def SchemaDefinition) bool {
|
||||
if len(def.Enum) > 0 {
|
||||
return false
|
||||
}
|
||||
if def.Ref != "" {
|
||||
return false
|
||||
}
|
||||
switch def.Type {
|
||||
case "string", "integer", "number", "boolean", "array":
|
||||
return false
|
||||
case "object":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// pascalCase converts a snake_case or kebab-case string to PascalCase,
|
||||
// with common acronyms kept uppercase.
|
||||
func pascalCase(s string) string {
|
||||
|
|
|
|||
|
|
@ -124,3 +124,46 @@ func TestParser_AdditionalPropertiesAlias_Good(t *testing.T) {
|
|||
t.Fatalf("got alias type %q, want map[string]any", alias.AliasType)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParser_PrimitiveAndCollectionAliases_Good(t *testing.T) {
|
||||
spec, err := LoadSpec("../../testdata/swagger.v1.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
types := ExtractTypes(spec)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
wantType string
|
||||
}{
|
||||
{name: "CommitStatusState", wantType: "string"},
|
||||
{name: "IssueFormFieldType", wantType: "string"},
|
||||
{name: "IssueFormFieldVisible", wantType: "string"},
|
||||
{name: "NotifySubjectType", wantType: "string"},
|
||||
{name: "ReviewStateType", wantType: "string"},
|
||||
{name: "StateType", wantType: "string"},
|
||||
{name: "TimeStamp", wantType: "int64"},
|
||||
{name: "IssueTemplateLabels", wantType: "[]string"},
|
||||
{name: "QuotaGroupList", wantType: "[]*QuotaGroup"},
|
||||
{name: "QuotaUsedArtifactList", wantType: "[]*QuotaUsedArtifact"},
|
||||
{name: "QuotaUsedAttachmentList", wantType: "[]*QuotaUsedAttachment"},
|
||||
{name: "QuotaUsedPackageList", wantType: "[]*QuotaUsedPackage"},
|
||||
{name: "CreatePullReviewCommentOptions", wantType: "CreatePullReviewComment"},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
gt, ok := types[tc.name]
|
||||
if !ok {
|
||||
t.Fatalf("type %q not found", tc.name)
|
||||
}
|
||||
if !gt.IsAlias {
|
||||
t.Fatalf("type %q should be emitted as an alias", tc.name)
|
||||
}
|
||||
if gt.AliasType != tc.wantType {
|
||||
t.Fatalf("type %q: got alias %q, want %q", tc.name, gt.AliasType, tc.wantType)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,15 +54,14 @@ type CommitStatus struct {
|
|||
Creator *User `json:"creator,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
ID int64 `json:"id,omitempty"`
|
||||
Status *CommitStatusState `json:"status,omitempty"`
|
||||
Status CommitStatusState `json:"status,omitempty"`
|
||||
TargetURL string `json:"target_url,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Updated time.Time `json:"updated_at,omitempty"`
|
||||
}
|
||||
|
||||
// CommitStatusState — CommitStatusState holds the state of a CommitStatus It can be "pending", "success", "error" and "failure"
|
||||
// CommitStatusState has no fields in the swagger spec.
|
||||
type CommitStatusState struct{}
|
||||
type CommitStatusState string
|
||||
|
||||
type CommitUser struct {
|
||||
Date string `json:"date,omitempty"`
|
||||
|
|
|
|||
|
|
@ -35,9 +35,7 @@ type Permission struct {
|
|||
}
|
||||
|
||||
// StateType — StateType issue state type
|
||||
// StateType has no fields in the swagger spec.
|
||||
type StateType struct{}
|
||||
type StateType string
|
||||
|
||||
// TimeStamp — TimeStamp defines a timestamp
|
||||
// TimeStamp has no fields in the swagger spec.
|
||||
type TimeStamp struct{}
|
||||
type TimeStamp int64
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ type Issue struct {
|
|||
PullRequest *PullRequestMeta `json:"pull_request,omitempty"`
|
||||
Ref string `json:"ref,omitempty"`
|
||||
Repository *RepositoryMeta `json:"repository,omitempty"`
|
||||
State *StateType `json:"state,omitempty"`
|
||||
State StateType `json:"state,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Updated time.Time `json:"updated_at,omitempty"`
|
||||
|
|
@ -123,17 +123,15 @@ type IssueDeadline struct {
|
|||
type IssueFormField struct {
|
||||
Attributes map[string]any `json:"attributes,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
Type *IssueFormFieldType `json:"type,omitempty"`
|
||||
Type IssueFormFieldType `json:"type,omitempty"`
|
||||
Validations map[string]any `json:"validations,omitempty"`
|
||||
Visible []*IssueFormFieldVisible `json:"visible,omitempty"`
|
||||
Visible []IssueFormFieldVisible `json:"visible,omitempty"`
|
||||
}
|
||||
|
||||
// IssueFormFieldType has no fields in the swagger spec.
|
||||
type IssueFormFieldType struct{}
|
||||
type IssueFormFieldType string
|
||||
|
||||
// IssueFormFieldVisible — IssueFormFieldVisible defines issue form field visible
|
||||
// IssueFormFieldVisible has no fields in the swagger spec.
|
||||
type IssueFormFieldVisible struct{}
|
||||
type IssueFormFieldVisible string
|
||||
|
||||
// IssueLabelsOption — IssueLabelsOption a collection of labels
|
||||
//
|
||||
|
|
@ -158,11 +156,10 @@ type IssueTemplate struct {
|
|||
Content string `json:"content,omitempty"`
|
||||
Fields []*IssueFormField `json:"body,omitempty"`
|
||||
FileName string `json:"file_name,omitempty"`
|
||||
Labels *IssueTemplateLabels `json:"labels,omitempty"`
|
||||
Labels IssueTemplateLabels `json:"labels,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Ref string `json:"ref,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
}
|
||||
|
||||
// IssueTemplateLabels has no fields in the swagger spec.
|
||||
type IssueTemplateLabels struct{}
|
||||
type IssueTemplateLabels []string
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ type Milestone struct {
|
|||
Description string `json:"description,omitempty"`
|
||||
ID int64 `json:"id,omitempty"`
|
||||
OpenIssues int64 `json:"open_issues,omitempty"`
|
||||
State *StateType `json:"state,omitempty"`
|
||||
State StateType `json:"state,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Updated time.Time `json:"updated_at,omitempty"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -213,8 +213,7 @@ type NewIssuePinsAllowed struct {
|
|||
}
|
||||
|
||||
// NotifySubjectType — NotifySubjectType represent type of notification subject
|
||||
// NotifySubjectType has no fields in the swagger spec.
|
||||
type NotifySubjectType struct{}
|
||||
type NotifySubjectType string
|
||||
|
||||
// PRBranchInfo — PRBranchInfo information about a branch
|
||||
type PRBranchInfo struct {
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ type NotificationSubject struct {
|
|||
HTMLURL string `json:"html_url,omitempty"`
|
||||
LatestCommentHTMLURL string `json:"latest_comment_html_url,omitempty"`
|
||||
LatestCommentURL string `json:"latest_comment_url,omitempty"`
|
||||
State *StateType `json:"state,omitempty"`
|
||||
State StateType `json:"state,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Type *NotifySubjectType `json:"type,omitempty"`
|
||||
Type NotifySubjectType `json:"type,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
|
|
|
|||
11
types/pr.go
11
types/pr.go
|
|
@ -39,8 +39,7 @@ type CreatePullReviewComment struct {
|
|||
// Usage:
|
||||
//
|
||||
// opts := CreatePullReviewCommentOptions{}
|
||||
// CreatePullReviewCommentOptions has no fields in the swagger spec.
|
||||
type CreatePullReviewCommentOptions struct{}
|
||||
type CreatePullReviewCommentOptions CreatePullReviewComment
|
||||
|
||||
// CreatePullReviewOptions — CreatePullReviewOptions are options to create a pull review
|
||||
//
|
||||
|
|
@ -51,7 +50,7 @@ type CreatePullReviewOptions struct {
|
|||
Body string `json:"body,omitempty"`
|
||||
Comments []*CreatePullReviewComment `json:"comments,omitempty"`
|
||||
CommitID string `json:"commit_id,omitempty"`
|
||||
Event *ReviewStateType `json:"event,omitempty"`
|
||||
Event ReviewStateType `json:"event,omitempty"`
|
||||
}
|
||||
|
||||
// EditPullRequestOption — EditPullRequestOption options when modify pull request
|
||||
|
|
@ -107,7 +106,7 @@ type PullRequest struct {
|
|||
RequestedReviewers []*User `json:"requested_reviewers,omitempty"`
|
||||
RequestedReviewersTeams []*Team `json:"requested_reviewers_teams,omitempty"`
|
||||
ReviewComments int64 `json:"review_comments,omitempty"` // number of review comments made on the diff of a PR review (not including comments on commits or issues in a PR)
|
||||
State *StateType `json:"state,omitempty"`
|
||||
State StateType `json:"state,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Updated time.Time `json:"updated_at,omitempty"`
|
||||
|
|
@ -133,7 +132,7 @@ type PullReview struct {
|
|||
ID int64 `json:"id,omitempty"`
|
||||
Official bool `json:"official,omitempty"`
|
||||
Stale bool `json:"stale,omitempty"`
|
||||
State *ReviewStateType `json:"state,omitempty"`
|
||||
State ReviewStateType `json:"state,omitempty"`
|
||||
Submitted time.Time `json:"submitted_at,omitempty"`
|
||||
Team *Team `json:"team,omitempty"`
|
||||
Updated time.Time `json:"updated_at,omitempty"`
|
||||
|
|
@ -176,5 +175,5 @@ type PullReviewRequestOptions struct {
|
|||
// opts := SubmitPullReviewOptions{Body: "example"}
|
||||
type SubmitPullReviewOptions struct {
|
||||
Body string `json:"body,omitempty"`
|
||||
Event *ReviewStateType `json:"event,omitempty"`
|
||||
Event ReviewStateType `json:"event,omitempty"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,12 +41,11 @@ type QuotaGroup struct {
|
|||
}
|
||||
|
||||
// QuotaGroupList — QuotaGroupList represents a list of quota groups
|
||||
// QuotaGroupList has no fields in the swagger spec.
|
||||
type QuotaGroupList struct{}
|
||||
type QuotaGroupList []*QuotaGroup
|
||||
|
||||
// QuotaInfo — QuotaInfo represents information about a user's quota
|
||||
type QuotaInfo struct {
|
||||
Groups *QuotaGroupList `json:"groups,omitempty"`
|
||||
Groups QuotaGroupList `json:"groups,omitempty"`
|
||||
Used *QuotaUsed `json:"used,omitempty"`
|
||||
}
|
||||
|
||||
|
|
@ -70,8 +69,7 @@ type QuotaUsedArtifact struct {
|
|||
}
|
||||
|
||||
// QuotaUsedArtifactList — QuotaUsedArtifactList represents a list of artifacts counting towards a user's quota
|
||||
// QuotaUsedArtifactList has no fields in the swagger spec.
|
||||
type QuotaUsedArtifactList struct{}
|
||||
type QuotaUsedArtifactList []*QuotaUsedArtifact
|
||||
|
||||
// QuotaUsedAttachment — QuotaUsedAttachment represents an attachment counting towards a user's quota
|
||||
type QuotaUsedAttachment struct {
|
||||
|
|
@ -82,8 +80,7 @@ type QuotaUsedAttachment struct {
|
|||
}
|
||||
|
||||
// QuotaUsedAttachmentList — QuotaUsedAttachmentList represents a list of attachment counting towards a user's quota
|
||||
// QuotaUsedAttachmentList has no fields in the swagger spec.
|
||||
type QuotaUsedAttachmentList struct{}
|
||||
type QuotaUsedAttachmentList []*QuotaUsedAttachment
|
||||
|
||||
// QuotaUsedPackage — QuotaUsedPackage represents a package counting towards a user's quota
|
||||
type QuotaUsedPackage struct {
|
||||
|
|
@ -95,8 +92,7 @@ type QuotaUsedPackage struct {
|
|||
}
|
||||
|
||||
// QuotaUsedPackageList — QuotaUsedPackageList represents a list of packages counting towards a user's quota
|
||||
// QuotaUsedPackageList has no fields in the swagger spec.
|
||||
type QuotaUsedPackageList struct{}
|
||||
type QuotaUsedPackageList []*QuotaUsedPackage
|
||||
|
||||
// QuotaUsedSize — QuotaUsedSize represents the size-based quota usage of a user
|
||||
type QuotaUsedSize struct {
|
||||
|
|
|
|||
|
|
@ -4,5 +4,4 @@ package types
|
|||
|
||||
|
||||
// ReviewStateType — ReviewStateType review state type
|
||||
// ReviewStateType has no fields in the swagger spec.
|
||||
type ReviewStateType struct{}
|
||||
type ReviewStateType string
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ type CombinedStatus struct {
|
|||
CommitURL string `json:"commit_url,omitempty"`
|
||||
Repository *Repository `json:"repository,omitempty"`
|
||||
SHA string `json:"sha,omitempty"`
|
||||
State *CommitStatusState `json:"state,omitempty"`
|
||||
State CommitStatusState `json:"state,omitempty"`
|
||||
Statuses []*CommitStatus `json:"statuses,omitempty"`
|
||||
TotalCount int64 `json:"total_count,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
|
|
@ -22,6 +22,6 @@ type CombinedStatus struct {
|
|||
type CreateStatusOption struct {
|
||||
Context string `json:"context,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
State *CommitStatusState `json:"state,omitempty"`
|
||||
State CommitStatusState `json:"state,omitempty"`
|
||||
TargetURL string `json:"target_url,omitempty"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ type User struct {
|
|||
// UserHeatmapData — UserHeatmapData represents the data needed to create a heatmap
|
||||
type UserHeatmapData struct {
|
||||
Contributions int64 `json:"contributions,omitempty"`
|
||||
Timestamp *TimeStamp `json:"timestamp,omitempty"`
|
||||
Timestamp TimeStamp `json:"timestamp,omitempty"`
|
||||
}
|
||||
|
||||
// UserSettings — UserSettings represents user settings
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ func TestUserService_GetQuota_Good(t *testing.T) {
|
|||
t.Errorf("wrong path: %s", r.URL.Path)
|
||||
}
|
||||
json.NewEncoder(w).Encode(types.QuotaInfo{
|
||||
Groups: &types.QuotaGroupList{},
|
||||
Groups: types.QuotaGroupList{},
|
||||
Used: &types.QuotaUsed{
|
||||
Size: &types.QuotaUsedSize{
|
||||
Repos: &types.QuotaUsedSizeRepos{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue