From 2a493b3f984e9307e2314d1db59f876d64c7dfff Mon Sep 17 00:00:00 2001
From: Bill Collins <bill.collins@hp.com>
Date: Fri, 28 Oct 2022 10:25:10 +0100
Subject: [PATCH 1/4] Add failing example of nullable subschema

---
 .../typescript-fetch-allOf-nullable.yaml      |  4 ++
 .../fetch/TypeScriptFetchModelTest.java       | 10 +++++
 .../test/resources/3_0/allOf-nullable.yaml    | 40 +++++++++++++++++++
 3 files changed, 54 insertions(+)
 create mode 100644 bin/configs/typescript-fetch-allOf-nullable.yaml
 create mode 100644 modules/openapi-generator/src/test/resources/3_0/allOf-nullable.yaml

diff --git a/bin/configs/typescript-fetch-allOf-nullable.yaml b/bin/configs/typescript-fetch-allOf-nullable.yaml
new file mode 100644
index 00000000000..89db874a19d
--- /dev/null
+++ b/bin/configs/typescript-fetch-allOf-nullable.yaml
@@ -0,0 +1,4 @@
+generatorName: typescript-fetch
+outputDir: samples/client/petstore/typescript-fetch/builds/allOf-nullable
+inputSpec: modules/openapi-generator/src/test/resources/3_0/allOf-nullable.yaml
+templateDir: modules/openapi-generator/src/main/resources/typescript-fetch
diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchModelTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchModelTest.java
index 259b7f55c91..fdef7148df8 100644
--- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchModelTest.java
+++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchModelTest.java
@@ -470,4 +470,14 @@ public class TypeScriptFetchModelTest {
         final Map<String, Schema> schemaBefore = openAPI.getComponents().getSchemas();
         Assert.assertEquals(schemaBefore.keySet(), Sets.newHashSet("club", "owner"));
     }
+
+    @Test(description = "Don't generate new schemas for nullable references")
+    public void testNestedNullableSchemas() {
+        final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/allOf-nullable.yaml");
+        final DefaultCodegen codegen = new TypeScriptFetchClientCodegen();
+        codegen.processOpts();
+        codegen.setOpenAPI(openAPI);
+        final Map<String, Schema> schemaBefore = openAPI.getComponents().getSchemas();
+        Assert.assertEquals(schemaBefore.keySet(), Sets.newHashSet("club", "owner"));
+    }
 }
diff --git a/modules/openapi-generator/src/test/resources/3_0/allOf-nullable.yaml b/modules/openapi-generator/src/test/resources/3_0/allOf-nullable.yaml
new file mode 100644
index 00000000000..dcb0ad9f786
--- /dev/null
+++ b/modules/openapi-generator/src/test/resources/3_0/allOf-nullable.yaml
@@ -0,0 +1,40 @@
+openapi: 3.0.1
+info:
+  version: 1.0.0
+  title: Example
+  license:
+    name: MIT
+servers:
+  - url: http://api.example.xyz/v1
+paths:
+  /person/display/{personId}:
+    get:
+      parameters:
+        - name: personId
+          in: path
+          required: true
+          description: The id of the person to retrieve
+          schema:
+            type: string
+      operationId: list
+      responses:
+        '200':
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/club"
+components:
+  schemas:
+    club:
+      properties:
+        owner:
+          allOf:
+            - $ref: '#/components/schemas/owner'
+          nullable: true
+
+    owner:
+      properties:
+        name:
+          type: string
+          maxLength: 255
\ No newline at end of file
-- 
GitLab


From 458f0f485dee5f182ad3e29f298499e6a84f79a2 Mon Sep 17 00:00:00 2001
From: Bill Collins <bill.collins@hp.com>
Date: Fri, 28 Oct 2022 10:46:20 +0100
Subject: [PATCH 2/4] Do not generate new subschemas when nullable

---
 .../org/openapitools/codegen/InlineModelResolver.java | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/InlineModelResolver.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/InlineModelResolver.java
