Add vendor folder to git

This commit is contained in:
Lucas Käldström 2017-06-26 19:23:05 +03:00
parent 66cf5eaafb
commit 183585f56f
No known key found for this signature in database
GPG key ID: 600FEFBBD0D40D21
6916 changed files with 2629581 additions and 1 deletions

View file

@ -0,0 +1,13 @@
all:
swift build
.build/debug/TemplateEncoder > Sources/gnostic_swift_generator/Templates.swift
swift build
cp .build/debug/gnostic_swift_generator gnostic_swift_generator
rm -f gnostic_swift_client gnostic_swift_server
ln -s gnostic_swift_generator gnostic_swift_client
ln -s gnostic_swift_generator gnostic_swift_server
clean:
rm -rf .build Packages
rm -rf gnostic_swift_client gnostic_swift_service

View file

@ -0,0 +1,30 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import PackageDescription
let package = Package(
name: "SwiftOpenAPIPlugin",
targets: [
Target(name: "gnostic_swift_generator",
dependencies: [
"TemplateEncoder",
]),
Target(name: "TemplateEncoder")
],
dependencies: [
.Package(url: "https://github.com/apple/swift-protobuf.git", Version(0,9,24)),
.Package(url: "https://github.com/kylef/Stencil.git", Version(0,8,0))
]
)

View file

@ -0,0 +1,15 @@
# OpenAPI Swift Generator Plugin
This directory contains an `openapic` plugin that can be used to generate a Swift client library and scaffolding for a Swift server for an API with an OpenAPI description.
The plugin can be invoked like this:
openapic bookstore.json --swift_generator_out=Bookstore
Where `Bookstore` is the name of a directory where the generated code will be written.
Both client and server code will be generated.
For example usage, see the [examples/bookstore](examples/bookstore) directory.
HTTP services are provided by the Kitura library.

View file

@ -0,0 +1,43 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
let TEMPLATES = "Templates"
var s = ""
s += "// GENERATED: DO NOT EDIT\n"
s += "//\n"
s += "// This file contains base64 encodings of templates used for Swift OpenAPI code generation.\n"
s += "//\n"
s += "func loadTemplates() -> [String:String] {\n"
s += " return [\n"
let filenames = try FileManager.default.contentsOfDirectory(atPath:TEMPLATES)
for filename in filenames {
if filename.hasSuffix(".tmpl") {
let fileURL = URL(fileURLWithPath:TEMPLATES + "/" + filename)
let filedata = try Data(contentsOf:fileURL)
let encoding = filedata.base64EncodedString()
var templatename = filename
if let extRange = templatename.range(of: ".tmpl") {
templatename.replaceSubrange(extRange, with: "")
}
s += " \"" + templatename + "\": \"" + encoding + "\",\n"
}
}
s += " ]\n"
s += "}\n"
print(s)

View file

@ -0,0 +1,313 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Stencil
extension String {
func capitalizingFirstLetter() -> String {
let first = String(characters.prefix(1)).capitalized
let other = String(characters.dropFirst())
return first + other
}
mutating func capitalizeFirstLetter() {
self = self.capitalizingFirstLetter()
}
}
class ServiceType {
var name : String = ""
var fields : [ServiceTypeField] = []
var isInterfaceType : Bool = false
}
class ServiceTypeField {
var name : String = ""
var typeName : String = ""
var isArrayType : Bool = false
var isCastableType : Bool = false
var isConvertibleType : Bool = false
var elementType : String = ""
var jsonName : String = ""
var position: String = "" // "body", "header", "formdata", "query", or "path"
var initialValue : String = ""
func setTypeForName(_ name : String, _ format : String) {
switch name {
case "integer":
if format == "int32" {
self.typeName = "Int32"
} else if format == "int64" {
self.typeName = "Int64"
} else {
self.typeName = "Int"
}
self.initialValue = "0"
self.isCastableType = true
default:
self.typeName = name.capitalizingFirstLetter()
self.initialValue = self.typeName + "()"
self.isConvertibleType = true
}
}
func setTypeForSchema(_ schema : Openapi_V2_Schema, optional : Bool = false) {
let ref = schema.ref
if ref != "" {
self.typeName = typeForRef(ref)
self.isConvertibleType = true
self.initialValue = self.typeName + "()"
}
if schema.hasType {
let types = schema.type.value
let format = schema.format
if types.count == 1 && types[0] == "string" {
self.typeName = "String"
self.isCastableType = true
self.initialValue = "\"\""
}
if types.count == 1 && types[0] == "integer" && format == "int32" {
self.typeName = "Int32"
self.isCastableType = true
self.initialValue = "0"
}
if types.count == 1 && types[0] == "array" && schema.hasItems {
// we have an array.., but of what?
let items = schema.items.schema
if items.count == 1 && items[0].ref != "" {
self.isArrayType = true
self.elementType = typeForRef(items[0].ref)
self.typeName = "[" + self.elementType + "]"
self.initialValue = "[]"
}
}
}
// this function is incomplete... so return a string representing anything that we don't handle
if self.typeName == "" {
self.typeName = "\(schema)"
}
if optional {
self.typeName += "?"
self.initialValue = "nil"
}
}
}
class ServiceMethod {
var name : String = ""
var path : String = ""
var method : String = ""
var description : String = ""
var handlerName : String = ""
var processorName : String = ""
var clientName : String = ""
var resultTypeName : String?
var parametersTypeName : String?
var responsesTypeName : String?
var parametersType : ServiceType?
var responsesType : ServiceType?
}
func propertyNameForResponseCode(_ code:String) -> String {
switch code {
case "200":
return "ok"
case "default":
return "error"
default:
return code
}
}
func typeForRef(_ ref : String) -> String {
let parts = ref.components(separatedBy:"/")
return parts.last!.capitalizingFirstLetter()
}
class ServiceRenderer {
private var templateEnvironment : Environment
private var name : String = ""
private var package: String = ""
private var types : [ServiceType] = []
private var methods : [ServiceMethod] = []
public init(document : Openapi_V2_Document) {
templateEnvironment = Environment(loader:TemplateLoader(), extensions:[TemplateExtensions()])
loadService(document:document)
}
private func loadServiceTypeFromParameters(_ name:String,
_ parameters:[Openapi_V2_ParametersItem])
-> ServiceType? {
let t = ServiceType()
t.name = name.capitalizingFirstLetter() + "Parameters"
for parametersItem in parameters {
let f = ServiceTypeField()
f.typeName = "\(parametersItem)"
switch parametersItem.oneof {
case .parameter(let parameter):
switch parameter.oneof {
case .bodyParameter(let bodyParameter):
f.name = bodyParameter.name
if bodyParameter.hasSchema {
f.setTypeForSchema(bodyParameter.schema)
f.position = "body"
}
case .nonBodyParameter(let nonBodyParameter):
switch (nonBodyParameter.oneof) {
case .headerParameterSubSchema(let headerParameter):
f.name = headerParameter.name
f.position = "header"
case .formDataParameterSubSchema(let formDataParameter):
f.name = formDataParameter.name
f.position = "formdata"
case .queryParameterSubSchema(let queryParameter):
f.name = queryParameter.name
f.position = "query"
case .pathParameterSubSchema(let pathParameter):
f.name = pathParameter.name
f.jsonName = pathParameter.name
f.position = "path"
f.setTypeForName(pathParameter.type, pathParameter.format)
default:
Log("?")
}
default:
Log("?")
}
case .jsonReference: // (let reference):
Log("?")
default:
Log("?")
}
t.fields.append(f)
}
if t.fields.count > 0 {
self.types.append(t)
return t
} else {
return nil
}
}
private func loadServiceTypeFromResponses(_ m:ServiceMethod,
_ name:String,
_ responses:Openapi_V2_Responses)
-> ServiceType? {
let t = ServiceType()
t.name = name.capitalizingFirstLetter() + "Responses"
for responseCode in responses.responseCode {
let f = ServiceTypeField()
f.name = propertyNameForResponseCode(responseCode.name)
f.jsonName = ""
switch responseCode.value.oneof {
case .response(let response):
let schema = response.schema
switch schema.oneof {
case .schema(let schema):
f.setTypeForSchema(schema, optional:true)
t.fields.append(f)
if f.name == "ok" {
m.resultTypeName = f.typeName.replacingOccurrences(of:"?", with:"")
}
default:
break
}
default:
break
}
}
if t.fields.count > 0 {
self.types.append(t)
return t
} else {
return nil
}
}
private func loadOperation(_ operation : Openapi_V2_Operation,
method : String,
path : String) {
let m = ServiceMethod()
m.name = operation.operationId
m.path = path
m.method = method
m.description = operation.description_p
m.handlerName = "handle" + m.name
m.processorName = "" + m.name
m.clientName = m.name
m.parametersType = loadServiceTypeFromParameters(m.name, operation.parameters)
if m.parametersType != nil {
m.parametersTypeName = m.parametersType!.name
}
m.responsesType = loadServiceTypeFromResponses(m, m.name, operation.responses)
if m.responsesType != nil {
m.responsesTypeName = m.responsesType!.name
}
self.methods.append(m)
}
private func loadService(document : Openapi_V2_Document) {
// collect service type descriptions
for pair in document.definitions.additionalProperties {
let t = ServiceType()
t.isInterfaceType = true
let schema = pair.value
for pair2 in schema.properties.additionalProperties {
let f = ServiceTypeField()
f.name = pair2.name
f.setTypeForSchema(pair2.value)
f.jsonName = pair2.name
t.fields.append(f)
}
t.name = pair.name.capitalizingFirstLetter()
self.types.append(t)
}
// collect service method descriptions
for pair in document.paths.path {
let v = pair.value
if v.hasGet {
loadOperation(v.get, method:"GET", path:pair.name)
}
if v.hasPost {
loadOperation(v.post, method:"POST", path:pair.name)
}
if v.hasPut {
loadOperation(v.put, method:"PUT", path:pair.name)
}
if v.hasDelete {
loadOperation(v.delete, method:"DELETE", path:pair.name)
}
}
}
public func generate(filenames : [String], response : inout Openapi_Plugin_V1_Response) throws {
let context = ["renderer": self]
for filename in filenames {
let clientcode = try templateEnvironment.renderTemplate(name:filename,
context:context)
if let data = stripMarkers(clientcode).data(using:.utf8) {
var clientfile = Openapi_Plugin_V1_File()
clientfile.name = filename
clientfile.data = data
response.files.append(clientfile)
}
}
}
}

