From ee1e3550d0d1d7f2bb013f4a8851e76eb484141c Mon Sep 17 00:00:00 2001
From: Yonas Kolb <yonaskolb@gmail.com>
Date: Thu, 11 Jan 2018 14:48:43 +0800
Subject: [PATCH] support response references

---
 Sources/Swagger/Reference.swift               | 14 +++++++
 Sources/Swagger/SwaggerSpec.swift             | 37 +++++++++++--------
 .../Sources/TestSpec/Requests/GetString.swift | 26 +++++++++++++
 Specs/TestSpec/spec.yml                       |  7 ++++
 4 files changed, 68 insertions(+), 16 deletions(-)

diff --git a/Sources/Swagger/Reference.swift b/Sources/Swagger/Reference.swift
index d13f56721..36d060103 100644
--- a/Sources/Swagger/Reference.swift
+++ b/Sources/Swagger/Reference.swift
@@ -31,6 +31,20 @@ public class Reference<T: JSONObjectConvertible> {
     func resolve(with value: T) {
         _value = value
     }
+
+    func getReferenceComponent(index: Int) -> String? {
+        let components = string.components(separatedBy: "/")
+        guard components.count > index else { return nil }
+        return components[index]
+    }
+
+    public var referenceType: String? {
+        return getReferenceComponent(index: 1)
+    }
+
+    public var referenceName: String? {
+        return getReferenceComponent(index: 2)
+    }
 }
 
 public enum PossibleReference<T: JSONObjectConvertible>: JSONObjectConvertible {
diff --git a/Sources/Swagger/SwaggerSpec.swift b/Sources/Swagger/SwaggerSpec.swift
index ef6c81d90..c0f4fdee6 100644
--- a/Sources/Swagger/SwaggerSpec.swift
+++ b/Sources/Swagger/SwaggerSpec.swift
@@ -122,28 +122,32 @@ extension SwaggerSpec: JSONObjectConvertible {
 
     func resolveReferences() {
 
-        func resolveDefinitionReference(_ reference: Reference<Schema>) {
-            let components = reference.string.components(separatedBy: "/")
-            if components.count == 3 && components[0] == "#" && components[1] == "definitions" {
-                let name = components[2]
-                if let schema = definitions.first(where: { $0.name == name }) {
-                    reference.resolve(with: schema.value)
-                }
+        func resolvePossibleReference<T>(_ reference: PossibleReference<T>, objects: [SwaggerObject<T>], type: String) {
+            if case let .reference(reference) = reference {
+                resolveReference(reference, objects: objects, type: type)
             }
         }
 
-        func resolveParameterReference(_ reference: PossibleReference<Parameter>) {
-            if case let .reference(reference) = reference {
-                let components = reference.string.components(separatedBy: "/")
-                if components.count == 3 && components[0] == "#" && components[1] == "parameters" {
-                    let name = components[2]
-                    if let param = parameters.first(where: { $0.name == name }) {
-                        reference.resolve(with: param.value)
-                    }
-                }
+        func resolveReference<T>(_ reference: Reference<T>, objects: [SwaggerObject<T>], type: String) {
+            if reference.referenceType == type,
+                let name = reference.referenceName,
+                let object = objects.first(where: { $0.name == name }) {
+                reference.resolve(with: object.value)
             }
         }
 
+        func resolveDefinitionReference(_ reference: Reference<Schema>) {
+            resolveReference(reference, objects: definitions, type: "definitions")
+        }
+
+        func resolveParameterReference(_ reference: PossibleReference<Parameter>) {
+            resolvePossibleReference(reference, objects: parameters, type: "parameters")
+        }
+
+        func resolveResponseReference(_ reference: PossibleReference<Response>) {
+            resolvePossibleReference(reference, objects: responses, type: "responses")
+        }
+
         func resolveSchema(_ schema: Schema) {
             switch schema.type {
             case let .reference(reference): resolveDefinitionReference(reference)
@@ -179,6 +183,7 @@ extension SwaggerSpec: JSONObjectConvertible {
             path.operations.forEach {
                 $0.pathParameters.forEach(resolveParameterReference)
                 $0.operationParameters.forEach(resolveParameterReference)
+                $0.responses.map{$0.response}.forEach(resolveResponseReference)
             }
         }
     }
diff --git a/Specs/TestSpec/generated/Swift/Sources/TestSpec/Requests/GetString.swift b/Specs/TestSpec/generated/Swift/Sources/TestSpec/Requests/GetString.swift
index 387cba761..925de031d 100644
--- a/Specs/TestSpec/generated/Swift/Sources/TestSpec/Requests/GetString.swift
+++ b/Specs/TestSpec/generated/Swift/Sources/TestSpec/Requests/GetString.swift
@@ -26,33 +26,59 @@ extension TestSpec {
             /** string response */
             case status200(String)
 
+            /** 400 response */
+            case status400(String)
+
             public var success: String? {
                 switch self {
                 case .status200(let response): return response
+                default: return nil
+                }
+            }
+
+            public var failure: String? {
+                switch self {
+                case .status400(let response): return response
+                default: return nil
+                }
+            }
+
+            /// either success or failure value. Success is anything in the 200..<300 status code range
+            public var responseResult: APIResponseResult<String, String> {
+                if let successValue = success {
+                    return .success(successValue)
+                } else if let failureValue = failure {
+                    return .failure(failureValue)
+                } else {
+                    fatalError("Response does not have success or failure response")
                 }
             }
 
             public var response: Any {
                 switch self {
                 case .status200(let response): return response
+                case .status400(let response): return response
                 }
             }
 
             public var statusCode: Int {
                 switch self {
                 case .status200: return 200
+                case .status400: return 400
                 }
             }
 
             public var successful: Bool {
                 switch self {
                 case .status200: return true
+                case .status400: return false
                 }
             }
 
             public init(statusCode: Int, data: Data) throws {
                 switch statusCode {
                 case 200: self = try .status200(JSONDecoder.decode(data: data))
+                case 400: self = try .status400(JSONDecoder.decode(data: data))
                 default: throw APIError.unexpectedStatusCode(statusCode: statusCode, data: data)
                 }
             }
diff --git a/Specs/TestSpec/spec.yml b/Specs/TestSpec/spec.yml
index 74967854c..e7c1d3841 100644
--- a/Specs/TestSpec/spec.yml
+++ b/Specs/TestSpec/spec.yml
@@ -70,6 +70,8 @@ paths:
           description: string response
           schema:
             type: string
+        400:
+          $ref: '#/responses/400'
 definitions:
   User:
     type: object
@@ -105,3 +107,8 @@ definitions:
     description: definition with only additional properties
     type: object
     additionalProperties: true
+responses:
+  400:
+    description: 400 response
+    schema:
+      type: string