index 40ebf90595a..e98965b0f1b 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/InlineModelResolver.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/InlineModelResolver.java
@@ -190,11 +190,16 @@ public class InlineModelResolver {
             // allOf, anyOf, oneOf
             ComposedSchema m = (ComposedSchema) schema;
 
-            if (m.getAllOf() != null && m.getAllOf().size() == 1 && m.getReadOnly() != null && m.getReadOnly()) {
-                // Check if this composed schema only contains an allOf and a readOnly.
+            boolean isSingleAllOf = m.getAllOf() != null && m.getAllOf().size() == 1;
+            boolean isReadOnly = m.getReadOnly() != null && m.getReadOnly();
+            boolean isNullable = m.getNullable() != null && m.getNullable();
+
+            if (isSingleAllOf && (isReadOnly || isNullable)) {
+                // Check if this composed schema only contains an allOf and a readOnly or nullable.
                 ComposedSchema c = new ComposedSchema();
                 c.setAllOf(m.getAllOf());
-                c.setReadOnly(true);
+                c.setReadOnly(m.getReadOnly());
+                c.setNullable(m.getNullable());
                 if (m.equals(c)) {
                     return isModelNeeded(m.getAllOf().get(0), visitedSchemas);
                 }
-- 
GitLab


From 310a565ce0eb07e2b83e814693bc9e6868579071 Mon Sep 17 00:00:00 2001
From: Bill Collins <bill.collins@hp.com>
Date: Fri, 28 Oct 2022 10:48:14 +0100
Subject: [PATCH 3/4] Generate client

---
 .../allOf-nullable/.openapi-generator-ignore  |  23 +
 .../allOf-nullable/.openapi-generator/FILES   |   7 +
 .../allOf-nullable/.openapi-generator/VERSION |   1 +
 .../builds/allOf-nullable/apis/DefaultApi.ts  |  62 +++
 .../builds/allOf-nullable/apis/index.ts       |   3 +
 .../builds/allOf-nullable/index.ts            |   5 +
 .../builds/allOf-nullable/models/Club.ts      |  72 ++++
 .../builds/allOf-nullable/models/Owner.ts     |  65 +++
 .../builds/allOf-nullable/models/index.ts     |   4 +
 .../builds/allOf-nullable/runtime.ts          | 407 ++++++++++++++++++
 10 files changed, 649 insertions(+)
 create mode 100644 samples/client/petstore/typescript-fetch/builds/allOf-nullable/.openapi-generator-ignore
 create mode 100644 samples/client/petstore/typescript-fetch/builds/allOf-nullable/.openapi-generator/FILES
 create mode 100644 samples/client/petstore/typescript-fetch/builds/allOf-nullable/.openapi-generator/VERSION
 create mode 100644 samples/client/petstore/typescript-fetch/builds/allOf-nullable/apis/DefaultApi.ts
 create mode 100644 samples/client/petstore/typescript-fetch/builds/allOf-nullable/apis/index.ts
 create mode 100644 samples/client/petstore/typescript-fetch/builds/allOf-nullable/index.ts
 create mode 100644 samples/client/petstore/typescript-fetch/builds/allOf-nullable/models/Club.ts
 create mode 100644 samples/client/petstore/typescript-fetch/builds/allOf-nullable/models/Owner.ts
 create mode 100644 samples/client/petstore/typescript-fetch/builds/allOf-nullable/models/index.ts
 create mode 100644 samples/client/petstore/typescript-fetch/builds/allOf-nullable/runtime.ts

diff --git a/samples/client/petstore/typescript-fetch/builds/allOf-nullable/.openapi-generator-ignore b/samples/client/petstore/typescript-fetch/builds/allOf-nullable/.openapi-generator-ignore
new file mode 100644
index 00000000000..7484ee590a3
--- /dev/null
+++ b/samples/client/petstore/typescript-fetch/builds/allOf-nullable/.openapi-generator-ignore
@@ -0,0 +1,23 @@
+# OpenAPI Generator Ignore
+# Generated by openapi-generator https://github.com/openapitools/openapi-generator
+
+# Use this file to prevent files from being overwritten by the generator.
+# The patterns follow closely to .gitignore or .dockerignore.
+
+# As an example, the C# client generator defines ApiClient.cs.
+# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
+#ApiClient.cs
+
+# You can match any string of characters against a directory, file or extension with a single asterisk (*):
+#foo/*/qux
+# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
+
+# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
+#foo/**/qux
+# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
+
+# You can also negate patterns with an exclamation (!).
+# For example, you can ignore all files in a docs folder with the file extension .md:
+#docs/*.md
+# Then explicitly reverse the ignore rule for a single file:
+#!docs/README.md
diff --git a/samples/client/petstore/typescript-fetch/builds/allOf-nullable/.openapi-generator/FILES b/samples/client/petstore/typescript-fetch/builds/allOf-nullable/.openapi-generator/FILES
new file mode 100644
index 00000000000..4036fef5186
--- /dev/null
+++ b/samples/client/petstore/typescript-fetch/builds/allOf-nullable/.openapi-generator/FILES
@@ -0,0 +1,7 @@
+apis/DefaultApi.ts
+apis/index.ts
+index.ts
+models/Club.ts
+models/Owner.ts
+models/index.ts
+runtime.ts
diff --git a/samples/client/petstore/typescript-fetch/builds/allOf-nullable/.openapi-generator/VERSION b/samples/client/petstore/typescript-fetch/builds/allOf-nullable/.openapi-generator/VERSION
new file mode 100644
index 00000000000..ed829dbcdde
--- /dev/null
+++ b/samples/client/petstore/typescript-fetch/builds/allOf-nullable/.openapi-generator/VERSION
@@ -0,0 +1 @@
+6.2.1-SNAPSHOT
\ No newline at end of file
diff --git a/samples/client/petstore/typescript-fetch/builds/allOf-nullable/apis/DefaultApi.ts b/samples/client/petstore/typescript-fetch/builds/allOf-nullable/apis/DefaultApi.ts
new file mode 100644
index 00000000000..416fd54366a
--- /dev/null
+++ b/samples/client/petstore/typescript-fetch/builds/allOf-nullable/apis/DefaultApi.ts
@@ -0,0 +1,62 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Example
+ * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
+ *
+ * The version of the OpenAPI document: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * https://openapi-generator.tech
+ * Do not edit the class manually.
+ */
+
+
+import * as runtime from '../runtime';
+import type {
+  Club,
+} from '../models';
+import {
+    ClubFromJSON,
+    ClubToJSON,
+} from '../models';
+
+export interface ListRequest {
+    personId: string;
+}
+
+/**
+ * 
+ */
+export class DefaultApi extends runtime.BaseAPI {
+
+    /**
+     */
+    async listRaw(requestParameters: ListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Club>> {
+        if (requestParameters.personId === null || requestParameters.personId === undefined) {
+            throw new runtime.RequiredError('personId','Required parameter requestParameters.personId was null or undefined when calling list.');
+        }
+
+        const queryParameters: any = {};
+
+        const headerParameters: runtime.HTTPHeaders = {};
+
+        const response = await this.request({
+            path: `/person/display/{personId}`.replace(`{${"personId"}}`, encodeURIComponent(String(requestParameters.personId))),
+            method: 'GET',
+            headers: headerParameters,
+            query: queryParameters,
+        }, initOverrides);
+
+        return new runtime.JSONApiResponse(response, (jsonValue) => ClubFromJSON(jsonValue));
+    }
+
+    /**
+     */
+    async list(requestParameters: ListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<Club> {
+        const response = await this.listRaw(requestParameters, initOverrides);
+        return await response.value();
+    }
+
+}
diff --git a/samples/client/petstore/typescript-fetch/builds/allOf-nullable/apis/index.ts b/samples/client/petstore/typescript-fetch/builds/allOf-nullable/apis/index.ts
new file mode 100644
index 00000000000..69c44c00fa0
--- /dev/null
+++ b/samples/client/petstore/typescript-fetch/builds/allOf-nullable/apis/index.ts
@@ -0,0 +1,3 @@
+/* tslint:disable */
+/* eslint-disable */
+export * from './DefaultApi';
diff --git a/samples/client/petstore/typescript-fetch/builds/allOf-nullable/index.ts b/samples/client/petstore/typescript-fetch/builds/allOf-nullable/index.ts
new file mode 100644
index 00000000000..be9d1edeefe
--- /dev/null
+++ b/samples/client/petstore/typescript-fetch/builds/allOf-nullable/index.ts
@@ -0,0 +1,5 @@
+/* tslint:disable */
+/* eslint-disable */
+export * from './runtime';
+export * from './apis';
+export * from './models';
diff --git a/samples/client/petstore/typescript-fetch/builds/allOf-nullable/models/Club.ts b/samples/client/petstore/typescript-fetch/builds/allOf-nullable/models/Club.ts
new file mode 100644
index 00000000000..99826741f46
--- /dev/null
+++ b/samples/client/petstore/typescript-fetch/builds/allOf-nullable/models/Club.ts
@@ -0,0 +1,72 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Example
+ * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
+ *
+ * The version of the OpenAPI document: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * https://openapi-generator.tech
+ * Do not edit the class manually.
+ */
+
+import { exists, mapValues } from '../runtime';
+import type { Owner } from './Owner';
+import {
+    OwnerFromJSON,
+    OwnerFromJSONTyped,
+    OwnerToJSON,
+} from './Owner';
+
+/**
+ * 
+ * @export
+ * @interface Club
+ */
+export interface Club {
+    /**
+     * 
+     * @type {Owner}
+     * @memberof Club
+     */
+    owner?: Owner | null;
+}
+
+/**
+ * Check if a given object implements the Club interface.
+ */
+export function instanceOfClub(value: object): boolean {
+    let isInstance = true;
+
+    return isInstance;
+}
+
+export function ClubFromJSON(json: any): Club {
+    return ClubFromJSONTyped(json, false);
+}
+
+export function ClubFromJSONTyped(json: any, ignoreDiscriminator: boolean): Club {
+    if ((json === undefined) || (json === null)) {
+        return json;
+    }
+    return {
+        
+        'owner': !exists(json, 'owner') ? undefined : OwnerFromJSON(json['owner']),
+    };
+}
+
+export function ClubToJSON(value?: Club | null): any {
+    if (value === undefined) {
+        return undefined;
+    }
+    if (value === null) {
+        return null;
+    }
+    return {
+        
+        'owner': OwnerToJSON(value.owner),
+    };
+}
+
diff --git a/samples/client/petstore/typescript-fetch/builds/allOf-nullable/models/Owner.ts b/samples/client/petstore/typescript-fetch/builds/allOf-nullable/models/Owner.ts
new file mode 100644
index 00000000000..70542125af9
--- /dev/null
+++ b/samples/client/petstore/typescript-fetch/builds/allOf-nullable/models/Owner.ts
@@ -0,0 +1,65 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Example
+ * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
+ *
+ * The version of the OpenAPI document: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * https://openapi-generator.tech
+ * Do not edit the class manually.
+ */
+
+import { exists, mapValues } from '../runtime';
+/**
+ * 
+ * @export
+ * @interface Owner
+ */
+export interface Owner {
+    /**
+     * 
+     * @type {string}
+     * @memberof Owner
+     */
+    name?: string;
+}
+
+/**
+ * Check if a given object implements the Owner interface.
+ */
+export function instanceOfOwner(value: object): boolean {
+    let isInstance = true;
+
+    return isInstance;
+}
+
+export function OwnerFromJSON(json: any): Owner {
+    return OwnerFromJSONTyped(json, false);
+}
+
+export function OwnerFromJSONTyped(json: any, ignoreDiscriminator: boolean): Owner {
+    if ((json === undefined) || (json === null)) {
+        return json;
+    }
+    return {
+        
+        'name': !exists(json, 'name') ? undefined : json['name'],
+    };
+}
+
+export function OwnerToJSON(value?: Owner | null): any {
+    if (value === undefined) {
+        return undefined;
+    }
+    if (value === null) {
+        return null;
+    }
+    return {
+        
+        'name': value.name,
+    };
+}
+
diff --git a/samples/client/petstore/typescript-fetch/builds/allOf-nullable/models/index.ts b/samples/client/petstore/typescript-fetch/builds/allOf-nullable/models/index.ts
new file mode 100644
index 00000000000..c5f449053c2
--- /dev/null
+++ b/samples/client/petstore/typescript-fetch/builds/allOf-nullable/models/index.ts
@@ -0,0 +1,4 @@
+/* tslint:disable */
+/* eslint-disable */
+export * from './Club';
+export * from './Owner';
diff --git a/samples/client/petstore/typescript-fetch/builds/allOf-nullable/runtime.ts b/samples/client/petstore/typescript-fetch/builds/allOf-nullable/runtime.ts
new file mode 100644
index 00000000000..cca25bd21cb
--- /dev/null
+++ b/samples/client/petstore/typescript-fetch/builds/allOf-nullable/runtime.ts
@@ -0,0 +1,407 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Example
+ * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
+ *
+ * The version of the OpenAPI document: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * https://openapi-generator.tech
+ * Do not edit the class manually.
+ */
+
+
+export const BASE_PATH = "http://api.example.xyz/v1".replace(/\/+$/, "");
+
+export interface ConfigurationParameters {
+    basePath?: string; // override base path
+    fetchApi?: FetchAPI; // override for fetch implementation
+    middleware?: Middleware[]; // middleware to apply before/after fetch requests
+    queryParamsStringify?: (params: HTTPQuery) => string; // stringify function for query strings
+    username?: string; // parameter for basic security
+    password?: string; // parameter for basic security
+    apiKey?: string | ((name: string) => string); // parameter for apiKey security
+    accessToken?: string | Promise<string> | ((name?: string, scopes?: string[]) => string | Promise<string>); // parameter for oauth2 security
+    headers?: HTTPHeaders; //header params we want to use on every request
+    credentials?: RequestCredentials; //value for the credentials param we want to use on each request
+}
+
+export class Configuration {
+    constructor(private configuration: ConfigurationParameters = {}) {}
+
+    set config(configuration: Configuration) {
+        this.configuration = configuration;
+    }
+
+    get basePath(): string {
+        return this.configuration.basePath != null ? this.configuration.basePath : BASE_PATH;
+    }
+
+    get fetchApi(): FetchAPI | undefined {
+        return this.configuration.fetchApi;
+    }
+
+    get middleware(): Middleware[] {
+        return this.configuration.middleware || [];
+    }
+
+    get queryParamsStringify(): (params: HTTPQuery) => string {
+        return this.configuration.queryParamsStringify || querystring;
+    }
+
+    get username(): string | undefined {
+        return this.configuration.username;
+    }
+
+    get password(): string | undefined {
+        return this.configuration.password;
+    }
+
+    get apiKey(): ((name: string) => string) | undefined {
+        const apiKey = this.configuration.apiKey;
+        if (apiKey) {
+            return typeof apiKey === 'function' ? apiKey : () => apiKey;
+        }
+        return undefined;
+    }
+
+    get accessToken(): ((name?: string, scopes?: string[]) => string | Promise<string>) | undefined {
+        const accessToken = this.configuration.accessToken;
+        if (accessToken) {
+            return typeof accessToken === 'function' ? accessToken : async () => accessToken;
+        }
+        return undefined;
+    }
+
+    get headers(): HTTPHeaders | undefined {
+        return this.configuration.headers;
+    }
+
+    get credentials(): RequestCredentials | undefined {
+        return this.configuration.credentials;
+    }
+}
+
+export const DefaultConfig = new Configuration();
+
+/**
+ * This is the base class for all generated API classes.
+ */
+export class BaseAPI {
+
+    private middleware: Middleware[];
+
+    constructor(protected configuration = DefaultConfig) {
+        this.middleware = configuration.middleware;
+    }
+
+    withMiddleware<T extends BaseAPI>(this: T, ...middlewares: Middleware[]) {
+        const next = this.clone<T>();
+        next.middleware = next.middleware.concat(...middlewares);
+        return next;
+    }
+
+    withPreMiddleware<T extends BaseAPI>(this: T, ...preMiddlewares: Array<Middleware['pre']>) {
+        const middlewares = preMiddlewares.map((pre) => ({ pre }));
+        return this.withMiddleware<T>(...middlewares);
+    }
+
+    withPostMiddleware<T extends BaseAPI>(this: T, ...postMiddlewares: Array<Middleware['post']>) {
+        const middlewares = postMiddlewares.map((post) => ({ post }));
+        return this.withMiddleware<T>(...middlewares);
+    }
+
+    protected async request(context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction): Promise<Response> {
+        const { url, init } = await this.createFetchParams(context, initOverrides);
+        const response = await this.fetchApi(url, init);
+        if (response && (response.status >= 200 && response.status < 300)) {
+            return response;
+        }
+        throw new ResponseError(response, 'Response returned an error code');
+    }
+
+    private async createFetchParams(context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction) {
+        let url = this.configuration.basePath + context.path;
+        if (context.query !== undefined && Object.keys(context.query).length !== 0) {
+            // only add the querystring to the URL if there are query parameters.
+            // this is done to avoid urls ending with a "?" character which buggy webservers
+            // do not handle correctly sometimes.
+            url += '?' + this.configuration.queryParamsStringify(context.query);
+        }
+
+        const headers = Object.assign({}, this.configuration.headers, context.headers);
+        Object.keys(headers).forEach(key => headers[key] === undefined ? delete headers[key] : {});
+
+        const initOverrideFn =
+            typeof initOverrides === "function"
+                ? initOverrides
+                : async () => initOverrides;
+
+        const initParams = {
+            method: context.method,
+            headers,
+            body: context.body,
+            credentials: this.configuration.credentials,
+        };
+
+        const overridedInit: RequestInit = {
+            ...initParams,
+            ...(await initOverrideFn({
+                init: initParams,
+                context,
+            }))
+        }
+
+        const init: RequestInit = {
+            ...overridedInit,
+            body:
+                isFormData(overridedInit.body) ||
+                overridedInit.body instanceof URLSearchParams ||
+                isBlob(overridedInit.body)
+                    ? overridedInit.body
+                    : JSON.stringify(overridedInit.body),
+        };
+
+        return { url, init };
+    }
+
+    private fetchApi = async (url: string, init: RequestInit) => {
+        let fetchParams = { url, init };
+        for (const middleware of this.middleware) {
+            if (middleware.pre) {
+                fetchParams = await middleware.pre({
+                    fetch: this.fetchApi,
+                    ...fetchParams,
+                }) || fetchParams;
+            }
+        }
+        let response = undefined;
+        try {
+            response = await (this.configuration.fetchApi || fetch)(fetchParams.url, fetchParams.init);
+        } catch (e) {
+            for (const middleware of this.middleware) {
+                if (middleware.onError) {
+                    response = await middleware.onError({
+                        fetch: this.fetchApi,
+                        url: fetchParams.url,
+                        init: fetchParams.init,
+                        error: e,
+                        response: response ? response.clone() : undefined,
+                    }) || response;
+                }
+            }
+            if (response === undefined) {
+              if (e instanceof Error) {
+                throw new FetchError(e, 'The request failed and the interceptors did not return an alternative response');
+              } else {
+                throw e;
+              }
+            }
+        }
+        for (const middleware of this.middleware) {
+            if (middleware.post) {
+                response = await middleware.post({
+                    fetch: this.fetchApi,
+                    url: fetchParams.url,
+                    init: fetchParams.init,
+                    response: response.clone(),
+                }) || response;
+            }
+        }
+        return response;
+    }
+
+    /**
+     * Create a shallow clone of `this` by constructing a new instance
+     * and then shallow cloning data members.
+     */
+    private clone<T extends BaseAPI>(this: T): T {
+        const constructor = this.constructor as any;
+        const next = new constructor(this.configuration);
+        next.middleware = this.middleware.slice();
+        return next;
+    }
+};
+
+function isBlob(value: any): value is Blob {
+    return typeof Blob !== 'undefined' && value instanceof Blob
+}
+
+function isFormData(value: any): value is FormData {
+    return typeof FormData !== "undefined" && value instanceof FormData
+}
+
+export class ResponseError extends Error {
+    override name: "ResponseError" = "ResponseError";
+    constructor(public response: Response, msg?: string) {
+        super(msg);
+    }
+}
+
+export class FetchError extends Error {
+    override name: "FetchError" = "FetchError";
+    constructor(public cause: Error, msg?: string) {
+        super(msg);
+    }
+}
+
+export class RequiredError extends Error {
+    override name: "RequiredError" = "RequiredError";
+    constructor(public field: string, msg?: string) {
+        super(msg);
+    }
+}
+
+export const COLLECTION_FORMATS = {
+    csv: ",",
+    ssv: " ",
+    tsv: "\t",
+    pipes: "|",
+};
+
+export type FetchAPI = WindowOrWorkerGlobalScope['fetch'];
+
+export type Json = any;
+export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD';
+export type HTTPHeaders = { [key: string]: string };
+export type HTTPQuery = { [key: string]: string | number | null | boolean | Array<string | number | null | boolean> | Set<string | number | null | boolean> | HTTPQuery };
+export type HTTPBody = Json | FormData | URLSearchParams;
+export type HTTPRequestInit = { headers?: HTTPHeaders; method: HTTPMethod; credentials?: RequestCredentials; body?: HTTPBody }
+export type ModelPropertyNaming = 'camelCase' | 'snake_case' | 'PascalCase' | 'original';
+
+export type InitOverrideFunction = (requestContext: { init: HTTPRequestInit, context: RequestOpts }) => Promise<RequestInit>
+
+export interface FetchParams {
+    url: string;
+    init: RequestInit;
+}
+
+export interface RequestOpts {
+    path: string;
+    method: HTTPMethod;
+    headers: HTTPHeaders;
+    query?: HTTPQuery;
+    body?: HTTPBody;
+}
+
+export function exists(json: any, key: string) {
+    const value = json[key];
+    return value !== null && value !== undefined;
+}
+
+export function querystring(params: HTTPQuery, prefix: string = ''): string {
+    return Object.keys(params)
+        .map(key => querystringSingleKey(key, params[key], prefix))
+        .filter(part => part.length > 0)
+        .join('&');
+}
+
+function querystringSingleKey(key: string, value: string | number | null | undefined | boolean | Array<string | number | null | boolean> | Set<string | number | null | boolean> | HTTPQuery, keyPrefix: string = ''): string {
+    const fullKey = keyPrefix + (keyPrefix.length ? `[${key}]` : key);
+    if (value instanceof Array) {
+        const multiValue = value.map(singleValue => encodeURIComponent(String(singleValue)))
+            .join(`&${encodeURIComponent(fullKey)}=`);
+        return `${encodeURIComponent(fullKey)}=${multiValue}`;
+    }
+    if (value instanceof Set) {
+        const valueAsArray = Array.from(value);
+        return querystringSingleKey(key, valueAsArray, keyPrefix);
+    }
+    if (value instanceof Date) {
+        return `${encodeURIComponent(fullKey)}=${encodeURIComponent(value.toISOString())}`;
+    }
+    if (value instanceof Object) {
+        return querystring(value as HTTPQuery, fullKey);
+    }
+    return `${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`;
+}
+
+export function mapValues(data: any, fn: (item: any) => any) {
+  return Object.keys(data).reduce(
+    (acc, key) => ({ ...acc, [key]: fn(data[key]) }),
+    {}
+  );
+}
+
+export function canConsumeForm(consumes: Consume[]): boolean {
+    for (const consume of consumes) {
+        if ('multipart/form-data' === consume.contentType) {
+            return true;
+        }
+    }
+    return false;
+}
+
+export interface Consume {
+    contentType: string
+}
+
+export interface RequestContext {
+    fetch: FetchAPI;
+    url: string;
+    init: RequestInit;
+}
+
+export interface ResponseContext {
+    fetch: FetchAPI;
+    url: string;
+    init: RequestInit;
+    response: Response;
+}
+
+export interface ErrorContext {
+    fetch: FetchAPI;
+    url: string;
+    init: RequestInit;
+    error: unknown;
+    response?: Response;
+}
+
+export interface Middleware {
+    pre?(context: RequestContext): Promise<FetchParams | void>;
+    post?(context: ResponseContext): Promise<Response | void>;
+    onError?(context: ErrorContext): Promise<Response | void>;
+}
+
+export interface ApiResponse<T> {
+    raw: Response;
+    value(): Promise<T>;
+}
+
+export interface ResponseTransformer<T> {
+    (json: any): T;
+}
+
+export class JSONApiResponse<T> {
+    constructor(public raw: Response, private transformer: ResponseTransformer<T> = (jsonValue: any) => jsonValue) {}
+
+    async value(): Promise<T> {
+        return this.transformer(await this.raw.json());
+    }
+}
+
+export class VoidApiResponse {
+    constructor(public raw: Response) {}
+
+    async value(): Promise<void> {
+        return undefined;
+    }
+}
+
+export class BlobApiResponse {
+    constructor(public raw: Response) {}
+
+    async value(): Promise<Blob> {
+        return await this.raw.blob();
+    };
+}
+
+export class TextApiResponse {
+    constructor(public raw: Response) {}
+
+    async value(): Promise<string> {
+        return await this.raw.text();
+    };
+}
-- 
GitLab


From 5bb27fd97bbb574622b8bac0a31b3cd85f89e811 Mon Sep 17 00:00:00 2001
From: Bill Collins <bill.collins@hp.com>
Date: Fri, 28 Oct 2022 11:26:47 +0100
Subject: [PATCH 4/4] Update go example schema/test

---
 .../org/openapitools/codegen/go/GoClientCodegenTest.java  | 2 +-
 .../client/petstore/go/go-petstore/api/openapi.yaml       | 8 +++-----
 2 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/go/GoClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/go/GoClientCodegenTest.java
index 894a105fb45..994a6ace976 100644
--- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/go/GoClientCodegenTest.java
+++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/go/GoClientCodegenTest.java
@@ -160,7 +160,7 @@ public class GoClientCodegenTest {
         List<File> files = generator.opts(configurator.toClientOptInput()).generate();
         files.forEach(File::deleteOnExit);
 
-        TestUtils.assertFileContains(Paths.get(output + "/model_example.go"), "Child NullableExampleChild");
+        TestUtils.assertFileContains(Paths.get(output + "/model_example.go"), "Child NullableChild");
     }
 
     @Test
diff --git a/samples/openapi3/client/petstore/go/go-petstore/api/openapi.yaml b/samples/openapi3/client/petstore/go/go-petstore/api/openapi.yaml
index 71097efe090..579ca479ac7 100644
--- a/samples/openapi3/client/petstore/go/go-petstore/api/openapi.yaml
+++ b/samples/openapi3/client/petstore/go/go-petstore/api/openapi.yaml
@@ -1942,7 +1942,9 @@ components:
     NullableAllOf:
       properties:
         child:
-          $ref: '#/components/schemas/NullableAllOf_child'
+          allOf:
+          - $ref: '#/components/schemas/NullableAllOfChild'
+          nullable: true
       type: object
     OneOfPrimitiveType:
       oneOf:
@@ -2144,10 +2146,6 @@ components:
           type: boolean
       type: object
       example: null
-    NullableAllOf_child:
-      allOf:
-      - $ref: '#/components/schemas/NullableAllOfChild'
-      nullable: true
     DuplicatedPropChild_allOf:
       properties:
         dup-prop:
-- 
GitLab