View file

@ -0,0 +1,157 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Stencil
func TemplateExtensions() -> Extension {
let ext = Extension()
ext.registerFilter("hasParameters") { (value: Any?, arguments: [Any?]) in
let method : ServiceMethod = value as! ServiceMethod
return method.parametersType != nil
}
ext.registerFilter("hasResponses") { (value: Any?, arguments: [Any?]) in
let method : ServiceMethod = value as! ServiceMethod
return method.responsesType != nil
}
ext.registerFilter("syncClientParametersDeclaration") { (value: Any?, arguments: [Any?]) in
let method : ServiceMethod = value as! ServiceMethod
var result = ""
if let parametersType = method.parametersType {
for field in parametersType.fields {
if result != "" {
result += ", "
}
result += field.name + " : " + field.typeName
}
}
return result
}
ext.registerFilter("syncClientReturnDeclaration") { (value: Any?, arguments: [Any?]) in
let method : ServiceMethod = value as! ServiceMethod
var result = ""
if let resultTypeName = method.resultTypeName {
result = " -> " + resultTypeName
}
return result
}
ext.registerFilter("asyncClientParametersDeclaration") { (value: Any?, arguments: [Any?]) in
let method : ServiceMethod = value as! ServiceMethod
var result = ""
if let parametersType = method.parametersType {
for field in parametersType.fields {
if result != "" {
result += ", "
}
result += field.name + " : " + field.typeName
}
}
// add callback
if result != "" {
result += ", "
}
if let resultTypeName = method.resultTypeName {
result += "callback : @escaping (" + resultTypeName + "?, Swift.Error?)->()"
} else {
result += "callback : @escaping (Swift.Error?)->()"
}
return result
}
ext.registerFilter("protocolParametersDeclaration") { (value: Any?, arguments: [Any?]) in
let method : ServiceMethod = value as! ServiceMethod
var result = ""
if let parametersTypeName = method.parametersTypeName {
result = "_ parameters : " + parametersTypeName
}
return result
}
ext.registerFilter("protocolReturnDeclaration") { (value: Any?, arguments: [Any?]) in
let method : ServiceMethod = value as! ServiceMethod
var result = ""
if let responsesTypeName = method.responsesTypeName {
result = "-> " + responsesTypeName
}
return result
}
ext.registerFilter("parameterFieldNames") { (value: Any?, arguments: [Any?]) in
let method : ServiceMethod = value as! ServiceMethod
var result = ""
if let parametersType = method.parametersType {
for field in parametersType.fields {
if result != "" {
result += ", "
}
result += field.name + ":" + field.name
}
}
return result
}
ext.registerFilter("parametersTypeFields") { (value: Any?, arguments: [Any?]) in
let method : ServiceMethod = value as! ServiceMethod
if let parametersType = method.parametersType {
return parametersType.fields
} else {
return []
}
}
ext.registerFilter("kituraPath") { (value: Any?, arguments: [Any?]) in
let method : ServiceMethod = value as! ServiceMethod
var path = method.path
if let parametersType = method.parametersType {
for field in parametersType.fields {
if field.position == "path" {
let original = "{" + field.jsonName + "}"
let replacement = ":" + field.jsonName
path = path.replacingOccurrences(of:original, with:replacement)
}
}
}
return path
}
ext.registerFilter("bodyParameterFieldName") { (value: Any?, arguments: [Any?]) in
let method : ServiceMethod = value as! ServiceMethod
if let parametersType = method.parametersType {
for field in parametersType.fields {
if field.position == "body" {
return field.name
}
}
}
return ""
}
ext.registerFilter("responsesHasFieldNamedOK") { (value: Any?, arguments: [Any?]) in
let method : ServiceMethod = value as! ServiceMethod
if let responsesType = method.responsesType {
for field in responsesType.fields {
if field.name == "ok" {
return true
}
}
}
return false
}
ext.registerFilter("responsesHasFieldNamedError") { (value: Any?, arguments: [Any?]) in
let method : ServiceMethod = value as! ServiceMethod
if let responsesType = method.responsesType {
for field in responsesType.fields {
if field.name == "error" {
return true
}
}
}
return false
}
return ext
}

View file

