github.com/speakeasy-api/sdk-gen-config@v1.14.2/configuration.go (about) 1 package config 2 3 import ( 4 "strings" 5 6 "github.com/AlekSi/pointer" 7 "github.com/mitchellh/mapstructure" 8 ) 9 10 const ( 11 v1 = "1.0.0" 12 v2 = "2.0.0" 13 Version = v2 14 15 GithubWritePermission = "write" 16 17 // Constants to be used as keys in the config files 18 Languages = "languages" 19 Mode = "mode" 20 GithubAccessToken = "github_access_token" 21 SpeakeasyApiKey = "speakeasy_api_key" 22 SpeakeasyServerURL = "speakeasy_server_url" 23 OpenAPIDocAuthHeader = "openapi_doc_auth_header" 24 OpenAPIDocAuthToken = "openapi_doc_auth_token" 25 OpenAPIDocs = "openapi_docs" 26 ) 27 28 type OptionalPropertyRenderingOption string 29 30 const ( 31 OptionalPropertyRenderingOptionAlways OptionalPropertyRenderingOption = "always" 32 OptionalPropertyRenderingOptionNever OptionalPropertyRenderingOption = "never" 33 OptionalPropertyRenderingOptionWithExample OptionalPropertyRenderingOption = "withExample" 34 ) 35 36 type UsageSnippets struct { 37 OptionalPropertyRendering OptionalPropertyRenderingOption `yaml:"optionalPropertyRendering"` 38 AdditionalProperties map[string]any `yaml:",inline"` // Captures any additional properties that are not explicitly defined for backwards/forwards compatibility 39 } 40 41 type Fixes struct { 42 NameResolutionDec2023 bool `yaml:"nameResolutionDec2023"` 43 ParameterOrderingFeb2024 bool `yaml:"parameterOrderingFeb2024"` 44 RequestResponseComponentNamesFeb2024 bool `yaml:"requestResponseComponentNamesFeb2024"` 45 AdditionalProperties map[string]any `yaml:",inline"` // Captures any additional properties that are not explicitly defined for backwards/forwards compatibility 46 } 47 48 type Auth struct { 49 OAuth2ClientCredentialsEnabled bool `yaml:"oAuth2ClientCredentialsEnabled"` 50 } 51 52 type Generation struct { 53 DevContainers *DevContainers `yaml:"devContainers,omitempty"` 54 BaseServerURL string `yaml:"baseServerUrl,omitempty"` 55 SDKClassName string `yaml:"sdkClassName,omitempty"` 56 MaintainOpenAPIOrder bool `yaml:"maintainOpenAPIOrder,omitempty"` 57 UsageSnippets *UsageSnippets `yaml:"usageSnippets,omitempty"` 58 UseClassNamesForArrayFields bool `yaml:"useClassNamesForArrayFields,omitempty"` 59 Fixes *Fixes `yaml:"fixes,omitempty"` 60 Auth *Auth `yaml:"auth,omitempty"` 61 62 AdditionalProperties map[string]any `yaml:",inline"` // Captures any additional properties that are not explicitly defined for backwards/forwards compatibility 63 } 64 65 type DevContainers struct { 66 Enabled bool `yaml:"enabled"` 67 // This can be a local path or a remote URL 68 SchemaPath string `yaml:"schemaPath"` 69 AdditionalProperties map[string]any `yaml:",inline"` // Captures any additional properties that are not explicitly defined for backwards/forwards compatibility 70 } 71 72 type LanguageConfig struct { 73 Version string `yaml:"version"` 74 Cfg map[string]any `yaml:",inline"` 75 } 76 77 type SDKGenConfigField struct { 78 Name string `yaml:"name" json:"name"` 79 Required bool `yaml:"required" json:"required"` 80 RequiredForPublishing *bool `yaml:"requiredForPublishing,omitempty" json:"required_for_publishing,omitempty"` 81 DefaultValue *any `yaml:"defaultValue,omitempty" json:"default_value,omitempty"` 82 Description *string `yaml:"description,omitempty" json:"description,omitempty"` 83 Language *string `yaml:"language,omitempty" json:"language,omitempty"` 84 SecretName *string `yaml:"secretName,omitempty" json:"secret_name,omitempty"` 85 ValidationRegex *string `yaml:"validationRegex,omitempty" json:"validation_regex,omitempty"` 86 ValidationMessage *string `yaml:"validationMessage,omitempty" json:"validation_message,omitempty"` 87 TestValue *any `yaml:"testValue,omitempty" json:"test_value,omitempty"` 88 } 89 90 type Configuration struct { 91 ConfigVersion string `yaml:"configVersion"` 92 Generation Generation `yaml:"generation"` 93 Languages map[string]LanguageConfig `yaml:",inline"` 94 New map[string]bool `yaml:"-"` 95 } 96 97 type PublishWorkflow struct { 98 Name string `yaml:"name"` 99 Permissions Permissions `yaml:"permissions,omitempty"` 100 On PublishOn `yaml:"on"` 101 Jobs Jobs `yaml:"jobs"` 102 } 103 104 type PublishOn struct { 105 Push Push `yaml:"push"` 106 } 107 108 type Push struct { 109 Branches []string `yaml:"branches"` 110 Paths []string `yaml:"paths"` 111 } 112 113 type GenerateWorkflow struct { 114 Name string `yaml:"name"` 115 Permissions Permissions `yaml:"permissions,omitempty"` 116 On GenerateOn `yaml:"on"` 117 Jobs Jobs `yaml:"jobs"` 118 } 119 120 type Permissions struct { 121 Checks string `yaml:"checks,omitempty"` 122 Contents string `yaml:"contents,omitempty"` 123 PullRequests string `yaml:"pull-requests,omitempty"` 124 Statuses string `yaml:"statuses,omitempty"` 125 } 126 127 type GenerateOn struct { 128 WorkflowDispatch WorkflowDispatch `yaml:"workflow_dispatch"` 129 Schedule []Schedule `yaml:"schedule,omitempty"` 130 } 131 132 type Jobs struct { 133 Generate Job `yaml:"generate,omitempty"` 134 Publish Job `yaml:"publish,omitempty"` 135 } 136 137 type Job struct { 138 Uses string `yaml:"uses"` 139 With map[string]any `yaml:"with,omitempty"` 140 Secrets map[string]string `yaml:"secrets,omitempty"` 141 } 142 143 type WorkflowDispatch struct { 144 Inputs Inputs `yaml:"inputs"` 145 } 146 147 type Schedule struct { 148 Cron string `yaml:"cron"` 149 } 150 151 type Inputs struct { 152 Force Force `yaml:"force"` 153 } 154 155 type Force struct { 156 Description string `yaml:"description"` 157 Type string `yaml:"type"` 158 Default bool `yaml:"default"` 159 } 160 161 func GetDefaultConfig(newSDK bool, getLangDefaultFunc GetLanguageDefaultFunc, langs map[string]bool) (*Configuration, error) { 162 defaults := GetGenerationDefaults(newSDK) 163 164 fields := map[string]any{} 165 for _, field := range defaults { 166 if field.DefaultValue != nil { 167 if strings.Contains(field.Name, ".") { 168 parts := strings.Split(field.Name, ".") 169 170 currMap := fields 171 172 for i, part := range parts { 173 if i == len(parts)-1 { 174 currMap[part] = *field.DefaultValue 175 } else { 176 if _, ok := currMap[part]; !ok { 177 currMap[part] = map[string]any{} 178 } 179 180 currMap = currMap[part].(map[string]any) 181 } 182 } 183 } else { 184 fields[field.Name] = *field.DefaultValue 185 } 186 } 187 } 188 189 var genConfig Generation 190 191 d, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 192 Result: &genConfig, 193 TagName: "yaml", 194 }) 195 if err != nil { 196 return nil, err 197 } 198 199 if err := d.Decode(fields); err != nil { 200 return nil, err 201 } 202 203 cfg := &Configuration{ 204 ConfigVersion: Version, 205 Generation: genConfig, 206 Languages: map[string]LanguageConfig{}, 207 New: map[string]bool{}, 208 } 209 210 for lang, new := range langs { 211 langDefault := &LanguageConfig{ 212 Version: "0.0.1", 213 } 214 215 if getLangDefaultFunc != nil { 216 var err error 217 langDefault, err = getLangDefaultFunc(lang, new) 218 if err != nil { 219 return nil, err 220 } 221 } 222 223 cfg.Languages[lang] = *langDefault 224 } 225 226 return cfg, nil 227 } 228 229 func GetGenerationDefaults(newSDK bool) []SDKGenConfigField { 230 return []SDKGenConfigField{ 231 { 232 Name: "baseServerURL", 233 Required: false, 234 DefaultValue: ptr(""), 235 Description: pointer.To("The base URL of the server. This value will be used if global servers are not defined in the spec."), 236 ValidationRegex: pointer.To(`^(https?):\/\/([\w\-]+\.)+\w+(\/.*)?$`), 237 ValidationMessage: pointer.To("Must be a valid server URL"), 238 }, 239 { 240 Name: "sdkClassName", 241 Required: false, 242 DefaultValue: ptr("SDK"), 243 Description: pointer.To("Generated name of the root SDK class"), 244 ValidationRegex: pointer.To(`^[\w.\-]+$`), 245 ValidationMessage: pointer.To("Letters, numbers, or .-_ only"), 246 }, 247 { 248 Name: "maintainOpenAPIOrder", 249 Required: false, 250 DefaultValue: ptr(newSDK), 251 Description: pointer.To("Maintains the order of things like parameters and fields when generating the SDK"), 252 }, 253 { 254 Name: "usageSnippets.optionalPropertyRendering", 255 Required: false, 256 DefaultValue: ptr(OptionalPropertyRenderingOptionWithExample), 257 Description: pointer.To("Controls how optional properties are rendered in usage snippets, by default they will be rendered when an example is present in the OpenAPI spec"), 258 }, 259 { 260 Name: "useClassNamesForArrayFields", 261 Required: false, 262 DefaultValue: ptr(newSDK), 263 Description: pointer.To("Use class names for array fields instead of the child's schema type"), 264 }, 265 { 266 Name: "fixes.nameResolutionDec2023", 267 Required: false, 268 DefaultValue: ptr(newSDK), 269 Description: pointer.To("Enables a number of breaking changes introduced in December 2023, that improve name resolution for inline schemas and reduce chances of name collisions"), 270 }, 271 { 272 Name: "fixes.parameterOrderingFeb2024", 273 Required: false, 274 DefaultValue: ptr(newSDK), 275 Description: pointer.To("Enables fixes to the ordering of parameters for an operation if they include multiple types of parameters (ie header, query, path) to match the order they are defined in the OpenAPI spec"), 276 }, 277 { 278 Name: "fixes.requestResponseComponentNamesFeb2024", 279 Required: false, 280 DefaultValue: ptr(newSDK), 281 Description: pointer.To("Enables fixes that will name inline schemas within request and response components with the component name of the parent if only one content type is defined"), 282 }, 283 { 284 Name: "fixes.methodSignaturesApr2024", 285 Required: false, 286 DefaultValue: ptr(newSDK), 287 Description: pointer.To("Enables fixes that will detect and mark optional request and security method arguments and order them according to optionality."), 288 }, 289 { 290 Name: "auth.oAuth2ClientCredentialsEnabled", 291 Required: false, 292 DefaultValue: ptr(newSDK), 293 Description: pointer.To("Enables support for OAuth2 client credentials grant type (Enterprise tier only)"), 294 }, 295 } 296 } 297 298 func (c *Configuration) GetGenerationFieldsMap() (map[string]any, error) { 299 fields := map[string]any{} 300 301 // Yes the decoder can encode too :face_palm: 302 d, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 303 Result: &fields, 304 TagName: "yaml", 305 }) 306 if err != nil { 307 return nil, err 308 } 309 310 if err := d.Decode(c.Generation); err != nil { 311 return nil, err 312 } 313 314 return fields, nil 315 } 316 317 func ptr(a any) *any { 318 return &a 319 }