diff --git a/internal/cmd/generate.go b/internal/cmd/generate.go index 160c383f..6ab364b4 100644 --- a/internal/cmd/generate.go +++ b/internal/cmd/generate.go @@ -20,6 +20,7 @@ import ( "github.com/hashicorp/cli" "github.com/pb33f/libopenapi" + high "github.com/pb33f/libopenapi/datamodel/high/v3" "github.com/pb33f/libopenapi/index" ) @@ -148,7 +149,7 @@ func (cmd *GenerateCommand) runInternal(logger *slog.Logger) error { // 5. Generate provider code spec w/ config oasExplorer := explorer.NewConfigExplorer(model.Model, *config) - providerCodeSpec, err := generateProviderCodeSpec(logger, oasExplorer, *config) + providerCodeSpec, err := generateProviderCodeSpec(logger, oasExplorer, &model.Model, *config) if err != nil { return err } @@ -181,7 +182,7 @@ func (cmd *GenerateCommand) runInternal(logger *slog.Logger) error { return nil } -func generateProviderCodeSpec(logger *slog.Logger, dora explorer.Explorer, cfg config.Config) (*spec.Specification, error) { +func generateProviderCodeSpec(logger *slog.Logger, dora explorer.Explorer, document *high.Document, cfg config.Config) (*spec.Specification, error) { // 1. Find TF resources in OAS explorerResources, err := dora.FindResources() if err != nil { @@ -201,21 +202,21 @@ func generateProviderCodeSpec(logger *slog.Logger, dora explorer.Explorer, cfg c } // 4. Use TF info to generate provider code spec for resources - resourceMapper := mapper.NewResourceMapper(explorerResources, cfg) + resourceMapper := mapper.NewResourceMapper(explorerResources, document, cfg) resourcesIR, err := resourceMapper.MapToIR(logger) if err != nil { return nil, fmt.Errorf("error generating provider code spec for resources: %w", err) } // 5. Use TF info to generate provider code spec for data sources - dataSourceMapper := mapper.NewDataSourceMapper(explorerDataSources, cfg) + dataSourceMapper := mapper.NewDataSourceMapper(explorerDataSources, document, cfg) dataSourcesIR, err := dataSourceMapper.MapToIR(logger) if err != nil { return nil, fmt.Errorf("error generating provider code spec for data sources: %w", err) } // 6. Use TF info to generate provider code spec for provider - providerMapper := mapper.NewProviderMapper(explorerProvider, cfg) + providerMapper := mapper.NewProviderMapper(explorerProvider, document, cfg) providerIR, err := providerMapper.MapToIR(logger) if err != nil { return nil, fmt.Errorf("error generating provider code spec for provider: %w", err) diff --git a/internal/mapper/datasource_mapper.go b/internal/mapper/datasource_mapper.go index ac0229e3..e49d1510 100644 --- a/internal/mapper/datasource_mapper.go +++ b/internal/mapper/datasource_mapper.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-codegen-openapi/internal/mapper/util" "github.com/hashicorp/terraform-plugin-codegen-spec/datasource" "github.com/hashicorp/terraform-plugin-codegen-spec/schema" + high "github.com/pb33f/libopenapi/datamodel/high/v3" ) var _ DataSourceMapper = dataSourceMapper{} @@ -25,13 +26,15 @@ type DataSourceMapper interface { type dataSourceMapper struct { dataSources map[string]explorer.DataSource + document *high.Document //nolint:unused // Might be useful later! cfg config.Config } -func NewDataSourceMapper(dataSources map[string]explorer.DataSource, cfg config.Config) DataSourceMapper { +func NewDataSourceMapper(dataSources map[string]explorer.DataSource, document *high.Document, cfg config.Config) DataSourceMapper { return dataSourceMapper{ dataSources: dataSources, + document: document, cfg: cfg, } } @@ -45,7 +48,7 @@ func (m dataSourceMapper) MapToIR(logger *slog.Logger) ([]datasource.DataSource, dataSource := m.dataSources[name] dLogger := logger.With("data_source", name) - schema, err := generateDataSourceSchema(dLogger, name, dataSource) + schema, err := generateDataSourceSchema(dLogger, name, dataSource, m.document) if err != nil { log.WarnLogOnError(dLogger, err, "skipping data source schema mapping") continue @@ -60,7 +63,7 @@ func (m dataSourceMapper) MapToIR(logger *slog.Logger) ([]datasource.DataSource, return dataSourceSchemas, nil } -func generateDataSourceSchema(logger *slog.Logger, name string, dataSource explorer.DataSource) (*datasource.Schema, error) { +func generateDataSourceSchema(logger *slog.Logger, name string, dataSource explorer.DataSource, document *high.Document) (*datasource.Schema, error) { dataSourceSchema := &datasource.Schema{ Attributes: []datasource.Attribute{}, } @@ -75,6 +78,7 @@ func generateDataSourceSchema(logger *slog.Logger, name string, dataSource explo } globalSchemaOpts := oas.GlobalSchemaOpts{ OverrideComputability: schema.Computed, + Document: document, } readResponseSchema, err := oas.BuildSchemaFromResponse(dataSource.ReadOp, schemaOpts, globalSchemaOpts) if err != nil { @@ -118,7 +122,9 @@ func generateDataSourceSchema(logger *slog.Logger, name string, dataSource explo OverrideDescription: param.Description, } - s, schemaErr := oas.BuildSchema(param.Schema, schemaOpts, oas.GlobalSchemaOpts{}) + s, schemaErr := oas.BuildSchema(param.Schema, schemaOpts, oas.GlobalSchemaOpts{ + Document: document, + }) if schemaErr != nil { log.WarnLogOnError(pLogger, schemaErr, "skipping mapping of read operation parameter") continue diff --git a/internal/mapper/oas/build.go b/internal/mapper/oas/build.go index bd763dfd..d0ab3695 100644 --- a/internal/mapper/oas/build.go +++ b/internal/mapper/oas/build.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "strconv" + "strings" "github.com/hashicorp/terraform-plugin-codegen-openapi/internal/mapper/util" @@ -97,7 +98,7 @@ func getSchemaFromMediaType(mediaTypes *orderedmap.Map[string, *high.MediaType], func BuildSchema(proxy *base.SchemaProxy, schemaOpts SchemaOpts, globalOpts GlobalSchemaOpts) (*OASSchema, *SchemaError) { resp := OASSchema{} - s, err := buildSchemaProxy(proxy) + s, err := buildSchemaProxy(proxy, globalOpts) if err != nil { return nil, err } @@ -125,12 +126,31 @@ func BuildSchema(proxy *base.SchemaProxy, schemaOpts SchemaOpts, globalOpts Glob // # Any other combinations of allOf, anyOf, or oneOf will return a SchemaError // // [schema composition]: https://json-schema.org/understanding-json-schema/reference/combining -func buildSchemaProxy(proxy *base.SchemaProxy) (*base.Schema, *SchemaError) { +func buildSchemaProxy(proxy *base.SchemaProxy, globalOpts GlobalSchemaOpts) (*base.Schema, *SchemaError) { s, err := proxy.BuildSchema() if err != nil { return nil, SchemaErrorFromProxy(fmt.Errorf("failed to build schema proxy - %w", err), proxy) } + // Check for discriminator patterns first - can work with oneOf, anyOf, allOf, or direct schemas + if s.Discriminator != nil { + // Prevent infinite recursion with depth limit + if globalOpts.DiscriminatorDepth > 5 { + // Skip discriminator processing at deep levels to prevent infinite recursion + return s, nil + } + + // Increment depth for recursive calls + nextGlobalOpts := globalOpts + nextGlobalOpts.DiscriminatorDepth++ + + flattenedSchema, err := flattenDiscriminatorSchema(s, proxy, nextGlobalOpts) + if err != nil { + return nil, err + } + return flattenedSchema, nil + } + // If there are no schema composition keywords, return the schema if len(s.AllOf) == 0 && len(s.AnyOf) == 0 && len(s.OneOf) == 0 { return s, nil @@ -138,7 +158,7 @@ func buildSchemaProxy(proxy *base.SchemaProxy) (*base.Schema, *SchemaError) { if len(s.AnyOf) > 0 { if len(s.AnyOf) == 2 { - schema, err := getMultiTypeSchema(s.AnyOf[0], s.AnyOf[1]) + schema, err := getMultiTypeSchema(s.AnyOf[0], s.AnyOf[1], globalOpts) if err != nil { return nil, err } @@ -152,7 +172,7 @@ func buildSchemaProxy(proxy *base.SchemaProxy) (*base.Schema, *SchemaError) { if len(s.OneOf) > 0 { if len(s.OneOf) == 2 { - schema, err := getMultiTypeSchema(s.OneOf[0], s.OneOf[1]) + schema, err := getMultiTypeSchema(s.OneOf[0], s.OneOf[1], globalOpts) if err != nil { return nil, err } @@ -166,7 +186,7 @@ func buildSchemaProxy(proxy *base.SchemaProxy) (*base.Schema, *SchemaError) { // If there is just one allOf, we can use it as the schema if len(s.AllOf) == 1 { - allOfSchema, err := buildSchemaProxy(s.AllOf[0]) + allOfSchema, err := buildSchemaProxy(s.AllOf[0], globalOpts) if err != nil { return nil, err } @@ -179,20 +199,28 @@ func buildSchemaProxy(proxy *base.SchemaProxy) (*base.Schema, *SchemaError) { return allOfSchema, nil } - // Combining multiple allOf schemas and their properties is possible here, but currently not supported - // See: https://github.com/hashicorp/terraform-plugin-codegen-openapi/issues/56 + // Handle multiple allOf schemas by merging their properties + if len(s.AllOf) > 1 { + composedSchema, err := composeAllOfSchemas(s, globalOpts) + if err != nil { + return nil, err + } + return composedSchema, nil + } + + // No schema composition keywords found return nil, SchemaErrorFromNode(fmt.Errorf("found %d allOf subschema(s), schema composition is currently not supported", len(s.AllOf)), s, AllOf) } // getMultiTypeSchema will check the types of both schemas provided and will return the non-null schema. If a null schema type is not // detected, an error will be returned as multi-types are not supported -func getMultiTypeSchema(proxyOne *base.SchemaProxy, proxyTwo *base.SchemaProxy) (*base.Schema, *SchemaError) { - firstSchema, err := buildSchemaProxy(proxyOne) +func getMultiTypeSchema(proxyOne *base.SchemaProxy, proxyTwo *base.SchemaProxy, globalOpts GlobalSchemaOpts) (*base.Schema, *SchemaError) { + firstSchema, err := buildSchemaProxy(proxyOne, globalOpts) if err != nil { return nil, err } - secondSchema, err := buildSchemaProxy(proxyTwo) + secondSchema, err := buildSchemaProxy(proxyTwo, globalOpts) if err != nil { return nil, err } @@ -256,6 +284,64 @@ func retrieveType(schema *base.Schema) (string, *SchemaError) { return "", SchemaErrorFromNode(fmt.Errorf("%v - %w", schema.Type, ErrMultiTypeSchema), schema, Type) } +// composeAllOfSchemas merges multiple allOf schemas into a single composed schema +// This is essential for discriminator patterns which use allOf to compose base + variant schemas +func composeAllOfSchemas(baseSchema *base.Schema, globalOpts GlobalSchemaOpts) (*base.Schema, *SchemaError) { + // Start with the base schema structure + composedSchema := &base.Schema{ + Type: baseSchema.Type, + Format: baseSchema.Format, + Description: baseSchema.Description, + Properties: orderedmap.New[string, *base.SchemaProxy](), + Required: []string{}, + ParentProxy: baseSchema.ParentProxy, + } + + // Copy base schema properties first + if baseSchema.Properties != nil { + for pair := range orderedmap.Iterate(context.TODO(), baseSchema.Properties) { + composedSchema.Properties.Set(pair.Key(), pair.Value()) + } + } + composedSchema.Required = append(composedSchema.Required, baseSchema.Required...) + + // Process each allOf sub-schema and merge their properties + for _, allOfProxy := range baseSchema.AllOf { + allOfSchema, err := buildSchemaProxy(allOfProxy, globalOpts) + if err != nil { + return nil, err + } + + // Merge properties from this allOf sub-schema + if allOfSchema.Properties != nil { + for pair := range orderedmap.Iterate(context.TODO(), allOfSchema.Properties) { + key := pair.Key() + value := pair.Value() + // Later schemas override earlier ones (right-to-left merge) + composedSchema.Properties.Set(key, value) + } + } + + // Merge required fields + composedSchema.Required = append(composedSchema.Required, allOfSchema.Required...) + + // Use the type and format from the first concrete schema that has them + if composedSchema.Type == nil && allOfSchema.Type != nil { + composedSchema.Type = allOfSchema.Type + } + if composedSchema.Format == "" && allOfSchema.Format != "" { + composedSchema.Format = allOfSchema.Format + } + + // Use description from the last schema that has one + if allOfSchema.Description != "" { + composedSchema.Description = allOfSchema.Description + } + } + + return composedSchema, nil +} + func isStringableType(t string) bool { switch t { case util.OAS_type_integer: @@ -268,3 +354,152 @@ func isStringableType(t string) bool { return false } } + +// flattenDiscriminatorSchema handles discriminator patterns by flattening all properties from the base schema +// and all discriminator sub-schemas into a single comprehensive schema. This ensures all possible fields +// are included in the generated Terraform resource schema. +// +// Discriminators can work with oneOf, anyOf, allOf, or direct mapping patterns. +func flattenDiscriminatorSchema(baseSchema *base.Schema, baseProxy *base.SchemaProxy, globalOpts GlobalSchemaOpts) (*base.Schema, *SchemaError) { + // Start with a copy of the base schema + flattenedSchema := &base.Schema{ + Type: baseSchema.Type, + Format: baseSchema.Format, + Description: baseSchema.Description, + Properties: orderedmap.New[string, *base.SchemaProxy](), + Required: []string{}, + ParentProxy: baseProxy, + } + + // Copy all base schema properties + if baseSchema.Properties != nil { + for pair := range orderedmap.Iterate(context.TODO(), baseSchema.Properties) { + flattenedSchema.Properties.Set(pair.Key(), pair.Value()) + } + } + + // Copy required fields from base schema + flattenedSchema.Required = append(flattenedSchema.Required, baseSchema.Required...) + + // Collect all sub-schemas from different composition patterns + var subSchemaProxies []*base.SchemaProxy + + // Handle oneOf discriminator pattern + if len(baseSchema.OneOf) > 0 { + subSchemaProxies = append(subSchemaProxies, baseSchema.OneOf...) + } + + // Handle anyOf discriminator pattern + if len(baseSchema.AnyOf) > 0 { + subSchemaProxies = append(subSchemaProxies, baseSchema.AnyOf...) + } + + // Handle allOf discriminator pattern (inheritance) + if len(baseSchema.AllOf) > 0 { + subSchemaProxies = append(subSchemaProxies, baseSchema.AllOf...) + } + + // Handle discriminator mapping if no composition keywords but mapping exists + if len(subSchemaProxies) == 0 && baseSchema.Discriminator != nil && baseSchema.Discriminator.Mapping != nil { + // Resolve external schema references from discriminator mapping + mappedSchemas, err := resolveDiscriminatorMapping(baseSchema.Discriminator.Mapping, globalOpts.Document) + if err != nil { + // Log error but continue with base schema - don't fail the entire process + return flattenedSchema, nil + } + subSchemaProxies = append(subSchemaProxies, mappedSchemas...) + } + + // Process each discriminator sub-schema and merge their properties + for _, subProxy := range subSchemaProxies { + subSchema, err := buildSchemaProxy(subProxy, globalOpts) + if err != nil { + // Log warning but continue - don't fail the entire process + continue + } + + // Merge properties from sub-schema + if subSchema.Properties != nil { + for pair := range orderedmap.Iterate(context.TODO(), subSchema.Properties) { + key := pair.Key() + value := pair.Value() + // Only add if property doesn't already exist (base schema takes precedence) + if _, exists := flattenedSchema.Properties.Get(key); !exists { + flattenedSchema.Properties.Set(key, value) + } + } + } + + // Merge required fields from sub-schema + // Note: In discriminator patterns, sub-schema required fields become optional in the flattened schema + // since they're only required for specific discriminator values + for _, requiredField := range subSchema.Required { + // Check if already in required list + found := false + for _, existing := range flattenedSchema.Required { + if existing == requiredField { + found = true + break + } + } + if !found { + // For discriminator sub-schemas, we don't make these fields required in the flattened schema + // They will be conditionally required based on discriminator value at runtime + } + } + } + + return flattenedSchema, nil +} + +// resolveDiscriminatorMapping resolves external schema references from a discriminator mapping +// and returns a slice of schema proxies for all mapped schemas +func resolveDiscriminatorMapping(mapping *orderedmap.Map[string, string], document *high.Document) ([]*base.SchemaProxy, *SchemaError) { + var resolvedProxies []*base.SchemaProxy + + // Iterate through all discriminator mapping entries + for pair := range orderedmap.Iterate(context.TODO(), mapping) { + // discriminatorValue := pair.Key() // e.g., "galaxy", "glue", "hive" + schemaRef := pair.Value() // e.g., "#/components/schemas/S3CatalogGalaxyMetastore" + + // Resolve the schema reference using the document context + resolvedProxy, err := resolveSchemaReference(schemaRef, document) + if err != nil { + // Continue with other mappings if one fails + continue + } + + resolvedProxies = append(resolvedProxies, resolvedProxy) + } + + return resolvedProxies, nil +} + +// resolveSchemaReference resolves a schema reference like "#/components/schemas/SchemaName" +// and returns the corresponding schema proxy +func resolveSchemaReference(reference string, document *high.Document) (*base.SchemaProxy, error) { + // Check if this is a components/schemas reference + if !strings.HasPrefix(reference, "#/components/schemas/") { + return nil, fmt.Errorf("unsupported reference format: %s", reference) + } + + // Extract the schema name from the reference + schemaName := strings.TrimPrefix(reference, "#/components/schemas/") + + // Check if document is provided + if document == nil { + return nil, fmt.Errorf("cannot resolve reference %s: no document provided", reference) + } + + // Look up the schema in the document's components + if document.Components == nil || document.Components.Schemas == nil { + return nil, fmt.Errorf("cannot resolve reference %s: no components/schemas in document", reference) + } + + schemaProxy, exists := document.Components.Schemas.Get(schemaName) + if !exists { + return nil, fmt.Errorf("schema not found: %s", schemaName) + } + + return schemaProxy, nil +} diff --git a/internal/mapper/oas/oas_schema.go b/internal/mapper/oas/oas_schema.go index 29b4ec86..06130b3c 100644 --- a/internal/mapper/oas/oas_schema.go +++ b/internal/mapper/oas/oas_schema.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-codegen-spec/schema" "github.com/pb33f/libopenapi/datamodel/high/base" + high "github.com/pb33f/libopenapi/datamodel/high/v3" "github.com/pb33f/libopenapi/orderedmap" ) @@ -31,6 +32,13 @@ type GlobalSchemaOpts struct { // create request for a resource, does not become required from a lower precedence operation, such as an // read response for a resource. OverrideComputability schema.ComputedOptionalRequired + + // Document provides access to the full OpenAPI document for resolving external schema references + // in discriminator mappings (e.g., "#/components/schemas/S3CatalogGalaxyMetastore") + Document *high.Document + + // DiscriminatorDepth tracks recursion depth to prevent infinite loops in discriminator resolution + DiscriminatorDepth int } // SchemaOpts is NOT passed recursively through built OASSchema structs, and will only be available to the top level schema. This is used diff --git a/internal/mapper/provider_mapper.go b/internal/mapper/provider_mapper.go index 7950c7d6..be8ff2cb 100644 --- a/internal/mapper/provider_mapper.go +++ b/internal/mapper/provider_mapper.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-codegen-openapi/internal/log" "github.com/hashicorp/terraform-plugin-codegen-openapi/internal/mapper/oas" "github.com/hashicorp/terraform-plugin-codegen-spec/provider" + high "github.com/pb33f/libopenapi/datamodel/high/v3" ) var _ ProviderMapper = providerMapper{} @@ -22,13 +23,15 @@ type ProviderMapper interface { type providerMapper struct { provider explorer.Provider + document *high.Document //nolint:unused // Might be useful later! cfg config.Config } -func NewProviderMapper(exploredProvider explorer.Provider, cfg config.Config) ProviderMapper { +func NewProviderMapper(exploredProvider explorer.Provider, document *high.Document, cfg config.Config) ProviderMapper { return providerMapper{ provider: exploredProvider, + document: document, cfg: cfg, } } @@ -44,7 +47,7 @@ func (m providerMapper) MapToIR(logger *slog.Logger) (*provider.Provider, error) pLogger := logger.With("provider", providerIR.Name) - providerSchema, err := generateProviderSchema(pLogger, m.provider) + providerSchema, err := generateProviderSchema(pLogger, m.provider, m.document) if err != nil { return nil, err } @@ -53,13 +56,15 @@ func (m providerMapper) MapToIR(logger *slog.Logger) (*provider.Provider, error) return &providerIR, nil } -func generateProviderSchema(logger *slog.Logger, exploredProvider explorer.Provider) (*provider.Schema, error) { +func generateProviderSchema(logger *slog.Logger, exploredProvider explorer.Provider, document *high.Document) (*provider.Schema, error) { providerSchema := &provider.Schema{} schemaOpts := oas.SchemaOpts{ Ignores: exploredProvider.Ignores, } - s, err := oas.BuildSchema(exploredProvider.SchemaProxy, schemaOpts, oas.GlobalSchemaOpts{}) + s, err := oas.BuildSchema(exploredProvider.SchemaProxy, schemaOpts, oas.GlobalSchemaOpts{ + Document: document, + }) if err != nil { return nil, err } diff --git a/internal/mapper/resource_mapper.go b/internal/mapper/resource_mapper.go index 1b393db2..751da996 100644 --- a/internal/mapper/resource_mapper.go +++ b/internal/mapper/resource_mapper.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-codegen-openapi/internal/mapper/util" "github.com/hashicorp/terraform-plugin-codegen-spec/resource" "github.com/hashicorp/terraform-plugin-codegen-spec/schema" + high "github.com/pb33f/libopenapi/datamodel/high/v3" ) var _ ResourceMapper = resourceMapper{} @@ -25,13 +26,15 @@ type ResourceMapper interface { type resourceMapper struct { resources map[string]explorer.Resource + document *high.Document //nolint:unused // Might be useful later! cfg config.Config } -func NewResourceMapper(resources map[string]explorer.Resource, cfg config.Config) ResourceMapper { +func NewResourceMapper(resources map[string]explorer.Resource, document *high.Document, cfg config.Config) ResourceMapper { return resourceMapper{ resources: resources, + document: document, cfg: cfg, } } @@ -45,7 +48,7 @@ func (m resourceMapper) MapToIR(logger *slog.Logger) ([]resource.Resource, error explorerResource := m.resources[name] rLogger := logger.With("resource", name) - schema, err := generateResourceSchema(rLogger, explorerResource) + schema, err := generateResourceSchema(rLogger, explorerResource, m.document) if err != nil { log.WarnLogOnError(rLogger, err, "skipping resource schema mapping") continue @@ -60,7 +63,7 @@ func (m resourceMapper) MapToIR(logger *slog.Logger) ([]resource.Resource, error return resourceSchemas, nil } -func generateResourceSchema(logger *slog.Logger, explorerResource explorer.Resource) (*resource.Schema, error) { +func generateResourceSchema(logger *slog.Logger, explorerResource explorer.Resource, document *high.Document) (*resource.Schema, error) { resourceSchema := &resource.Schema{ Attributes: []resource.Attribute{}, } @@ -73,7 +76,9 @@ func generateResourceSchema(logger *slog.Logger, explorerResource explorer.Resou schemaOpts := oas.SchemaOpts{ Ignores: explorerResource.SchemaOptions.Ignores, } - createRequestSchema, err := oas.BuildSchemaFromRequest(explorerResource.CreateOp, schemaOpts, oas.GlobalSchemaOpts{}) + createRequestSchema, err := oas.BuildSchemaFromRequest(explorerResource.CreateOp, schemaOpts, oas.GlobalSchemaOpts{ + Document: document, + }) if err != nil { return nil, err } @@ -93,6 +98,7 @@ func generateResourceSchema(logger *slog.Logger, explorerResource explorer.Resou } globalSchemaOpts := oas.GlobalSchemaOpts{ OverrideComputability: schema.Computed, + Document: document, } createResponseSchema, err := oas.BuildSchemaFromResponse(explorerResource.CreateOp, schemaOpts, globalSchemaOpts) if err != nil { @@ -121,6 +127,7 @@ func generateResourceSchema(logger *slog.Logger, explorerResource explorer.Resou } globalSchemaOpts = oas.GlobalSchemaOpts{ OverrideComputability: schema.Computed, + Document: document, } readResponseSchema, err := oas.BuildSchemaFromResponse(explorerResource.ReadOp, schemaOpts, globalSchemaOpts) if err != nil { @@ -151,7 +158,10 @@ func generateResourceSchema(logger *slog.Logger, explorerResource explorer.Resou Ignores: explorerResource.SchemaOptions.Ignores, OverrideDescription: param.Description, } - globalSchemaOpts := oas.GlobalSchemaOpts{OverrideComputability: schema.ComputedOptional} + globalSchemaOpts := oas.GlobalSchemaOpts{ + OverrideComputability: schema.ComputedOptional, + Document: document, + } s, schemaErr := oas.BuildSchema(param.Schema, schemaOpts, globalSchemaOpts) if schemaErr != nil {