Skip to content

Calculate required response header parameters for initialize #150

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

takeshi-1000
Copy link
Contributor

@takeshi-1000 takeshi-1000 commented Jul 29, 2023

Motivation

Resolve #30

Modifications

Define a computed property on StructBlueprint that tells you whether the struct has an empty initializer, and it is used when response header struct blueprint is created.

Result

If one or more required response header parameters are present,
Generates code that needs to set request header parameters when create response.

For example you define the yaml below

      responses:
        '200':
          description: A success response with a greeting.
          headers:
            X-Extra-Arguments:
              required: false
              description: "A description here."
              content:
                application/json:
                  schema:
                    $ref: '#/components/schemas/Greeting'

Types.swift is generated like below, and you can set the "headers" parameter if necessary ,

スクリーンショット 2023-07-29 14 44 45

if you set required true

            X-Extra-Arguments:
              required: true

Types.swift is generated like below, and you should set reponse headers when initialize

スクリーンショット 2023-07-29 14 52 55

Test Plan

Add test optional response header in SnipetBasedReferenceTests, and modify ReferenceSource a little.

@takeshi-1000 takeshi-1000 changed the title Calculate required reponse header parameters for initialize Calculate required response header parameters for initialize Jul 29, 2023
Copy link
Contributor

@czechboy0 czechboy0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @takeshi-1000, thanks for picking this up!

I think the fix can be achieved using a little more general method, which will be usable in more places than just response headers.

Step 1: Define a computed property on StructBlueprint that tells you whether the struct has an empty initializer. For example:

extension StructBlueprint {
    
    /// A Boolean value indicating whether the struct can be initialized using
    /// an empty initializer.
    ///
    /// For example, when all the properties of the struct have a default value,
    /// the struct can be initialized using `Foo()`. This is important for
    /// other types referencing this type.
    var hasEmptyInit: Bool {
        // If at least one property requires an explicit value, this struct
        // cannot have an empty initializer.
        !properties.contains(where: { $0.defaultValue == nil })
    }
}

Step 2: Use this computed property instead of checking if headers property array is empty (like we do today).

I haven't tested this fully, but my local diff when I tried it looked something like:

diff --git a/Sources/_OpenAPIGeneratorCore/Translator/CommonTypes/StructBlueprint.swift b/Sources/_OpenAPIGeneratorCore/Translator/CommonTypes/StructBlueprint.swift
index 244c269..55ffffa 100644
--- a/Sources/_OpenAPIGeneratorCore/Translator/CommonTypes/StructBlueprint.swift
+++ b/Sources/_OpenAPIGeneratorCore/Translator/CommonTypes/StructBlueprint.swift
@@ -80,6 +80,21 @@ struct StructBlueprint {
     var properties: [PropertyBlueprint]
 }
 
+extension StructBlueprint {
+
+    /// A Boolean value indicating whether the struct can be initialized using
+    /// an empty initializer.
+    ///
+    /// For example, when all the properties of the struct have a default value,
+    /// the struct can be initialized using `Foo()`. This is important for
+    /// other types referencing this type.
+    var hasEmptyInit: Bool {
+        // If at least one property requires an explicit value, this struct
+        // cannot have an empty initializer.
+        !properties.contains(where: { $0.defaultValue == nil })
+    }
+}
+
 /// A structure that contains the information about an OpenAPI object property
 /// that is required to generate a matching Swift property.
 struct PropertyBlueprint {
diff --git a/Sources/_OpenAPIGeneratorCore/Translator/Responses/translateResponse.swift b/Sources/_OpenAPIGeneratorCore/Translator/Responses/translateResponse.swift
index da2eada..60c7383 100644
--- a/Sources/_OpenAPIGeneratorCore/Translator/Responses/translateResponse.swift
+++ b/Sources/_OpenAPIGeneratorCore/Translator/Responses/translateResponse.swift
@@ -36,20 +36,21 @@ extension TypesFileTranslator {
         let headerProperties: [PropertyBlueprint] = try headers.map { header in
             try parseResponseHeaderAsProperty(for: header)
         }
+        let headersStructBlueprint: StructBlueprint = .init(
+            comment: nil,
+            access: config.access,
+            typeName: headersTypeName,
+            conformances: Constants.Operation.Output.Payload.Headers.conformances,
+            properties: headerProperties
+        )
         let headersStructDecl = translateStructBlueprint(
-            .init(
-                comment: nil,
-                access: config.access,
-                typeName: headersTypeName,
-                conformances: Constants.Operation.Output.Payload.Headers.conformances,
-                properties: headerProperties
-            )
+            headersStructBlueprint
         )
         let headersProperty = PropertyBlueprint(
             comment: .doc("Received HTTP response headers"),
             originalName: Constants.Operation.Output.Payload.Headers.variableName,
             typeUsage: headersTypeName.asUsage,
-            default: headerProperties.isEmpty ? .emptyInit : nil,
+            default: headersStructBlueprint.hasEmptyInit ? .emptyInit : nil,
             associatedDeclarations: [
                 headersStructDecl
             ],

See if the above actually works with your snippet-based tests (which are great, thanks for adding them!). The file-based reference tests can be reverted, I don't think we need to expand them here, the snippet tests cover this well.

@takeshi-1000
Copy link
Contributor Author

@czechboy0
Thank you for your detailed suggestions!
I didn't come up with it, but it looks reasonable so I applied it.
I also changed the test

@czechboy0
Copy link
Contributor

Awesome, thank you very much @takeshi-1000! Can you just update the PR description to reflect the changes now, and otherwise this should be ready to go? 🙏

@czechboy0 czechboy0 added the 🔨 semver/patch No public API change. label Jul 31, 2023
@takeshi-1000
Copy link
Contributor Author

@czechboy0
I updated, could you check please?

@czechboy0 czechboy0 requested a review from simonjbeaumont July 31, 2023 14:10
Copy link
Collaborator

@simonjbeaumont simonjbeaumont left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a couple of minor nits from me, otherwise looks super—thank you!

@czechboy0 czechboy0 merged commit 8d5b397 into apple:main Aug 1, 2023
@takeshi-1000 takeshi-1000 deleted the calculate-requiredReponseHeaderParameters-forInitialize branch August 1, 2023 13:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🔨 semver/patch No public API change.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Recurse when calculating whether an init param is required
3 participants