diff --git a/Package.swift b/Package.swift index 637a25e37..b2f52ee81 100644 --- a/Package.swift +++ b/Package.swift @@ -46,7 +46,7 @@ let package = Package( .package(url: "https://github.com/apple/swift-collections", from: "1.1.4"), // Read OpenAPI documents - .package(url: "https://github.com/mattpolzin/OpenAPIKit", from: "3.9.0"), + .package(url: "https://github.com/mattpolzin/OpenAPIKit", from: "6.0.0", traits: []), .package(url: "https://github.com/jpsim/Yams", "4.0.0"..<"7.0.0"), // CLI Tool @@ -165,4 +165,4 @@ for target in package.targets { case .macro, .plugin, .system, .binary: () // not applicable @unknown default: () // we don't know what to do here, do nothing } -}// --- END: STANDARD CROSS-REPO SETTINGS DO NOT EDIT --- // +} // --- END: STANDARD CROSS-REPO SETTINGS DO NOT EDIT --- // diff --git a/Sources/_OpenAPIGeneratorCore/Extensions/OpenAPIKit.swift b/Sources/_OpenAPIGeneratorCore/Extensions/OpenAPIKit.swift index ea055e6bb..791c11c11 100644 --- a/Sources/_OpenAPIGeneratorCore/Extensions/OpenAPIKit.swift +++ b/Sources/_OpenAPIGeneratorCore/Extensions/OpenAPIKit.swift @@ -22,12 +22,42 @@ extension Either { /// - Throws: An error if there's an issue looking up the value in the components. func resolve(in components: OpenAPI.Components) throws -> B where A == OpenAPI.Reference { switch self { - case let .a(a): return try components.lookup(a) + case let .a(a): return try components.assumeLookupOnce(a) case let .b(b): return b } } } +extension OpenAPI.Components { + func assumeLookupOnce(_ reference: OpenAPI.Reference) + throws -> ReferenceType + { + guard let result = try lookupOnce(reference).b else { + throw JSONReferenceParsingError.componentsReferenceEntryUnsupported(reference.absoluteString) + } + return result + } + + func assumeLookupOnce(_ reference: JSONReference) throws + -> ReferenceType + { + guard let result = try lookupOnce(reference).b else { + throw JSONReferenceParsingError.componentsReferenceEntryUnsupported(reference.absoluteString) + } + return result + } + + func assumeLookupOnce( + _ maybeReference: Either, ReferenceType> + ) throws -> ReferenceType { + guard let result = try lookupOnce(maybeReference).b else { + throw JSONReferenceParsingError.componentsReferenceEntryUnsupported(maybeReference.a?.absoluteString) + } + return result + } + +} + extension JSONSchema.Schema { /// Returns the name of the schema. diff --git a/Sources/_OpenAPIGeneratorCore/Hooks/FilteredDocument.swift b/Sources/_OpenAPIGeneratorCore/Hooks/FilteredDocument.swift index 82b32d88d..e057eb9fc 100644 --- a/Sources/_OpenAPIGeneratorCore/Hooks/FilteredDocument.swift +++ b/Sources/_OpenAPIGeneratorCore/Hooks/FilteredDocument.swift @@ -103,8 +103,8 @@ struct FilteredDocumentBuilder { guard let methods = requiredEndpoints[path] else { continue } switch pathItem { case .a(let reference): - components.pathItems[try reference.internalComponentKey] = try document.components.lookup(reference) - .filteringEndpoints { methods.contains($0.method) } + components.pathItems[try reference.internalComponentKey] = try document.components + .assumeLookupOnce(reference).filteringEndpoints { methods.contains($0.method) } case .b(let pathItem): filteredDocument.paths[path] = .b(pathItem.filteringEndpoints { methods.contains($0.method) }) } @@ -169,7 +169,7 @@ struct FilteredDocumentBuilder { /// - Parameter name: The key in the `#/components/schemas` map in the OpenAPI document. /// - Throws: If the named schema does not exist in original OpenAPI document. mutating func includeSchema(_ name: String) throws { - try includeSchema(.a(OpenAPI.Reference.component(named: name))) + try includeComponentsReferencedBy(.reference(.component(named: name))) } } @@ -198,16 +198,7 @@ private extension FilteredDocumentBuilder { switch maybeReference { case .a(let reference): guard try requiredPathItemReferences.insert(reference.internalComponentKey).inserted else { return } - try includeComponentsReferencedBy(try document.components.lookup(reference)) - case .b(let value): try includeComponentsReferencedBy(value) - } - } - - mutating func includeSchema(_ maybeReference: Either, JSONSchema>) throws { - switch maybeReference { - case .a(let reference): - guard try requiredSchemaReferences.insert(reference.internalComponentKey).inserted else { return } - try includeComponentsReferencedBy(try document.components.lookup(reference)) + try includeComponentsReferencedBy(try document.components.assumeLookupOnce(reference)) case .b(let value): try includeComponentsReferencedBy(value) } } @@ -218,7 +209,7 @@ private extension FilteredDocumentBuilder { switch maybeReference { case .a(let reference): guard try requiredParameterReferences.insert(reference.internalComponentKey).inserted else { return } - try includeComponentsReferencedBy(try document.components.lookup(reference)) + try includeComponentsReferencedBy(try document.components.assumeLookupOnce(reference)) case .b(let value): try includeComponentsReferencedBy(value) } } @@ -229,7 +220,7 @@ private extension FilteredDocumentBuilder { switch maybeReference { case .a(let reference): guard try requiredResponseReferences.insert(reference.internalComponentKey).inserted else { return } - try includeComponentsReferencedBy(try document.components.lookup(reference)) + try includeComponentsReferencedBy(try document.components.assumeLookupOnce(reference)) case .b(let value): try includeComponentsReferencedBy(value) } } @@ -238,7 +229,7 @@ private extension FilteredDocumentBuilder { switch maybeReference { case .a(let reference): guard try requiredHeaderReferences.insert(reference.internalComponentKey).inserted else { return } - try includeComponentsReferencedBy(try document.components.lookup(reference)) + try includeComponentsReferencedBy(try document.components.assumeLookupOnce(reference)) case .b(let value): try includeComponentsReferencedBy(value) } } @@ -247,7 +238,7 @@ private extension FilteredDocumentBuilder { switch maybeReference { case .a(let reference): guard try requiredLinkReferences.insert(reference.internalComponentKey).inserted else { return } - try includeComponentsReferencedBy(try document.components.lookup(reference)) + try includeComponentsReferencedBy(try document.components.assumeLookupOnce(reference)) case .b(let value): try includeComponentsReferencedBy(value) } } @@ -258,7 +249,7 @@ private extension FilteredDocumentBuilder { switch maybeReference { case .a(let reference): guard try requiredCallbacksReferences.insert(reference.internalComponentKey).inserted else { return } - try includeComponentsReferencedBy(try document.components.lookup(reference)) + try includeComponentsReferencedBy(try document.components.assumeLookupOnce(reference)) case .b(let value): try includeComponentsReferencedBy(value) } } @@ -269,7 +260,7 @@ private extension FilteredDocumentBuilder { switch maybeReference { case .a(let reference): guard try requiredRequestReferences.insert(reference.internalComponentKey).inserted else { return } - try includeComponentsReferencedBy(try document.components.lookup(reference)) + try includeComponentsReferencedBy(try document.components.assumeLookupOnce(reference)) case .b(let value): try includeComponentsReferencedBy(value) } } @@ -278,7 +269,7 @@ private extension FilteredDocumentBuilder { switch maybeReference { case .a(let reference): guard try requiredExampleReferences.insert(reference.internalComponentKey).inserted else { return } - try includeComponentsReferencedBy(try document.components.lookup(reference)) + try includeComponentsReferencedBy(try document.components.assumeLookupOnce(reference)) case .b(let value): try includeComponentsReferencedBy(value) } } @@ -287,7 +278,7 @@ private extension FilteredDocumentBuilder { for (path, maybePathItemReference) in document.paths { let originalPathItem: OpenAPI.PathItem switch maybePathItemReference { - case .a(let reference): originalPathItem = try document.components.lookup(reference) + case .a(let reference): originalPathItem = try document.components.assumeLookupOnce(reference) case .b(let pathItem): originalPathItem = pathItem } @@ -330,7 +321,7 @@ private extension FilteredDocumentBuilder { case .reference(let reference, _): let referenceKey = try OpenAPI.ComponentKey(stringLiteral: reference.requiredName) guard requiredSchemaReferences.insert(referenceKey).inserted else { return } - try includeComponentsReferencedBy(document.components.lookup(reference)) + try includeComponentsReferencedBy(document.components.lookupOnce(reference).flattenToJsonSchema()) case .object(_, let object): for schema in object.properties.values { try includeComponentsReferencedBy(schema) } @@ -362,20 +353,22 @@ private extension FilteredDocumentBuilder { switch schemaContext.schema { case .a(let reference): guard try requiredSchemaReferences.insert(reference.internalComponentKey).inserted else { return } - try includeComponentsReferencedBy(try document.components.lookup(reference)) + try includeComponentsReferencedBy(try document.components.assumeLookupOnce(reference)) case .b(let schema): try includeComponentsReferencedBy(schema) } - case .b(let contentMap): - for value in contentMap.values { - switch value.schema { - case .a(let reference): - guard try requiredSchemaReferences.insert(reference.internalComponentKey).inserted else { return } - try includeComponentsReferencedBy(try document.components.lookup(reference)) - case .b(let schema): try includeComponentsReferencedBy(schema) - case .none: continue - } - } + case .b(let contentMap): for value in contentMap.values { try includeComponentsReferencedBy(value) } + } + } + + mutating func includeComponentsReferencedBy( + _ contentMapEntry: Either, OpenAPI.Content> + ) throws { + let content: OpenAPI.Content + switch contentMapEntry { + case .a(let ref): content = try document.components.assumeLookupOnce(ref) + case .b(let value): content = value } + try includeComponentsReferencedBy(content) } mutating func includeComponentsReferencedBy(_ response: OpenAPI.Response) throws { @@ -385,8 +378,8 @@ private extension FilteredDocumentBuilder { } mutating func includeComponentsReferencedBy(_ content: OpenAPI.Content) throws { - if let schema = content.schema { try includeSchema(schema) } - if let encoding = content.encoding { + if let schema = content.schema { try includeComponentsReferencedBy(schema) } + if let encoding = content.encodingMap { for encoding in encoding.values { if let headers = encoding.headers { for header in headers.values { try includeHeader(header) } } } diff --git a/Sources/_OpenAPIGeneratorCore/Parser/YamsParser.swift b/Sources/_OpenAPIGeneratorCore/Parser/YamsParser.swift index ac646b56b..2e8f4d404 100644 --- a/Sources/_OpenAPIGeneratorCore/Parser/YamsParser.swift +++ b/Sources/_OpenAPIGeneratorCore/Parser/YamsParser.swift @@ -54,13 +54,6 @@ public struct YamsParser: ParserProtocol { let decoder = YAMLDecoder() let openapiData = input.contents - let decodingOptions = [ - DocumentConfiguration.versionMapKey: [ - // Until we move to OpenAPIKit v5.0+ we will parse OAS 3.2.0 as if it were OAS 3.1.2 - "3.2.0": OpenAPI.Document.Version.v3_1_2 - ] - ] - struct OpenAPIVersionedDocument: Decodable { var openapi: String? } let versionedDocument: OpenAPIVersionedDocument @@ -82,12 +75,7 @@ public struct YamsParser: ParserProtocol { document = openAPI30Document.convert(to: .v3_1_0) case "3.1.0", "3.1.1", "3.1.2": document = try decoder.decode(OpenAPIKit.OpenAPI.Document.self, from: input.contents) - case "3.2.0": - document = try decoder.decode( - OpenAPIKit.OpenAPI.Document.self, - from: input.contents, - userInfo: decodingOptions - ) + case "3.2.0": document = try decoder.decode(OpenAPIKit.OpenAPI.Document.self, from: input.contents) default: throw Diagnostic.openAPIVersionError( versionString: "openapi: \(openAPIVersion)", diff --git a/Sources/_OpenAPIGeneratorCore/Parser/validateDoc.swift b/Sources/_OpenAPIGeneratorCore/Parser/validateDoc.swift index 8b83a9505..ed6e08a20 100644 --- a/Sources/_OpenAPIGeneratorCore/Parser/validateDoc.swift +++ b/Sources/_OpenAPIGeneratorCore/Parser/validateDoc.swift @@ -67,6 +67,7 @@ func validateContentTypes(in doc: ParsedOpenAPIRepresentation, validate: (String } for (key, component) in doc.components.requestBodies { + let component = try doc.components.assumeLookupOnce(component) for contentType in component.content.keys { if !validate(contentType.rawValue) { throw Diagnostic.error( @@ -81,6 +82,7 @@ func validateContentTypes(in doc: ParsedOpenAPIRepresentation, validate: (String } for (key, component) in doc.components.responses { + let component = try doc.components.assumeLookupOnce(component) for contentType in component.content.keys { if !validate(contentType.rawValue) { throw Diagnostic.error( @@ -124,21 +126,26 @@ func validateReferences(in doc: ParsedOpenAPIRepresentation) throws { func validateReferencesInContentTypes(_ content: OpenAPI.Content.Map, location: String) throws { for (contentKey, contentType) in content { - if let reference = contentType.schema?.reference { - try validateReference( - reference, - in: doc.components, - location: location + "/content/\(contentKey.rawValue)/schema" - ) - } - if let eitherExamples = contentType.examples?.values { - for example in eitherExamples { - if let reference = example.reference { - try validateReference( - reference, - in: doc.components, - location: location + "/content/\(contentKey.rawValue)/examples" - ) + switch contentType { + case .a(let ref): + try validateReference(ref, in: doc.components, location: location + "/content/\(contentKey.rawValue)") + case .b(let contentType): + if let reference: JSONReference = contentType.schema?.reference { + try validateReference( + .init(reference), + in: doc.components, + location: location + "/content/\(contentKey.rawValue)/schema" + ) + } + if let eitherExamples = contentType.examples?.values { + for example in eitherExamples { + if let reference = example.reference { + try validateReference( + reference, + in: doc.components, + location: location + "/content/\(contentKey.rawValue)/examples" + ) + } } } } diff --git a/Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/translateRawEnum.swift b/Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/translateRawEnum.swift index 737abe571..e1b984ebc 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/translateRawEnum.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/CommonTranslations/translateRawEnum.swift @@ -100,7 +100,9 @@ extension FileTranslator { // In nullable enum schemas, empty strings are parsed as Void. // This is unlikely to be fixed, so handling that case here. // https://github.com/apple/swift-openapi-generator/issues/118 - if isNullable && anyValue is Void { + // Also handle nil values in nullable schemas. + let isNullValue = anyValue is Void || (anyValue as? String) == nil + if isNullable && isNullValue { try addIfUnique(id: .string(""), caseName: context.safeNameGenerator.swiftMemberName(for: "")) } else { guard let rawValue = anyValue as? String else { diff --git a/Sources/_OpenAPIGeneratorCore/Translator/Content/ContentInspector.swift b/Sources/_OpenAPIGeneratorCore/Translator/Content/ContentInspector.swift index cc4cf8fc2..bf149670b 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/Content/ContentInspector.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/Content/ContentInspector.swift @@ -97,7 +97,7 @@ extension FileTranslator { return try contents.compactMap { key, value in try parseContentIfSupported( contentKey: key, - contentValue: value, + contentValue: try components.assumeLookupOnce(value), excludeBinary: excludeBinary, isRequired: isRequired, foundIn: foundIn + "/\(key.rawValue)" @@ -129,12 +129,17 @@ extension FileTranslator { let chosenContent: (type: ContentType, schema: SchemaContent, content: OpenAPI.Content)? if let (contentType, contentValue) = mapWithContentTypes.first(where: { $0.type.isJSON }) { - chosenContent = (contentType, .init(contentType: contentType, schema: contentValue.schema), contentValue) + let contentValue = try components.assumeLookupOnce(contentValue) + chosenContent = ( + contentType, .init(contentType: contentType, schema: contentValue.schema.map(Either.schema)), + contentValue + ) } else if !excludeBinary, let (contentType, contentValue) = mapWithContentTypes.first(where: { $0.type.isBinary }) { + let contentValue = try components.assumeLookupOnce(contentValue) chosenContent = ( - contentType, .init(contentType: contentType, schema: .b(.string(contentEncoding: .binary))), + contentType, .init(contentType: contentType, schema: .schema(.string(contentEncoding: .binary))), contentValue ) } else { @@ -188,8 +193,10 @@ extension FileTranslator { foundIn: "\(foundIn), content \(contentType.originallyCasedTypeAndSubtype)" ) } - if contentType.isJSON { return .init(contentType: contentType, schema: contentValue.schema) } - if contentType.isUrlEncodedForm { return .init(contentType: contentType, schema: contentValue.schema) } + if contentType.isJSON { return .init(contentType: contentType, schema: contentValue.schema.map(Either.schema)) } + if contentType.isUrlEncodedForm { + return .init(contentType: contentType, schema: contentValue.schema.map(Either.schema)) + } if contentType.isMultipart { guard isRequired else { try diagnostics.emit( @@ -201,10 +208,14 @@ extension FileTranslator { ) return nil } - return .init(contentType: contentType, schema: contentValue.schema, encoding: contentValue.encoding) + return .init( + contentType: contentType, + schema: contentValue.schema.map(Either.schema), + encoding: contentValue.encodingMap + ) } if !excludeBinary, contentType.isBinary { - return .init(contentType: contentType, schema: .b(.string(contentEncoding: .binary))) + return .init(contentType: contentType, schema: .schema(.string(contentEncoding: .binary))) } try diagnostics.emitUnsupported("Unsupported content", foundIn: foundIn) return nil diff --git a/Sources/_OpenAPIGeneratorCore/Translator/Multipart/MultipartContentInspector.swift b/Sources/_OpenAPIGeneratorCore/Translator/Multipart/MultipartContentInspector.swift index 19dcf61f3..540d3beaf 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/Multipart/MultipartContentInspector.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/Multipart/MultipartContentInspector.swift @@ -297,16 +297,15 @@ extension FileTranslator { case .string(_, let context): candidateSource = try inferStringContent(context) case .object, .all, .one, .any, .fragment, .array: candidateSource = .infer(.complex) case .reference(let ref, _): - guard let source = try inferSchema(components.lookup(ref))?.1 else { return nil } + guard let source = try inferSchema(components.assumeLookupOnce(ref))?.1 else { return nil } candidateSource = source } } else { candidateSource = .infer(.complex) } case .reference(let ref, _): - guard let (refRepetitionKind, refCandidateSource) = try inferSchema(components.lookup(ref)) else { - return nil - } + guard let (refRepetitionKind, refCandidateSource) = try inferSchema(components.assumeLookupOnce(ref)) + else { return nil } repetitionKind = refRepetitionKind candidateSource = refCandidateSource } @@ -365,7 +364,12 @@ extension FileTranslator { func visitContentMap(_ contentMap: OpenAPI.Content.Map) throws { for (key, value) in contentMap { guard try key.asGeneratorContentType.isMultipart else { continue } - guard let schema = value.schema, case let .a(ref) = schema, let name = ref.name, + let content: OpenAPI.Content + switch value { + case .a(let ref): content = try components.assumeLookupOnce(ref) + case .b(let value): content = value + } + guard let ref = content.schema?.reference, let name = ref.name, let componentKey = OpenAPI.ComponentKey(rawValue: name) else { continue } refs.insert(componentKey) @@ -377,7 +381,7 @@ extension FileTranslator { if let requestBodyEither = operation.requestBody { let requestBody: OpenAPI.Request switch requestBodyEither { - case .a(let ref): requestBody = try components.lookup(ref) + case .a(let ref): requestBody = try components.assumeLookupOnce(ref) case .b(let value): requestBody = value } try visitContentMap(requestBody.content) @@ -385,7 +389,7 @@ extension FileTranslator { for responseOutcome in operation.responseOutcomes { let response: OpenAPI.Response switch responseOutcome.response { - case .a(let ref): response = try components.lookup(ref) + case .a(let ref): response = try components.assumeLookupOnce(ref) case .b(let value): response = value } try visitContentMap(response.content) @@ -395,7 +399,7 @@ extension FileTranslator { for (_, value) in paths { let pathItem: OpenAPI.PathItem switch value { - case .a(let ref): pathItem = try components.lookup(ref) + case .a(let ref): pathItem = try components.assumeLookupOnce(ref) case .b(let value): pathItem = value } try visitPath(pathItem) diff --git a/Sources/_OpenAPIGeneratorCore/Translator/Parameters/TypedParameter.swift b/Sources/_OpenAPIGeneratorCore/Translator/Parameters/TypedParameter.swift index 7ac1f18fc..e3953b2cf 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/Parameters/TypedParameter.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/Parameters/TypedParameter.swift @@ -99,7 +99,7 @@ extension FileTranslator { // Collect the parameter let parameter: OpenAPI.Parameter switch unresolvedParameter { - case let .a(ref): parameter = try components.lookup(ref) + case let .a(ref): parameter = try components.assumeLookupOnce(ref) case let .b(_parameter): parameter = _parameter } @@ -151,6 +151,9 @@ extension FileTranslator { case .cookie: try diagnostics.emitUnsupported("Cookie params", foundIn: foundIn) return nil + case .querystring: + try diagnostics.emitUnsupported("QueryString params", foundIn: foundIn) + return nil } case let .b(contentMap): @@ -165,14 +168,11 @@ extension FileTranslator { codingStrategy = typedContent.content.contentType.codingStrategy // Defaults are defined by the OpenAPI specification: - // https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#fixed-fields-10 - switch parameter.location { - case .query, .cookie: - style = .form - explode = true - case .path, .header: - style = .simple - explode = false + // https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.2.0.md#fixed-fields-10 + style = OpenAPI.Parameter.SchemaContext.Style.default(for: parameter.location) + switch style { + case .form: explode = true + default: explode = false } } @@ -221,6 +221,7 @@ extension OpenAPI.Parameter.Context.Location { case .header: return "Headers" case .query: return "Query" case .cookie: return "Cookies" + case .querystring: return "QueryString" } } @@ -236,6 +237,7 @@ extension OpenAPI.Parameter.Context.Location { case .header: return "headers" case .query: return "query" case .cookie: return "cookies" + case .querystring: return "querystring" } } } diff --git a/Sources/_OpenAPIGeneratorCore/Translator/RequestBody/TypedRequestBody.swift b/Sources/_OpenAPIGeneratorCore/Translator/RequestBody/TypedRequestBody.swift index 2279bd1a2..707534585 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/RequestBody/TypedRequestBody.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/RequestBody/TypedRequestBody.swift @@ -73,7 +73,7 @@ extension FileTranslator { let isInlined: Bool switch unresolvedRequest { case .a(let reference): - request = try components.lookup(reference) + request = try components.assumeLookupOnce(reference) isInlined = false case .b(let _request): request = _request diff --git a/Sources/_OpenAPIGeneratorCore/Translator/Responses/TypedResponse.swift b/Sources/_OpenAPIGeneratorCore/Translator/Responses/TypedResponse.swift index ee4e371f0..78f9b7318 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/Responses/TypedResponse.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/Responses/TypedResponse.swift @@ -45,7 +45,7 @@ extension FileTranslator { switch unresolvedResponse { case .a(let reference): typeName = try typeAssigner.typeName(for: reference) - response = try components.lookup(reference) + response = try components.assumeLookupOnce(reference) isInlined = false case .b(let _response): let responseKind = outcome.status.value.asKind diff --git a/Sources/_OpenAPIGeneratorCore/Translator/Responses/TypedResponseHeader.swift b/Sources/_OpenAPIGeneratorCore/Translator/Responses/TypedResponseHeader.swift index f8b0e866b..78e127a54 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/Responses/TypedResponseHeader.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/Responses/TypedResponseHeader.swift @@ -105,7 +105,7 @@ extension FileTranslator { // Collect the header let header: OpenAPI.Header switch unresolvedResponseHeader { - case let .a(ref): header = try components.lookup(ref) + case let .a(ref): header = try components.assumeLookupOnce(ref) case let .b(_header): header = _header } @@ -132,12 +132,16 @@ extension FileTranslator { switch unresolvedResponseHeader { case let .a(ref): type = try typeAssigner.typeName(for: ref).asUsage case .b: - switch schema { - case let .a(reference): type = try typeAssigner.typeName(for: reference).asUsage - case let .b(schema): + // we want to look under both OpenAPI.Reference and + // JSONSchema.reference so we flatten the value before inspecting + // it: + let unboxedSchema = schema.flattenToJsonSchema() + switch unboxedSchema.value { + case let .reference(reference, _): type = try typeAssigner.typeName(for: reference).asUsage + default: type = try typeAssigner.typeUsage( forParameterNamed: name, - withSchema: schema, + withSchema: unboxedSchema, components: components, inParent: parent ) diff --git a/Sources/_OpenAPIGeneratorCore/Translator/TypeAssignment/TypeAssigner.swift b/Sources/_OpenAPIGeneratorCore/Translator/TypeAssignment/TypeAssigner.swift index 1555731d3..c011a0bc4 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/TypeAssignment/TypeAssigner.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/TypeAssignment/TypeAssigner.swift @@ -91,12 +91,16 @@ struct TypeAssigner { ) throws -> TypeUsage? { let associatedType: TypeUsage? if let schema { - switch schema { - case let .a(reference): associatedType = try typeName(for: reference).asUsage - case let .b(schema): + // we want to look under both OpenAPI.Reference and + // JSONSchema.reference so we flatten the value before inspecting + // it: + let unboxedSchema = schema.flattenToJsonSchema() + switch unboxedSchema.value { + case let .reference(reference, _): associatedType = try typeName(for: reference).asUsage + default: associatedType = try _typeUsage( forPotentiallyInlinedValueNamed: hint, - withSchema: schema, + withSchema: unboxedSchema, components: components, inParent: parent, subtype: .appendScope @@ -534,6 +538,10 @@ enum JSONReferenceParsingError: Swift.Error { /// An error thrown when parsing a JSON reference that points to /// other OpenAPI documents. case externalPathsUnsupported(String) + + // An error thrown when following a reference leads to a component entry + // that is itself a reference. + case componentsReferenceEntryUnsupported(String?) } extension JSONReferenceParsingError: CustomStringConvertible { @@ -543,6 +551,8 @@ extension JSONReferenceParsingError: CustomStringConvertible { return "JSON references outside of #/components are not supported, found: \(string ?? "")" case let .externalPathsUnsupported(string): return "External JSON references are not supported, found: \(string)" + case let .componentsReferenceEntryUnsupported(string): + return "#/components entries that are themselves references are not supported, found: \(string ?? "")" } } } diff --git a/Sources/_OpenAPIGeneratorCore/Translator/TypeAssignment/TypeMatcher.swift b/Sources/_OpenAPIGeneratorCore/Translator/TypeAssignment/TypeMatcher.swift index 1c503ae74..2dd1b16e5 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/TypeAssignment/TypeMatcher.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/TypeAssignment/TypeMatcher.swift @@ -156,8 +156,16 @@ struct TypeMatcher { ) -> OpenAPI.Reference? { // If the schema is a ref AND no encoding is provided, we can reference the type. // Otherwise, we must inline. - guard case .a(let ref) = schema, encoding == nil || encoding!.isEmpty else { return nil } - return ref + guard let schema, encoding == nil || encoding!.isEmpty else { return nil } + + // we want to look under both OpenAPI.Reference and + // JSONSchema.reference so we flatten the value before inspecting + // it: + let unboxedSchema = schema.flattenToJsonSchema() + switch unboxedSchema.value { + case let .reference(ref, _): return .init(ref) + default: return nil + } } /// Returns a Boolean value that indicates whether the schema @@ -199,7 +207,7 @@ struct TypeMatcher { // only key-value pair schemas can be valid recursive types. return true } - let targetSchema = try components.lookup(ref) + let targetSchema = try components.assumeLookupOnce(ref) try referenceStack.push(ref) defer { referenceStack.pop() } return try isKeyValuePair(targetSchema, referenceStack: &referenceStack, components: components) @@ -231,7 +239,7 @@ struct TypeMatcher { } let schemaToCheck: JSONSchema switch schema { - case .a(let ref): schemaToCheck = try components.lookup(ref) + case .a(let ref): schemaToCheck = try components.assumeLookupOnce(ref) case let .b(schema): schemaToCheck = schema } return try isKeyValuePair(schemaToCheck, referenceStack: &referenceStack, components: components) @@ -246,7 +254,7 @@ struct TypeMatcher { func isOptional(_ schema: JSONSchema, components: OpenAPI.Components) throws -> Bool { if schema.nullable || !schema.required { return true } guard case .reference(let ref, _) = schema.value else { return false } - let targetSchema = try components.lookup(ref) + let targetSchema = try components.assumeLookupOnce(ref) return try isOptional(targetSchema, components: components) } @@ -263,7 +271,7 @@ struct TypeMatcher { } switch schema { case .a(let ref): - let targetSchema = try components.lookup(ref) + let targetSchema = try components.assumeLookupOnce(ref) return try isOptional(targetSchema, components: components) case .b(let schema): return try isOptional(schema, components: components) } diff --git a/Sources/_OpenAPIGeneratorCore/Translator/TypeAssignment/isSchemaSupported.swift b/Sources/_OpenAPIGeneratorCore/Translator/TypeAssignment/isSchemaSupported.swift index 49fb4466f..1a4153121 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/TypeAssignment/isSchemaSupported.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/TypeAssignment/isSchemaSupported.swift @@ -145,7 +145,7 @@ extension FileTranslator { return .supported } // reference is supported iff the existing type is supported - let existingSchema = try components.lookup(ref) + let existingSchema = try components.assumeLookupOnce(ref) try referenceStack.push(ref) defer { referenceStack.pop() } return try isSchemaSupported(existingSchema, referenceStack: &referenceStack) @@ -192,11 +192,15 @@ extension FileTranslator { // fragment type is supported return .supported } - switch schema { - case .a: + // we want to look under both OpenAPI.Reference and + // JSONSchema.reference so we flatten the value before inspecting + // it: + let unboxedSchema = schema.flattenToJsonSchema() + switch unboxedSchema.value { + case .reference: // references are supported return .supported - case let .b(schema): return try isSchemaSupported(schema, referenceStack: &referenceStack) + default: return try isSchemaSupported(unboxedSchema, referenceStack: &referenceStack) } } @@ -315,7 +319,7 @@ extension FileTranslator { return .supported } // reference is supported iff the existing type is supported - let referencedSchema = try components.lookup(ref) + let referencedSchema = try components.assumeLookupOnce(ref) try referenceStack.push(ref) defer { referenceStack.pop() } return try isObjectishSchemaAndSupported(referencedSchema, referenceStack: &referenceStack) @@ -365,7 +369,7 @@ extension FileTranslator { return .supported } // reference is supported iff the existing type is supported - let referencedSchema = try components.lookup(ref) + let referencedSchema = try components.assumeLookupOnce(ref) try referenceStack.push(ref) defer { referenceStack.pop() } return try isObjectOrRefToObjectSchemaAndSupported(referencedSchema, referenceStack: &referenceStack) @@ -391,7 +395,7 @@ extension FileTranslator { return nil } // reference is supported iff the existing type is supported - let referencedSchema = try components.lookup(ref) + let referencedSchema = try components.assumeLookupOnce(ref) try referenceStack.push(ref) defer { referenceStack.pop() } return try flattenedTopLevelMultipartObject(referencedSchema, referenceStack: &referenceStack) diff --git a/Sources/_OpenAPIGeneratorCore/Translator/TypesTranslator/translateComponents.swift b/Sources/_OpenAPIGeneratorCore/Translator/TypesTranslator/translateComponents.swift index 54a1b6b9f..7a832b061 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/TypesTranslator/translateComponents.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/TypesTranslator/translateComponents.swift @@ -29,10 +29,17 @@ extension TypesFileTranslator { { let schemas = try translateSchemas(components.schemas, multipartSchemaNames: multipartSchemaNames) - let parameters = try translateComponentParameters(components.parameters) - let requestBodies = try translateComponentRequestBodies(components.requestBodies) - let responses = try translateComponentResponses(components.responses) - let headers = try translateComponentHeaders(components.headers) + let resolvedParameters = try components.parameters.mapValues { try components.assumeLookupOnce($0) } + let parameters = try translateComponentParameters(resolvedParameters) + let resolvedRequestBodies = try components.requestBodies.mapValues { try components.assumeLookupOnce($0) } + let requestBodies = try translateComponentRequestBodies(resolvedRequestBodies) + let resolvedResponses = try components.responses.mapValues { try components.assumeLookupOnce($0) } + let responses = try translateComponentResponses(resolvedResponses) + let resolvedHeaders = try components.headers.mapValues { + (header: Either, OpenAPI.Header>) in + try components.assumeLookupOnce(header) + } + let headers = try translateComponentHeaders(resolvedHeaders) let componentsDecl: Declaration = .commentable( .doc( diff --git a/Tests/OpenAPIGeneratorCoreTests/Parser/Test_YamsParser.swift b/Tests/OpenAPIGeneratorCoreTests/Parser/Test_YamsParser.swift index 46bcd0294..71939e14d 100644 --- a/Tests/OpenAPIGeneratorCoreTests/Parser/Test_YamsParser.swift +++ b/Tests/OpenAPIGeneratorCoreTests/Parser/Test_YamsParser.swift @@ -126,7 +126,7 @@ final class Test_YamsParser: Test_Core { /foo.yaml: error: Found neither a $ref nor a PathItem in Document.paths['/system']. PathItem could not be decoded because: - Inconsistency encountered when parsing `Vendor Extension` for the **GET** endpoint under `/system`: Found at least one vendor extension property that does not begin with the required 'x-' prefix. Invalid properties: [ resonance ].. + Problem encountered when parsing `Vendor Extension` for the **GET** endpoint under `/system`: Found at least one vendor extension property that does not begin with the required 'x-' prefix. Invalid properties: [ resonance ].. """ assertThrownError(try _test(yaml), expectedDiagnostic: expected) } diff --git a/Tests/OpenAPIGeneratorCoreTests/Parser/Test_validateDoc.swift b/Tests/OpenAPIGeneratorCoreTests/Parser/Test_validateDoc.swift index a4a48f39b..b876620df 100644 --- a/Tests/OpenAPIGeneratorCoreTests/Parser/Test_validateDoc.swift +++ b/Tests/OpenAPIGeneratorCoreTests/Parser/Test_validateDoc.swift @@ -81,13 +81,15 @@ final class Test_validateDoc: Test_Core { .init( get: .init( requestBody: .b( - .init(content: [.init(rawValue: "application/xml")!: .init(schema: .string)]) + .init(content: [.init(rawValue: "application/xml")!: .content(.init(schema: .string))]) ), responses: [ .init(integerLiteral: 200): .b( .init( description: "Test description 1", - content: [.init(rawValue: "application/json")!: .init(schema: .string)] + content: [ + .init(rawValue: "application/json")!: .content(.init(schema: .string)) + ] ) ) ] @@ -97,12 +99,14 @@ final class Test_validateDoc: Test_Core { "/path2": .b( .init( get: .init( - requestBody: .b(.init(content: [.init(rawValue: "text/html")!: .init(schema: .string)])), + requestBody: .b( + .init(content: [.init(rawValue: "text/html")!: .content(.init(schema: .string))]) + ), responses: [ .init(integerLiteral: 200): .b( .init( description: "Test description 2", - content: [.init(rawValue: "text/plain")!: .init(schema: .string)] + content: [.init(rawValue: "text/plain")!: .content(.init(schema: .string))] ) ) ] @@ -127,12 +131,16 @@ final class Test_validateDoc: Test_Core { "/path1": .b( .init( get: .init( - requestBody: .b(.init(content: [.init(rawValue: "application/")!: .init(schema: .string)])), + requestBody: .b( + .init(content: [.init(rawValue: "application/")!: .content(.init(schema: .string))]) + ), responses: [ .init(integerLiteral: 200): .b( .init( description: "Test description 1", - content: [.init(rawValue: "application/json")!: .init(schema: .string)] + content: [ + .init(rawValue: "application/json")!: .content(.init(schema: .string)) + ] ) ) ] @@ -142,12 +150,14 @@ final class Test_validateDoc: Test_Core { "/path2": .b( .init( get: .init( - requestBody: .b(.init(content: [.init(rawValue: "text/html")!: .init(schema: .string)])), + requestBody: .b( + .init(content: [.init(rawValue: "text/html")!: .content(.init(schema: .string))]) + ), responses: [ .init(integerLiteral: 200): .b( .init( description: "Test description 2", - content: [.init(rawValue: "text/plain")!: .init(schema: .string)] + content: [.init(rawValue: "text/plain")!: .content(.init(schema: .string))] ) ) ] @@ -179,13 +189,15 @@ final class Test_validateDoc: Test_Core { .init( get: .init( requestBody: .b( - .init(content: [.init(rawValue: "application/xml")!: .init(schema: .string)]) + .init(content: [.init(rawValue: "application/xml")!: .content(.init(schema: .string))]) ), responses: [ .init(integerLiteral: 200): .b( .init( description: "Test description 1", - content: [.init(rawValue: "application/json")!: .init(schema: .string)] + content: [ + .init(rawValue: "application/json")!: .content(.init(schema: .string)) + ] ) ) ] @@ -195,12 +207,14 @@ final class Test_validateDoc: Test_Core { "/path2": .b( .init( get: .init( - requestBody: .b(.init(content: [.init(rawValue: "text/html")!: .init(schema: .string)])), + requestBody: .b( + .init(content: [.init(rawValue: "text/html")!: .content(.init(schema: .string))]) + ), responses: [ .init(integerLiteral: 200): .b( .init( description: "Test description 2", - content: [.init(rawValue: "/plain")!: .init(schema: .string)] + content: [.init(rawValue: "/plain")!: .content(.init(schema: .string))] ) ) ] @@ -232,13 +246,15 @@ final class Test_validateDoc: Test_Core { .init( get: .init( requestBody: .b( - .init(content: [.init(rawValue: "application/xml")!: .init(schema: .string)]) + .init(content: [.init(rawValue: "application/xml")!: .content(.init(schema: .string))]) ), responses: [ .init(integerLiteral: 200): .b( .init( description: "Test description 1", - content: [.init(rawValue: "application/json")!: .init(schema: .string)] + content: [ + .init(rawValue: "application/json")!: .content(.init(schema: .string)) + ] ) ) ] @@ -246,9 +262,11 @@ final class Test_validateDoc: Test_Core { ) ) ], - components: .init(requestBodies: [ - "exampleRequestBody1": .init(content: [.init(rawValue: "application/pdf")!: .init(schema: .string)]), - "exampleRequestBody2": .init(content: [.init(rawValue: "image/")!: .init(schema: .string)]), + components: .direct(requestBodies: [ + "exampleRequestBody1": .init(content: [ + .init(rawValue: "application/pdf")!: .content(.init(schema: .string)) + ]), + "exampleRequestBody2": .init(content: [.init(rawValue: "image/")!: .content(.init(schema: .string))]), ]) ) XCTAssertThrowsError( @@ -273,13 +291,15 @@ final class Test_validateDoc: Test_Core { .init( get: .init( requestBody: .b( - .init(content: [.init(rawValue: "application/xml")!: .init(schema: .string)]) + .init(content: [.init(rawValue: "application/xml")!: .content(.init(schema: .string))]) ), responses: [ .init(integerLiteral: 200): .b( .init( description: "Test description 1", - content: [.init(rawValue: "application/json")!: .init(schema: .string)] + content: [ + .init(rawValue: "application/json")!: .content(.init(schema: .string)) + ] ) ) ] @@ -287,14 +307,14 @@ final class Test_validateDoc: Test_Core { ) ) ], - components: .init(responses: [ + components: .direct(responses: [ "exampleRequestBody1": .init( description: "Test description 1", - content: [.init(rawValue: "application/pdf")!: .init(schema: .string)] + content: [.init(rawValue: "application/pdf")!: .content(.init(schema: .string))] ), "exampleRequestBody2": .init( description: "Test description 2", - content: [.init(rawValue: "")!: .init(schema: .string)] + content: [.init(rawValue: "")!: .content(.init(schema: .string))] ), ]) ) @@ -321,12 +341,15 @@ final class Test_validateDoc: Test_Core { get: .init( parameters: .init( arrayLiteral: .b( - .init( + .path( name: "ID", - context: .path, content: [ - .init(rawValue: "text/plain")!: .init( - schema: .a(.component(named: "Path1ParametersContentSchemaReference")) + .init(rawValue: "text/plain")!: .content( + .init( + schema: .reference( + .component(named: "Path1ParametersContentSchemaReference") + ) + ) ) ] ) @@ -340,8 +363,12 @@ final class Test_validateDoc: Test_Core { .init( description: "ResponseDescription", content: [ - .init(rawValue: "text/plain")!: .init( - schema: .a(.component(named: "ResponsesContentSchemaReference")) + .init(rawValue: "text/plain")!: .content( + .init( + schema: .reference( + .component(named: "ResponsesContentSchemaReference") + ) + ) ) ] ) @@ -360,8 +387,10 @@ final class Test_validateDoc: Test_Core { parameters: .init(arrayLiteral: .a(.component(named: "Path3ExampleID"))), requestBody: .b( .init(content: [ - .init(rawValue: "text/html")!: .init( - schema: .a(.component(named: "RequestBodyContentSchemaReference")) + .init(rawValue: "text/html")!: .content( + .init( + schema: .reference(.component(named: "RequestBodyContentSchemaReference")) + ) ) ]) ), @@ -371,7 +400,7 @@ final class Test_validateDoc: Test_Core { ) ), ], - components: .init( + components: .direct( schemas: [ "ResponsesContentSchemaReference": .init(schema: .string(.init(), .init())), "RequestBodyContentSchemaReference": .init(schema: .integer(.init(), .init())), @@ -379,8 +408,8 @@ final class Test_validateDoc: Test_Core { ], responses: ["ResponsesReference": .init(description: "Description")], parameters: [ - "Path3ExampleID": .init(name: "ID", context: .path, content: .init()), - "Path1ParametersReference": .init(name: "Schema", context: .path, schema: .array), + "Path3ExampleID": .path(name: "ID", content: .init()), + "Path1ParametersReference": .path(name: "Schema", schema: .array), ], requestBodies: [ "RequestBodyReference": .init(content: .init()) @@ -404,8 +433,10 @@ final class Test_validateDoc: Test_Core { get: .init( requestBody: .b( .init(content: [ - .init(rawValue: "text/html")!: .init( - schema: .a(.component(named: "RequestBodyContentSchemaReference")) + .init(rawValue: "text/html")!: .content( + .init( + schema: .reference(.component(named: "RequestBodyContentSchemaReference")) + ) ) ]) ), diff --git a/Tests/OpenAPIGeneratorCoreTests/Translator/Operations/Test_OperationDescription.swift b/Tests/OpenAPIGeneratorCoreTests/Translator/Operations/Test_OperationDescription.swift index abd341a71..c48244326 100644 --- a/Tests/OpenAPIGeneratorCoreTests/Translator/Operations/Test_OperationDescription.swift +++ b/Tests/OpenAPIGeneratorCoreTests/Translator/Operations/Test_OperationDescription.swift @@ -19,10 +19,10 @@ final class Test_OperationDescription: Test_Core { func testAllParameters_duplicates_retainOnlyOperationParameters() throws { let pathLevelParameter = UnresolvedParameter.b( - OpenAPI.Parameter(name: "test", context: .query(required: false), schema: .integer) + OpenAPI.Parameter.query(name: "test", required: false, schema: .integer) ) let operationLevelParameter = UnresolvedParameter.b( - OpenAPI.Parameter(name: "test", context: .query(required: false), schema: .string) + OpenAPI.Parameter.query(name: "test", required: false, schema: .string) ) let pathItem = OpenAPI.PathItem( @@ -37,11 +37,9 @@ final class Test_OperationDescription: Test_Core { func testAllParameters_duplicates_keepsDuplicatesFromDifferentLocation() throws { let pathLevelParameter = UnresolvedParameter.b( - OpenAPI.Parameter(name: "test", context: .query(required: false), schema: .integer) - ) - let operationLevelParameter = UnresolvedParameter.b( - OpenAPI.Parameter(name: "test", context: .path, schema: .string) + OpenAPI.Parameter.query(name: "test", required: false, schema: .integer) ) + let operationLevelParameter = UnresolvedParameter.b(OpenAPI.Parameter.path(name: "test", schema: .string)) let pathItem = OpenAPI.PathItem( parameters: [pathLevelParameter], @@ -55,13 +53,13 @@ final class Test_OperationDescription: Test_Core { func testAllParameters_duplicates_ordering() throws { let pathLevelParameter = UnresolvedParameter.b( - OpenAPI.Parameter(name: "test1", context: .query(required: false), schema: .integer) + OpenAPI.Parameter.query(name: "test1", required: false, schema: .integer) ) let duplicatedParameter = UnresolvedParameter.b( - OpenAPI.Parameter(name: "test2", context: .query(required: false), schema: .integer) + OpenAPI.Parameter.query(name: "test2", required: false, schema: .integer) ) let operationLevelParameter = UnresolvedParameter.b( - OpenAPI.Parameter(name: "test3", context: .query(required: false), schema: .string) + OpenAPI.Parameter.query(name: "test3", required: false, schema: .string) ) let pathItem = OpenAPI.PathItem( diff --git a/Tests/OpenAPIGeneratorCoreTests/Translator/TypeAssignment/Test_TypeMatcher.swift b/Tests/OpenAPIGeneratorCoreTests/Translator/TypeAssignment/Test_TypeMatcher.swift index ec8aafc77..45e5944e6 100644 --- a/Tests/OpenAPIGeneratorCoreTests/Translator/TypeAssignment/Test_TypeMatcher.swift +++ b/Tests/OpenAPIGeneratorCoreTests/Translator/TypeAssignment/Test_TypeMatcher.swift @@ -245,7 +245,7 @@ final class Test_TypeMatcher: Test_Core { static let multipartElementTypeReferenceIfReferenceableTypes: [(UnresolvedSchema?, OrderedDictionary?, String?)] = [ (nil, nil, nil), (.b(.string), nil, nil), (.a(.component(named: "Foo")), nil, "Foo"), - (.a(.component(named: "Foo")), ["foo": .init(contentType: .json)], nil), + (.a(.component(named: "Foo")), ["foo": .init(contentTypes: [.json])], nil), ] func testMultipartElementTypeReferenceIfReferenceableTypes() throws { for (schema, encoding, name) in Self.multipartElementTypeReferenceIfReferenceableTypes { diff --git a/Tests/OpenAPIGeneratorCoreTests/Translator/TypesTranslator/Test_translateSchemas.swift b/Tests/OpenAPIGeneratorCoreTests/Translator/TypesTranslator/Test_translateSchemas.swift index 1d1e89f52..ee25c10f9 100644 --- a/Tests/OpenAPIGeneratorCoreTests/Translator/TypesTranslator/Test_translateSchemas.swift +++ b/Tests/OpenAPIGeneratorCoreTests/Translator/TypesTranslator/Test_translateSchemas.swift @@ -35,7 +35,7 @@ class Test_translateSchemas: Test_Core { ( schemaWithWarnings, [ - "warning: Schema warning: Inconsistency encountered when parsing `OpenAPI Schema`: Found schema attributes not consistent with the type specified: string. Specifically, attributes for these other types: [\"array\"]. [context: codingPath=, contextString=, subjectName=OpenAPI Schema]" + "warning: Schema warning: Problem encountered when parsing `OpenAPI Schema`: Found schema attributes not consistent with the type specified: string. Specifically, attributes for these other types: [\"array\"]. [context: codingPath=, contextString=, subjectName=OpenAPI Schema]" ] ), ] diff --git a/Tests/OpenAPIGeneratorReferenceTests/CompatabilityTest.swift b/Tests/OpenAPIGeneratorReferenceTests/CompatabilityTest.swift index b99a4c89d..62cd19ada 100644 --- a/Tests/OpenAPIGeneratorReferenceTests/CompatabilityTest.swift +++ b/Tests/OpenAPIGeneratorReferenceTests/CompatabilityTest.swift @@ -88,7 +88,7 @@ final class CompatibilityTest: XCTestCase { "https://raw.githubusercontent.com/discourse/discourse_api_docs/8182f1b21ca62cc9ac85fd3a82cae8304033a672/openapi.yml", license: .apache, expectedDiagnostics: [ - "Validation warning: Inconsistency encountered when parsing `OpenAPI Schema`: Found nothing but unsupported attributes..", + "Validation warning: Problem encountered when parsing `OpenAPI Schema`: Found nothing but unsupported attributes..", "Multipart request bodies must always be required, but found an optional one - skipping. Mark as `required: true` to get this body generated.", ], skipBuild: compatibilityTestSkipBuild @@ -100,15 +100,15 @@ final class CompatibilityTest: XCTestCase { "https://raw.githubusercontent.com/github/rest-api-description/322663c9c909974af16363b4dc0873c428bdbe34/descriptions-next/api.github.com/api.github.com.yaml", license: .mit, expectedDiagnostics: [ - "Validation warning: Inconsistency encountered when parsing `OpenAPI Schema`: Found schema attributes not consistent with the type specified: array. Specifically, attributes for these other types: [\"object\"].", - "Validation warning: Inconsistency encountered when parsing `OpenAPI Schema`: Found schema attributes not consistent with the type specified: string. Specifically, attributes for these other types: [\"object\"].", - "Validation warning: Inconsistency encountered when parsing `OpenAPI Schema`: Found nothing but unsupported attributes..", - "Schema warning: Inconsistency encountered when parsing `OpenAPI Schema`: Found schema attributes not consistent with the type specified: string. Specifically, attributes for these other types: [\"array\"].", - "Validation warning: Inconsistency encountered when parsing `Schema`: A schema contains properties for multiple types of schemas, namely: [\"array\", \"object\"]..", - "Validation warning: Inconsistency encountered when parsing `OpenAPI Schema`: Found schema attributes not consistent with the type specified: string. Specifically, attributes for these other types: [\"array\"].", + "Validation warning: Problem encountered when parsing `OpenAPI Schema`: Found schema attributes not consistent with the type specified: array. Specifically, attributes for these other types: [\"object\"].", + "Validation warning: Problem encountered when parsing `OpenAPI Schema`: Found schema attributes not consistent with the type specified: string. Specifically, attributes for these other types: [\"object\"].", + "Validation warning: Problem encountered when parsing `OpenAPI Schema`: Found nothing but unsupported attributes..", + "Schema warning: Problem encountered when parsing `OpenAPI Schema`: Found schema attributes not consistent with the type specified: string. Specifically, attributes for these other types: [\"array\"].", + "Validation warning: Problem encountered when parsing `Schema`: A schema contains properties for multiple types of schemas, namely: [\"array\", \"object\"]..", + "Validation warning: Problem encountered when parsing `OpenAPI Schema`: Found schema attributes not consistent with the type specified: string. Specifically, attributes for these other types: [\"array\"].", "A property name only appears in the required list, but not in the properties map - this is likely a typo; skipping this property.", - "Schema warning: Inconsistency encountered when parsing `OpenAPI Schema`: Found schema attributes not consistent with the type specified: array. Specifically, attributes for these other types: [\"object\"].", - "Schema warning: Inconsistency encountered when parsing `Schema`: A schema contains properties for multiple types of schemas, namely: [\"array\", \"object\"]..", + "Schema warning: Problem encountered when parsing `OpenAPI Schema`: Found schema attributes not consistent with the type specified: array. Specifically, attributes for these other types: [\"object\"].", + "Schema warning: Problem encountered when parsing `Schema`: A schema contains properties for multiple types of schemas, namely: [\"array\", \"object\"]..", "Schema \"null\" is not supported, reason: \"schema type\", skipping", ], skipBuild: true @@ -121,11 +121,11 @@ final class CompatibilityTest: XCTestCase { license: .mit, expectedDiagnostics: [ "Multipart request bodies must always be required, but found an optional one - skipping. Mark as `required: true` to get this body generated.", - "Validation warning: Inconsistency encountered when parsing `OpenAPI Schema`: Found nothing but unsupported attributes..", - "Validation warning: Inconsistency encountered when parsing `OpenAPI Schema`: Found schema attributes not consistent with the type specified: string. Specifically, attributes for these other types: [\"array\"].", + "Validation warning: Problem encountered when parsing `OpenAPI Schema`: Found nothing but unsupported attributes..", + "Validation warning: Problem encountered when parsing `OpenAPI Schema`: Found schema attributes not consistent with the type specified: string. Specifically, attributes for these other types: [\"array\"].", "A property name only appears in the required list, but not in the properties map - this is likely a typo; skipping this property.", - "Validation warning: Inconsistency encountered when parsing `OpenAPI Schema`: Found schema attributes not consistent with the type specified: string. Specifically, attributes for these other types: [\"object\"].", - "Schema warning: Inconsistency encountered when parsing `OpenAPI Schema`: Found schema attributes not consistent with the type specified: string. Specifically, attributes for these other types: [\"array\"].", + "Validation warning: Problem encountered when parsing `OpenAPI Schema`: Found schema attributes not consistent with the type specified: string. Specifically, attributes for these other types: [\"object\"].", + "Schema warning: Problem encountered when parsing `OpenAPI Schema`: Found schema attributes not consistent with the type specified: string. Specifically, attributes for these other types: [\"array\"].", "Schema \"null\" is not supported, reason: \"schema type\", skipping", ], skipBuild: true diff --git a/Tests/OpenAPIGeneratorReferenceTests/SnippetBasedReferenceTests.swift b/Tests/OpenAPIGeneratorReferenceTests/SnippetBasedReferenceTests.swift index c014e0215..4a08b8ed0 100644 --- a/Tests/OpenAPIGeneratorReferenceTests/SnippetBasedReferenceTests.swift +++ b/Tests/OpenAPIGeneratorReferenceTests/SnippetBasedReferenceTests.swift @@ -6321,7 +6321,9 @@ extension SnippetBasedReferenceTests { line: UInt = #line ) throws { let translator = try makeTypesTranslator(componentsYAML: componentsYAML) - let translation = try translator.translateComponentHeaders(translator.components.headers) + let components = translator.components + let componentHeaders = try components.headers.mapValues { try components.assumeLookupOnce($0) } + let translation = try translator.translateComponentHeaders(componentHeaders) try XCTAssertSwiftEquivalent(translation, expectedSwift, file: file, line: line) } @@ -6333,7 +6335,9 @@ extension SnippetBasedReferenceTests { line: UInt = #line ) throws { let translator = try makeTypesTranslator(accessModifier: accessModifier, componentsYAML: componentsYAML) - let translation = try translator.translateComponentParameters(translator.components.parameters) + let components = translator.components + let componentParameters = try components.parameters.mapValues { try components.assumeLookupOnce($0) } + let translation = try translator.translateComponentParameters(componentParameters) try XCTAssertSwiftEquivalent(translation, expectedSwift, file: file, line: line) } @@ -6379,8 +6383,9 @@ extension SnippetBasedReferenceTests { try XCTAssertSwiftEquivalent(generatedSchemasStructuredSwift, expectedSchemasSwift, file: file, line: line) } if let expectedRequestBodiesSwift { + let componentRequestBodies = try components.requestBodies.mapValues { try components.assumeLookupOnce($0) } let generatedRequestBodiesStructuredSwift = try types.translateComponentRequestBodies( - document.components.requestBodies + componentRequestBodies ) try XCTAssertSwiftEquivalent( generatedRequestBodiesStructuredSwift, @@ -6438,9 +6443,9 @@ extension SnippetBasedReferenceTests { try XCTAssertSwiftEquivalent(generatedSchemasStructuredSwift, expectedSchemasSwift, file: file, line: line) } if let expectedResponsesSwift { - let generatedRequestBodiesStructuredSwift = try types.translateComponentResponses( - document.components.responses - ) + let components = document.components + let componentResponses = try components.responses.mapValues { try components.assumeLookupOnce($0) } + let generatedRequestBodiesStructuredSwift = try types.translateComponentResponses(componentResponses) try XCTAssertSwiftEquivalent( generatedRequestBodiesStructuredSwift, expectedResponsesSwift, @@ -6496,7 +6501,9 @@ extension SnippetBasedReferenceTests { ignoredDiagnosticMessages: ignoredDiagnosticMessages, componentsYAML: componentsYAML ) - let translation = try translator.translateComponentResponses(translator.components.responses) + let components = translator.components + let componentResponses = try components.responses.mapValues { try components.assumeLookupOnce($0) } + let translation = try translator.translateComponentResponses(componentResponses) try XCTAssertSwiftEquivalent(translation, expectedSwift, file: file, line: line) } @@ -6513,7 +6520,9 @@ extension SnippetBasedReferenceTests { ignoredDiagnosticMessages: ignoredDiagnosticMessages, componentsYAML: componentsYAML ) - let translation = try translator.translateComponentRequestBodies(translator.components.requestBodies) + let components = translator.components + let componentRequestBodies = try components.requestBodies.mapValues { try components.assumeLookupOnce($0) } + let translation = try translator.translateComponentRequestBodies(componentRequestBodies) try XCTAssertSwiftEquivalent(translation, expectedSwift, file: file, line: line) }