@ -0,0 +1,51 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Stencil
import Foundation
// A class for loading Stencil templates from compiled-in representations
public class TemplateLoader: Loader {
private var templates: [String:String]
public init() {
self.templates = loadTemplates()
}
public func loadTemplate(name: String, environment: Environment) throws -> Template {
if let encoding = templates[name],
let data = Data(base64Encoded: encoding, options:[]),
let template = String(data:data, encoding:.utf8) {
return environment.templateClass.init(templateString: template,
environment: environment,
name: name)
} else {
throw TemplateDoesNotExist(templateNames: [name], loader: self)
}
}
public func loadTemplate(names: [String], environment: Environment) throws -> Template {
for name in names {
if let encoding = templates[name],
let data = Data(base64Encoded: encoding, options:[]),
let template = String(data:data, encoding:.utf8) {
return environment.templateClass.init(templateString: template,
environment: environment,
name: name)
}
}
throw TemplateDoesNotExist(templateNames: names, loader: self)
}
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,36 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
// Code templates use "//-" prefixes to comment-out template operators
// to keep them from interfering with Swift code formatting tools.
// Use this to remove them after templates have been expanded.
func stripMarkers(_ code:String) -> String {
let inputLines = code.components(separatedBy:"\n")
var outputLines : [String] = []
for line in inputLines {
if line.contains("//-") {
let removed = line.replacingOccurrences(of:"//-", with:"")
if (removed.trimmingCharacters(in:CharacterSet.whitespaces) != "") {
outputLines.append(removed)
}
} else {
outputLines.append(line)
}
}
return outputLines.joined(separator:"\n")
}

View file

@ -0,0 +1,100 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
// The I/O code below is derived from Apple's swift-protobuf project.
// https://github.com/apple/swift-protobuf
// BEGIN swift-protobuf derivation
#if os(Linux)
import Glibc
#else
import Darwin.C
#endif
enum PluginError: Error {
/// Raised for any errors reading the input
case readFailure
}
// Alias clib's write() so Stdout.write(bytes:) can call it.
private let _write = write
class Stdin {
static func readall() throws -> Data {
let fd: Int32 = 0
let buffSize = 32
var buff = [UInt8]()
while true {
var fragment = [UInt8](repeating: 0, count: buffSize)
let count = read(fd, &fragment, buffSize)
if count < 0 {
throw PluginError.readFailure
}
if count < buffSize {
buff += fragment[0..<count]
return Data(bytes: buff)
}
buff += fragment
}
}
}
class Stdout {
static func write(bytes: Data) {
bytes.withUnsafeBytes { (p: UnsafePointer<UInt8>) -> () in
_ = _write(1, p, bytes.count)
}
}
}
struct CodePrinter {
private(set) var content = ""
private var currentIndentDepth = 0
private var currentIndent = ""
private var atLineStart = true
mutating func print(_ text: String...) {
for t in text {
for c in t.characters {
if c == "\n" {
content.append(c)
atLineStart = true
} else {
if atLineStart {
content.append(currentIndent)
atLineStart = false
}
content.append(c)
}
}
}
}
mutating private func resetIndent() {
currentIndent = (0..<currentIndentDepth).map { Int -> String in return " " } .joined(separator:"")
}
mutating func indent() {
currentIndentDepth += 1
resetIndent()
}
mutating func outdent() {
currentIndentDepth -= 1
resetIndent()
}
}
// END swift-protobuf derivation

View file

@ -0,0 +1,51 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
func Log(_ message : String) {
FileHandle.standardError.write((message + "\n").data(using:.utf8)!)
}
func main() throws {
// read the OpenAPI document
let rawRequest = try Stdin.readall()
let request = try Openapi_Plugin_V1_Request(protobuf: rawRequest)
let wrapper = request.wrapper
let document = try Openapi_V2_Document(protobuf:wrapper.value)
// build the service renderer
let renderer = ServiceRenderer(document:document)
// generate the desired files
var response = Openapi_Plugin_V1_Response()
var filenames : [String]
switch CommandLine.arguments[0] {
case "openapi_swift_client":
filenames = ["client.swift", "types.swift", "fetch.swift"]
case "openapi_swift_server":
filenames = ["server.swift", "types.swift"]
default:
filenames = ["client.swift", "server.swift", "types.swift", "fetch.swift"]
}
try renderer.generate(filenames:filenames, response:&response)
// return the results
let serializedResponse = try response.serializeProtobuf()
Stdout.write(bytes: serializedResponse)
}
try main()

View file

@ -0,0 +1,517 @@
/*
* DO NOT EDIT.
*
* Generated by the protocol buffer compiler.
* Source: plugin.proto
*
*/
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// openapic (aka the OpenAPI Compiler) can be extended via plugins.
// A plugin is just a program that reads a Request from stdin
// and writes a Response to stdout.
//
// A plugin executable needs only to be placed somewhere in the path. The
// plugin should be named "openapi_$NAME", and will then be used when the
// flag "--${NAME}_out" is passed to openapic.
import Foundation
import SwiftProtobuf
/// The version number of OpenAPI compiler.
public struct Openapi_Plugin_V1_Version: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Version"}
public var protoMessageName: String {return "Version"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"major": 1,
"minor": 2,
"patch": 3,
"suffix": 4,
]}
public var protoFieldNames: [String: Int] {return [
"major": 1,
"minor": 2,
"patch": 3,
"suffix": 4,
]}
public var major: Int32 = 0
public var minor: Int32 = 0
public var patch: Int32 = 0
/// A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should
/// be empty for mainline stable releases.
public var suffix: String = ""
public init() {}
public init(major: Int32? = nil,
minor: Int32? = nil,
patch: Int32? = nil,
suffix: String? = nil)
{
if let v = major {
self.major = v
}
if let v = minor {
self.minor = v
}
if let v = patch {
self.patch = v
}
if let v = suffix {
self.suffix = v
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularField(fieldType: ProtobufInt32.self, value: &major)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufInt32.self, value: &minor)
case 3: handled = try setter.decodeSingularField(fieldType: ProtobufInt32.self, value: &patch)
case 4: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &suffix)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if major != 0 {
try visitor.visitSingularField(fieldType: ProtobufInt32.self, value: major, protoFieldNumber: 1, protoFieldName: "major", jsonFieldName: "major", swiftFieldName: "major")
}
if minor != 0 {
try visitor.visitSingularField(fieldType: ProtobufInt32.self, value: minor, protoFieldNumber: 2, protoFieldName: "minor", jsonFieldName: "minor", swiftFieldName: "minor")
}
if patch != 0 {
try visitor.visitSingularField(fieldType: ProtobufInt32.self, value: patch, protoFieldNumber: 3, protoFieldName: "patch", jsonFieldName: "patch", swiftFieldName: "patch")
}
if suffix != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: suffix, protoFieldNumber: 4, protoFieldName: "suffix", jsonFieldName: "suffix", swiftFieldName: "suffix")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Version) -> Bool {
if major != other.major {return false}
if minor != other.minor {return false}
if patch != other.patch {return false}
if suffix != other.suffix {return false}
return true
}
}
/// A parameter passed to the plugin from (or through) the OpenAPI compiler.
public struct Openapi_Plugin_V1_Parameter: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Parameter"}
public var protoMessageName: String {return "Parameter"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"name": 1,
"value": 2,
]}
public var protoFieldNames: [String: Int] {return [
"name": 1,
"value": 2,
]}
/// The name of the parameter as specified in the option string
public var name: String = ""
/// The parameter value as specified in the option string
public var value: String = ""
public init() {}
public init(name: String? = nil,
value: String? = nil)
{
if let v = name {
self.name = v
}
if let v = value {
self.value = v
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &name)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &value)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if name != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: name, protoFieldNumber: 1, protoFieldName: "name", jsonFieldName: "name", swiftFieldName: "name")
}
if value != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: value, protoFieldNumber: 2, protoFieldName: "value", jsonFieldName: "value", swiftFieldName: "value")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Parameter) -> Bool {
if name != other.name {return false}
if value != other.value {return false}
return true
}
}
/// An encoded Request is written to the plugin's stdin.
public struct Openapi_Plugin_V1_Request: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Request"}
public var protoMessageName: String {return "Request"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"wrapper": 1,
"outputPath": 2,
"parameters": 3,
"compilerVersion": 4,
]}
public var protoFieldNames: [String: Int] {return [
"wrapper": 1,
"output_path": 2,
"parameters": 3,
"compiler_version": 4,
]}
private class _StorageClass {
typealias ProtobufExtendedMessage = Openapi_Plugin_V1_Request
var _wrapper: Openapi_Plugin_V1_Wrapper? = nil
var _outputPath: String = ""
var _parameters: [Openapi_Plugin_V1_Parameter] = []
var _compilerVersion: Openapi_Plugin_V1_Version? = nil
init() {}
func decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularMessageField(fieldType: Openapi_Plugin_V1_Wrapper.self, value: &_wrapper)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &_outputPath)
case 3: handled = try setter.decodeRepeatedMessageField(fieldType: Openapi_Plugin_V1_Parameter.self, value: &_parameters)
case 4: handled = try setter.decodeSingularMessageField(fieldType: Openapi_Plugin_V1_Version.self, value: &_compilerVersion)
default:
handled = false
}
return handled
}
func traverse(visitor: inout ProtobufVisitor) throws {
if let v = _wrapper {
try visitor.visitSingularMessageField(value: v, protoFieldNumber: 1, protoFieldName: "wrapper", jsonFieldName: "wrapper", swiftFieldName: "wrapper")
}
if _outputPath != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: _outputPath, protoFieldNumber: 2, protoFieldName: "output_path", jsonFieldName: "outputPath", swiftFieldName: "outputPath")
}
if !_parameters.isEmpty {
try visitor.visitRepeatedMessageField(value: _parameters, protoFieldNumber: 3, protoFieldName: "parameters", jsonFieldName: "parameters", swiftFieldName: "parameters")
}
if let v = _compilerVersion {
try visitor.visitSingularMessageField(value: v, protoFieldNumber: 4, protoFieldName: "compiler_version", jsonFieldName: "compilerVersion", swiftFieldName: "compilerVersion")
}
}
func isEqualTo(other: _StorageClass) -> Bool {
if _wrapper != other._wrapper {return false}
if _outputPath != other._outputPath {return false}
if _parameters != other._parameters {return false}
if _compilerVersion != other._compilerVersion {return false}
return true
}
func copy() -> _StorageClass {
let clone = _StorageClass()
clone._wrapper = _wrapper
clone._outputPath = _outputPath
clone._parameters = _parameters
clone._compilerVersion = _compilerVersion
return clone
}
}
private var _storage = _StorageClass()
/// A wrapped OpenAPI document to process.
public var wrapper: Openapi_Plugin_V1_Wrapper {
get {return _storage._wrapper ?? Openapi_Plugin_V1_Wrapper()}
set {_uniqueStorage()._wrapper = newValue}
}
/// Output path specified in the plugin invocation.
public var outputPath: String {
get {return _storage._outputPath}
set {_uniqueStorage()._outputPath = newValue}
}
/// Plugin parameters parsed from the invocation string.
public var parameters: [Openapi_Plugin_V1_Parameter] {
get {return _storage._parameters}
set {_uniqueStorage()._parameters = newValue}
}
/// The version number of openapi compiler.
public var compilerVersion: Openapi_Plugin_V1_Version {
get {return _storage._compilerVersion ?? Openapi_Plugin_V1_Version()}
set {_uniqueStorage()._compilerVersion = newValue}
}
public init() {}
public init(wrapper: Openapi_Plugin_V1_Wrapper? = nil,
outputPath: String? = nil,
parameters: [Openapi_Plugin_V1_Parameter] = [],
compilerVersion: Openapi_Plugin_V1_Version? = nil)
{
let storage = _uniqueStorage()
storage._wrapper = wrapper
if let v = outputPath {
storage._outputPath = v
}
if !parameters.isEmpty {
storage._parameters = parameters
}
storage._compilerVersion = compilerVersion
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
return try _uniqueStorage().decodeField(setter: &setter, protoFieldNumber: protoFieldNumber)
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
try _storage.traverse(visitor: &visitor)
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Request) -> Bool {
return _storage === other._storage || _storage.isEqualTo(other: other._storage)
}
private mutating func _uniqueStorage() -> _StorageClass {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _storage.copy()
}
return _storage
}
}
/// The plugin writes an encoded Response to stdout.
public struct Openapi_Plugin_V1_Response: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Response"}
public var protoMessageName: String {return "Response"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"errors": 1,
"files": 2,
]}
public var protoFieldNames: [String: Int] {return [
"errors": 1,
"files": 2,
]}
/// Error message. If non-empty, the plugin failed.
/// The plugin process should exit with status code zero
/// even if it reports an error in this way.
///
/// This should be used to indicate errors which prevent the plugin from
/// operating as intended. Errors which indicate a problem in openapic
/// itself -- such as the input Document being unparseable -- should be
/// reported by writing a message to stderr and exiting with a non-zero
/// status code.
public var errors: [String] = []
/// file output, each file will be written by openapic to an appropriate location.
public var files: [Openapi_Plugin_V1_File] = []
public init() {}
public init(errors: [String] = [],
files: [Openapi_Plugin_V1_File] = [])
{
if !errors.isEmpty {
self.errors = errors
}
if !files.isEmpty {
self.files = files
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeRepeatedField(fieldType: ProtobufString.self, value: &errors)
case 2: handled = try setter.decodeRepeatedMessageField(fieldType: Openapi_Plugin_V1_File.self, value: &files)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if !errors.isEmpty {
try visitor.visitRepeatedField(fieldType: ProtobufString.self, value: errors, protoFieldNumber: 1, protoFieldName: "errors", jsonFieldName: "errors", swiftFieldName: "errors")
}
if !files.isEmpty {
try visitor.visitRepeatedMessageField(value: files, protoFieldNumber: 2, protoFieldName: "files", jsonFieldName: "files", swiftFieldName: "files")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Response) -> Bool {
if errors != other.errors {return false}
if files != other.files {return false}
return true
}
}
/// File describes a file generated by a plugin.
public struct Openapi_Plugin_V1_File: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_File"}
public var protoMessageName: String {return "File"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"name": 1,
"data": 2,
]}
public var protoFieldNames: [String: Int] {return [
"name": 1,
"data": 2,
]}
/// name of the file
public var name: String = ""
/// data to be written to the file
public var data: Data = Data()
public init() {}
public init(name: String? = nil,
data: Data? = nil)
{
if let v = name {
self.name = v
}
if let v = data {
self.data = v
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &name)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufBytes.self, value: &data)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if name != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: name, protoFieldNumber: 1, protoFieldName: "name", jsonFieldName: "name", swiftFieldName: "name")
}
if data != Data() {
try visitor.visitSingularField(fieldType: ProtobufBytes.self, value: data, protoFieldNumber: 2, protoFieldName: "data", jsonFieldName: "data", swiftFieldName: "data")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_File) -> Bool {
if name != other.name {return false}
if data != other.data {return false}
return true
}
}
/// Wrapper wraps an OpenAPI document with its version.
public struct Openapi_Plugin_V1_Wrapper: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Wrapper"}
public var protoMessageName: String {return "Wrapper"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"name": 1,
"version": 2,
"value": 3,
]}
public var protoFieldNames: [String: Int] {return [
"name": 1,
"version": 2,
"value": 3,
]}
/// filename or URL of the wrapped document
public var name: String = ""
/// version of the OpenAPI specification that is used by the wrapped document
public var version: String = ""
/// valid serialized protocol buffer of the named OpenAPI specification version
public var value: Data = Data()
public init() {}
public init(name: String? = nil,
version: String? = nil,
value: Data? = nil)
{
if let v = name {
self.name = v
}
if let v = version {
self.version = v
}
if let v = value {
self.value = v
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &name)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &version)
case 3: handled = try setter.decodeSingularField(fieldType: ProtobufBytes.self, value: &value)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if name != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: name, protoFieldNumber: 1, protoFieldName: "name", jsonFieldName: "name", swiftFieldName: "name")
}
if version != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: version, protoFieldNumber: 2, protoFieldName: "version", jsonFieldName: "version", swiftFieldName: "version")
}
if value != Data() {
try visitor.visitSingularField(fieldType: ProtobufBytes.self, value: value, protoFieldNumber: 3, protoFieldName: "value", jsonFieldName: "value", swiftFieldName: "value")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Wrapper) -> Bool {
if name != other.name {return false}
if version != other.version {return false}
if value != other.value {return false}
return true
}
}

