Skip to content

Commit c2a76a7

Browse files
committed
chore: iterate
1 parent 328534e commit c2a76a7

File tree

5 files changed

+489
-253
lines changed

5 files changed

+489
-253
lines changed

configuration.go

Lines changed: 65 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
"github.com/mitchellh/mapstructure"
88
"github.com/speakeasy-api/openapi/pointer"
9+
jsg "github.com/swaggest/jsonschema-go"
910
)
1011

1112
const (
@@ -43,21 +44,35 @@ const (
4344
SDKInitStyleBuilder SDKInitStyle = "builder"
4445
)
4546

47+
// ServerIndex is a type that can be either a string (server ID) or an integer (server index)
48+
type ServerIndex string
49+
50+
func (ServerIndex) PrepareJSONSchema(schema *jsg.Schema) error {
51+
// Set the type to an array of types [string, integer]
52+
schema.Type = &jsg.Type{
53+
SliceOfSimpleTypeValues: []jsg.SimpleType{jsg.String, jsg.Integer},
54+
}
55+
schema.WithDescription("Controls which server is shown in usage snippets. If unset, no server will be shown. If an integer, it will be used as the server index. Otherwise, it will look for a matching server ID.")
56+
return nil
57+
}
58+
4659
type UsageSnippets struct {
47-
OptionalPropertyRendering OptionalPropertyRenderingOption `yaml:"optionalPropertyRendering"`
48-
SDKInitStyle SDKInitStyle `yaml:"sdkInitStyle"`
49-
ServerToShowInSnippets string `yaml:"serverToShowInSnippets,omitempty"` // If unset, no server will be shown, if an integer, use as server_idx, else look for a matching id
50-
AdditionalProperties map[string]any `yaml:",inline"` // Captures any additional properties that are not explicitly defined for backwards/forwards compatibility
60+
_ struct{} `additionalProperties:"true" description:"Configuration for usage snippets"`
61+
OptionalPropertyRendering OptionalPropertyRenderingOption `yaml:"optionalPropertyRendering" enum:"always,never,withExample" description:"Controls how optional properties are rendered in usage snippets"`
62+
SDKInitStyle SDKInitStyle `yaml:"sdkInitStyle" enum:"constructor,builder" description:"Controls how the SDK initialization is depicted in usage snippets"`
63+
ServerToShowInSnippets ServerIndex `yaml:"serverToShowInSnippets,omitempty"` // If unset, no server will be shown, if an integer, use as server_idx, else look for a matching id
64+
AdditionalProperties map[string]any `yaml:",inline" jsonschema:"-"` // Captures any additional properties that are not explicitly defined for backwards/forwards compatibility
5165
}
5266

5367
type Fixes struct {
54-
NameResolutionDec2023 bool `yaml:"nameResolutionDec2023,omitempty"`
55-
NameResolutionFeb2025 bool `yaml:"nameResolutionFeb2025"`
56-
ParameterOrderingFeb2024 bool `yaml:"parameterOrderingFeb2024"`
57-
RequestResponseComponentNamesFeb2024 bool `yaml:"requestResponseComponentNamesFeb2024"`
58-
SecurityFeb2025 bool `yaml:"securityFeb2025"`
59-
SharedErrorComponentsApr2025 bool `yaml:"sharedErrorComponentsApr2025"`
60-
AdditionalProperties map[string]any `yaml:",inline"` // Captures any additional properties that are not explicitly defined for backwards/forwards compatibility
68+
_ struct{} `additionalProperties:"true" description:"Fixes applied to the SDK generation"`
69+
NameResolutionDec2023 bool `yaml:"nameResolutionDec2023,omitempty" description:"Enables name resolution fixes from December 2023"`
70+
NameResolutionFeb2025 bool `yaml:"nameResolutionFeb2025" description:"Enables name resolution fixes from February 2025"`
71+
ParameterOrderingFeb2024 bool `yaml:"parameterOrderingFeb2024" description:"Enables parameter ordering fixes from February 2024"`
72+
RequestResponseComponentNamesFeb2024 bool `yaml:"requestResponseComponentNamesFeb2024" description:"Enables request and response component naming fixes from February 2024"`
73+
SecurityFeb2025 bool `yaml:"securityFeb2025" description:"Enables fixes and refactoring for security that were introduced in February 2025"`
74+
SharedErrorComponentsApr2025 bool `yaml:"sharedErrorComponentsApr2025" description:"Enables fixes that mean that when a component is used in both 2XX and 4XX responses, only the top level component will be duplicated to the errors scope as opposed to the entire component tree"`
75+
AdditionalProperties map[string]any `yaml:",inline" jsonschema:"-"` // Captures any additional properties that are not explicitly defined for backwards/forwards compatibility
6176
}
6277

6378
func (f *Fixes) UnmarshalYAML(unmarshal func(interface{}) error) error {
@@ -78,28 +93,32 @@ func (f *Fixes) UnmarshalYAML(unmarshal func(interface{}) error) error {
7893
}
7994

8095
type Auth struct {
81-
OAuth2ClientCredentialsEnabled bool `yaml:"oAuth2ClientCredentialsEnabled"`
82-
OAuth2PasswordEnabled bool `yaml:"oAuth2PasswordEnabled"`
83-
HoistGlobalSecurity bool `yaml:"hoistGlobalSecurity"`
96+
_ struct{} `additionalProperties:"false" description:"Authentication configuration"`
97+
OAuth2ClientCredentialsEnabled bool `yaml:"oAuth2ClientCredentialsEnabled" description:"Enables support for OAuth2 client credentials grant type"`
98+
OAuth2PasswordEnabled bool `yaml:"oAuth2PasswordEnabled" description:"Enables support for OAuth2 resource owner password credentials grant type"`
99+
HoistGlobalSecurity bool `yaml:"hoistGlobalSecurity" description:"Enables hoisting of operation-level security schemes to global level when no global security is defined"`
84100
}
85101

86102
type Tests struct {
87-
GenerateTests bool `yaml:"generateTests"`
88-
GenerateNewTests bool `yaml:"generateNewTests"`
89-
SkipResponseBodyAssertions bool `yaml:"skipResponseBodyAssertions"`
103+
_ struct{} `additionalProperties:"true" description:"Test generation configuration"`
104+
GenerateTests bool `yaml:"generateTests" description:"Enables generation of tests"`
105+
GenerateNewTests bool `yaml:"generateNewTests" description:"Enables generation of new tests for any new operations in the OpenAPI specification"`
106+
SkipResponseBodyAssertions bool `yaml:"skipResponseBodyAssertions" description:"Skip asserting that the client got the same response bodies returned by the mock server"`
107+
AdditionalProperties map[string]any `yaml:",inline" jsonschema:"-"` // Captures any additional properties that are not explicitly defined for backwards/forwards compatibility
90108
}
91109

92110
// PersistentEdits configures whether user edits to generated SDKs persist across regenerations
93111
// When enabled, user changes are preserved via 3-way merge with Git tracking
94112
type PersistentEdits struct {
113+
_ struct{} `additionalProperties:"true" description:"Configures whether user edits to generated SDKs persist across regenerations"`
95114
// Enabled allows user edits to generated SDK code to persist through regeneration
96115
// Requires Git repository and creates a pristine branch for tracking
97-
Enabled bool `yaml:"enabled,omitempty"`
116+
Enabled bool `yaml:"enabled,omitempty" description:"Enables preservation of user edits across SDK regenerations. Requires Git repository."`
98117

99118
// PristineBranch specifies the Git branch name for tracking pristine generated code
100119
// Defaults to "sdk-pristine" if not specified
101-
PristineBranch string `yaml:"pristineBranch,omitempty"`
102-
AdditionalProperties map[string]any `yaml:",inline"` // Captures any additional properties
120+
PristineBranch string `yaml:"pristineBranch,omitempty" description:"The Git branch name for tracking pristine generated code. Defaults to 'sdk-pristine' if not specified."`
121+
AdditionalProperties map[string]any `yaml:",inline" jsonschema:"-"` // Captures any additional properties
103122
}
104123

105124
type AllOfMergeStrategy string
@@ -110,24 +129,26 @@ const (
110129
)
111130

112131
type Schemas struct {
113-
AllOfMergeStrategy AllOfMergeStrategy `yaml:"allOfMergeStrategy"`
132+
_ struct{} `additionalProperties:"false" description:"Schema processing configuration"`
133+
AllOfMergeStrategy AllOfMergeStrategy `yaml:"allOfMergeStrategy" enum:"deepMerge,shallowMerge" description:"Controls how allOf schemas are merged"`
114134
}
115135

116136
type Generation struct {
137+
_ struct{} `additionalProperties:"true" description:"Generation configuration"`
117138
DevContainers *DevContainers `yaml:"devContainers,omitempty"`
118-
BaseServerURL string `yaml:"baseServerUrl,omitempty"`
119-
SDKClassName string `yaml:"sdkClassName,omitempty"`
120-
MaintainOpenAPIOrder bool `yaml:"maintainOpenAPIOrder,omitempty"`
121-
DeduplicateErrors bool `yaml:"deduplicateErrors,omitempty"`
139+
BaseServerURL string `yaml:"baseServerUrl,omitempty" description:"The base URL of the server. This value will be used if global servers are not defined in the spec."`
140+
SDKClassName string `yaml:"sdkClassName,omitempty" description:"Generated name of the root SDK class"`
141+
MaintainOpenAPIOrder bool `yaml:"maintainOpenAPIOrder,omitempty" description:"Maintains the order of parameters and fields in the OpenAPI specification"`
142+
DeduplicateErrors bool `yaml:"deduplicateErrors,omitempty" description:"Deduplicates errors that have the same schema"`
122143
UsageSnippets *UsageSnippets `yaml:"usageSnippets,omitempty"`
123-
UseClassNamesForArrayFields bool `yaml:"useClassNamesForArrayFields,omitempty"`
144+
UseClassNamesForArrayFields bool `yaml:"useClassNamesForArrayFields,omitempty" description:"Use class names for array fields instead of the child's schema type"`
124145
Fixes *Fixes `yaml:"fixes,omitempty"`
125146
Auth *Auth `yaml:"auth,omitempty"`
126-
SkipErrorSuffix bool `yaml:"skipErrorSuffix,omitempty"`
127-
InferSSEOverload bool `yaml:"inferSSEOverload,omitempty"`
128-
SDKHooksConfigAccess bool `yaml:"sdkHooksConfigAccess,omitempty"`
147+
SkipErrorSuffix bool `yaml:"skipErrorSuffix,omitempty" description:"Skips the automatic addition of an error suffix to error types"`
148+
InferSSEOverload bool `yaml:"inferSSEOverload,omitempty" description:"Generates an overload if generator detects that the request body field 'stream: true' is used for client intent to request 'text/event-stream' response"`
149+
SDKHooksConfigAccess bool `yaml:"sdkHooksConfigAccess,omitempty" description:"Enables access to the SDK configuration from hooks"`
129150
Schemas Schemas `yaml:"schemas"`
130-
RequestBodyFieldName string `yaml:"requestBodyFieldName"`
151+
RequestBodyFieldName string `yaml:"requestBodyFieldName" description:"The name of the field to use for the request body in generated SDKs"`
131152

132153
// Mock server generation configuration.
133154
MockServer *MockServer `yaml:"mockServer,omitempty"`
@@ -136,25 +157,28 @@ type Generation struct {
136157
PersistentEdits *PersistentEdits `yaml:"persistentEdits,omitempty"`
137158
Tests Tests `yaml:"tests,omitempty"`
138159

139-
AdditionalProperties map[string]any `yaml:",inline"` // Captures any additional properties that are not explicitly defined for backwards/forwards compatibility
160+
AdditionalProperties map[string]any `yaml:",inline" jsonschema:"-"` // Captures any additional properties that are not explicitly defined for backwards/forwards compatibility
140161
}
141162

142163
type DevContainers struct {
143-
Enabled bool `yaml:"enabled"`
164+
_ struct{} `additionalProperties:"true" description:"Dev container configuration"`
165+
Enabled bool `yaml:"enabled" description:"Whether dev containers are enabled"`
144166
// This can be a local path or a remote URL
145-
SchemaPath string `yaml:"schemaPath"`
146-
AdditionalProperties map[string]any `yaml:",inline"` // Captures any additional properties that are not explicitly defined for backwards/forwards compatibility
167+
SchemaPath string `yaml:"schemaPath" description:"Path to the schema file for the dev container"`
168+
AdditionalProperties map[string]any `yaml:",inline" jsonschema:"-"` // Captures any additional properties that are not explicitly defined for backwards/forwards compatibility
147169
}
148170

149171
// Generation configuration for the inter-templated mockserver target for test generation.
150172
type MockServer struct {
173+
_ struct{} `additionalProperties:"false" description:"Mock server generation configuration"`
151174
// Disables the code generation of the mockserver target.
152-
Disabled bool `yaml:"disabled"`
175+
Disabled bool `yaml:"disabled" description:"Disables the code generation of the mock server target"`
153176
}
154177

155178
type LanguageConfig struct {
156-
Version string `yaml:"version"`
157-
Cfg map[string]any `yaml:",inline"`
179+
_ struct{} `additionalProperties:"true" description:"Language-specific SDK configuration"`
180+
Version string `yaml:"version" description:"SDK version"`
181+
Cfg map[string]any `yaml:",inline" jsonschema:"-"`
158182
}
159183

160184
type SDKGenConfigField struct {
@@ -172,9 +196,10 @@ type SDKGenConfigField struct {
172196

173197
// Ensure you update schema/gen.config.schema.json on changes
174198
type Configuration struct {
175-
ConfigVersion string `yaml:"configVersion"`
176-
Generation Generation `yaml:"generation"`
177-
Languages map[string]LanguageConfig `yaml:",inline"`
199+
_ struct{} `title:"Gen YAML Configuration Schema" additionalProperties:"false"`
200+
ConfigVersion string `yaml:"configVersion" description:"The version of the configuration file" minLength:"1" required:"true"`
201+
Generation Generation `yaml:"generation" required:"true"`
202+
Languages map[string]LanguageConfig `yaml:",inline" jsonschema:"-"`
178203
New map[string]bool `yaml:"-"`
179204
}
180205

@@ -397,18 +422,6 @@ func GetGenerationDefaults(newSDK bool) []SDKGenConfigField {
397422
DefaultValue: ptr(newSDK),
398423
Description: pointer.From("Maintains the order of things like parameters and fields when generating the SDK"),
399424
},
400-
{
401-
Name: "persistentEdits.enabled",
402-
Required: false,
403-
DefaultValue: ptr(false),
404-
Description: pointer.From("Enables preservation of user edits across SDK regenerations. Requires Git repository."),
405-
},
406-
{
407-
Name: "persistentEdits.pristineBranch",
408-
Required: false,
409-
DefaultValue: ptr("sdk-pristine"),
410-
Description: pointer.From("The Git branch name for tracking pristine generated code."),
411-
},
412425
{
413426
Name: "deduplicateErrors",
414427
Required: false,

configuration_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package config_test
2+
3+
//go:generate sh -c "cd tools/schema-gen && go run . -type config -out ../../schemas/gen.config.schema.json"
4+
5+
import (
6+
"bytes"
7+
"os"
8+
"os/exec"
9+
"path/filepath"
10+
"testing"
11+
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
// TestConfigSchemaInSync verifies that gen.config.schema.json is in sync with
16+
// what the schema generator produces from the Configuration struct.
17+
func TestConfigSchemaInSync(t *testing.T) {
18+
// Generate schema from current Go structs
19+
cmd := exec.Command("go", "run", ".", "-type", "config", "-out", "-")
20+
cmd.Dir = filepath.Join("tools", "schema-gen")
21+
22+
var stdout, stderr bytes.Buffer
23+
cmd.Stdout = &stdout
24+
cmd.Stderr = &stderr
25+
26+
err := cmd.Run()
27+
require.NoError(t, err, "schema generator failed: %s", stderr.String())
28+
29+
// Read the committed schema
30+
committedPath := filepath.Join("schemas", "gen.config.schema.json")
31+
committedBytes, err := os.ReadFile(committedPath)
32+
require.NoError(t, err, "Failed to read committed schema")
33+
34+
// Compare byte-for-byte
35+
generated := stdout.Bytes()
36+
require.Equal(t, string(committedBytes), string(generated),
37+
"Generated config schema does not match committed schemas/gen.config.schema.json.\n"+
38+
"Run: cd tools/schema-gen && go run . -type config -out ../../schemas/gen.config.schema.json\n"+
39+
"Then commit the updated file.")
40+
}

lockfile/io.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,26 @@ func Load(data []byte, opts ...LoadOption) (*LockFile, error) {
3939
lf.TrackedFiles = sequencedmap.New[string, TrackedFile]()
4040
}
4141

42+
// Migrate old "integrity" field to "last_write_checksum"
43+
for path := range lf.TrackedFiles.Keys() {
44+
tf, ok := lf.TrackedFiles.Get(path)
45+
if !ok {
46+
continue
47+
}
48+
49+
// Check if integrity exists in AdditionalProperties
50+
if tf.AdditionalProperties != nil {
51+
if integrity, ok := tf.AdditionalProperties["integrity"].(string); ok {
52+
// Migrate to LastWriteChecksum if not already set
53+
if tf.LastWriteChecksum == "" {
54+
tf.LastWriteChecksum = integrity
55+
}
56+
// Remove from AdditionalProperties
57+
delete(tf.AdditionalProperties, "integrity")
58+
lf.TrackedFiles.Set(path, tf)
59+
}
60+
}
61+
}
62+
4263
return &lf, nil
4364
}

0 commit comments

Comments
 (0)