View file

@ -0,0 +1,123 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Client code
import Foundation
import Dispatch
enum ClientError: Swift.Error {
case errorWithCode(Int)
}
public class Client {
var service : String
public init(service: String) {
self.service = service
}
//-{% for serviceMethod in renderer.methods %}
// {{ serviceMethod.description }} Asynchronous.
public func {{ serviceMethod.name }}({{ serviceMethod|asyncClientParametersDeclaration }}) throws {
var path = self.service
path = path + "{{ serviceMethod.path }}"
//-{% for serviceTypeField in serviceMethod|parametersTypeFields %}
//-{% if serviceTypeField.position == "path" %}
path = path.replacingOccurrences(of:"{"+"{{ serviceTypeField.name }}"+"}", with:"\({{ serviceTypeField.name }})")
//-{% endif %}
//-{% endfor %}
guard let url = URL(string:path) else {
throw ClientError.errorWithCode(0)
}
var request = URLRequest(url:url)
request.httpMethod = "{{ serviceMethod.method }}"
//-{% for serviceTypeField in serviceMethod|parametersTypeFields %}
//-{% if serviceTypeField.position == "body" %}
let jsonObject = {{ serviceTypeField.name }}.jsonObject()
request.httpBody = try JSONSerialization.data(withJSONObject:jsonObject)
//-{% endif %}
//-{% endfor %}
//-{% if serviceMethod|hasResponses %}
fetch(request) {(data, response, error) in
if error != nil {
callback(nil, ClientError.errorWithCode(0))
return
}
guard let httpResponse = response else {
callback(nil, ClientError.errorWithCode(0))
return
}
if httpResponse.statusCode == 200 {
if let data = data {
let jsonObject = try! JSONSerialization.jsonObject(with:data)
if let value = {{ serviceMethod.resultTypeName }}(jsonObject:jsonObject) {
callback(value, nil)
return
}
}
callback(nil, nil)
} else {
callback(nil, ClientError.errorWithCode(httpResponse.statusCode))
}
}
//-{% else %}
fetch(request) {(data, response, error) in
if error != nil {
callback(ClientError.errorWithCode(0))
return
}
guard let httpResponse = response else {
callback(ClientError.errorWithCode(0))
return
}
if httpResponse.statusCode == 200 {
callback(nil)
} else {
callback(ClientError.errorWithCode(httpResponse.statusCode))
}
}
//-{% endif %}
}
// {{ serviceMethod.description }} Synchronous.
public func {{ serviceMethod.name }}({{ serviceMethod|syncClientParametersDeclaration }}) throws {{ serviceMethod|syncClientReturnDeclaration }} {
let sem = DispatchSemaphore(value: 0)
//-{% if serviceMethod|hasResponses %}
var response : {{ serviceMethod.resultTypeName }}?
//-{% endif %}
var error : Swift.Error?
//-{% if serviceMethod|hasResponses %}
try {{ serviceMethod.name }}({{ serviceMethod|parameterFieldNames }}) {r, e in
response = r
//-{% else %}
try {{ serviceMethod.name }}({{ serviceMethod|parameterFieldNames }}) {e in
//-{% endif %}
error = e
sem.signal()
}
sem.wait()
if let actualError = error {
throw actualError
}
//-{% if serviceMethod|hasResponses %}
if let actualResponse = response {
return actualResponse
} else {
throw ClientError.errorWithCode(0)
}
//-{% endif %}
}
//-{% endfor %}
}

View file

@ -0,0 +1,143 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
import Dispatch
import KituraNet
// fetch makes a synchronous request using KituraNet's ClientRequest class
// https://github.com/IBM-Swift/Kitura-net/blob/master/Sources/KituraNet/ClientRequest.swift
public func fetch(_ urlRequest: URLRequest) -> (Data?, HTTPURLResponse?, Error?) {
var data: Data?
var urlResponse: HTTPURLResponse?
let error: Error? = nil // make this mutable when we start using it
let sem = DispatchSemaphore(value: 0)
guard let method = urlRequest.httpMethod else {
return (data, urlResponse, error)
}
guard let url = urlRequest.url else {
return (data, urlResponse, error)
}
guard let scheme = url.scheme else {
return (data, urlResponse, error)
}
guard let host = url.host else {
return (data, urlResponse, error)
}
guard let port = url.port else {
return (data, urlResponse, error)
}
let options : [ClientRequest.Options] = [
.method(method),
.schema(scheme),
.hostname(host),
.port(Int16(port)),
.path(url.path),
// headers, etc
]
let request = HTTP.request(options) { (response) in
guard let response = response else {
sem.signal()
return
}
var responseData = Data()
do {
let code = response.httpStatusCode
try response.readAllData(into: &responseData)
data = responseData
urlResponse = HTTPURLResponse(url:url,
statusCode:code.rawValue,
httpVersion:"HTTP/1.1",
headerFields:[:])
sem.signal()
return
} catch {
sem.signal()
return
}
}
if let requestData = urlRequest.httpBody {
request.write(from:requestData)
}
request.end() // send the request
// now wait on the semaphore for a response
let result = sem.wait(timeout: DispatchTime.distantFuture)
switch result {
case .success:
return (data, urlResponse, error)
default: // includes .timeout
return (data, urlResponse, error)
}
}
// fetch makes an asynchronous request using KituraNet's ClientRequest class
// https://github.com/IBM-Swift/Kitura-net/blob/master/Sources/KituraNet/ClientRequest.swift
public func fetch(_ urlRequest: URLRequest, callback:@escaping (Data?, HTTPURLResponse?, Error?) -> ()) {
var data: Data?
var urlResponse: HTTPURLResponse?
let error: Error? = nil // make this mutable when we start using it
guard let method = urlRequest.httpMethod else {
callback (data, urlResponse, error)
return
}
guard let url = urlRequest.url else {
callback (data, urlResponse, error)
return
}
guard let scheme = url.scheme else {
callback (data, urlResponse, error)
return
}
guard let host = url.host else {
callback (data, urlResponse, error)
return
}
guard let port = url.port else {
callback (data, urlResponse, error)
return
}
let options : [ClientRequest.Options] = [
.method(method),
.schema(scheme),
.hostname(host),
.port(Int16(port)),
.path(url.path),
// headers, etc
]
let request = HTTP.request(options) { (response) in
guard let response = response else {
callback (data, urlResponse, nil)
return
}
var responseData = Data()
do {
let code = response.httpStatusCode
try response.readAllData(into: &responseData)
data = responseData
urlResponse = HTTPURLResponse(url:url,
statusCode:code.rawValue,
httpVersion:"HTTP/1.1",
headerFields:[:])
callback (data, urlResponse, nil)
return
} catch {
callback (data, urlResponse, nil)
return
}
}
if let requestData = urlRequest.httpBody {
request.write(from:requestData)
}
request.end() // send the request
}

View file

@ -0,0 +1,127 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Service code
import Kitura
import KituraNet
import Foundation
// A server requires an instance of an implementation of this protocol.
public protocol Service {
//-{% for serviceMethod in renderer.methods %}
// {{ serviceMethod.description }}
func {{ serviceMethod.name }} ({{ serviceMethod|protocolParametersDeclaration }}) throws {{ serviceMethod|protocolReturnDeclaration }}
//-{% endfor %}
}
func intValue(_ s:String?) -> Int64 {
guard let s = s else {
return 0
}
guard let value = Int64(s) else {
return 0
}
return value
}
public func server(service : Service) -> Router {
// Create a new router
let router = Router()
//-{% for serviceMethod in renderer.methods %}
// {{ serviceMethod.description }}
router.{{ serviceMethod.method|lowercase }}("{{ serviceMethod|kituraPath }}") { req, res, next in
//-{% if serviceMethod|hasParameters %}
// instantiate the parameters structure
let parameters = {{ serviceMethod.parametersTypeName }}()
//-{% for serviceTypeField in serviceMethod|parametersTypeFields %}
//-{% if serviceTypeField.position == "path" %}
parameters.{{ serviceTypeField.name }} = intValue(req.parameters["{{ serviceTypeField.name }}"])
//-{% endif %}
//-{% endfor %}
//-{% if serviceMethod.method == "POST" %}
// deserialize request from post data
let bodyString = try req.readString() ?? ""
guard let bodyData = bodyString.data(using:.utf8) else {
try res.send(status:.badRequest).end()
return
}
var jsonObject : Any? = nil
do {
jsonObject = try JSONSerialization.jsonObject(with:bodyData)
} catch {
try res.send(status:.badRequest).end()
return
}
guard let bodyObject = {{ serviceMethod.resultTypeName }}(jsonObject:jsonObject) else {
try res.send(status:.badRequest).end()
return
}
parameters.{{ serviceMethod|bodyParameterFieldName }} = bodyObject
//-{% endif %}
//-{% endif %}
//-{% if serviceMethod|hasParameters %}
//-{% if serviceMethod|hasResponses %}
let responses = try service.{{ serviceMethod.name }}(parameters)
//-{% else %}
try service.{{ serviceMethod.name }}(parameters)
//-{% endif %}
//-{% else %}
//-{% if serviceMethod|hasResponses %}
let responses = try service.{{ serviceMethod.name }}()
//-{% else %}
try service.{{ serviceMethod.name }}()
//-{% endif %}
//-{% endif %}
//-{% if serviceMethod|hasResponses %}
//-{% if serviceMethod|responsesHasFieldNamedOK %}
if let ok = responses.ok {
let jsonObject = ok.jsonObject()
let responseData = try JSONSerialization.data(withJSONObject:jsonObject)
try res.send(data:responseData).end()
return
}
//-{% endif %}
//-{% if serviceMethod|responsesHasFieldNamedError %}
if let errorResponse = responses.error {
guard let statusCode = HTTPStatusCode(rawValue:Int(errorResponse.code)) else {
try res.send(status:.unknown).end()
return
}
try res.send(status:statusCode).end()
return
}
//-{% endif %}
try res.send(status:.internalServerError).end()
//-{% else %}
try res.send(status:.OK).end()
//-{% endif %}
}
//-{% endfor %}
return router
}
public func initialize(service: Service, port:Int) {
// Create a new router
let router = server(service:service)
// Add an HTTP server and connect it to the router
Kitura.addHTTPServer(onPort:port, with: router)
}
public func run() {
// Start the Kitura runloop (this call never returns)
Kitura.run()
}

View file

@ -0,0 +1,83 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Common type declarations
//-{% for serviceType in renderer.types %}
public class {{ serviceType.name }} : CustomStringConvertible {
//-{% for serviceTypeField in serviceType.fields %}
public var {{serviceTypeField.name}} : {{serviceTypeField.typeName}} = {{serviceTypeField.initialValue}}
//-{% endfor %}
public init() {}
//-{% if serviceType.isInterfaceType %}
public init?(jsonObject: Any?) {
if let jsonDictionary = jsonObject as? [String:Any] {
//-{% for serviceTypeField in serviceType.fields %}
if let value : Any = jsonDictionary["{{ serviceTypeField.jsonName }}"] {
//-{% if serviceTypeField.isArrayType %}
var outArray : [{{ serviceTypeField.elementType }}] = []
let array = value as! [Any]
for arrayValue in array {
if let element = {{ serviceTypeField.elementType }}(jsonObject:arrayValue) {
outArray.append(element)
}
}
self.{{serviceTypeField.name}} = outArray
//-{% endif %}
//-{% if serviceTypeField.isCastableType %}
self.{{serviceTypeField.name}} = value as! {{serviceTypeField.typeName}}
//-{% endif %}
//-{% if serviceTypeField.isConvertableType %}
self.{{serviceTypeField.name}} = {{serviceTypeField.typeName}}(value)
//-{% endif %}
}
//-{% endfor %}
} else {
return nil
}
}
public func jsonObject() -> Any {
var result : [String:Any] = [:]
//-{% for serviceTypeField in serviceType.fields %}
//-{% if serviceTypeField.isArrayType %}
var outArray : [Any] = []
for arrayValue in self.{{ serviceTypeField.name }} {
outArray.append(arrayValue.jsonObject())
}
result["{{ serviceTypeField.jsonName }}"] = outArray
//-{% endif %}
//-{% if serviceTypeField.isCastableType %}
result["{{ serviceTypeField.jsonName }}"] = self.{{serviceTypeField.name}}
//-{% endif %}
//-{% if serviceTypeField.isConvertableType %}
result["{{ serviceTypeField.jsonName }}"] = self.{{serviceTypeField.name}}.jsonObject()
//-{% endif %}
//-{% endfor %}
return result
}
//-{% endif %}
public var description: String{
return "[{{ serviceType.name }}" +
//-{% for serviceTypeField in serviceType.fields %}
" {{ serviceTypeField.name }}: \(self.{{ serviceTypeField.name }})" +
//-{% endfor %}
"]"
}
}
//-{% endfor %}

View file

@ -0,0 +1,9 @@
all:
rm -f gnostic_swift_generator
ln -s ../../gnostic_swift_generator
gnostic bookstore.json --swift_generator_out=Sources/Bookstore
swift build
test:
swift test

View file

@ -0,0 +1,27 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import PackageDescription
let package = Package(
name: "BookstoreExample",
targets: [
Target(name: "Server",
dependencies: ["Bookstore"]),
],
dependencies: [
.Package(url: "https://github.com/IBM-Swift/Kitura.git", majorVersion: 1, minor: 4)
]
)

View file

@ -0,0 +1,22 @@
# Bookstore Example
This directory contains an OpenAPI description of a simple bookstore API.
Use this example to try the `openapi_swift_generator` plugin, which
generates Swift code that implements an API client and server for
an OpenAPI description.
Run `make all` to build and install `openapic` and the Swift plugin.
It will generate both client and server code. The API client and
server code will be in the `Sources/Bookstore` package.
The `Sources/Server` directory contains additional code that completes the server.
To build and run the server, do the following:
swift build
.build/debug/Server &
To test the service with the generated client, run `swift build`.
Tests are in the `Tests` directory and use client
code generated in `Bookstore` to verify the service.

View file

@ -0,0 +1,128 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Bookstore
class Server : Service {
private var shelves : [Int64:Shelf] = [:]
private var books : [Int64:[Int64:Book]] = [:]
private var lastShelfIndex : Int64 = 0
private var lastBookIndex : Int64 = 0
// Return all shelves in the bookstore.
func listShelves () throws -> ListShelvesResponses {
let responses = ListShelvesResponses()
let response = ListShelvesResponse()
var shelves : [Shelf] = []
for pair in self.shelves {
shelves.append(pair.value)
}
response.shelves = shelves
responses.ok = response
return responses
}
// Create a new shelf in the bookstore.
func createShelf (_ parameters : CreateShelfParameters) throws -> CreateShelfResponses {
lastShelfIndex += 1
let shelf = parameters.shelf
shelf.name = "shelves/\(lastShelfIndex)"
shelves[lastShelfIndex] = shelf
let responses = CreateShelfResponses()
responses.ok = shelf
return responses
}
// Delete all shelves.
func deleteShelves () throws {
shelves = [:]
books = [:]
lastShelfIndex = 0
lastBookIndex = 0
}
// Get a single shelf resource with the given ID.
func getShelf (_ parameters : GetShelfParameters) throws -> GetShelfResponses {
let responses = GetShelfResponses()
if let shelf : Shelf = shelves[parameters.shelf] {
responses.ok = shelf
} else {
let err = Error()
err.code = 404
err.message = "not found"
responses.error = err
}
return responses
}
// Delete a single shelf with the given ID.
func deleteShelf (_ parameters : DeleteShelfParameters) throws {
shelves[parameters.shelf] = nil
books[parameters.shelf] = nil
}
// Return all books in a shelf with the given ID.
func listBooks (_ parameters : ListBooksParameters) throws -> ListBooksResponses {
let responses = ListBooksResponses()
let response = ListBooksResponse()
var books : [Book] = []
if let shelfBooks = self.books[parameters.shelf] {
for pair in shelfBooks {
books.append(pair.value)
}
}
response.books = books
responses.ok = response
return responses
}
// Create a new book on the shelf.
func createBook (_ parameters : CreateBookParameters) throws -> CreateBookResponses {
let responses = CreateBookResponses()
lastBookIndex += 1
let shelf = parameters.shelf
let book = parameters.book
book.name = "shelves/\(shelf)/books/\(lastBookIndex)"
if var shelfBooks = self.books[shelf] {
shelfBooks[lastBookIndex] = book
self.books[shelf] = shelfBooks
} else {
var shelfBooks : [Int64:Book] = [:]
shelfBooks[lastBookIndex] = book
self.books[shelf] = shelfBooks
}
responses.ok = book
return responses
}
// Get a single book with a given ID from a shelf.
func getBook (_ parameters : GetBookParameters) throws -> GetBookResponses {
let responses = GetBookResponses()
if let shelfBooks = self.books[parameters.shelf],
let book = shelfBooks[parameters.book] {
responses.ok = book
} else {
let err = Error()
err.code = 404
err.message = "not found"
responses.error = err
}
return responses
}
// Delete a single book with a given ID from a shelf.
func deleteBook (_ parameters : DeleteBookParameters) throws {
if var shelfBooks = self.books[parameters.shelf] {
shelfBooks[parameters.book] = nil
self.books[parameters.shelf] = shelfBooks
}
}
}
initialize(service:Server(), port:8080)
run()

View file

@ -0,0 +1,208 @@
/*
*
* Copyright 2017, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
import XCTest
import Foundation
@testable import Bookstore
func Log(_ message : String) {
FileHandle.standardError.write((message + "\n").data(using:.utf8)!)
}
let service = "http://localhost:8080"
class BookstoreTests: XCTestCase {
func testBasic() {
// create a client
let b = Bookstore.Client(service:service)
Log("// reset the service by deleting all shelves")
do {
try b.deleteShelves()
} catch (let error) {
XCTFail("\(error)")
}
Log("// verify that the service has no shelves")
do {
let response = try b.listShelves()
XCTAssertEqual(response.shelves.count, 0)
} catch (let error) {
XCTFail("\(error)")
}
Log("// attempting to get a shelf should return an error")
do {
let _ = try b.getShelf(shelf:1)
XCTFail("server error")
} catch {
}
Log("// attempting to get a book should return an error")
do {
let _ = try b.getBook(shelf:1, book:2)
} catch {
}
Log("// add a shelf")
do {
let shelf = Shelf()
shelf.theme = "mysteries"
let response = try b.createShelf(shelf:shelf)
if (response.name != "shelves/1") ||
(response.theme != "mysteries") {
XCTFail("mismatch")
}
} catch (let error) {
XCTFail("\(error)")
}
Log("// add another shelf")
do {
let shelf = Shelf()
shelf.theme = "comedies"
let response = try b.createShelf(shelf:shelf)
if (response.name != "shelves/2") ||
(response.theme != "comedies") {
XCTFail("mismatch")
}
} catch (let error) {
XCTFail("\(error)")
}
Log("// get the first shelf that was added")
do {
let response = try b.getShelf(shelf:1)
if (response.name != "shelves/1") ||
(response.theme != "mysteries") {
XCTFail("mismatch")
}
} catch (let error) {
XCTFail("\(error)")
}
Log("// list shelves and verify that there are 2")
do {
let response = try b.listShelves()
XCTAssertEqual(response.shelves.count, 2)
} catch (let error) {
XCTFail("\(error)")
}
Log("// delete a shelf")
do {
try b.deleteShelf(shelf:2)
} catch (let error) {
XCTFail("\(error)")
}
Log("// list shelves and verify that there is only 1")
do {
let response = try b.listShelves()
XCTAssertEqual(response.shelves.count, 1)
} catch (let error) {
XCTFail("\(error)")
}
Log("// list books on a shelf, verify that there are none")
do {
let response = try b.listBooks(shelf:1)
XCTAssertEqual(response.books.count, 0)
} catch (let error) {
XCTFail("\(error)")
}
Log("// create a book")
do {
let book = Book()
book.author = "Agatha Christie"
book.title = "And Then There Were None"
let _ = try b.createBook(shelf:1, book:book)
} catch (let error) {
XCTFail("\(error)")
}
Log("// create another book")
do {
let book = Book()
book.author = "Agatha Christie"
book.title = "Murder on the Orient Express"
let _ = try b.createBook(shelf:1, book:book)
} catch (let error) {
XCTFail("\(error)")
}
Log("// get the first book that was added")
do {
let response = try b.getBook(shelf:1, book:1)
if (response.author != "Agatha Christie") ||
(response.title != "And Then There Were None") {
XCTFail("mismatch")
}
} catch (let error) {
XCTFail("\(error)")
}
Log("// list the books on a shelf and verify that there are 2")
do {
let response = try b.listBooks(shelf:1)
XCTAssertEqual(response.books.count, 2)
} catch (let error) {
XCTFail("\(error)")
}
Log("// delete a book")
do {
try b.deleteBook(shelf:1, book:2)
} catch (let error) {
XCTFail("\(error)")
}
Log("// list the books on a shelf and verify that is only 1")
do {
let response = try b.listBooks(shelf:1)
XCTAssertEqual(response.books.count, 1)
} catch (let error) {
XCTFail("\(error)")
}
Log("// verify the handling of a badly-formed request")
var path = service
path = path + "/shelves"
guard let url = URL(string:path) else {
XCTFail("Failed to construct URL")
return
}
var request = URLRequest(url:url)
request.httpMethod = "POST"
request.httpBody = "".data(using:.utf8)
let (_, response, _) = fetch(request)
// we expect a 400 (Bad Request) code
if let response = response {
XCTAssertEqual(response.statusCode, 400)
} else {
// Failed requests are returning nil responses on Linux. For now we'll say that is OK.
//XCTFail("Null response for bad request")
}
}
}
extension BookstoreTests {
static var allTests : [(String, (BookstoreTests) -> () throws -> Void)] {
return [
("testBasic", testBasic),
]
}
}

View file

@ -0,0 +1,38 @@
/*
*
* Copyright 2017, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
import XCTest
@testable import BookstoreTests
XCTMain([
testCase(BookstoreTests.allTests),
])

View file

@ -0,0 +1,357 @@
{
"swagger": "2.0",
"info": {
"description": "A simple Bookstore API example.",
"title": "Bookstore",
"version": "1.0.0"
},
"host": "generated-bookstore.appspot.com",
"basePath": "/",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"schemes": [
"https"
],
"paths": {
"/shelves": {
"get": {
"description": "Return all shelves in the bookstore.",
"operationId": "listShelves",
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "List of shelves in the bookstore.",
"schema": {
"$ref": "#/definitions/listShelvesResponse"
}
}
},
"security": [
]
},
"post": {
"description": "Create a new shelf in the bookstore.",
"operationId": "createShelf",
"parameters": [
{
"description": "A shelf resource to create.",
"in": "body",
"name": "shelf",
"required": true,
"schema": {
"$ref": "#/definitions/shelf"
}
}
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "A newly created shelf resource.",
"schema": {
"$ref": "#/definitions/shelf"
}
}
}
},
"delete": {
"description": "Delete all shelves.",
"operationId": "deleteShelves",
"responses": {
"default": {
"description": "An empty response body."
}
}
}
},
"/shelves/{shelf}": {
"get": {
"description": "Get a single shelf resource with the given ID.",
"operationId": "getShelf",
"parameters": [
{
"description": "ID of the shelf to get.",
"format": "int64",
"in": "path",
"name": "shelf",
"required": true,
"type": "integer"
}
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "A shelf resource.",
"schema": {
"$ref": "#/definitions/shelf"
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
},
"delete": {
"description": "Delete a single shelf with the given ID.",
"operationId": "deleteShelf",
"parameters": [
{
"description": "ID of the shelf to delete.",
"format": "int64",
"in": "path",
"name": "shelf",
"required": true,
"type": "integer"
}
],
"responses": {
"default": {
"description": "An empty response body."
}
}
}
},
"/shelves/{shelf}/books": {
"get": {
"description": "Return all books in a shelf with the given ID.",
"operationId": "listBooks",
"parameters": [
{
"description": "ID of the shelf whose books should be returned.",
"format": "int64",
"in": "path",
"name": "shelf",
"required": true,
"type": "integer"
}
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "List of books on the specified shelf.",
"schema": {
"$ref": "#/definitions/listBooksResponse"
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
},
"post": {
"description": "Create a new book on the shelf.",
"operationId": "createBook",
"parameters": [
{
"description": "ID of the shelf where the book should be created.",
"format": "int64",
"in": "path",
"name": "shelf",
"required": true,
"type": "integer"
},
{
"description": "Book to create.",
"in": "body",
"name": "book",
"required": true,
"schema": {
"$ref": "#/definitions/book"
}
}
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "A newly created book resource.",
"schema": {
"$ref": "#/definitions/book"
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
}
},
"/shelves/{shelf}/books/{book}": {
"get": {
"description": "Get a single book with a given ID from a shelf.",
"operationId": "getBook",
"parameters": [
{
"description": "ID of the shelf from which to get the book.",
"format": "int64",
"in": "path",
"name": "shelf",
"required": true,
"type": "integer"
},
{
"description": "ID of the book to get from the shelf.",
"format": "int64",
"in": "path",
"name": "book",
"required": true,
"type": "integer"
}
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "A book resource.",
"schema": {
"$ref": "#/definitions/book"
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/definitions/Error"
}
}
}
},
"delete": {
"description": "Delete a single book with a given ID from a shelf.",
"operationId": "deleteBook",
"parameters": [
{
"description": "ID of the shelf from which to delete the book.",
"format": "int64",
"in": "path",
"name": "shelf",
"required": true,
"type": "integer"
},
{
"description": "ID of the book to delete from the shelf.",
"format": "int64",
"in": "path",
"name": "book",
"required": true,
"type": "integer"
}
],
"responses": {
"default": {
"description": "An empty response body."
}
}
}
}
},
"definitions": {
"book": {
"properties": {
"author": {
"type": "string"
},
"name": {
"type": "string"
},
"title": {
"type": "string"
}
},
"required": [
"name",
"author",
"title"
]
},
"listBooksResponse": {
"properties": {
"books": {
"items": {
"$ref": "#/definitions/book"
},
"type": "array"
}
},
"required": [
"books"
],
"type": "object"
},
"listShelvesResponse": {
"properties": {
"shelves": {
"items": {
"$ref": "#/definitions/shelf"
},
"type": "array"
}
},
"type": "object"
},
"shelf": {
"properties": {
"name": {
"type": "string"
},
"theme": {
"type": "string"
}
},
"required": [
"name",
"theme"
]
},
"error": {
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"message": {
"type": "string"
}
}
}
},
"security": [
{
"api_key": [
]
}
],
"securityDefinitions": {
"api_key": {
"in": "query",
"name": "key",
"type": "apiKey"
}
}
}

View file

@ -0,0 +1,22 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import PackageDescription
let package = Package(
name: "openapi_swift_sample",
dependencies: [
.Package(url: "https://github.com/apple/swift-protobuf.git", Version(0,9,24))
]
)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,100 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
// The I/O code below is derived from Apple's swift-protobuf project.
// https://github.com/apple/swift-protobuf
// BEGIN swift-protobuf derivation
#if os(Linux)
import Glibc
#else
import Darwin.C
#endif
enum PluginError: Error {
/// Raised for any errors reading the input
case readFailure
}
// Alias clib's write() so Stdout.write(bytes:) can call it.
private let _write = write
class Stdin {
static func readall() throws -> Data {
let fd: Int32 = 0
let buffSize = 32
var buff = [UInt8]()
while true {
var fragment = [UInt8](repeating: 0, count: buffSize)
let count = read(fd, &fragment, buffSize)
if count < 0 {
throw PluginError.readFailure
}
if count < buffSize {
buff += fragment[0..<count]
return Data(bytes: buff)
}
buff += fragment
}
}
}
class Stdout {
static func write(bytes: Data) {
bytes.withUnsafeBytes { (p: UnsafePointer<UInt8>) -> () in
_ = _write(1, p, bytes.count)
}
}
}
struct CodePrinter {
private(set) var content = ""
private var currentIndentDepth = 0
private var currentIndent = ""
private var atLineStart = true
mutating func print(_ text: String...) {
for t in text {
for c in t.characters {
if c == "\n" {
content.append(c)
atLineStart = true
} else {
if atLineStart {
content.append(currentIndent)
atLineStart = false
}
content.append(c)
}
}
}
}
mutating private func resetIndent() {
currentIndent = (0..<currentIndentDepth).map { Int -> String in return " " } .joined(separator:"")
}
mutating func indent() {
currentIndentDepth += 1
resetIndent()
}
mutating func outdent() {
currentIndentDepth -= 1
resetIndent()
}
}
// END swift-protobuf derivation

View file

@ -0,0 +1,71 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
func printDocument(document:Openapi_V2_Document,
name:String,
version:String) -> String {
var code = CodePrinter()
code.print("READING \(name) (\(version))\n")
code.print("Swagger: \(document.swagger)\n")
code.print("Host: \(document.host)\n")
code.print("BasePath: \(document.basePath)\n")
if document.hasInfo {
code.print("Info:\n")
code.indent()
if document.info.title != "" {
code.print("Title: \(document.info.title)\n")
}
if document.info.description_p != "" {
code.print("Description: \(document.info.description_p)\n")
}
if document.info.version != "" {
code.print("Version: \(document.info.version)\n")
}
code.outdent()
}
code.print("Paths:\n")
code.indent()
for pair in document.paths.path {
let v = pair.value
if v.hasGet {
code.print("GET \(pair.name)\n")
}
if v.hasPost {
code.print("POST \(pair.name)\n")
}
}
code.outdent()
return code.content
}
func main() throws {
var response = Openapi_Plugin_V1_Response()
let rawRequest = try Stdin.readall()
let request = try Openapi_Plugin_V1_Request(protobuf: rawRequest)
let wrapper = request.wrapper
let document = try Openapi_V2_Document(protobuf:wrapper.value)
let report = printDocument(document:document, name:wrapper.name, version:wrapper.version)
if let reportData = report.data(using:.utf8) {
var file = Openapi_Plugin_V1_File()
file.name = "report.txt"
file.data = reportData
response.files.append(file)
}
let serializedResponse = try response.serializeProtobuf()
Stdout.write(bytes: serializedResponse)
}
try main()

View file

@ -0,0 +1,517 @@
/*
* DO NOT EDIT.
*
* Generated by the protocol buffer compiler.
* Source: plugin.proto
*
*/
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// openapic (aka the OpenAPI Compiler) can be extended via plugins.
// A plugin is just a program that reads a Request from stdin
// and writes a Response to stdout.
//
// A plugin executable needs only to be placed somewhere in the path. The
// plugin should be named "openapi_$NAME", and will then be used when the
// flag "--${NAME}_out" is passed to openapic.
import Foundation
import SwiftProtobuf
/// The version number of OpenAPI compiler.
public struct Openapi_Plugin_V1_Version: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Version"}
public var protoMessageName: String {return "Version"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"major": 1,
"minor": 2,
"patch": 3,
"suffix": 4,
]}
public var protoFieldNames: [String: Int] {return [
"major": 1,
"minor": 2,
"patch": 3,
"suffix": 4,
]}
public var major: Int32 = 0
public var minor: Int32 = 0
public var patch: Int32 = 0
/// A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should
/// be empty for mainline stable releases.
public var suffix: String = ""
public init() {}
public init(major: Int32? = nil,
minor: Int32? = nil,
patch: Int32? = nil,
suffix: String? = nil)
{
if let v = major {
self.major = v
}
if let v = minor {
self.minor = v
}
if let v = patch {
self.patch = v
}
if let v = suffix {
self.suffix = v
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularField(fieldType: ProtobufInt32.self, value: &major)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufInt32.self, value: &minor)
case 3: handled = try setter.decodeSingularField(fieldType: ProtobufInt32.self, value: &patch)
case 4: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &suffix)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if major != 0 {
try visitor.visitSingularField(fieldType: ProtobufInt32.self, value: major, protoFieldNumber: 1, protoFieldName: "major", jsonFieldName: "major", swiftFieldName: "major")
}
if minor != 0 {
try visitor.visitSingularField(fieldType: ProtobufInt32.self, value: minor, protoFieldNumber: 2, protoFieldName: "minor", jsonFieldName: "minor", swiftFieldName: "minor")
}
if patch != 0 {
try visitor.visitSingularField(fieldType: ProtobufInt32.self, value: patch, protoFieldNumber: 3, protoFieldName: "patch", jsonFieldName: "patch", swiftFieldName: "patch")
}
if suffix != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: suffix, protoFieldNumber: 4, protoFieldName: "suffix", jsonFieldName: "suffix", swiftFieldName: "suffix")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Version) -> Bool {
if major != other.major {return false}
if minor != other.minor {return false}
if patch != other.patch {return false}
if suffix != other.suffix {return false}
return true
}
}
/// A parameter passed to the plugin from (or through) the OpenAPI compiler.
public struct Openapi_Plugin_V1_Parameter: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Parameter"}
public var protoMessageName: String {return "Parameter"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"name": 1,
"value": 2,
]}
public var protoFieldNames: [String: Int] {return [
"name": 1,
"value": 2,
]}
/// The name of the parameter as specified in the option string
public var name: String = ""
/// The parameter value as specified in the option string
public var value: String = ""
public init() {}
public init(name: String? = nil,
value: String? = nil)
{
if let v = name {
self.name = v
}
if let v = value {
self.value = v
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &name)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &value)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if name != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: name, protoFieldNumber: 1, protoFieldName: "name", jsonFieldName: "name", swiftFieldName: "name")
}
if value != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: value, protoFieldNumber: 2, protoFieldName: "value", jsonFieldName: "value", swiftFieldName: "value")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Parameter) -> Bool {
if name != other.name {return false}
if value != other.value {return false}
return true
}
}
/// An encoded Request is written to the plugin's stdin.
public struct Openapi_Plugin_V1_Request: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Request"}
public var protoMessageName: String {return "Request"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"wrapper": 1,
"outputPath": 2,
"parameters": 3,
"compilerVersion": 4,
]}
public var protoFieldNames: [String: Int] {return [
"wrapper": 1,
"output_path": 2,
"parameters": 3,
"compiler_version": 4,
]}
private class _StorageClass {
typealias ProtobufExtendedMessage = Openapi_Plugin_V1_Request
var _wrapper: Openapi_Plugin_V1_Wrapper? = nil
var _outputPath: String = ""
var _parameters: [Openapi_Plugin_V1_Parameter] = []
var _compilerVersion: Openapi_Plugin_V1_Version? = nil
init() {}
func decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularMessageField(fieldType: Openapi_Plugin_V1_Wrapper.self, value: &_wrapper)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &_outputPath)
case 3: handled = try setter.decodeRepeatedMessageField(fieldType: Openapi_Plugin_V1_Parameter.self, value: &_parameters)
case 4: handled = try setter.decodeSingularMessageField(fieldType: Openapi_Plugin_V1_Version.self, value: &_compilerVersion)
default:
handled = false
}
return handled
}
func traverse(visitor: inout ProtobufVisitor) throws {
if let v = _wrapper {
try visitor.visitSingularMessageField(value: v, protoFieldNumber: 1, protoFieldName: "wrapper", jsonFieldName: "wrapper", swiftFieldName: "wrapper")
}
if _outputPath != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: _outputPath, protoFieldNumber: 2, protoFieldName: "output_path", jsonFieldName: "outputPath", swiftFieldName: "outputPath")
}
if !_parameters.isEmpty {
try visitor.visitRepeatedMessageField(value: _parameters, protoFieldNumber: 3, protoFieldName: "parameters", jsonFieldName: "parameters", swiftFieldName: "parameters")
}
if let v = _compilerVersion {
try visitor.visitSingularMessageField(value: v, protoFieldNumber: 4, protoFieldName: "compiler_version", jsonFieldName: "compilerVersion", swiftFieldName: "compilerVersion")
}
}
func isEqualTo(other: _StorageClass) -> Bool {
if _wrapper != other._wrapper {return false}
if _outputPath != other._outputPath {return false}
if _parameters != other._parameters {return false}
if _compilerVersion != other._compilerVersion {return false}
return true
}
func copy() -> _StorageClass {
let clone = _StorageClass()
clone._wrapper = _wrapper
clone._outputPath = _outputPath
clone._parameters = _parameters
clone._compilerVersion = _compilerVersion
return clone
}
}
private var _storage = _StorageClass()
/// A wrapped OpenAPI document to process.
public var wrapper: Openapi_Plugin_V1_Wrapper {
get {return _storage._wrapper ?? Openapi_Plugin_V1_Wrapper()}
set {_uniqueStorage()._wrapper = newValue}
}
/// Output path specified in the plugin invocation.
public var outputPath: String {
get {return _storage._outputPath}
set {_uniqueStorage()._outputPath = newValue}
}
/// Plugin parameters parsed from the invocation string.
public var parameters: [Openapi_Plugin_V1_Parameter] {
get {return _storage._parameters}
set {_uniqueStorage()._parameters = newValue}
}
/// The version number of openapi compiler.
public var compilerVersion: Openapi_Plugin_V1_Version {
get {return _storage._compilerVersion ?? Openapi_Plugin_V1_Version()}
set {_uniqueStorage()._compilerVersion = newValue}
}
public init() {}
public init(wrapper: Openapi_Plugin_V1_Wrapper? = nil,
outputPath: String? = nil,
parameters: [Openapi_Plugin_V1_Parameter] = [],
compilerVersion: Openapi_Plugin_V1_Version? = nil)
{
let storage = _uniqueStorage()
storage._wrapper = wrapper
if let v = outputPath {
storage._outputPath = v
}
if !parameters.isEmpty {
storage._parameters = parameters
}
storage._compilerVersion = compilerVersion
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
return try _uniqueStorage().decodeField(setter: &setter, protoFieldNumber: protoFieldNumber)
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
try _storage.traverse(visitor: &visitor)
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Request) -> Bool {
return _storage === other._storage || _storage.isEqualTo(other: other._storage)
}
private mutating func _uniqueStorage() -> _StorageClass {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _storage.copy()
}
return _storage
}
}
/// The plugin writes an encoded Response to stdout.
public struct Openapi_Plugin_V1_Response: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Response"}
public var protoMessageName: String {return "Response"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"errors": 1,
"files": 2,
]}
public var protoFieldNames: [String: Int] {return [
"errors": 1,
"files": 2,
]}
/// Error message. If non-empty, the plugin failed.
/// The plugin process should exit with status code zero
/// even if it reports an error in this way.
///
/// This should be used to indicate errors which prevent the plugin from
/// operating as intended. Errors which indicate a problem in openapic
/// itself -- such as the input Document being unparseable -- should be
/// reported by writing a message to stderr and exiting with a non-zero
/// status code.
public var errors: [String] = []
/// file output, each file will be written by openapic to an appropriate location.
public var files: [Openapi_Plugin_V1_File] = []
public init() {}
public init(errors: [String] = [],
files: [Openapi_Plugin_V1_File] = [])
{
if !errors.isEmpty {
self.errors = errors
}
if !files.isEmpty {
self.files = files
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeRepeatedField(fieldType: ProtobufString.self, value: &errors)
case 2: handled = try setter.decodeRepeatedMessageField(fieldType: Openapi_Plugin_V1_File.self, value: &files)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if !errors.isEmpty {
try visitor.visitRepeatedField(fieldType: ProtobufString.self, value: errors, protoFieldNumber: 1, protoFieldName: "errors", jsonFieldName: "errors", swiftFieldName: "errors")
}
if !files.isEmpty {
try visitor.visitRepeatedMessageField(value: files, protoFieldNumber: 2, protoFieldName: "files", jsonFieldName: "files", swiftFieldName: "files")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Response) -> Bool {
if errors != other.errors {return false}
if files != other.files {return false}
return true
}
}
/// File describes a file generated by a plugin.
public struct Openapi_Plugin_V1_File: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_File"}
public var protoMessageName: String {return "File"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"name": 1,
"data": 2,
]}
public var protoFieldNames: [String: Int] {return [
"name": 1,
"data": 2,
]}
/// name of the file
public var name: String = ""
/// data to be written to the file
public var data: Data = Data()
public init() {}
public init(name: String? = nil,
data: Data? = nil)
{
if let v = name {
self.name = v
}
if let v = data {
self.data = v
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &name)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufBytes.self, value: &data)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if name != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: name, protoFieldNumber: 1, protoFieldName: "name", jsonFieldName: "name", swiftFieldName: "name")
}
if data != Data() {
try visitor.visitSingularField(fieldType: ProtobufBytes.self, value: data, protoFieldNumber: 2, protoFieldName: "data", jsonFieldName: "data", swiftFieldName: "data")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_File) -> Bool {
if name != other.name {return false}
if data != other.data {return false}
return true
}
}
/// Wrapper wraps an OpenAPI document with its version.
public struct Openapi_Plugin_V1_Wrapper: ProtobufGeneratedMessage {
public var swiftClassName: String {return "Openapi_Plugin_V1_Wrapper"}
public var protoMessageName: String {return "Wrapper"}
public var protoPackageName: String {return "openapi.plugin.v1"}
public var jsonFieldNames: [String: Int] {return [
"name": 1,
"version": 2,
"value": 3,
]}
public var protoFieldNames: [String: Int] {return [
"name": 1,
"version": 2,
"value": 3,
]}
/// filename or URL of the wrapped document
public var name: String = ""
/// version of the OpenAPI specification that is used by the wrapped document
public var version: String = ""
/// valid serialized protocol buffer of the named OpenAPI specification version
public var value: Data = Data()
public init() {}
public init(name: String? = nil,
version: String? = nil,
value: Data? = nil)
{
if let v = name {
self.name = v
}
if let v = version {
self.version = v
}
if let v = value {
self.value = v
}
}
public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool {
let handled: Bool
switch protoFieldNumber {
case 1: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &name)
case 2: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &version)
case 3: handled = try setter.decodeSingularField(fieldType: ProtobufBytes.self, value: &value)
default:
handled = false
}
return handled
}
public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws {
if name != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: name, protoFieldNumber: 1, protoFieldName: "name", jsonFieldName: "name", swiftFieldName: "name")
}
if version != "" {
try visitor.visitSingularField(fieldType: ProtobufString.self, value: version, protoFieldNumber: 2, protoFieldName: "version", jsonFieldName: "version", swiftFieldName: "version")
}
if value != Data() {
try visitor.visitSingularField(fieldType: ProtobufBytes.self, value: value, protoFieldNumber: 3, protoFieldName: "value", jsonFieldName: "value", swiftFieldName: "value")
}
}
public func _protoc_generated_isEqualTo(other: Openapi_Plugin_V1_Wrapper) -> Bool {
if name != other.name {return false}
if version != other.version {return false}
if value != other.value {return false}
return true
}
}