├── .github
└── CODEOWNERS
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Examples
├── Google
│ ├── Compute
│ │ ├── GENERATE.sh
│ │ ├── Makefile
│ │ ├── Package.swift
│ │ ├── README.md
│ │ └── disco-compute-v1.json
│ ├── Language
│ │ ├── GENERATE.sh
│ │ ├── Makefile
│ │ ├── Package.swift
│ │ ├── README.md
│ │ ├── disco-language-v1.json
│ │ └── samples
│ │ │ └── ironman.txt
│ └── Translate
│ │ ├── GENERATE.sh
│ │ ├── Makefile
│ │ ├── Package.swift
│ │ ├── README.md
│ │ └── disco-translate-v2.json
└── ThirdParty
│ └── Spotify
│ ├── GENERATE.sh
│ ├── Makefile
│ ├── Package.swift
│ ├── README.md
│ ├── disco-spotify-v1.json
│ └── disco-spotify-v1.yaml
├── LICENSE
├── Makefile
├── Package.swift
├── README.md
├── SECURITY.md
├── Sources
├── Client
│ └── bigquery.swift
├── Discovery
│ └── Discovery.swift
├── GoogleAPIRuntime
│ ├── JSONAny.swift
│ └── Service.swift
├── google-api-swift-generator
│ └── main.swift
└── google-cli-swift-generator
│ └── main.swift
└── renovate.json
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # Code owners file.
2 | # This file controls who is tagged for review for any given pull request.
3 | #
4 | # For syntax help see:
5 | # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax
6 |
7 | * @googleapis/lang-swift
8 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Code of Conduct
2 |
3 | As contributors and maintainers of this project,
4 | and in the interest of fostering an open and welcoming community,
5 | we pledge to respect all people who contribute through reporting issues,
6 | posting feature requests, updating documentation,
7 | submitting pull requests or patches, and other activities.
8 |
9 | We are committed to making participation in this project
10 | a harassment-free experience for everyone,
11 | regardless of level of experience, gender, gender identity and expression,
12 | sexual orientation, disability, personal appearance,
13 | body size, race, ethnicity, age, religion, or nationality.
14 |
15 | Examples of unacceptable behavior by participants include:
16 |
17 | * The use of sexualized language or imagery
18 | * Personal attacks
19 | * Trolling or insulting/derogatory comments
20 | * Public or private harassment
21 | * Publishing other's private information,
22 | such as physical or electronic
23 | addresses, without explicit permission
24 | * Other unethical or unprofessional conduct.
25 |
26 | Project maintainers have the right and responsibility to remove, edit, or reject
27 | comments, commits, code, wiki edits, issues, and other contributions
28 | that are not aligned to this Code of Conduct.
29 | By adopting this Code of Conduct,
30 | project maintainers commit themselves to fairly and consistently
31 | applying these principles to every aspect of managing this project.
32 | Project maintainers who do not follow or enforce the Code of Conduct
33 | may be permanently removed from the project team.
34 |
35 | This code of conduct applies both within project spaces and in public spaces
36 | when an individual is representing the project or its community.
37 |
38 | Instances of abusive, harassing, or otherwise unacceptable behavior
39 | may be reported by opening an issue
40 | or contacting one or more of the project maintainers.
41 |
42 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0,
43 | available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/)
44 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement. You (or your employer) retain the copyright to your contribution,
10 | this simply gives us permission to use and redistribute your contributions as
11 | part of the project. Head over to to see
12 | your current agreements on file or to sign a new one.
13 |
14 | You generally only need to submit a CLA once, so if you've already submitted one
15 | (even if it was for a different project), you probably don't need to do it
16 | again.
17 |
18 | ## Code reviews
19 |
20 | All submissions, including submissions by project members, require review. We
21 | use GitHub pull requests for this purpose. Consult
22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
23 | information on using pull requests.
24 |
25 | ## Getting started
26 |
27 | These steps outline how to:
28 | - get a discovery document
29 | - convert the document into a swift file
30 | - convert the swift file into a library
31 | - try out the library
32 |
33 | ### Download a discovery document
34 |
35 | When you open this project in xcode, the dependencies should already be installed.
36 | You should find and download a discovery document to use. You can find these through google's [API Discovery Service](https://developers.google.com/discovery/v1/reference/apis/list).
37 | Use the `Try this API` side-panel to fetch details about the discovery document, then follow the link returned under `items.discoveryRestUrl` to see the document.
38 |
39 | Download the raw json into a file in your project.
40 |
41 | ### Generating a client
42 |
43 | Open a terminal in the root directory of the project. Build the project:
44 |
45 | ```sh
46 | swift build -c debug
47 | ```
48 |
49 | Run the command line tool, `google-api-swift-generator`, passing your discovery document file as the argument. It should write a swift file to stdout.
50 |
51 | ```sh
52 | ./.build/debug/google-api-swift-generator discovery-document.json
53 | ```
54 |
55 | In order to turn this into a library to try out, you should create another directory in Sources to keep the source code tht gets generated.
56 |
57 | ```sh
58 | mkdir Sources/Client
59 | ```
60 |
61 | Write the output of the API generator to a file in your Client directory
62 |
63 | ```sh
64 | ./.build/debug/google-api-swift-generator discovery-document.json > Sources/Client/client.swift
65 | ```
66 |
67 | you need to modify the [Package.swift](./Package.swift) to build another library, that you can then consume.
68 |
69 | add the following library to the products array:
70 |
71 | ```swift
72 | .library(name: "Client", targets: ["Client"])
73 | ```
74 |
75 | Add the following target to the targets array:
76 |
77 | ```swift
78 | .target(name: "Client", dependencies: ["GoogleAPIRuntime"], path: "Sources/Client")
79 | ```
80 |
81 | and build the project again form the command line.
82 |
83 | If the project no longer compiles, there is likely an error in the output of the generator that needs to be addressed.
84 | To have Xcode provide warnings and errors in your client library code, under Product > Scheme, change the scheme to Client, and rebuild the project.
85 |
86 | ### Using the client
87 |
88 | In order to use the client and verify that it's behaviour is as expected, you need a service account key with the appropriate permissions granted to use the API you're targetting.
89 |
90 | Download a service account key file from the [google cloud console](https://console.cloud.google.com/iam-admin/serviceaccounts) or generate one using the gcloud command line tool, ensuring you've granted appropriate access. Add the json key file to your project, or store elsewhere on your local machine.
91 |
92 | In order to test the library, create a new folder in sources called Usage, and a file called main.swift
93 |
94 | ```sh
95 | mkdir Sources/Usage
96 | touch Sources/Usage/main.swift
97 | ```
98 |
99 | Add an executable to the [Package.swift](./Package.swift) under products:
100 |
101 | ```swift
102 | .executable(name: "Usage", targets: ["Usage"])
103 | ```
104 |
105 | and add a target to the targets array:
106 | ```swift
107 | .target(name: "Usage", dependencies: ["Client"], path: "Sources/Usage")
108 | ```
109 |
110 | Now you can add code to the file `Sources/Usage/main.swift` to try the library, and see if it works as expected.
111 |
112 | For example, using the Bigquery service discovery document, the usage might look like so:
113 |
114 | ```swift
115 | import Foundation
116 | import Client
117 | import OAuth2
118 |
119 | let scopes = ["https://www.googleapis.com/auth/cloud-platform"]
120 | let serviceAccountFileURL = URL(fileURLWithPath: ProcessInfo.processInfo.environment["SERVICE_ACCOUNT_KEY_PATH"]!)
121 | let googleServiceAccount = ServiceAccountTokenProvider.init(credentialsURL: serviceAccountFileURL, scopes: scopes)!
122 |
123 | let bqService = try! Bigquery(tokenProvider: googleServiceAccount)
124 |
125 | let params: Bigquery.DatasetsListParameters = Bigquery.DatasetsListParameters(all: nil, filter: nil, maxResults: nil, pageToken: nil, projectId: "bigquery-public-data")
126 |
127 | do {
128 | try bqService.datasets_list(parameters: params) { (datasetsOpt: Bigquery.DatasetList?, error: Error?) in
129 | guard let datasets = datasetsOpt else {
130 | print("Could not get datasets")
131 | return
132 | }
133 | print(datasets)
134 | print("Done.")
135 | }
136 | RunLoop.main.run(until: Date(timeIntervalSinceNow: 5))
137 | } catch let error {
138 | print("Exited with an error: \(error.localizedDescription)")
139 | }
140 | ```
141 |
--------------------------------------------------------------------------------
/Examples/Google/Compute/GENERATE.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Copyright 2019 Google Inc. All Rights Reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | mkdir -p Sources
18 |
19 | ROOT=../../../.build/debug
20 | $ROOT/google-api-swift-generator disco-compute-v1.json > Sources/Compute.swift
21 | $ROOT/google-cli-swift-generator disco-compute-v1.json > Sources/main.swift
22 |
--------------------------------------------------------------------------------
/Examples/Google/Compute/Makefile:
--------------------------------------------------------------------------------
1 |
2 | all:
3 | swift build -c debug
4 |
5 | project:
6 | swift package generate-xcodeproj
7 |
8 | clean:
9 | rm -rf .build Package.pins Package.resolved
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Examples/Google/Compute/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:4.2
2 |
3 | // Copyright 2019 Google Inc. All Rights Reserved.
4 | //
5 | // Licensed under the Apache License, Version 2.0 (the "License");
6 | // you may not use this file except in compliance with the License.
7 | // You may obtain a copy of the License at
8 | //
9 | // http://www.apache.org/licenses/LICENSE-2.0
10 | //
11 | // Unless required by applicable law or agreed to in writing, software
12 | // distributed under the License is distributed on an "AS IS" BASIS,
13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | // See the License for the specific language governing permissions and
15 | // limitations under the License.
16 |
17 |
18 | import PackageDescription
19 |
20 | let package = Package(
21 | name: "Cloud Compute",
22 | dependencies: [
23 | .package(url: "https://github.com/googleapis/google-auth-library-swift.git", from: "0.5.3"),
24 | .package(url: "https://github.com/kylef/Commander.git", .upToNextMinor(from: "0.9.2")),
25 | .package(path: "../../.."),
26 | ],
27 | targets: [
28 | .target(name: "Compute", dependencies: ["OAuth2", "GoogleAPIRuntime", "Commander"], path: "Sources"),
29 | ]
30 | )
31 |
--------------------------------------------------------------------------------
/Examples/Google/Compute/README.md:
--------------------------------------------------------------------------------
1 | # Compute sample
2 |
3 | This project contains Swift sample code that calls the Google Cloud Compute API.
4 |
5 |
--------------------------------------------------------------------------------
/Examples/Google/Language/GENERATE.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Copyright 2019 Google Inc. All Rights Reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | mkdir -p Sources
18 |
19 | ROOT=../../../.build/debug
20 | $ROOT/google-api-swift-generator disco-language-v1.json > Sources/Language.swift
21 | $ROOT/google-cli-swift-generator disco-language-v1.json > Sources/main.swift
22 |
--------------------------------------------------------------------------------
/Examples/Google/Language/Makefile:
--------------------------------------------------------------------------------
1 |
2 | all:
3 | swift build -c debug
4 |
5 | project:
6 | swift package generate-xcodeproj
7 |
8 | clean:
9 | rm -rf .build Package.pins Package.resolved
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Examples/Google/Language/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:4.2
2 |
3 | // Copyright 2019 Google Inc. All Rights Reserved.
4 | //
5 | // Licensed under the Apache License, Version 2.0 (the "License");
6 | // you may not use this file except in compliance with the License.
7 | // You may obtain a copy of the License at
8 | //
9 | // http://www.apache.org/licenses/LICENSE-2.0
10 | //
11 | // Unless required by applicable law or agreed to in writing, software
12 | // distributed under the License is distributed on an "AS IS" BASIS,
13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | // See the License for the specific language governing permissions and
15 | // limitations under the License.
16 |
17 |
18 | import PackageDescription
19 |
20 | let package = Package(
21 | name: "Cloud Natural Language",
22 | dependencies: [
23 | .package(url: "https://github.com/googleapis/google-auth-library-swift.git", from: "0.5.3"),
24 | .package(url: "https://github.com/kylef/Commander.git", .upToNextMinor(from: "0.9.2")),
25 |
26 | .package(path: "../../.."),
27 | ],
28 | targets: [
29 | .target(name: "Language", dependencies: ["OAuth2", "GoogleAPIRuntime", "Commander"], path: "Sources"),
30 | ]
31 | )
32 |
--------------------------------------------------------------------------------
/Examples/Google/Language/README.md:
--------------------------------------------------------------------------------
1 | # Cloud Natural Language sample
2 |
3 | This project contains Swift sample code that calls the Google Cloud Natural
4 | Language API.
5 |
6 |
--------------------------------------------------------------------------------
/Examples/Google/Language/disco-language-v1.json:
--------------------------------------------------------------------------------
1 | {
2 | "servicePath": "",
3 | "description": "Provides natural language understanding technologies, such as sentiment analysis, entity recognition, entity sentiment analysis, and other text annotations, to developers.",
4 | "kind": "discovery#restDescription",
5 | "basePath": "",
6 | "id": "language:v1",
7 | "documentationLink": "https://cloud.google.com/natural-language/",
8 | "revision": "20190203",
9 | "discoveryVersion": "v1",
10 | "version_module": true,
11 | "schemas": {
12 | "Features": {
13 | "properties": {
14 | "classifyText": {
15 | "description": "Classify the full document into categories.",
16 | "type": "boolean"
17 | },
18 | "extractSyntax": {
19 | "description": "Extract syntax information.",
20 | "type": "boolean"
21 | },
22 | "extractDocumentSentiment": {
23 | "description": "Extract document-level sentiment.",
24 | "type": "boolean"
25 | },
26 | "extractEntitySentiment": {
27 | "description": "Extract entities and their associated sentiment.",
28 | "type": "boolean"
29 | },
30 | "extractEntities": {
31 | "description": "Extract entities.",
32 | "type": "boolean"
33 | }
34 | },
35 | "id": "Features",
36 | "description": "All available features for sentiment, syntax, and semantic analysis.\nSetting each one to true will enable that specific analysis for the input.",
37 | "type": "object"
38 | },
39 | "Document": {
40 | "description": "################################################################ #\n\nRepresents the input to API methods.",
41 | "type": "object",
42 | "properties": {
43 | "gcsContentUri": {
44 | "description": "The Google Cloud Storage URI where the file content is located.\nThis URI must be of the form: gs://bucket_name/object_name. For more\ndetails, see https://cloud.google.com/storage/docs/reference-uris.\nNOTE: Cloud Storage object versioning is not supported.",
45 | "type": "string"
46 | },
47 | "language": {
48 | "description": "The language of the document (if not specified, the language is\nautomatically detected). Both ISO and BCP-47 language codes are\naccepted.\u003cbr\u003e\n[Language Support](/natural-language/docs/languages)\nlists currently supported languages for each API method.\nIf the language (either specified by the caller or automatically detected)\nis not supported by the called API method, an `INVALID_ARGUMENT` error\nis returned.",
49 | "type": "string"
50 | },
51 | "type": {
52 | "enumDescriptions": [
53 | "The content type is not specified.",
54 | "Plain text",
55 | "HTML"
56 | ],
57 | "enum": [
58 | "TYPE_UNSPECIFIED",
59 | "PLAIN_TEXT",
60 | "HTML"
61 | ],
62 | "description": "Required. If the type is not set or is `TYPE_UNSPECIFIED`,\nreturns an `INVALID_ARGUMENT` error.",
63 | "type": "string"
64 | },
65 | "content": {
66 | "description": "The content of the input in string format.\nCloud audit logging exempt since it is based on user data.",
67 | "type": "string"
68 | }
69 | },
70 | "id": "Document"
71 | },
72 | "AnalyzeEntitiesRequest": {
73 | "description": "The entity analysis request message.",
74 | "type": "object",
75 | "properties": {
76 | "encodingType": {
77 | "enum": [
78 | "NONE",
79 | "UTF8",
80 | "UTF16",
81 | "UTF32"
82 | ],
83 | "description": "The encoding type used by the API to calculate offsets.",
84 | "type": "string",
85 | "enumDescriptions": [
86 | "If `EncodingType` is not specified, encoding-dependent information (such as\n`begin_offset`) will be set at `-1`.",
87 | "Encoding-dependent information (such as `begin_offset`) is calculated based\non the UTF-8 encoding of the input. C++ and Go are examples of languages\nthat use this encoding natively.",
88 | "Encoding-dependent information (such as `begin_offset`) is calculated based\non the UTF-16 encoding of the input. Java and Javascript are examples of\nlanguages that use this encoding natively.",
89 | "Encoding-dependent information (such as `begin_offset`) is calculated based\non the UTF-32 encoding of the input. Python is an example of a language\nthat uses this encoding natively."
90 | ]
91 | },
92 | "document": {
93 | "$ref": "Document",
94 | "description": "Input document."
95 | }
96 | },
97 | "id": "AnalyzeEntitiesRequest"
98 | },
99 | "ClassifyTextRequest": {
100 | "properties": {
101 | "document": {
102 | "$ref": "Document",
103 | "description": "Input document."
104 | }
105 | },
106 | "id": "ClassifyTextRequest",
107 | "description": "The document classification request message.",
108 | "type": "object"
109 | },
110 | "AnalyzeEntitySentimentResponse": {
111 | "properties": {
112 | "entities": {
113 | "description": "The recognized entities in the input document with associated sentiments.",
114 | "type": "array",
115 | "items": {
116 | "$ref": "Entity"
117 | }
118 | },
119 | "language": {
120 | "description": "The language of the text, which will be the same as the language specified\nin the request or, if not specified, the automatically-detected language.\nSee Document.language field for more details.",
121 | "type": "string"
122 | }
123 | },
124 | "id": "AnalyzeEntitySentimentResponse",
125 | "description": "The entity-level sentiment analysis response message.",
126 | "type": "object"
127 | },
128 | "AnalyzeSentimentResponse": {
129 | "properties": {
130 | "language": {
131 | "description": "The language of the text, which will be the same as the language specified\nin the request or, if not specified, the automatically-detected language.\nSee Document.language field for more details.",
132 | "type": "string"
133 | },
134 | "sentences": {
135 | "description": "The sentiment for all the sentences in the document.",
136 | "type": "array",
137 | "items": {
138 | "$ref": "Sentence"
139 | }
140 | },
141 | "documentSentiment": {
142 | "description": "The overall sentiment of the input document.",
143 | "$ref": "Sentiment"
144 | }
145 | },
146 | "id": "AnalyzeSentimentResponse",
147 | "description": "The sentiment analysis response message.",
148 | "type": "object"
149 | },
150 | "AnalyzeSyntaxResponse": {
151 | "description": "The syntax analysis response message.",
152 | "type": "object",
153 | "properties": {
154 | "language": {
155 | "description": "The language of the text, which will be the same as the language specified\nin the request or, if not specified, the automatically-detected language.\nSee Document.language field for more details.",
156 | "type": "string"
157 | },
158 | "sentences": {
159 | "description": "Sentences in the input document.",
160 | "type": "array",
161 | "items": {
162 | "$ref": "Sentence"
163 | }
164 | },
165 | "tokens": {
166 | "description": "Tokens, along with their syntactic information, in the input document.",
167 | "type": "array",
168 | "items": {
169 | "$ref": "Token"
170 | }
171 | }
172 | },
173 | "id": "AnalyzeSyntaxResponse"
174 | },
175 | "AnnotateTextResponse": {
176 | "properties": {
177 | "sentences": {
178 | "description": "Sentences in the input document. Populated if the user enables\nAnnotateTextRequest.Features.extract_syntax.",
179 | "type": "array",
180 | "items": {
181 | "$ref": "Sentence"
182 | }
183 | },
184 | "tokens": {
185 | "description": "Tokens, along with their syntactic information, in the input document.\nPopulated if the user enables\nAnnotateTextRequest.Features.extract_syntax.",
186 | "type": "array",
187 | "items": {
188 | "$ref": "Token"
189 | }
190 | },
191 | "documentSentiment": {
192 | "description": "The overall sentiment for the document. Populated if the user enables\nAnnotateTextRequest.Features.extract_document_sentiment.",
193 | "$ref": "Sentiment"
194 | },
195 | "categories": {
196 | "description": "Categories identified in the input document.",
197 | "type": "array",
198 | "items": {
199 | "$ref": "ClassificationCategory"
200 | }
201 | },
202 | "language": {
203 | "description": "The language of the text, which will be the same as the language specified\nin the request or, if not specified, the automatically-detected language.\nSee Document.language field for more details.",
204 | "type": "string"
205 | },
206 | "entities": {
207 | "description": "Entities, along with their semantic information, in the input document.\nPopulated if the user enables\nAnnotateTextRequest.Features.extract_entities.",
208 | "type": "array",
209 | "items": {
210 | "$ref": "Entity"
211 | }
212 | }
213 | },
214 | "id": "AnnotateTextResponse",
215 | "description": "The text annotations response message.",
216 | "type": "object"
217 | },
218 | "DependencyEdge": {
219 | "description": "Represents dependency parse tree information for a token. (For more\ninformation on dependency labels, see\nhttp://www.aclweb.org/anthology/P13-2017",
220 | "type": "object",
221 | "properties": {
222 | "label": {
223 | "enumDescriptions": [
224 | "Unknown",
225 | "Abbreviation modifier",
226 | "Adjectival complement",
227 | "Adverbial clause modifier",
228 | "Adverbial modifier",
229 | "Adjectival modifier of an NP",
230 | "Appositional modifier of an NP",
231 | "Attribute dependent of a copular verb",
232 | "Auxiliary (non-main) verb",
233 | "Passive auxiliary",
234 | "Coordinating conjunction",
235 | "Clausal complement of a verb or adjective",
236 | "Conjunct",
237 | "Clausal subject",
238 | "Clausal passive subject",
239 | "Dependency (unable to determine)",
240 | "Determiner",
241 | "Discourse",
242 | "Direct object",
243 | "Expletive",
244 | "Goes with (part of a word in a text not well edited)",
245 | "Indirect object",
246 | "Marker (word introducing a subordinate clause)",
247 | "Multi-word expression",
248 | "Multi-word verbal expression",
249 | "Negation modifier",
250 | "Noun compound modifier",
251 | "Noun phrase used as an adverbial modifier",
252 | "Nominal subject",
253 | "Passive nominal subject",
254 | "Numeric modifier of a noun",
255 | "Element of compound number",
256 | "Punctuation mark",
257 | "Parataxis relation",
258 | "Participial modifier",
259 | "The complement of a preposition is a clause",
260 | "Object of a preposition",
261 | "Possession modifier",
262 | "Postverbal negative particle",
263 | "Predicate complement",
264 | "Preconjunt",
265 | "Predeterminer",
266 | "Prefix",
267 | "Prepositional modifier",
268 | "The relationship between a verb and verbal morpheme",
269 | "Particle",
270 | "Associative or possessive marker",
271 | "Quantifier phrase modifier",
272 | "Relative clause modifier",
273 | "Complementizer in relative clause",
274 | "Ellipsis without a preceding predicate",
275 | "Referent",
276 | "Remnant",
277 | "Reparandum",
278 | "Root",
279 | "Suffix specifying a unit of number",
280 | "Suffix",
281 | "Temporal modifier",
282 | "Topic marker",
283 | "Clause headed by an infinite form of the verb that modifies a noun",
284 | "Vocative",
285 | "Open clausal complement",
286 | "Name suffix",
287 | "Name title",
288 | "Adverbial phrase modifier",
289 | "Causative auxiliary",
290 | "Helper auxiliary",
291 | "Rentaishi (Prenominal modifier)",
292 | "Foreign words",
293 | "Keyword",
294 | "List for chains of comparable items",
295 | "Nominalized clause",
296 | "Nominalized clausal subject",
297 | "Nominalized clausal passive",
298 | "Compound of numeric modifier",
299 | "Copula",
300 | "Dislocated relation (for fronted/topicalized elements)",
301 | "Aspect marker",
302 | "Genitive modifier",
303 | "Genitive object",
304 | "Infinitival modifier",
305 | "Measure",
306 | "Nominal complement of a noun"
307 | ],
308 | "enum": [
309 | "UNKNOWN",
310 | "ABBREV",
311 | "ACOMP",
312 | "ADVCL",
313 | "ADVMOD",
314 | "AMOD",
315 | "APPOS",
316 | "ATTR",
317 | "AUX",
318 | "AUXPASS",
319 | "CC",
320 | "CCOMP",
321 | "CONJ",
322 | "CSUBJ",
323 | "CSUBJPASS",
324 | "DEP",
325 | "DET",
326 | "DISCOURSE",
327 | "DOBJ",
328 | "EXPL",
329 | "GOESWITH",
330 | "IOBJ",
331 | "MARK",
332 | "MWE",
333 | "MWV",
334 | "NEG",
335 | "NN",
336 | "NPADVMOD",
337 | "NSUBJ",
338 | "NSUBJPASS",
339 | "NUM",
340 | "NUMBER",
341 | "P",
342 | "PARATAXIS",
343 | "PARTMOD",
344 | "PCOMP",
345 | "POBJ",
346 | "POSS",
347 | "POSTNEG",
348 | "PRECOMP",
349 | "PRECONJ",
350 | "PREDET",
351 | "PREF",
352 | "PREP",
353 | "PRONL",
354 | "PRT",
355 | "PS",
356 | "QUANTMOD",
357 | "RCMOD",
358 | "RCMODREL",
359 | "RDROP",
360 | "REF",
361 | "REMNANT",
362 | "REPARANDUM",
363 | "ROOT",
364 | "SNUM",
365 | "SUFF",
366 | "TMOD",
367 | "TOPIC",
368 | "VMOD",
369 | "VOCATIVE",
370 | "XCOMP",
371 | "SUFFIX",
372 | "TITLE",
373 | "ADVPHMOD",
374 | "AUXCAUS",
375 | "AUXVV",
376 | "DTMOD",
377 | "FOREIGN",
378 | "KW",
379 | "LIST",
380 | "NOMC",
381 | "NOMCSUBJ",
382 | "NOMCSUBJPASS",
383 | "NUMC",
384 | "COP",
385 | "DISLOCATED",
386 | "ASP",
387 | "GMOD",
388 | "GOBJ",
389 | "INFMOD",
390 | "MES",
391 | "NCOMP"
392 | ],
393 | "description": "The parse label for the token.",
394 | "type": "string"
395 | },
396 | "headTokenIndex": {
397 | "description": "Represents the head of this token in the dependency tree.\nThis is the index of the token which has an arc going to this token.\nThe index is the position of the token in the array of tokens returned\nby the API method. If this token is a root token, then the\n`head_token_index` is its own index.",
398 | "format": "int32",
399 | "type": "integer"
400 | }
401 | },
402 | "id": "DependencyEdge"
403 | },
404 | "Token": {
405 | "description": "Represents the smallest syntactic building block of the text.",
406 | "type": "object",
407 | "properties": {
408 | "partOfSpeech": {
409 | "$ref": "PartOfSpeech",
410 | "description": "Parts of speech tag for this token."
411 | },
412 | "text": {
413 | "description": "The token text.",
414 | "$ref": "TextSpan"
415 | },
416 | "dependencyEdge": {
417 | "description": "Dependency tree parse for this token.",
418 | "$ref": "DependencyEdge"
419 | },
420 | "lemma": {
421 | "description": "[Lemma](https://en.wikipedia.org/wiki/Lemma_%28morphology%29) of the token.",
422 | "type": "string"
423 | }
424 | },
425 | "id": "Token"
426 | },
427 | "TextSpan": {
428 | "properties": {
429 | "beginOffset": {
430 | "description": "The API calculates the beginning offset of the content in the original\ndocument according to the EncodingType specified in the API request.",
431 | "format": "int32",
432 | "type": "integer"
433 | },
434 | "content": {
435 | "description": "The content of the output text.",
436 | "type": "string"
437 | }
438 | },
439 | "id": "TextSpan",
440 | "description": "Represents an output piece of text.",
441 | "type": "object"
442 | },
443 | "EntityMention": {
444 | "description": "Represents a mention for an entity in the text. Currently, proper noun\nmentions are supported.",
445 | "type": "object",
446 | "properties": {
447 | "text": {
448 | "$ref": "TextSpan",
449 | "description": "The mention text."
450 | },
451 | "type": {
452 | "enum": [
453 | "TYPE_UNKNOWN",
454 | "PROPER",
455 | "COMMON"
456 | ],
457 | "description": "The type of the entity mention.",
458 | "type": "string",
459 | "enumDescriptions": [
460 | "Unknown",
461 | "Proper name",
462 | "Common noun (or noun compound)"
463 | ]
464 | },
465 | "sentiment": {
466 | "$ref": "Sentiment",
467 | "description": "For calls to AnalyzeEntitySentiment or if\nAnnotateTextRequest.Features.extract_entity_sentiment is set to\ntrue, this field will contain the sentiment expressed for this mention of\nthe entity in the provided document."
468 | }
469 | },
470 | "id": "EntityMention"
471 | },
472 | "ClassifyTextResponse": {
473 | "description": "The document classification response message.",
474 | "type": "object",
475 | "properties": {
476 | "categories": {
477 | "description": "Categories representing the input document.",
478 | "type": "array",
479 | "items": {
480 | "$ref": "ClassificationCategory"
481 | }
482 | }
483 | },
484 | "id": "ClassifyTextResponse"
485 | },
486 | "Sentence": {
487 | "description": "Represents a sentence in the input document.",
488 | "type": "object",
489 | "properties": {
490 | "sentiment": {
491 | "$ref": "Sentiment",
492 | "description": "For calls to AnalyzeSentiment or if\nAnnotateTextRequest.Features.extract_document_sentiment is set to\ntrue, this field will contain the sentiment for the sentence."
493 | },
494 | "text": {
495 | "description": "The sentence text.",
496 | "$ref": "TextSpan"
497 | }
498 | },
499 | "id": "Sentence"
500 | },
501 | "Sentiment": {
502 | "properties": {
503 | "magnitude": {
504 | "description": "A non-negative number in the [0, +inf) range, which represents\nthe absolute magnitude of sentiment regardless of score (positive or\nnegative).",
505 | "format": "float",
506 | "type": "number"
507 | },
508 | "score": {
509 | "description": "Sentiment score between -1.0 (negative sentiment) and 1.0\n(positive sentiment).",
510 | "format": "float",
511 | "type": "number"
512 | }
513 | },
514 | "id": "Sentiment",
515 | "description": "Represents the feeling associated with the entire text or entities in\nthe text.",
516 | "type": "object"
517 | },
518 | "AnalyzeEntitySentimentRequest": {
519 | "description": "The entity-level sentiment analysis request message.",
520 | "type": "object",
521 | "properties": {
522 | "encodingType": {
523 | "enumDescriptions": [
524 | "If `EncodingType` is not specified, encoding-dependent information (such as\n`begin_offset`) will be set at `-1`.",
525 | "Encoding-dependent information (such as `begin_offset`) is calculated based\non the UTF-8 encoding of the input. C++ and Go are examples of languages\nthat use this encoding natively.",
526 | "Encoding-dependent information (such as `begin_offset`) is calculated based\non the UTF-16 encoding of the input. Java and Javascript are examples of\nlanguages that use this encoding natively.",
527 | "Encoding-dependent information (such as `begin_offset`) is calculated based\non the UTF-32 encoding of the input. Python is an example of a language\nthat uses this encoding natively."
528 | ],
529 | "enum": [
530 | "NONE",
531 | "UTF8",
532 | "UTF16",
533 | "UTF32"
534 | ],
535 | "description": "The encoding type used by the API to calculate offsets.",
536 | "type": "string"
537 | },
538 | "document": {
539 | "$ref": "Document",
540 | "description": "Input document."
541 | }
542 | },
543 | "id": "AnalyzeEntitySentimentRequest"
544 | },
545 | "PartOfSpeech": {
546 | "properties": {
547 | "form": {
548 | "enumDescriptions": [
549 | "Form is not applicable in the analyzed language or is not predicted.",
550 | "Adnomial",
551 | "Auxiliary",
552 | "Complementizer",
553 | "Final ending",
554 | "Gerund",
555 | "Realis",
556 | "Irrealis",
557 | "Short form",
558 | "Long form",
559 | "Order form",
560 | "Specific form"
561 | ],
562 | "enum": [
563 | "FORM_UNKNOWN",
564 | "ADNOMIAL",
565 | "AUXILIARY",
566 | "COMPLEMENTIZER",
567 | "FINAL_ENDING",
568 | "GERUND",
569 | "REALIS",
570 | "IRREALIS",
571 | "SHORT",
572 | "LONG",
573 | "ORDER",
574 | "SPECIFIC"
575 | ],
576 | "description": "The grammatical form.",
577 | "type": "string"
578 | },
579 | "number": {
580 | "enum": [
581 | "NUMBER_UNKNOWN",
582 | "SINGULAR",
583 | "PLURAL",
584 | "DUAL"
585 | ],
586 | "description": "The grammatical number.",
587 | "type": "string",
588 | "enumDescriptions": [
589 | "Number is not applicable in the analyzed language or is not predicted.",
590 | "Singular",
591 | "Plural",
592 | "Dual"
593 | ]
594 | },
595 | "voice": {
596 | "enum": [
597 | "VOICE_UNKNOWN",
598 | "ACTIVE",
599 | "CAUSATIVE",
600 | "PASSIVE"
601 | ],
602 | "description": "The grammatical voice.",
603 | "type": "string",
604 | "enumDescriptions": [
605 | "Voice is not applicable in the analyzed language or is not predicted.",
606 | "Active",
607 | "Causative",
608 | "Passive"
609 | ]
610 | },
611 | "aspect": {
612 | "enum": [
613 | "ASPECT_UNKNOWN",
614 | "PERFECTIVE",
615 | "IMPERFECTIVE",
616 | "PROGRESSIVE"
617 | ],
618 | "description": "The grammatical aspect.",
619 | "type": "string",
620 | "enumDescriptions": [
621 | "Aspect is not applicable in the analyzed language or is not predicted.",
622 | "Perfective",
623 | "Imperfective",
624 | "Progressive"
625 | ]
626 | },
627 | "mood": {
628 | "enumDescriptions": [
629 | "Mood is not applicable in the analyzed language or is not predicted.",
630 | "Conditional",
631 | "Imperative",
632 | "Indicative",
633 | "Interrogative",
634 | "Jussive",
635 | "Subjunctive"
636 | ],
637 | "enum": [
638 | "MOOD_UNKNOWN",
639 | "CONDITIONAL_MOOD",
640 | "IMPERATIVE",
641 | "INDICATIVE",
642 | "INTERROGATIVE",
643 | "JUSSIVE",
644 | "SUBJUNCTIVE"
645 | ],
646 | "description": "The grammatical mood.",
647 | "type": "string"
648 | },
649 | "tag": {
650 | "enumDescriptions": [
651 | "Unknown",
652 | "Adjective",
653 | "Adposition (preposition and postposition)",
654 | "Adverb",
655 | "Conjunction",
656 | "Determiner",
657 | "Noun (common and proper)",
658 | "Cardinal number",
659 | "Pronoun",
660 | "Particle or other function word",
661 | "Punctuation",
662 | "Verb (all tenses and modes)",
663 | "Other: foreign words, typos, abbreviations",
664 | "Affix"
665 | ],
666 | "enum": [
667 | "UNKNOWN",
668 | "ADJ",
669 | "ADP",
670 | "ADV",
671 | "CONJ",
672 | "DET",
673 | "NOUN",
674 | "NUM",
675 | "PRON",
676 | "PRT",
677 | "PUNCT",
678 | "VERB",
679 | "X",
680 | "AFFIX"
681 | ],
682 | "description": "The part of speech tag.",
683 | "type": "string"
684 | },
685 | "gender": {
686 | "enum": [
687 | "GENDER_UNKNOWN",
688 | "FEMININE",
689 | "MASCULINE",
690 | "NEUTER"
691 | ],
692 | "description": "The grammatical gender.",
693 | "type": "string",
694 | "enumDescriptions": [
695 | "Gender is not applicable in the analyzed language or is not predicted.",
696 | "Feminine",
697 | "Masculine",
698 | "Neuter"
699 | ]
700 | },
701 | "person": {
702 | "enumDescriptions": [
703 | "Person is not applicable in the analyzed language or is not predicted.",
704 | "First",
705 | "Second",
706 | "Third",
707 | "Reflexive"
708 | ],
709 | "enum": [
710 | "PERSON_UNKNOWN",
711 | "FIRST",
712 | "SECOND",
713 | "THIRD",
714 | "REFLEXIVE_PERSON"
715 | ],
716 | "description": "The grammatical person.",
717 | "type": "string"
718 | },
719 | "proper": {
720 | "enumDescriptions": [
721 | "Proper is not applicable in the analyzed language or is not predicted.",
722 | "Proper",
723 | "Not proper"
724 | ],
725 | "enum": [
726 | "PROPER_UNKNOWN",
727 | "PROPER",
728 | "NOT_PROPER"
729 | ],
730 | "description": "The grammatical properness.",
731 | "type": "string"
732 | },
733 | "case": {
734 | "enum": [
735 | "CASE_UNKNOWN",
736 | "ACCUSATIVE",
737 | "ADVERBIAL",
738 | "COMPLEMENTIVE",
739 | "DATIVE",
740 | "GENITIVE",
741 | "INSTRUMENTAL",
742 | "LOCATIVE",
743 | "NOMINATIVE",
744 | "OBLIQUE",
745 | "PARTITIVE",
746 | "PREPOSITIONAL",
747 | "REFLEXIVE_CASE",
748 | "RELATIVE_CASE",
749 | "VOCATIVE"
750 | ],
751 | "description": "The grammatical case.",
752 | "type": "string",
753 | "enumDescriptions": [
754 | "Case is not applicable in the analyzed language or is not predicted.",
755 | "Accusative",
756 | "Adverbial",
757 | "Complementive",
758 | "Dative",
759 | "Genitive",
760 | "Instrumental",
761 | "Locative",
762 | "Nominative",
763 | "Oblique",
764 | "Partitive",
765 | "Prepositional",
766 | "Reflexive",
767 | "Relative",
768 | "Vocative"
769 | ]
770 | },
771 | "tense": {
772 | "enumDescriptions": [
773 | "Tense is not applicable in the analyzed language or is not predicted.",
774 | "Conditional",
775 | "Future",
776 | "Past",
777 | "Present",
778 | "Imperfect",
779 | "Pluperfect"
780 | ],
781 | "enum": [
782 | "TENSE_UNKNOWN",
783 | "CONDITIONAL_TENSE",
784 | "FUTURE",
785 | "PAST",
786 | "PRESENT",
787 | "IMPERFECT",
788 | "PLUPERFECT"
789 | ],
790 | "description": "The grammatical tense.",
791 | "type": "string"
792 | },
793 | "reciprocity": {
794 | "enum": [
795 | "RECIPROCITY_UNKNOWN",
796 | "RECIPROCAL",
797 | "NON_RECIPROCAL"
798 | ],
799 | "description": "The grammatical reciprocity.",
800 | "type": "string",
801 | "enumDescriptions": [
802 | "Reciprocity is not applicable in the analyzed language or is not\npredicted.",
803 | "Reciprocal",
804 | "Non-reciprocal"
805 | ]
806 | }
807 | },
808 | "id": "PartOfSpeech",
809 | "description": "Represents part of speech information for a token. Parts of speech\nare as defined in\nhttp://www.lrec-conf.org/proceedings/lrec2012/pdf/274_Paper.pdf",
810 | "type": "object"
811 | },
812 | "ClassificationCategory": {
813 | "properties": {
814 | "confidence": {
815 | "description": "The classifier's confidence of the category. Number represents how certain\nthe classifier is that this category represents the given text.",
816 | "format": "float",
817 | "type": "number"
818 | },
819 | "name": {
820 | "description": "The name of the category representing the document, from the [predefined\ntaxonomy](/natural-language/docs/categories).",
821 | "type": "string"
822 | }
823 | },
824 | "id": "ClassificationCategory",
825 | "description": "Represents a category returned from the text classifier.",
826 | "type": "object"
827 | },
828 | "AnalyzeSyntaxRequest": {
829 | "description": "The syntax analysis request message.",
830 | "type": "object",
831 | "properties": {
832 | "encodingType": {
833 | "enumDescriptions": [
834 | "If `EncodingType` is not specified, encoding-dependent information (such as\n`begin_offset`) will be set at `-1`.",
835 | "Encoding-dependent information (such as `begin_offset`) is calculated based\non the UTF-8 encoding of the input. C++ and Go are examples of languages\nthat use this encoding natively.",
836 | "Encoding-dependent information (such as `begin_offset`) is calculated based\non the UTF-16 encoding of the input. Java and Javascript are examples of\nlanguages that use this encoding natively.",
837 | "Encoding-dependent information (such as `begin_offset`) is calculated based\non the UTF-32 encoding of the input. Python is an example of a language\nthat uses this encoding natively."
838 | ],
839 | "enum": [
840 | "NONE",
841 | "UTF8",
842 | "UTF16",
843 | "UTF32"
844 | ],
845 | "description": "The encoding type used by the API to calculate offsets.",
846 | "type": "string"
847 | },
848 | "document": {
849 | "description": "Input document.",
850 | "$ref": "Document"
851 | }
852 | },
853 | "id": "AnalyzeSyntaxRequest"
854 | },
855 | "AnalyzeEntitiesResponse": {
856 | "properties": {
857 | "language": {
858 | "description": "The language of the text, which will be the same as the language specified\nin the request or, if not specified, the automatically-detected language.\nSee Document.language field for more details.",
859 | "type": "string"
860 | },
861 | "entities": {
862 | "description": "The recognized entities in the input document.",
863 | "type": "array",
864 | "items": {
865 | "$ref": "Entity"
866 | }
867 | }
868 | },
869 | "id": "AnalyzeEntitiesResponse",
870 | "description": "The entity analysis response message.",
871 | "type": "object"
872 | },
873 | "Entity": {
874 | "description": "Represents a phrase in the text that is a known entity, such as\na person, an organization, or location. The API associates information, such\nas salience and mentions, with entities.",
875 | "type": "object",
876 | "properties": {
877 | "name": {
878 | "description": "The representative name for the entity.",
879 | "type": "string"
880 | },
881 | "type": {
882 | "enum": [
883 | "UNKNOWN",
884 | "PERSON",
885 | "LOCATION",
886 | "ORGANIZATION",
887 | "EVENT",
888 | "WORK_OF_ART",
889 | "CONSUMER_GOOD",
890 | "OTHER"
891 | ],
892 | "description": "The entity type.",
893 | "type": "string",
894 | "enumDescriptions": [
895 | "Unknown",
896 | "Person",
897 | "Location",
898 | "Organization",
899 | "Event",
900 | "Work of art",
901 | "Consumer goods",
902 | "Other types"
903 | ]
904 | },
905 | "metadata": {
906 | "additionalProperties": {
907 | "type": "string"
908 | },
909 | "description": "Metadata associated with the entity.\n\nCurrently, Wikipedia URLs and Knowledge Graph MIDs are provided, if\navailable. The associated keys are \"wikipedia_url\" and \"mid\", respectively.",
910 | "type": "object"
911 | },
912 | "salience": {
913 | "description": "The salience score associated with the entity in the [0, 1.0] range.\n\nThe salience score for an entity provides information about the\nimportance or centrality of that entity to the entire document text.\nScores closer to 0 are less salient, while scores closer to 1.0 are highly\nsalient.",
914 | "format": "float",
915 | "type": "number"
916 | },
917 | "sentiment": {
918 | "$ref": "Sentiment",
919 | "description": "For calls to AnalyzeEntitySentiment or if\nAnnotateTextRequest.Features.extract_entity_sentiment is set to\ntrue, this field will contain the aggregate sentiment expressed for this\nentity in the provided document."
920 | },
921 | "mentions": {
922 | "description": "The mentions of this entity in the input document. The API currently\nsupports proper noun mentions.",
923 | "type": "array",
924 | "items": {
925 | "$ref": "EntityMention"
926 | }
927 | }
928 | },
929 | "id": "Entity"
930 | },
931 | "AnnotateTextRequest": {
932 | "properties": {
933 | "encodingType": {
934 | "enum": [
935 | "NONE",
936 | "UTF8",
937 | "UTF16",
938 | "UTF32"
939 | ],
940 | "description": "The encoding type used by the API to calculate offsets.",
941 | "type": "string",
942 | "enumDescriptions": [
943 | "If `EncodingType` is not specified, encoding-dependent information (such as\n`begin_offset`) will be set at `-1`.",
944 | "Encoding-dependent information (such as `begin_offset`) is calculated based\non the UTF-8 encoding of the input. C++ and Go are examples of languages\nthat use this encoding natively.",
945 | "Encoding-dependent information (such as `begin_offset`) is calculated based\non the UTF-16 encoding of the input. Java and Javascript are examples of\nlanguages that use this encoding natively.",
946 | "Encoding-dependent information (such as `begin_offset`) is calculated based\non the UTF-32 encoding of the input. Python is an example of a language\nthat uses this encoding natively."
947 | ]
948 | },
949 | "document": {
950 | "description": "Input document.",
951 | "$ref": "Document"
952 | },
953 | "features": {
954 | "description": "The enabled features.",
955 | "$ref": "Features"
956 | }
957 | },
958 | "id": "AnnotateTextRequest",
959 | "description": "The request message for the text annotation API, which can perform multiple\nanalysis types (sentiment, entities, and syntax) in one call.",
960 | "type": "object"
961 | },
962 | "AnalyzeSentimentRequest": {
963 | "description": "The sentiment analysis request message.",
964 | "type": "object",
965 | "properties": {
966 | "encodingType": {
967 | "enumDescriptions": [
968 | "If `EncodingType` is not specified, encoding-dependent information (such as\n`begin_offset`) will be set at `-1`.",
969 | "Encoding-dependent information (such as `begin_offset`) is calculated based\non the UTF-8 encoding of the input. C++ and Go are examples of languages\nthat use this encoding natively.",
970 | "Encoding-dependent information (such as `begin_offset`) is calculated based\non the UTF-16 encoding of the input. Java and Javascript are examples of\nlanguages that use this encoding natively.",
971 | "Encoding-dependent information (such as `begin_offset`) is calculated based\non the UTF-32 encoding of the input. Python is an example of a language\nthat uses this encoding natively."
972 | ],
973 | "enum": [
974 | "NONE",
975 | "UTF8",
976 | "UTF16",
977 | "UTF32"
978 | ],
979 | "description": "The encoding type used by the API to calculate sentence offsets.",
980 | "type": "string"
981 | },
982 | "document": {
983 | "$ref": "Document",
984 | "description": "Input document."
985 | }
986 | },
987 | "id": "AnalyzeSentimentRequest"
988 | },
989 | "Status": {
990 | "description": "The `Status` type defines a logical error model that is suitable for different\nprogramming environments, including REST APIs and RPC APIs. It is used by\n[gRPC](https://github.com/grpc). The error model is designed to be:\n\n- Simple to use and understand for most users\n- Flexible enough to meet unexpected needs\n\n# Overview\n\nThe `Status` message contains three pieces of data: error code, error message,\nand error details. The error code should be an enum value of\ngoogle.rpc.Code, but it may accept additional error codes if needed. The\nerror message should be a developer-facing English message that helps\ndevelopers *understand* and *resolve* the error. If a localized user-facing\nerror message is needed, put the localized message in the error details or\nlocalize it in the client. The optional error details may contain arbitrary\ninformation about the error. There is a predefined set of error detail types\nin the package `google.rpc` that can be used for common error conditions.\n\n# Language mapping\n\nThe `Status` message is the logical representation of the error model, but it\nis not necessarily the actual wire format. When the `Status` message is\nexposed in different client libraries and different wire protocols, it can be\nmapped differently. For example, it will likely be mapped to some exceptions\nin Java, but more likely mapped to some error codes in C.\n\n# Other uses\n\nThe error model and the `Status` message can be used in a variety of\nenvironments, either with or without APIs, to provide a\nconsistent developer experience across different environments.\n\nExample uses of this error model include:\n\n- Partial errors. If a service needs to return partial errors to the client,\n it may embed the `Status` in the normal response to indicate the partial\n errors.\n\n- Workflow errors. A typical workflow has multiple steps. Each step may\n have a `Status` message for error reporting.\n\n- Batch operations. If a client uses batch request and batch response, the\n `Status` message should be used directly inside batch response, one for\n each error sub-response.\n\n- Asynchronous operations. If an API call embeds asynchronous operation\n results in its response, the status of those operations should be\n represented directly using the `Status` message.\n\n- Logging. If some API errors are stored in logs, the message `Status` could\n be used directly after any stripping needed for security/privacy reasons.",
991 | "type": "object",
992 | "properties": {
993 | "message": {
994 | "description": "A developer-facing error message, which should be in English. Any\nuser-facing error message should be localized and sent in the\ngoogle.rpc.Status.details field, or localized by the client.",
995 | "type": "string"
996 | },
997 | "details": {
998 | "description": "A list of messages that carry the error details. There is a common set of\nmessage types for APIs to use.",
999 | "type": "array",
1000 | "items": {
1001 | "additionalProperties": {
1002 | "description": "Properties of the object. Contains field @type with type URL.",
1003 | "type": "any"
1004 | },
1005 | "type": "object"
1006 | }
1007 | },
1008 | "code": {
1009 | "description": "The status code, which should be an enum value of google.rpc.Code.",
1010 | "format": "int32",
1011 | "type": "integer"
1012 | }
1013 | },
1014 | "id": "Status"
1015 | }
1016 | },
1017 | "protocol": "rest",
1018 | "icons": {
1019 | "x16": "http://www.google.com/images/icons/product/search-16.gif",
1020 | "x32": "http://www.google.com/images/icons/product/search-32.gif"
1021 | },
1022 | "canonicalName": "Cloud Natural Language",
1023 | "auth": {
1024 | "oauth2": {
1025 | "scopes": {
1026 | "https://www.googleapis.com/auth/cloud-language": {
1027 | "description": "Apply machine learning models to reveal the structure and meaning of text"
1028 | },
1029 | "https://www.googleapis.com/auth/cloud-platform": {
1030 | "description": "View and manage your data across Google Cloud Platform services"
1031 | }
1032 | }
1033 | }
1034 | },
1035 | "rootUrl": "https://language.googleapis.com/",
1036 | "ownerDomain": "google.com",
1037 | "name": "language",
1038 | "batchPath": "batch",
1039 | "fullyEncodeReservedExpansion": true,
1040 | "title": "Cloud Natural Language API",
1041 | "ownerName": "Google",
1042 | "resources": {
1043 | "documents": {
1044 | "methods": {
1045 | "analyzeEntitySentiment": {
1046 | "response": {
1047 | "$ref": "AnalyzeEntitySentimentResponse"
1048 | },
1049 | "parameterOrder": [],
1050 | "httpMethod": "POST",
1051 | "scopes": [
1052 | "https://www.googleapis.com/auth/cloud-language",
1053 | "https://www.googleapis.com/auth/cloud-platform"
1054 | ],
1055 | "parameters": {},
1056 | "flatPath": "v1/documents:analyzeEntitySentiment",
1057 | "path": "v1/documents:analyzeEntitySentiment",
1058 | "id": "language.documents.analyzeEntitySentiment",
1059 | "request": {
1060 | "$ref": "AnalyzeEntitySentimentRequest"
1061 | },
1062 | "description": "Finds entities, similar to AnalyzeEntities in the text and analyzes\nsentiment associated with each entity and its mentions."
1063 | },
1064 | "analyzeEntities": {
1065 | "path": "v1/documents:analyzeEntities",
1066 | "id": "language.documents.analyzeEntities",
1067 | "request": {
1068 | "$ref": "AnalyzeEntitiesRequest"
1069 | },
1070 | "description": "Finds named entities (currently proper names and common nouns) in the text\nalong with entity types, salience, mentions for each entity, and\nother properties.",
1071 | "response": {
1072 | "$ref": "AnalyzeEntitiesResponse"
1073 | },
1074 | "parameterOrder": [],
1075 | "httpMethod": "POST",
1076 | "scopes": [
1077 | "https://www.googleapis.com/auth/cloud-language",
1078 | "https://www.googleapis.com/auth/cloud-platform"
1079 | ],
1080 | "parameters": {},
1081 | "flatPath": "v1/documents:analyzeEntities"
1082 | },
1083 | "analyzeSyntax": {
1084 | "description": "Analyzes the syntax of the text and provides sentence boundaries and\ntokenization along with part of speech tags, dependency trees, and other\nproperties.",
1085 | "request": {
1086 | "$ref": "AnalyzeSyntaxRequest"
1087 | },
1088 | "httpMethod": "POST",
1089 | "parameterOrder": [],
1090 | "response": {
1091 | "$ref": "AnalyzeSyntaxResponse"
1092 | },
1093 | "parameters": {},
1094 | "scopes": [
1095 | "https://www.googleapis.com/auth/cloud-language",
1096 | "https://www.googleapis.com/auth/cloud-platform"
1097 | ],
1098 | "flatPath": "v1/documents:analyzeSyntax",
1099 | "id": "language.documents.analyzeSyntax",
1100 | "path": "v1/documents:analyzeSyntax"
1101 | },
1102 | "annotateText": {
1103 | "request": {
1104 | "$ref": "AnnotateTextRequest"
1105 | },
1106 | "description": "A convenience method that provides all the features that analyzeSentiment,\nanalyzeEntities, and analyzeSyntax provide in one call.",
1107 | "response": {
1108 | "$ref": "AnnotateTextResponse"
1109 | },
1110 | "parameterOrder": [],
1111 | "httpMethod": "POST",
1112 | "scopes": [
1113 | "https://www.googleapis.com/auth/cloud-language",
1114 | "https://www.googleapis.com/auth/cloud-platform"
1115 | ],
1116 | "parameters": {},
1117 | "flatPath": "v1/documents:annotateText",
1118 | "path": "v1/documents:annotateText",
1119 | "id": "language.documents.annotateText"
1120 | },
1121 | "classifyText": {
1122 | "path": "v1/documents:classifyText",
1123 | "id": "language.documents.classifyText",
1124 | "description": "Classifies a document into categories.",
1125 | "request": {
1126 | "$ref": "ClassifyTextRequest"
1127 | },
1128 | "response": {
1129 | "$ref": "ClassifyTextResponse"
1130 | },
1131 | "parameterOrder": [],
1132 | "httpMethod": "POST",
1133 | "parameters": {},
1134 | "scopes": [
1135 | "https://www.googleapis.com/auth/cloud-language",
1136 | "https://www.googleapis.com/auth/cloud-platform"
1137 | ],
1138 | "flatPath": "v1/documents:classifyText"
1139 | },
1140 | "analyzeSentiment": {
1141 | "path": "v1/documents:analyzeSentiment",
1142 | "id": "language.documents.analyzeSentiment",
1143 | "request": {
1144 | "$ref": "AnalyzeSentimentRequest"
1145 | },
1146 | "description": "Analyzes the sentiment of the provided text.",
1147 | "response": {
1148 | "$ref": "AnalyzeSentimentResponse"
1149 | },
1150 | "parameterOrder": [],
1151 | "httpMethod": "POST",
1152 | "scopes": [
1153 | "https://www.googleapis.com/auth/cloud-language",
1154 | "https://www.googleapis.com/auth/cloud-platform"
1155 | ],
1156 | "parameters": {},
1157 | "flatPath": "v1/documents:analyzeSentiment"
1158 | }
1159 | }
1160 | }
1161 | },
1162 | "parameters": {
1163 | "access_token": {
1164 | "location": "query",
1165 | "description": "OAuth access token.",
1166 | "type": "string"
1167 | },
1168 | "key": {
1169 | "location": "query",
1170 | "description": "API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.",
1171 | "type": "string"
1172 | },
1173 | "upload_protocol": {
1174 | "location": "query",
1175 | "description": "Upload protocol for media (e.g. \"raw\", \"multipart\").",
1176 | "type": "string"
1177 | },
1178 | "prettyPrint": {
1179 | "location": "query",
1180 | "description": "Returns response with indentations and line breaks.",
1181 | "type": "boolean",
1182 | "default": "true"
1183 | },
1184 | "quotaUser": {
1185 | "location": "query",
1186 | "description": "Available to use for quota purposes for server-side applications. Can be any arbitrary string assigned to a user, but should not exceed 40 characters.",
1187 | "type": "string"
1188 | },
1189 | "fields": {
1190 | "location": "query",
1191 | "description": "Selector specifying which fields to include in a partial response.",
1192 | "type": "string"
1193 | },
1194 | "uploadType": {
1195 | "location": "query",
1196 | "description": "Legacy upload protocol for media (e.g. \"media\", \"multipart\").",
1197 | "type": "string"
1198 | },
1199 | "callback": {
1200 | "location": "query",
1201 | "description": "JSONP",
1202 | "type": "string"
1203 | },
1204 | "oauth_token": {
1205 | "location": "query",
1206 | "description": "OAuth 2.0 token for the current user.",
1207 | "type": "string"
1208 | },
1209 | "$.xgafv": {
1210 | "enum": [
1211 | "1",
1212 | "2"
1213 | ],
1214 | "description": "V1 error format.",
1215 | "type": "string",
1216 | "enumDescriptions": [
1217 | "v1 error format",
1218 | "v2 error format"
1219 | ],
1220 | "location": "query"
1221 | },
1222 | "alt": {
1223 | "enum": [
1224 | "json",
1225 | "media",
1226 | "proto"
1227 | ],
1228 | "type": "string",
1229 | "enumDescriptions": [
1230 | "Responses with Content-Type of application/json",
1231 | "Media download with context-dependent Content-Type",
1232 | "Responses with Content-Type of application/x-protobuf"
1233 | ],
1234 | "location": "query",
1235 | "description": "Data format for response.",
1236 | "default": "json"
1237 | }
1238 | },
1239 | "version": "v1",
1240 | "baseUrl": "https://language.googleapis.com/"
1241 | }
1242 |
--------------------------------------------------------------------------------
/Examples/Google/Language/samples/ironman.txt:
--------------------------------------------------------------------------------
1 | Tony Stark, who has inherited the defense contractor Stark Industries from his father, is in war-torn Afghanistan with his friend and military liaison Lieutenant Colonel James Rhodes to demonstrate the new "Jericho" missile. After the demonstration, the convoy is ambushed and Stark is critically wounded by a rocket-propelled grenade used by the attackers: one of his company's own. He is captured and imprisoned in a cave by a terrorist group, the Ten Rings; Yinsen, a fellow captive who is a doctor, implants an electromagnet into Stark's chest to keep the shrapnel shards that wounded him from reaching his heart and killing him. Ten Rings leader Raza offers Stark freedom in exchange for building a Jericho missile for the group, but Tony and Yinsen know that Raza will not keep his word.
2 |
3 | Stark and Yinsen secretly build a small, powerful electric generator called an arc reactor to power Stark's electromagnet and a prototypical suit of powered armor to aid in their escape. Although they keep the suit hidden almost to completion, the Ten Rings discover their hostages' intentions and attack the workshop. Yinsen sacrifices himself to divert them while the suit is completed. The armored Stark battles his way out of the cave to find the dying Yinsen, then burns the Ten Rings' weapons in anger and flies away, crashing in the desert and destroying the suit. After being rescued by Rhodes, Stark returns home and announces that his company will no longer manufacture weapons. Obadiah Stane, his father's old partner and the company's manager, advises Stark that this may ruin Stark Industries and his father's legacy. In his home workshop, Stark builds a sleeker, more powerful version of his improvised armor suit as well as a more powerful arc reactor for his chest. Personal assistant Pepper Potts places the original reactor inside a small glass showcase. Though Stane requests details, a suspicious Stark keeps his work to himself.
4 |
5 | At a charity event held by Stark Industries, reporter Christine Everhart informs Stark that his company's weapons were recently delivered to the Ten Rings and are being used to attack Yinsen's home village, Gulmira. Stark then learns that Stane has been arms trafficking to criminals worldwide and is staging a coup to replace him as Stark Industries' CEO. Stark dons his new armor and flies to Afghanistan, where he saves the villagers. While flying home, Stark is shot at by two F-22 Raptor fighter jets. He reveals his secret identity to Rhodes over the phone in an attempt to end the attack. Meanwhile, the Ten Rings gather the pieces of Stark's prototype suit and meet with Stane, who subdues Raza and has the rest of the group killed. Stane has a massive new suit reverse engineered from the wreckage. Seeking to track his company's illegal shipments, Stark sends Potts to hack into its database. She discovers that Stane hired the Ten Rings to kill Stark, but the group reneged. Potts meets with Agent Phil Coulson of S.H.I.E.L.D., an intelligence agency, to inform him of Stane's activities.
6 |
7 | Stane's scientists cannot duplicate Stark's miniaturized arc reactor, so Stane ambushes Stark at his home and takes the one from his chest. Stark manages to get to his original reactor to replace it. Potts and several S.H.I.E.L.D. agents attempt to arrest Stane, but he dons his suit and attacks them. Stark fights Stane, but is outmatched without his new reactor to run his suit at full capacity. The fight carries Stark and Stane to the top of the Stark Industries building, and Stark instructs Potts to overload the large arc reactor powering the building. This unleashes a massive electrical surge that causes Stane and his armor to fall into the exploding reactor, killing him. The next day, at a press conference, Stark defies suggestions from S.H.I.E.L.D. and publicly admits to being "Iron Man."
8 |
9 | In a post-credits scene, S.H.I.E.L.D. Director Nick Fury visits Stark at home, telling him that Iron Man is not "the only superhero in the world", and explaining that he wants to discuss the "Avenger Initiative".
10 |
11 |
--------------------------------------------------------------------------------
/Examples/Google/Translate/GENERATE.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Copyright 2019 Google Inc. All Rights Reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | mkdir -p Sources
18 |
19 | ROOT=../../../.build/debug
20 | $ROOT/google-api-swift-generator disco-translate-v2.json > Sources/Translate.swift
21 | $ROOT/google-cli-swift-generator disco-translate-v2.json > Sources/main.swift
22 |
--------------------------------------------------------------------------------
/Examples/Google/Translate/Makefile:
--------------------------------------------------------------------------------
1 |
2 | all:
3 | swift build -c debug
4 |
5 | project:
6 | swift package generate-xcodeproj
7 |
8 | clean:
9 | rm -rf .build Package.pins Package.resolved
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Examples/Google/Translate/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:4.2
2 |
3 | // Copyright 2019 Google Inc. All Rights Reserved.
4 | //
5 | // Licensed under the Apache License, Version 2.0 (the "License");
6 | // you may not use this file except in compliance with the License.
7 | // You may obtain a copy of the License at
8 | //
9 | // http://www.apache.org/licenses/LICENSE-2.0
10 | //
11 | // Unless required by applicable law or agreed to in writing, software
12 | // distributed under the License is distributed on an "AS IS" BASIS,
13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | // See the License for the specific language governing permissions and
15 | // limitations under the License.
16 |
17 |
18 | import PackageDescription
19 |
20 | let package = Package(
21 | name: "Cloud Translation",
22 | dependencies: [
23 | .package(url: "https://github.com/googleapis/google-auth-library-swift.git", from: "0.5.3"),
24 | .package(url: "https://github.com/kylef/Commander.git", .upToNextMinor(from: "0.9.2")),
25 | .package(path: "../../.."),
26 | ],
27 | targets: [
28 | .target(name: "Translate", dependencies: ["OAuth2", "GoogleAPIRuntime", "Commander"], path: "Sources"),
29 | ]
30 | )
31 |
--------------------------------------------------------------------------------
/Examples/Google/Translate/README.md:
--------------------------------------------------------------------------------
1 | # Translate sample
2 |
3 | This project contains Swift sample code that calls the Google Translate API.
4 |
5 |
--------------------------------------------------------------------------------
/Examples/Google/Translate/disco-translate-v2.json:
--------------------------------------------------------------------------------
1 | {
2 | "rootUrl": "https://translation.googleapis.com/",
3 | "ownerDomain": "google.com",
4 | "name": "translate",
5 | "batchPath": "batch/translate",
6 | "title": "Google Cloud Translation API",
7 | "features": [
8 | "dataWrapper"
9 | ],
10 | "ownerName": "Google",
11 | "resources": {
12 | "detections": {
13 | "methods": {
14 | "list": {
15 | "httpMethod": "GET",
16 | "parameterOrder": [
17 | "q"
18 | ],
19 | "response": {
20 | "$ref": "DetectionsListResponse"
21 | },
22 | "parameters": {
23 | "q": {
24 | "repeated": true,
25 | "location": "query",
26 | "description": "The input text upon which to perform language detection. Repeat this\nparameter to perform language detection on multiple text inputs.",
27 | "type": "string",
28 | "required": true
29 | }
30 | },
31 | "scopes": [
32 | "https://www.googleapis.com/auth/cloud-translation",
33 | "https://www.googleapis.com/auth/cloud-platform"
34 | ],
35 | "id": "language.detections.list",
36 | "path": "v2/detect",
37 | "description": "Detects the language of text within a request."
38 | },
39 | "detect": {
40 | "request": {
41 | "$ref": "DetectLanguageRequest"
42 | },
43 | "description": "Detects the language of text within a request.",
44 | "response": {
45 | "$ref": "DetectionsListResponse"
46 | },
47 | "parameterOrder": [],
48 | "httpMethod": "POST",
49 | "scopes": [
50 | "https://www.googleapis.com/auth/cloud-translation",
51 | "https://www.googleapis.com/auth/cloud-platform"
52 | ],
53 | "parameters": {},
54 | "path": "v2/detect",
55 | "id": "language.detections.detect"
56 | }
57 | }
58 | },
59 | "languages": {
60 | "methods": {
61 | "list": {
62 | "description": "Returns a list of supported languages for translation.",
63 | "response": {
64 | "$ref": "LanguagesListResponse"
65 | },
66 | "httpMethod": "GET",
67 | "parameters": {
68 | "model": {
69 | "location": "query",
70 | "description": "The model type for which supported languages should be returned.",
71 | "type": "string"
72 | },
73 | "target": {
74 | "location": "query",
75 | "description": "The language to use to return localized, human readable names of supported\nlanguages.",
76 | "type": "string"
77 | }
78 | },
79 | "scopes": [
80 | "https://www.googleapis.com/auth/cloud-translation",
81 | "https://www.googleapis.com/auth/cloud-platform"
82 | ],
83 | "path": "v2/languages",
84 | "id": "language.languages.list"
85 | }
86 | }
87 | },
88 | "translations": {
89 | "methods": {
90 | "list": {
91 | "response": {
92 | "$ref": "TranslationsListResponse"
93 | },
94 | "parameterOrder": [
95 | "q",
96 | "target"
97 | ],
98 | "httpMethod": "GET",
99 | "scopes": [
100 | "https://www.googleapis.com/auth/cloud-translation",
101 | "https://www.googleapis.com/auth/cloud-platform"
102 | ],
103 | "parameters": {
104 | "target": {
105 | "description": "The language to use for translation of the input text, set to one of the\nlanguage codes listed in Language Support.",
106 | "type": "string",
107 | "required": true,
108 | "location": "query"
109 | },
110 | "format": {
111 | "enumDescriptions": [
112 | "Specifies the input is in HTML",
113 | "Specifies the input is in plain textual format"
114 | ],
115 | "location": "query",
116 | "enum": [
117 | "html",
118 | "text"
119 | ],
120 | "description": "The format of the source text, in either HTML (default) or plain-text. A\nvalue of \"html\" indicates HTML and a value of \"text\" indicates plain-text.",
121 | "type": "string"
122 | },
123 | "model": {
124 | "location": "query",
125 | "description": "The `model` type requested for this translation. Valid values are\nlisted in public documentation.",
126 | "type": "string"
127 | },
128 | "q": {
129 | "type": "string",
130 | "required": true,
131 | "repeated": true,
132 | "location": "query",
133 | "description": "The input text to translate. Repeat this parameter to perform translation\noperations on multiple text inputs."
134 | },
135 | "source": {
136 | "location": "query",
137 | "description": "The language of the source text, set to one of the language codes listed in\nLanguage Support. If the source language is not specified, the API will\nattempt to identify the source language automatically and return it within\nthe response.",
138 | "type": "string"
139 | },
140 | "cid": {
141 | "description": "The customization id for translate",
142 | "type": "string",
143 | "repeated": true,
144 | "location": "query"
145 | }
146 | },
147 | "path": "v2",
148 | "id": "language.translations.list",
149 | "description": "Translates input text, returning translated text."
150 | },
151 | "translate": {
152 | "scopes": [
153 | "https://www.googleapis.com/auth/cloud-translation",
154 | "https://www.googleapis.com/auth/cloud-platform"
155 | ],
156 | "parameters": {},
157 | "id": "language.translations.translate",
158 | "path": "v2",
159 | "request": {
160 | "$ref": "TranslateTextRequest"
161 | },
162 | "description": "Translates input text, returning translated text.",
163 | "httpMethod": "POST",
164 | "parameterOrder": [],
165 | "response": {
166 | "$ref": "TranslationsListResponse"
167 | }
168 | }
169 | }
170 | }
171 | },
172 | "parameters": {
173 | "upload_protocol": {
174 | "description": "Upload protocol for media (e.g. \"raw\", \"multipart\").",
175 | "type": "string",
176 | "location": "query"
177 | },
178 | "prettyPrint": {
179 | "location": "query",
180 | "description": "Returns response with indentations and line breaks.",
181 | "type": "boolean",
182 | "default": "true"
183 | },
184 | "uploadType": {
185 | "location": "query",
186 | "description": "Legacy upload protocol for media (e.g. \"media\", \"multipart\").",
187 | "type": "string"
188 | },
189 | "fields": {
190 | "description": "Selector specifying which fields to include in a partial response.",
191 | "type": "string",
192 | "location": "query"
193 | },
194 | "callback": {
195 | "type": "string",
196 | "location": "query",
197 | "description": "JSONP"
198 | },
199 | "$.xgafv": {
200 | "description": "V1 error format.",
201 | "type": "string",
202 | "enumDescriptions": [
203 | "v1 error format",
204 | "v2 error format"
205 | ],
206 | "location": "query",
207 | "enum": [
208 | "1",
209 | "2"
210 | ]
211 | },
212 | "alt": {
213 | "type": "string",
214 | "enumDescriptions": [
215 | "Responses with Content-Type of application/json",
216 | "Media download with context-dependent Content-Type",
217 | "Responses with Content-Type of application/x-protobuf"
218 | ],
219 | "location": "query",
220 | "description": "Data format for response.",
221 | "default": "json",
222 | "enum": [
223 | "json",
224 | "media",
225 | "proto"
226 | ]
227 | },
228 | "key": {
229 | "type": "string",
230 | "location": "query",
231 | "description": "API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token."
232 | },
233 | "access_token": {
234 | "location": "query",
235 | "description": "OAuth access token.",
236 | "type": "string"
237 | },
238 | "quotaUser": {
239 | "description": "Available to use for quota purposes for server-side applications. Can be any arbitrary string assigned to a user, but should not exceed 40 characters. Overrides userIp if both are provided.",
240 | "type": "string",
241 | "location": "query"
242 | },
243 | "pp": {
244 | "description": "Pretty-print response.",
245 | "type": "boolean",
246 | "default": "true",
247 | "location": "query"
248 | },
249 | "oauth_token": {
250 | "location": "query",
251 | "description": "OAuth 2.0 token for the current user.",
252 | "type": "string"
253 | },
254 | "bearer_token": {
255 | "location": "query",
256 | "description": "OAuth bearer token.",
257 | "type": "string"
258 | }
259 | },
260 | "version": "v2",
261 | "baseUrl": "https://translation.googleapis.com/language/translate/",
262 | "kind": "discovery#restDescription",
263 | "description": "The Google Cloud Translation API lets websites and programs integrate with\n Google Translate programmatically.",
264 | "servicePath": "language/translate/",
265 | "basePath": "/language/translate/",
266 | "revision": "20170525",
267 | "documentationLink": "https://code.google.com/apis/language/translate/v2/getting_started.html",
268 | "id": "translate:v2",
269 | "discoveryVersion": "v1",
270 | "schemas": {
271 | "LanguagesResource": {
272 | "type": "object",
273 | "properties": {
274 | "language": {
275 | "description": "Supported language code, generally consisting of its ISO 639-1\nidentifier. (E.g. 'en', 'ja'). In certain cases, BCP-47 codes including\nlanguage + region identifiers are returned (e.g. 'zh-TW' and 'zh-CH')",
276 | "type": "string"
277 | },
278 | "name": {
279 | "type": "string",
280 | "description": "Human readable name of the language localized to the target language."
281 | }
282 | },
283 | "id": "LanguagesResource"
284 | },
285 | "DetectionsListResponse": {
286 | "type": "object",
287 | "properties": {
288 | "detections": {
289 | "description": "A detections contains detection results of several text",
290 | "type": "array",
291 | "items": {
292 | "$ref": "DetectionsResource"
293 | }
294 | }
295 | },
296 | "id": "DetectionsListResponse"
297 | },
298 | "GetSupportedLanguagesRequest": {
299 | "properties": {
300 | "target": {
301 | "description": "The language to use to return localized, human readable names of supported\nlanguages.",
302 | "type": "string"
303 | }
304 | },
305 | "id": "GetSupportedLanguagesRequest",
306 | "description": "The request message for discovering supported languages.",
307 | "type": "object"
308 | },
309 | "LanguagesListResponse": {
310 | "type": "object",
311 | "properties": {
312 | "languages": {
313 | "type": "array",
314 | "items": {
315 | "$ref": "LanguagesResource"
316 | },
317 | "description": "List of source/target languages supported by the translation API. If target parameter is unspecified, the list is sorted by the ASCII code point order of the language code. If target parameter is specified, the list is sorted by the collation order of the language name in the target language."
318 | }
319 | },
320 | "id": "LanguagesListResponse"
321 | },
322 | "TranslationsResource": {
323 | "type": "object",
324 | "properties": {
325 | "model": {
326 | "description": "The `model` type used for this translation. Valid values are\nlisted in public documentation. Can be different from requested `model`.\nPresent only if specific model type was explicitly requested.",
327 | "type": "string"
328 | },
329 | "translatedText": {
330 | "description": "Text translated into the target language.",
331 | "type": "string"
332 | },
333 | "detectedSourceLanguage": {
334 | "description": "The source language of the initial request, detected automatically, if\nno source language was passed within the initial request. If the\nsource language was passed, auto-detection of the language will not\noccur and this field will be empty.",
335 | "type": "string"
336 | }
337 | },
338 | "id": "TranslationsResource"
339 | },
340 | "DetectionsResource": {
341 | "description": "An array of languages which we detect for the given text The most likely language list first.",
342 | "type": "array",
343 | "items": {
344 | "type": "object",
345 | "properties": {
346 | "confidence": {
347 | "description": "The confidence of the detection result of this language.",
348 | "format": "float",
349 | "type": "number"
350 | },
351 | "isReliable": {
352 | "type": "boolean",
353 | "description": "A boolean to indicate is the language detection result reliable."
354 | },
355 | "language": {
356 | "description": "The language we detected.",
357 | "type": "string"
358 | }
359 | }
360 | },
361 | "id": "DetectionsResource"
362 | },
363 | "TranslationsListResponse": {
364 | "description": "The main language translation response message.",
365 | "type": "object",
366 | "properties": {
367 | "translations": {
368 | "description": "Translations contains list of translation results of given text",
369 | "type": "array",
370 | "items": {
371 | "$ref": "TranslationsResource"
372 | }
373 | }
374 | },
375 | "id": "TranslationsListResponse"
376 | },
377 | "TranslateTextRequest": {
378 | "description": "The main translation request message for the Cloud Translation API.",
379 | "type": "object",
380 | "properties": {
381 | "model": {
382 | "description": "The `model` type requested for this translation. Valid values are\nlisted in public documentation.",
383 | "type": "string"
384 | },
385 | "target": {
386 | "description": "The language to use for translation of the input text, set to one of the\nlanguage codes listed in Language Support.",
387 | "type": "string"
388 | },
389 | "q": {
390 | "description": "The input text to translate. Repeat this parameter to perform translation\noperations on multiple text inputs.",
391 | "type": "array",
392 | "items": {
393 | "type": "string"
394 | }
395 | },
396 | "format": {
397 | "description": "The format of the source text, in either HTML (default) or plain-text. A\nvalue of \"html\" indicates HTML and a value of \"text\" indicates plain-text.",
398 | "type": "string"
399 | },
400 | "source": {
401 | "description": "The language of the source text, set to one of the language codes listed in\nLanguage Support. If the source language is not specified, the API will\nattempt to identify the source language automatically and return it within\nthe response.",
402 | "type": "string"
403 | }
404 | },
405 | "id": "TranslateTextRequest"
406 | },
407 | "DetectLanguageRequest": {
408 | "description": "The request message for language detection.",
409 | "type": "object",
410 | "properties": {
411 | "q": {
412 | "description": "The input text upon which to perform language detection. Repeat this\nparameter to perform language detection on multiple text inputs.",
413 | "type": "array",
414 | "items": {
415 | "type": "string"
416 | }
417 | }
418 | },
419 | "id": "DetectLanguageRequest"
420 | }
421 | },
422 | "protocol": "rest",
423 | "icons": {
424 | "x16": "https://www.google.com/images/icons/product/translate-16.png",
425 | "x32": "https://www.google.com/images/icons/product/translate-32.png"
426 | },
427 | "canonicalName": "Translate",
428 | "auth": {
429 | "oauth2": {
430 | "scopes": {
431 | "https://www.googleapis.com/auth/cloud-translation": {
432 | "description": "Translate text from one language to another using Google Translate"
433 | },
434 | "https://www.googleapis.com/auth/cloud-platform": {
435 | "description": "View and manage your data across Google Cloud Platform services"
436 | }
437 | }
438 | }
439 | }
440 | }
441 |
--------------------------------------------------------------------------------
/Examples/ThirdParty/Spotify/GENERATE.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Copyright 2019 Google Inc. All Rights Reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | # Regenerate the Spotify client library from a YAML-format Discovery Document.
18 | # (For ease of human-editing, we write the Discovery Document with YAML)
19 |
20 | # YAML is converted to JSON with j2y2j
21 | # available at https://github.com/googleapis/gnostic/blob/master/tools/j2y2j
22 | j2y2j disco-spotify-v1.yaml --json > disco-spotify-v1.json
23 |
24 | mkdir -p Sources
25 | ROOT=../../../.build/debug
26 |
27 | $ROOT/google-api-swift-generator disco-spotify-v1.json > Sources/Spotify.swift
28 | $ROOT/google-cli-swift-generator disco-spotify-v1.json > Sources/main.swift
29 |
--------------------------------------------------------------------------------
/Examples/ThirdParty/Spotify/Makefile:
--------------------------------------------------------------------------------
1 |
2 | all:
3 | swift build -c debug
4 |
5 | project:
6 | swift package generate-xcodeproj
7 |
8 | clean:
9 | rm -rf .build Package.pins Package.resolved
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Examples/ThirdParty/Spotify/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:4.2
2 |
3 | // Copyright 2019 Google Inc. All Rights Reserved.
4 | //
5 | // Licensed under the Apache License, Version 2.0 (the "License");
6 | // you may not use this file except in compliance with the License.
7 | // You may obtain a copy of the License at
8 | //
9 | // http://www.apache.org/licenses/LICENSE-2.0
10 | //
11 | // Unless required by applicable law or agreed to in writing, software
12 | // distributed under the License is distributed on an "AS IS" BASIS,
13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | // See the License for the specific language governing permissions and
15 | // limitations under the License.
16 |
17 |
18 | import PackageDescription
19 |
20 | let package = Package(
21 | name: "Spotify Client",
22 | dependencies: [
23 | .package(url: "https://github.com/googleapis/google-auth-library-swift.git", from: "0.5.3"),
24 | .package(url: "https://github.com/kylef/Commander.git", .upToNextMinor(from: "0.9.2")),
25 | .package(path: "../../.."),
26 | ],
27 | targets: [
28 | .target(name: "SpotifyClient", dependencies: ["OAuth2", "GoogleAPIRuntime", "Commander"], path: "Sources"),
29 | ]
30 | )
31 |
--------------------------------------------------------------------------------
/Examples/ThirdParty/Spotify/README.md:
--------------------------------------------------------------------------------
1 | # Spotify sample
2 |
3 | This directory contains a partial client for the Spotify Web API
4 | that was generated from a handwritten Discovery Document description
5 | of the API.
6 |
7 | Please note that the client and API description are both currently
8 | incomplete (contributions welcome!).
9 |
--------------------------------------------------------------------------------
/Examples/ThirdParty/Spotify/disco-spotify-v1.json:
--------------------------------------------------------------------------------
1 | {
2 | "kind": "discovery#restDescription",
3 | "etag": "",
4 | "discoveryVersion": "v1",
5 | "id": "spotify:v1",
6 | "name": "spotify",
7 | "version": "v1",
8 | "revision": "20190210",
9 | "title": "Spotify Web API",
10 | "description": "Based on simple REST principles, the Spotify Web API endpoints return JSON metadata about music artists, albums, and tracks, directly from the Spotify Data Catalogue.",
11 | "ownerDomain": "spotify.com",
12 | "ownerName": "Spotify",
13 | "icons": {
14 | "x16": "https://www.gstatic.com/images/branding/product/1x/googleg_16dp.png",
15 | "x32": "https://www.gstatic.com/images/branding/product/1x/googleg_32dp.png"
16 | },
17 | "documentationLink": "https://developer.spotify.com/documentation/web-api/",
18 | "protocol": "rest",
19 | "baseUrl": "https://api.spotify.com/v1/",
20 | "basePath": "/v1/",
21 | "rootUrl": "https://api.spotify.com/",
22 | "servicePath": "v1/",
23 | "batchPath": "",
24 | "auth": {
25 | },
26 | "parameters": {
27 | },
28 | "schemas": {
29 | "AlbumFull": {
30 | "id": "AlbumFull",
31 | "type": "object",
32 | "properties": {
33 | "album_group": {
34 | "type": "string"
35 | },
36 | "album_type": {
37 | "type": "string"
38 | },
39 | "artists": {
40 | "type": "array",
41 | "items": {
42 | "type": "ArtistSimplified"
43 | }
44 | },
45 | "available_markets": {
46 | "type": "array",
47 | "items": {
48 | "type": "string"
49 | }
50 | },
51 | "copyrights": {
52 | "type": "array",
53 | "items": {
54 | "type": "Copyright"
55 | }
56 | },
57 | "external_ids": {
58 | "type": "ExternalIds"
59 | },
60 | "external_urls": {
61 | "type": "ExternalUrls"
62 | },
63 | "genres": {
64 | "type": "array",
65 | "items": {
66 | "type": "string"
67 | }
68 | },
69 | "href": {
70 | "type": "string"
71 | },
72 | "id": {
73 | "type": "string"
74 | },
75 | "images": {
76 | "type": "array",
77 | "items": {
78 | "type": "Image"
79 | }
80 | },
81 | "label": {
82 | "type": "string"
83 | },
84 | "name": {
85 | "type": "string"
86 | },
87 | "popularity": {
88 | "type": "integer"
89 | },
90 | "release_date": {
91 | "type": "string"
92 | },
93 | "release_date_precision": {
94 | "type": "string"
95 | },
96 | "restrictions": {
97 | "type": "Restrictions"
98 | },
99 | "tracks": {
100 | "type": "array",
101 | "items": {
102 | "type": "TrackSimplified"
103 | }
104 | },
105 | "type": {
106 | "type": "string"
107 | },
108 | "uri": {
109 | "type": "string"
110 | }
111 | }
112 | },
113 | "AlbumSimplified": {
114 | "id": "AlbumSimplified",
115 | "type": "object",
116 | "properties": {
117 | "album_group": {
118 | "type": "string"
119 | },
120 | "album_type": {
121 | "type": "string"
122 | },
123 | "artists": {
124 | "type": "array",
125 | "items": {
126 | "type": "ArtistSimplified"
127 | }
128 | },
129 | "available_markets": {
130 | "type": "array",
131 | "items": {
132 | "type": "string"
133 | }
134 | },
135 | "external_urls": {
136 | "type": "ExternalUrls"
137 | },
138 | "href": {
139 | "type": "string"
140 | },
141 | "id": {
142 | "type": "string"
143 | },
144 | "images": {
145 | "type": "array",
146 | "items": {
147 | "type": "Image"
148 | }
149 | },
150 | "name": {
151 | "type": "string"
152 | },
153 | "release_date": {
154 | "type": "string"
155 | },
156 | "release_date_precision": {
157 | "type": "string"
158 | },
159 | "restrictions": {
160 | "type": "Restrictions"
161 | },
162 | "type": {
163 | "type": "string"
164 | },
165 | "uri": {
166 | "type": "string"
167 | }
168 | }
169 | },
170 | "ArtistFull": {
171 | "id": "ArtistFull",
172 | "type": "object",
173 | "properties": {
174 | "external_urls": {
175 | "type": "ExternalUrls"
176 | },
177 | "followers": {
178 | "type": "Followers"
179 | },
180 | "genres": {
181 | "type": "array",
182 | "items": {
183 | "type": "string"
184 | }
185 | },
186 | "href": {
187 | "type": "string"
188 | },
189 | "id": {
190 | "type": "string"
191 | },
192 | "images": {
193 | "type": "array",
194 | "items": {
195 | "type": "Image"
196 | }
197 | },
198 | "name": {
199 | "type": "string"
200 | },
201 | "popularity": {
202 | "type": "integer"
203 | },
204 | "type": {
205 | "type": "string"
206 | },
207 | "uri": {
208 | "type": "string"
209 | }
210 | }
211 | },
212 | "ArtistSimplified": {
213 | "id": "ArtistSimplified",
214 | "type": "object",
215 | "properties": {
216 | "external_urls": {
217 | "type": "ExternalUrls"
218 | },
219 | "href": {
220 | "type": "string"
221 | },
222 | "id": {
223 | "type": "string"
224 | },
225 | "name": {
226 | "type": "string"
227 | },
228 | "type": {
229 | "type": "string"
230 | },
231 | "uri": {
232 | "type": "string"
233 | }
234 | }
235 | },
236 | "Context": {
237 | "id": "Context",
238 | "type": "object",
239 | "properties": {
240 | "type": {
241 | "type": "string"
242 | },
243 | "href": {
244 | "type": "string"
245 | },
246 | "external_urls": {
247 | "type": "ExternalUrls"
248 | },
249 | "uri": {
250 | "type": "string"
251 | }
252 | }
253 | },
254 | "Copyright": {
255 | "id": "Copyright",
256 | "type": "object",
257 | "properties": {
258 | "text": {
259 | "type": "string"
260 | },
261 | "type": {
262 | "type": "string"
263 | }
264 | }
265 | },
266 | "CurrentlyPlaying": {
267 | "id": "CurrentlyPlaying",
268 | "type": "object",
269 | "properties": {
270 | "context": {
271 | "type": "Context"
272 | },
273 | "timestamp": {
274 | "type": "integer"
275 | },
276 | "progress_ms": {
277 | "type": "integer"
278 | },
279 | "is_playing": {
280 | "type": "boolean"
281 | },
282 | "item": {
283 | "type": "TrackFull"
284 | },
285 | "currently_playing_type": {
286 | "type": "string"
287 | }
288 | }
289 | },
290 | "ExternalIds": {
291 | "id": "ExternalIds",
292 | "type": "object",
293 | "additionalProperties": {
294 | "type": "string"
295 | }
296 | },
297 | "ExternalUrls": {
298 | "id": "ExternalUrls",
299 | "type": "object",
300 | "additionalProperties": {
301 | "type": "string"
302 | }
303 | },
304 | "Followers": {
305 | "id": "Followers",
306 | "type": "object",
307 | "properties": {
308 | "href": {
309 | "type": "string"
310 | },
311 | "total": {
312 | "type": "integer"
313 | }
314 | }
315 | },
316 | "Image": {
317 | "id": "Image",
318 | "type": "object",
319 | "properties": {
320 | "height": {
321 | "type": "integer"
322 | },
323 | "url": {
324 | "type": "string"
325 | },
326 | "width": {
327 | "type": "integer"
328 | }
329 | }
330 | },
331 | "Restrictions": {
332 | "id": "Restrictions",
333 | "type": "object"
334 | },
335 | "TrackFull": {
336 | "id": "TrackFull",
337 | "type": "object",
338 | "properties": {
339 | "album": {
340 | "type": "AlbumSimplified"
341 | },
342 | "artists": {
343 | "type": "array",
344 | "items": {
345 | "type": "ArtistSimplified"
346 | }
347 | },
348 | "available_markets": {
349 | "type": "array",
350 | "items": {
351 | "type": "string"
352 | }
353 | },
354 | "disc_number": {
355 | "type": "integer"
356 | },
357 | "duration_ms": {
358 | "type": "integer"
359 | },
360 | "explicit": {
361 | "type": "boolean"
362 | },
363 | "external_ids": {
364 | "type": "ExternalIds"
365 | },
366 | "external_urls": {
367 | "type": "ExternalUrls"
368 | },
369 | "href": {
370 | "type": "string"
371 | },
372 | "id": {
373 | "type": "string"
374 | },
375 | "is_playable": {
376 | "type": "boolean"
377 | },
378 | "linked_from": {
379 | "type": "TrackLink"
380 | },
381 | "restriction": {
382 | "type": "Restrictions"
383 | },
384 | "name": {
385 | "type": "string"
386 | },
387 | "popularity": {
388 | "type": "integer"
389 | },
390 | "preview_url": {
391 | "type": "string"
392 | },
393 | "track_number": {
394 | "type": "integer"
395 | },
396 | "type": {
397 | "type": "string"
398 | },
399 | "uri": {
400 | "type": "string"
401 | },
402 | "is_local": {
403 | "type": "boolean"
404 | }
405 | }
406 | },
407 | "TrackSimplified": {
408 | "id": "TrackSimplified",
409 | "type": "object",
410 | "properties": {
411 | "artists": {
412 | "type": "array",
413 | "items": {
414 | "type": "ArtistSimplified"
415 | }
416 | },
417 | "available_markets": {
418 | "type": "array",
419 | "items": {
420 | "type": "string"
421 | }
422 | },
423 | "disc_number": {
424 | "type": "integer"
425 | },
426 | "duration_ms": {
427 | "type": "integer"
428 | },
429 | "explicit": {
430 | "type": "boolean"
431 | },
432 | "external_urls": {
433 | "type": "ExternalUrls"
434 | },
435 | "href": {
436 | "type": "string"
437 | },
438 | "id": {
439 | "type": "string"
440 | },
441 | "is_playable": {
442 | "type": "boolean"
443 | },
444 | "linked_from": {
445 | "type": "TrackLink"
446 | },
447 | "restriction": {
448 | "type": "Restrictions"
449 | },
450 | "name": {
451 | "type": "string"
452 | },
453 | "preview_url": {
454 | "type": "string"
455 | },
456 | "track_number": {
457 | "type": "integer"
458 | },
459 | "type": {
460 | "type": "string"
461 | },
462 | "uri": {
463 | "type": "string"
464 | },
465 | "is_local": {
466 | "type": "boolean"
467 | }
468 | }
469 | },
470 | "TrackLink": {
471 | "id": "TrackLink",
472 | "type": "object",
473 | "properties": {
474 | "external_urls": {
475 | "type": "ExternalUrls"
476 | },
477 | "href": {
478 | "type": "string"
479 | },
480 | "id": {
481 | "type": "string"
482 | },
483 | "type": {
484 | "type": "string"
485 | },
486 | "uri": {
487 | "type": "string"
488 | }
489 | }
490 | },
491 | "User": {
492 | "id": "User",
493 | "type": "object",
494 | "properties": {
495 | "birthdate": {
496 | "type": "string"
497 | },
498 | "country": {
499 | "type": "string"
500 | },
501 | "display_name": {
502 | "type": "string"
503 | },
504 | "email": {
505 | "type": "string"
506 | },
507 | "external_urls": {
508 | "type": "ExternalUrls"
509 | },
510 | "followers": {
511 | "type": "Followers"
512 | },
513 | "href": {
514 | "type": "string"
515 | },
516 | "id": {
517 | "type": "string"
518 | },
519 | "images": {
520 | "type": "array",
521 | "items": {
522 | "type": "Image"
523 | }
524 | },
525 | "product": {
526 | "type": "string"
527 | },
528 | "type": {
529 | "type": "string"
530 | },
531 | "uri": {
532 | "type": "string"
533 | }
534 | }
535 | },
536 | "Offset": {
537 | "id": "Offset",
538 | "type": "object",
539 | "properties": {
540 | "position": {
541 | "type": "integer"
542 | },
543 | "uri": {
544 | "type": "string"
545 | }
546 | }
547 | },
548 | "PlayRequest": {
549 | "id": "PlayRequest",
550 | "type": "object",
551 | "properties": {
552 | "context_uri": {
553 | "type": "string"
554 | },
555 | "uris": {
556 | "type": "array",
557 | "items": {
558 | "type": "string"
559 | },
560 | "description": "The URI to play. May be repeated."
561 | },
562 | "offset": {
563 | "type": "Offset"
564 | },
565 | "position_ms": {
566 | "type": "integer"
567 | }
568 | }
569 | },
570 | "ArrayOfArtistFull": {
571 | "id": "ArrayOfArtistFull",
572 | "type": "object",
573 | "properties": {
574 | "artists": {
575 | "type": "array",
576 | "items": {
577 | "type": "ArtistFull"
578 | }
579 | }
580 | }
581 | },
582 | "PageOfAlbumSimplified": {
583 | "id": "PageOfAlbumSimplified",
584 | "type": "object",
585 | "properties": {
586 | "href": {
587 | "type": "string"
588 | },
589 | "items": {
590 | "type": "array",
591 | "items": {
592 | "type": "AlbumSimplified"
593 | }
594 | },
595 | "limit": {
596 | "type": "integer"
597 | },
598 | "next": {
599 | "type": "string"
600 | },
601 | "offset": {
602 | "type": "integer"
603 | },
604 | "previous": {
605 | "type": "string"
606 | },
607 | "total": {
608 | "type": "integer"
609 | }
610 | }
611 | }
612 | },
613 | "resources": {
614 | "artists": {
615 | "methods": {
616 | "get": {
617 | "id": "artists.get",
618 | "path": "artists/{id}",
619 | "httpMethod": "GET",
620 | "description": "Get an artist.",
621 | "parameters": {
622 | "id": {
623 | "type": "string",
624 | "description": "The Spotify ID of the artist.",
625 | "required": true,
626 | "location": "path"
627 | }
628 | },
629 | "response": {
630 | "$ref": "ArtistFull"
631 | }
632 | },
633 | "getArtistAlbums": {
634 | "id": "artists.getArtistAlbums",
635 | "path": "artists/{id}/albums",
636 | "httpMethod": "GET",
637 | "description": "Get an artist's albums.",
638 | "parameters": {
639 | "id": {
640 | "type": "string",
641 | "description": "The Spotify ID of the artist.",
642 | "required": true,
643 | "location": "path"
644 | },
645 | "include_groups": {
646 | "type": "string",
647 | "description": "A comma-separated list of keywords that will be used to filter the response. If not supplied, all album types will be returned. Valid values are album, single, appears_on, compilation",
648 | "location": "query"
649 | },
650 | "market": {
651 | "type": "string",
652 | "description": "An ISO 3166-1 alpha-2 country code or the string from_token. Supply this parameter to limit the response to one particular geographical market.",
653 | "location": "query"
654 | },
655 | "limit": {
656 | "type": "integer",
657 | "description": "The number of album objects to return. Default 20. Minimum 1. Maximum 50.",
658 | "location": "query"
659 | },
660 | "offset": {
661 | "type": "integer",
662 | "description": "The index of the first album to return. Default 0 (i.e., the first album). Use with limit to get the next set of albums.",
663 | "location": "query"
664 | }
665 | },
666 | "response": {
667 | "$ref": "PageOfAlbumSimplified"
668 | }
669 | },
670 | "getMultiple": {
671 | "id": "artists.getMultiple",
672 | "path": "artists",
673 | "httpMethod": "GET",
674 | "description": "Get multiple artists.",
675 | "parameters": {
676 | "ids": {
677 | "type": "string",
678 | "description": "A comma-separated list of artist IDs.",
679 | "required": true,
680 | "location": "query"
681 | }
682 | },
683 | "response": {
684 | "$ref": "ArrayOfArtistFull"
685 | }
686 | }
687 | }
688 | },
689 | "player": {
690 | "methods": {
691 | "currentlyPlaying": {
692 | "id": "player.currentlyPlaying",
693 | "path": "me/player/currently-playing",
694 | "httpMethod": "GET",
695 | "description": "Get the user's currently playing track.",
696 | "parameters": {
697 | "market": {
698 | "type": "string",
699 | "description": "An ISO 3166-1 alpha-2 country code or the string from_token. Provide this parameter if you want to apply Track Relinking.",
700 | "required": true,
701 | "location": "query"
702 | }
703 | },
704 | "parameterOrder": [
705 | "market"
706 | ],
707 | "response": {
708 | "$ref": "CurrentlyPlaying"
709 | },
710 | "scopes": [
711 | "user-read-currently-playing",
712 | "user-read-playback-state"
713 | ]
714 | },
715 | "play": {
716 | "id": "player.play",
717 | "path": "me/player/play",
718 | "httpMethod": "PUT",
719 | "description": "Start/resume a user's playback.",
720 | "request": {
721 | "$ref": "PlayRequest"
722 | },
723 | "scopes": [
724 | "user-modify-playback-state"
725 | ]
726 | }
727 | }
728 | },
729 | "users": {
730 | "methods": {
731 | "me": {
732 | "id": "users.me",
733 | "path": "me",
734 | "httpMethod": "GET",
735 | "description": "Get the user's profile.",
736 | "response": {
737 | "$ref": "User"
738 | },
739 | "scopes": [
740 | "user-read-email",
741 | "user-read-private",
742 | "user-read-birthdate"
743 | ]
744 | },
745 | "profile": {
746 | "id": "users.profile",
747 | "path": "users/{user_id}",
748 | "httpMethod": "GET",
749 | "description": "Get a user's profile.",
750 | "parameters": {
751 | "user_id": {
752 | "type": "string",
753 | "description": "The user's Spotify user id.",
754 | "required": true,
755 | "location": "path"
756 | }
757 | },
758 | "response": {
759 | "$ref": "User"
760 | }
761 | }
762 | }
763 | }
764 | }
765 | }
766 |
--------------------------------------------------------------------------------
/Examples/ThirdParty/Spotify/disco-spotify-v1.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2019 Google Inc. All Rights Reserved.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | #
15 | # limitations under the License.
16 | kind: discovery#restDescription
17 | etag: ""
18 | discoveryVersion: v1
19 | id: spotify:v1
20 | name: spotify
21 | version: v1
22 | revision: "20190210"
23 | title: Spotify Web API
24 | description: Based on simple REST principles, the Spotify Web API endpoints return
25 | JSON metadata about music artists, albums, and tracks, directly from the Spotify
26 | Data Catalogue.
27 | ownerDomain: spotify.com
28 | ownerName: Spotify
29 | icons:
30 | x16: https://www.gstatic.com/images/branding/product/1x/googleg_16dp.png
31 | x32: https://www.gstatic.com/images/branding/product/1x/googleg_32dp.png
32 | documentationLink: https://developer.spotify.com/documentation/web-api/
33 | protocol: rest
34 | baseUrl: https://api.spotify.com/v1/
35 | basePath: /v1/
36 | rootUrl: https://api.spotify.com/
37 | servicePath: v1/
38 | batchPath: ""
39 | auth: {}
40 | parameters: {}
41 | schemas:
42 | AlbumFull:
43 | id: AlbumFull
44 | type: object
45 | properties:
46 | album_group:
47 | type: string
48 | album_type:
49 | type: string
50 | artists:
51 | type: array
52 | items:
53 | type: ArtistSimplified
54 | available_markets:
55 | type: array
56 | items:
57 | type: string
58 | copyrights:
59 | type: array
60 | items:
61 | type: Copyright
62 | external_ids:
63 | type: ExternalIds
64 | external_urls:
65 | type: ExternalUrls
66 | genres:
67 | type: array
68 | items:
69 | type: string
70 | href:
71 | type: string
72 | id:
73 | type: string
74 | images:
75 | type: array
76 | items:
77 | type: Image
78 | label:
79 | type: string
80 | name:
81 | type: string
82 | popularity:
83 | type: integer
84 | release_date:
85 | type: string
86 | release_date_precision:
87 | type: string
88 | restrictions:
89 | type: Restrictions
90 | tracks:
91 | type: array
92 | items:
93 | type: TrackSimplified
94 | type:
95 | type: string
96 | uri:
97 | type: string
98 | AlbumSimplified:
99 | id: AlbumSimplified
100 | type: object
101 | properties:
102 | album_group:
103 | type: string
104 | album_type:
105 | type: string
106 | artists:
107 | type: array
108 | items:
109 | type: ArtistSimplified
110 | available_markets:
111 | type: array
112 | items:
113 | type: string
114 | external_urls:
115 | type: ExternalUrls
116 | href:
117 | type: string
118 | id:
119 | type: string
120 | images:
121 | type: array
122 | items:
123 | type: Image
124 | name:
125 | type: string
126 | release_date:
127 | type: string
128 | release_date_precision:
129 | type: string
130 | restrictions:
131 | type: Restrictions
132 | type:
133 | type: string
134 | uri:
135 | type: string
136 | ArtistFull:
137 | id: ArtistFull
138 | type: object
139 | properties:
140 | external_urls:
141 | type: ExternalUrls
142 | followers:
143 | type: Followers
144 | genres:
145 | type: array
146 | items:
147 | type: string
148 | href:
149 | type: string
150 | id:
151 | type: string
152 | images:
153 | type: array
154 | items:
155 | type: Image
156 | name:
157 | type: string
158 | popularity:
159 | type: integer
160 | type:
161 | type: string
162 | uri:
163 | type: string
164 | ArtistSimplified:
165 | id: ArtistSimplified
166 | type: object
167 | properties:
168 | external_urls:
169 | type: ExternalUrls
170 | href:
171 | type: string
172 | id:
173 | type: string
174 | name:
175 | type: string
176 | type:
177 | type: string
178 | uri:
179 | type: string
180 | Context:
181 | id: Context
182 | type: object
183 | properties:
184 | type:
185 | type: string
186 | href:
187 | type: string
188 | external_urls:
189 | type: ExternalUrls
190 | uri:
191 | type: string
192 | Copyright:
193 | id: Copyright
194 | type: object
195 | properties:
196 | text:
197 | type: string
198 | type:
199 | type: string
200 | CurrentlyPlaying:
201 | id: CurrentlyPlaying
202 | type: object
203 | properties:
204 | context:
205 | type: Context
206 | timestamp:
207 | type: integer
208 | progress_ms:
209 | type: integer
210 | is_playing:
211 | type: boolean
212 | item:
213 | type: TrackFull
214 | currently_playing_type:
215 | type: string
216 | ExternalIds:
217 | id: ExternalIds
218 | type: object
219 | additionalProperties:
220 | type: string
221 | ExternalUrls:
222 | id: ExternalUrls
223 | type: object
224 | additionalProperties:
225 | type: string
226 | Followers:
227 | id: Followers
228 | type: object
229 | properties:
230 | href:
231 | type: string
232 | total:
233 | type: integer
234 | Image:
235 | id: Image
236 | type: object
237 | properties:
238 | height:
239 | type: integer
240 | url:
241 | type: string
242 | width:
243 | type: integer
244 | Restrictions:
245 | id: Restrictions
246 | type: object
247 | TrackFull:
248 | id: TrackFull
249 | type: object
250 | properties:
251 | album:
252 | type: AlbumSimplified
253 | artists:
254 | type: array
255 | items:
256 | type: ArtistSimplified
257 | available_markets:
258 | type: array
259 | items:
260 | type: string
261 | disc_number:
262 | type: integer
263 | duration_ms:
264 | type: integer
265 | explicit:
266 | type: boolean
267 | external_ids:
268 | type: ExternalIds
269 | external_urls:
270 | type: ExternalUrls
271 | href:
272 | type: string
273 | id:
274 | type: string
275 | is_playable:
276 | type: boolean
277 | linked_from:
278 | type: TrackLink
279 | restriction:
280 | type: Restrictions
281 | name:
282 | type: string
283 | popularity:
284 | type: integer
285 | preview_url:
286 | type: string
287 | track_number:
288 | type: integer
289 | type:
290 | type: string
291 | uri:
292 | type: string
293 | is_local:
294 | type: boolean
295 | TrackSimplified:
296 | id: TrackSimplified
297 | type: object
298 | properties:
299 | artists:
300 | type: array
301 | items:
302 | type: ArtistSimplified
303 | available_markets:
304 | type: array
305 | items:
306 | type: string
307 | disc_number:
308 | type: integer
309 | duration_ms:
310 | type: integer
311 | explicit:
312 | type: boolean
313 | external_urls:
314 | type: ExternalUrls
315 | href:
316 | type: string
317 | id:
318 | type: string
319 | is_playable:
320 | type: boolean
321 | linked_from:
322 | type: TrackLink
323 | restriction:
324 | type: Restrictions
325 | name:
326 | type: string
327 | preview_url:
328 | type: string
329 | track_number:
330 | type: integer
331 | type:
332 | type: string
333 | uri:
334 | type: string
335 | is_local:
336 | type: boolean
337 | TrackLink:
338 | id: TrackLink
339 | type: object
340 | properties:
341 | external_urls:
342 | type: ExternalUrls
343 | href:
344 | type: string
345 | id:
346 | type: string
347 | type:
348 | type: string
349 | uri:
350 | type: string
351 | User:
352 | id: User
353 | type: object
354 | properties:
355 | birthdate:
356 | type: string
357 | country:
358 | type: string
359 | display_name:
360 | type: string
361 | email:
362 | type: string
363 | external_urls:
364 | type: ExternalUrls
365 | followers:
366 | type: Followers
367 | href:
368 | type: string
369 | id:
370 | type: string
371 | images:
372 | type: array
373 | items:
374 | type: Image
375 | product:
376 | type: string
377 | type:
378 | type: string
379 | uri:
380 | type: string
381 | Offset:
382 | id: Offset
383 | type: object
384 | properties:
385 | position:
386 | type: integer
387 | uri:
388 | type: string
389 | PlayRequest:
390 | id: PlayRequest
391 | type: object
392 | properties:
393 | context_uri:
394 | type: string
395 | uris:
396 | type: array
397 | items:
398 | type: string
399 | description: The URI to play. May be repeated.
400 | offset:
401 | type: Offset
402 | position_ms:
403 | type: integer
404 | ArrayOfArtistFull:
405 | id: ArrayOfArtistFull
406 | type: object
407 | properties:
408 | artists:
409 | type: array
410 | items:
411 | type: ArtistFull
412 | PageOfAlbumSimplified:
413 | id: PageOfAlbumSimplified
414 | type: object
415 | properties:
416 | href:
417 | type: string
418 | items:
419 | type: array
420 | items:
421 | type: AlbumSimplified
422 | limit:
423 | type: integer
424 | next:
425 | type: string
426 | offset:
427 | type: integer
428 | previous:
429 | type: string
430 | total:
431 | type: integer
432 | resources:
433 | artists:
434 | methods:
435 | get:
436 | id: artists.get
437 | path: artists/{id}
438 | httpMethod: GET
439 | description: Get an artist.
440 | parameters:
441 | id:
442 | type: string
443 | description: The Spotify ID of the artist.
444 | required: true
445 | location: path
446 | response:
447 | $ref: ArtistFull
448 | getArtistAlbums:
449 | id: artists.getArtistAlbums
450 | path: artists/{id}/albums
451 | httpMethod: GET
452 | description: Get an artist's albums.
453 | parameters:
454 | id:
455 | type: string
456 | description: The Spotify ID of the artist.
457 | required: true
458 | location: path
459 | include_groups:
460 | type: string
461 | description: A comma-separated list of keywords that will be used to filter the response. If not supplied, all album types will be returned. Valid values are album, single, appears_on, compilation
462 | location: query
463 | market:
464 | type: string
465 | description: An ISO 3166-1 alpha-2 country code or the string from_token. Supply this parameter to limit the response to one particular geographical market.
466 | location: query
467 | limit:
468 | type: integer
469 | description: The number of album objects to return. Default 20. Minimum 1. Maximum 50.
470 | location: query
471 | offset:
472 | type: integer
473 | description: The index of the first album to return. Default 0 (i.e., the first album). Use with limit to get the next set of albums.
474 | location: query
475 | response:
476 | $ref: PageOfAlbumSimplified
477 | getMultiple:
478 | id: artists.getMultiple
479 | path: artists
480 | httpMethod: GET
481 | description: Get multiple artists.
482 | parameters:
483 | ids:
484 | type: string
485 | description: A comma-separated list of artist IDs.
486 | required: true
487 | location: query
488 | response:
489 | $ref: ArrayOfArtistFull
490 | player:
491 | methods:
492 | currentlyPlaying:
493 | id: player.currentlyPlaying
494 | path: me/player/currently-playing
495 | httpMethod: GET
496 | description: Get the user's currently playing track.
497 | parameters:
498 | market:
499 | type: string
500 | description: An ISO 3166-1 alpha-2 country code or the string from_token.
501 | Provide this parameter if you want to apply Track Relinking.
502 | required: true
503 | location: query
504 | parameterOrder:
505 | - market
506 | response:
507 | $ref: CurrentlyPlaying
508 | scopes:
509 | - user-read-currently-playing
510 | - user-read-playback-state
511 | play:
512 | id: player.play
513 | path: me/player/play
514 | httpMethod: PUT
515 | description: Start/resume a user's playback.
516 | request:
517 | $ref: PlayRequest
518 | scopes:
519 | - user-modify-playback-state
520 | users:
521 | methods:
522 | me:
523 | id: users.me
524 | path: me
525 | httpMethod: GET
526 | description: Get the user's profile.
527 | response:
528 | $ref: User
529 | scopes:
530 | - user-read-email
531 | - user-read-private
532 | - user-read-birthdate
533 | profile:
534 | id: users.profile
535 | path: users/{user_id}
536 | httpMethod: GET
537 | description: Get a user's profile.
538 | parameters:
539 | user_id:
540 | type: string
541 | description: The user's Spotify user id.
542 | required: true
543 | location: path
544 | response:
545 | $ref: User
546 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
204 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 |
2 | all:
3 | swift build -c debug
4 |
5 | project:
6 | swift package generate-xcodeproj
7 |
8 | clean:
9 | rm -rf .build Package.pins Package.resolved
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.2
2 |
3 | // Copyright 2019 Google Inc. All Rights Reserved.
4 | //
5 | // Licensed under the Apache License, Version 2.0 (the "License");
6 | // you may not use this file except in compliance with the License.
7 | // You may obtain a copy of the License at
8 | //
9 | // http://www.apache.org/licenses/LICENSE-2.0
10 | //
11 | // Unless required by applicable law or agreed to in writing, software
12 | // distributed under the License is distributed on an "AS IS" BASIS,
13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | // See the License for the specific language governing permissions and
15 | // limitations under the License.
16 |
17 |
18 | import PackageDescription
19 |
20 | let package = Package(
21 | name: "google-api-swift-client",
22 | platforms: [
23 | .macOS(.v10_12), .iOS(.v9), .tvOS(.v9)
24 | ],
25 | products: [
26 | .library(name: "GoogleAPIRuntime", targets: ["GoogleAPIRuntime"]),
27 | .library(name: "Discovery", targets: ["Discovery"]),
28 | ],
29 | dependencies: [
30 | .package(name: "Auth", url: "https://github.com/googleapis/google-auth-library-swift.git", from: "0.5.3"),
31 |
32 | ],
33 | targets: [
34 | .target(name: "google-api-swift-generator", dependencies: ["Discovery"], path: "Sources/google-api-swift-generator"),
35 | .target(name: "google-cli-swift-generator", dependencies: ["Discovery"], path: "Sources/google-cli-swift-generator"),
36 | .target(name: "GoogleAPIRuntime", dependencies: [.product(name: "OAuth2", package: "Auth")], path: "Sources/GoogleAPIRuntime"),
37 | .target(name: "Discovery", dependencies: [], path: "Sources/Discovery"),
38 | ]
39 | )
40 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Swift REST Client Generator for Google APIs
2 |
3 | This project contains Swift code that generates a API clients from Discovery Documents
4 | produced by the [Google API Discovery Service](https://developers.google.com/discovery/).
5 |
6 | It is experimental work-in-progress, but provides a good start
7 | at calling many Google APIs from Swift. Our focus is on supporting
8 | calls from server-side and command-line Swift applications, but
9 | calls from any platform with Swift support should be possible.
10 |
11 | ## Usage
12 |
13 | Running `google-api-swift-generator` will list all available Google APIs and generate a Swift client file in your current directory.
14 |
15 | ```sh
16 | ./google-api-swift-generator
17 | 1) Abusive Experience Report API
18 | 2) Accelerated Mobile Pages (AMP) URL API
19 | 3) Access Approval API
20 | 4) Access Context Manager API
21 | 5) Ad Exchange Buyer API
22 | 6) Ad Exchange Buyer API II
23 | 7) Ad Experience Report API
24 | 8) Admin Reports API
25 | 9) AdSense Management API
26 | 10) AdSense Host API
27 | 11) G Suite Alert Center API
28 | 12) Google Analytics API
29 | [...]
30 | 165) Web Security Scanner API
31 | 166) YouTube Data API
32 | 167) YouTube Analytics API
33 | 168) YouTube Reporting API
34 | Please enter the number corresponding to the service or 0 to exit
35 | > 166
36 | wrote /private/var/folders/km/h0s9nvsd1n58sbrn2wjsf1h80000gn/T/youtubev3.swift
37 | ```
38 |
39 | It is also possible to run the generator with Discovery Documents downloaded from the Discovery Service by passing the json file as argument:
40 |
41 | Running the following creates a file named `discovery-document-filename.swift` in your current directory.
42 |
43 | ```sh
44 | google-api-swift-generator
45 | ```
46 |
47 | This project also includes an experimental CLI generator that
48 | generates command-line interfaces for APIs. These CLIs depend
49 | on the generated client libraries and are produced with the
50 | `google-cli-swift-generator` command. Running the following
51 | writes a CLI `main.swift` to standard output:
52 |
53 | ```sh
54 | google-cli-swift-generator
55 | ```
56 |
57 | The [Examples](Examples) directory contains several example
58 | clients including scripts for generating both client libraries and
59 | CLIs.
60 |
61 | ## Contributing
62 |
63 | We'd love to collaborate on this. See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
64 |
65 | ## Copyright
66 |
67 | Copyright 2019, Google Inc.
68 |
69 | ## License
70 |
71 | Released under the Apache 2.0 license.
72 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | To report a security issue, please use [g.co/vulnz](https://g.co/vulnz).
4 |
5 | The Google Security Team will respond within 5 working days of your report on g.co/vulnz.
6 |
7 | We use g.co/vulnz for our intake, and do coordination and disclosure here using GitHub Security Advisory to privately discuss and fix the issue.
8 |
--------------------------------------------------------------------------------
/Sources/Discovery/Discovery.swift:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import Foundation
16 |
17 | // This default license is prepended to generated files.
18 | // You are welcome to replace it if you are generating clients for non-Google APIs.
19 | public let License = """
20 | // Copyright 2019 Google Inc. All Rights Reserved.
21 | //
22 | // Licensed under the Apache License, Version 2.0 (the "License");
23 | // you may not use this file except in compliance with the License.
24 | // You may obtain a copy of the License at
25 | //
26 | // http://www.apache.org/licenses/LICENSE-2.0
27 | //
28 | // Unless required by applicable law or agreed to in writing, software
29 | // distributed under the License is distributed on an "AS IS" BASIS,
30 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31 | // See the License for the specific language governing permissions and
32 | // limitations under the License.
33 |
34 | // NOTE: This file is automatically-generated!
35 | """
36 |
37 | extension String {
38 | public func capitalized() -> String {
39 | return prefix(1).capitalized + dropFirst()
40 | }
41 |
42 | public func camelCased() -> String {
43 | let characterSet: CharacterSet = .init(charactersIn: "-.")
44 | let components = self.components(separatedBy: characterSet)
45 | var firstValue = components[0]
46 | if firstValue.starts(with: "$") || firstValue.starts(with: "@") {
47 | firstValue = String(firstValue.dropFirst())
48 | }
49 | let remainingWords = components.dropFirst().map {$0.capitalized()}.joined(separator: "")
50 | return "\(firstValue)\(remainingWords)"
51 | }
52 |
53 | public func upperCamelCased() -> String {
54 | let characterSet: CharacterSet = .init(charactersIn: "-.")
55 | let components = self.components(separatedBy: characterSet)
56 | var firstValue = components[0]
57 | if firstValue.starts(with: "$") || firstValue.starts(with: "@") {
58 | firstValue = String(firstValue.dropFirst())
59 | }
60 | let remainingWords = components.dropFirst().map {$0.capitalized()}.joined(separator: "")
61 | return "\(firstValue)\(remainingWords)"
62 | }
63 |
64 | public func snakeCased() -> String {
65 | return self.components(separatedBy: "-").joined(separator: "_")
66 | }
67 |
68 | public func oneLine() -> String {
69 | return self
70 | .replacingOccurrences(of:"\n", with:" ")
71 | .replacingOccurrences(of:"\"", with:"'")
72 | }
73 |
74 | public func escaped() -> String {
75 | if self == "protocol" { // add other reserved words as needed
76 | return "`" + self + "`"
77 | } else {
78 | return self
79 | }
80 | }
81 |
82 | public mutating func addLine() {
83 | self += "\n"
84 | }
85 |
86 | public mutating func addLine(_ line: String) {
87 | self += line + "\n"
88 | }
89 |
90 | public mutating func addLine(indent: Int, _ line: String) {
91 | self += String(repeating: " ", count: indent) + line + "\n"
92 | }
93 |
94 | public mutating func addTextWithoutLinebreak(_ line: String) {
95 | self += line
96 | }
97 | }
98 |
99 | public class Auth_OAuth2: Codable {
100 | public var scopes : [String : AuthScope]
101 | }
102 |
103 | public class Auth : Codable {
104 | public var oauth2 : Auth_OAuth2
105 | }
106 |
107 | public class AuthScope : Codable {
108 | public var description : String
109 | }
110 |
111 | public class Icon : Codable {
112 | public var x16 : String
113 | public var x32 : String
114 | }
115 |
116 | public class DirectoryItem : Codable {
117 | public var kind : String
118 | public var id : String
119 | public var name : String
120 | public var version : String
121 | public var title : String
122 | public var description : String
123 | public var discoveryRestUrl : URL
124 | public var icons : Icon
125 | public var documentationLink : String?
126 | public var labels : [String]?
127 | public var preferred : Bool
128 | }
129 |
130 | public class DirectoryList : Codable {
131 | public var kind : String
132 | public var discoveryVersion : String
133 | public var items : [DirectoryItem]
134 | }
135 |
136 | public class Schema : Codable {
137 | public var id : String?
138 | public var type : String?
139 | public var ref : String?
140 | public var description : String?
141 | public var `default` : String?
142 | public var required : Bool?
143 | public var format : String?
144 | public var pattern : String?
145 | public var minimum : String?
146 | public var maximum : String?
147 | public var `enum` : [String]?
148 | public var enumDescriptions : [String]?
149 | public var repeated : Bool?
150 | public var location : String?
151 | public var properties : [String : Schema]?
152 | public var additionalProperties : Schema?
153 | public var items : Schema?
154 | public var annotations: [String : [String]]?
155 |
156 | enum CodingKeys : String, CodingKey {
157 | case id
158 | case type
159 | case ref = "$ref"
160 | case description
161 | case `default`
162 | case required
163 | case format
164 | case pattern
165 | case minimum
166 | case maximum
167 | case `enum`
168 | case enumDescriptions
169 | case repeated
170 | case location
171 | case properties
172 | case additionalProperties
173 | case items
174 | case annotations
175 | }
176 |
177 | public func Type(objectName: String? = nil) -> String {
178 | if let type = type {
179 | switch type {
180 | case "string": return "String"
181 | case "integer": return "Int"
182 | case "number": return "Double"
183 | case "boolean": return "Bool"
184 | case "any": return "JSONAny"
185 | case "array":
186 | return "[" + self.ItemsType(objectName: objectName) + "]"
187 | case "object":
188 | let potentialName = objectName ?? "Object"
189 | let escapingNames = ["Type", "Error"]
190 | return escapingNames.contains(potentialName) ? "Custom_" + potentialName : potentialName
191 | default:
192 | return type
193 | }
194 | }
195 | if let ref = ref {
196 | let escapingNames = ["Type", "Error"]
197 | if escapingNames.contains(ref) { return "Custom_" + ref }
198 | return ref
199 | }
200 | return "UNKNOWN SCHEMA TYPE"
201 | }
202 |
203 | public func ItemsType(objectName: String? = nil) -> String {
204 | if let items = items {
205 | return items.Type(objectName: objectName)
206 | } else {
207 | return "UNKNOWN ITEM TYPE"
208 | }
209 | }
210 |
211 | public func Comment() -> String {
212 | if let description = description {
213 | return " // " + description
214 | } else {
215 | return ""
216 | }
217 | }
218 | }
219 |
220 | public class SchemaReference : Codable {
221 | public var ref : String
222 | enum CodingKeys : String, CodingKey {
223 | case ref = "$ref"
224 | }
225 | }
226 |
227 | public class Method : Codable {
228 | public var id : String?
229 | public var path : String?
230 | public var httpMethod : String?
231 | public var description : String?
232 | public var parameters : [String : Schema]?
233 | public var parameterOrder : [String]?
234 | public var request : SchemaReference?
235 | public var response : SchemaReference?
236 | public var scopes : [String]?
237 | public var supportsMediaDownload : Bool?
238 | public var supportsMediaUpload : Bool?
239 | public var mediaUpload : MediaUpload?
240 | public var supportsSubscription: Bool?
241 | public var flatPath : String?
242 |
243 | public func HasResponse() -> Bool {
244 | if response != nil {
245 | return true
246 | }
247 | return false
248 | }
249 |
250 | public func ResponseTypeName() -> String {
251 | if let response = response {
252 | return response.ref
253 | }
254 | return "ERROR-UNKNOWN-RESPONSE-TYPE"
255 | }
256 |
257 | public func HasRequest() -> Bool {
258 | if request != nil {
259 | return true
260 | }
261 | return false
262 | }
263 |
264 | public func RequestTypeName() -> String {
265 | if let request = request {
266 | return request.ref
267 | }
268 | return "ERROR-UNKNOWN-REQUEST-TYPE"
269 | }
270 |
271 | public func HasParameters() -> Bool {
272 | if let parameters = parameters {
273 | return parameters.count > 0
274 | }
275 | return false
276 | }
277 |
278 | public func ParametersTypeName(resource : String, method : String) -> String {
279 | if parameters != nil {
280 | return resource.upperCamelCased() + method.upperCamelCased() + "Parameters"
281 | }
282 | return "ERROR-UNKNOWN-PARAMETERS-TYPE"
283 | }
284 | }
285 |
286 | public class MediaUpload : Codable {
287 | public var accept : [String]?
288 | public var maxSize : String?
289 | public var protocols: [String : MediaUploadProtocol]?
290 | }
291 |
292 | public class MediaUploadProtocol : Codable {
293 | public var multipart : Bool?
294 | public var path : String?
295 | }
296 |
297 | public class Resource : Codable {
298 | public var methods : [String : Method]?
299 | public var resources : [String : Resource]?
300 | }
301 |
302 | public class Service : Codable {
303 | public var kind : String
304 | public var discoveryVersion : String
305 | public var id : String
306 | public var name : String
307 | public var version : String
308 | public var revision : String
309 | public var title : String
310 | public var description : String
311 | public var icons : [String : String]
312 | public var documentationLink : String
313 | public var labels : [String]?
314 | public var `protocol` : String?
315 | public var baseUrl : String
316 | public var basePath : String
317 | public var rootUrl : String
318 | public var servicePath : String
319 | public var batchPath : String
320 | public var parameters : [String : Schema]
321 | public var auth: Auth?
322 | public var features : [String]?
323 | public var schemas : [String : Schema]?
324 | public var methods : [String : Method]?
325 | public var resources : [String : Resource]?
326 |
327 | func BaseURLWithVersion() -> String {
328 | return self.baseUrl + self.version + "/"
329 | }
330 |
331 | public func schema(name: String) -> Schema? {
332 | if let schemas = schemas {
333 | return schemas[name]
334 | } else {
335 | return nil
336 | }
337 | }
338 | }
339 |
--------------------------------------------------------------------------------
/Sources/GoogleAPIRuntime/JSONAny.swift:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import Foundation
16 |
17 | // https://stackoverflow.com/questions/46279992/any-when-decoding-json-with-codable
18 | public class JSONAny: Codable {
19 | public let value: Any
20 |
21 | static func decodingError(forCodingPath codingPath: [CodingKey]) -> DecodingError {
22 | let context = DecodingError.Context(codingPath: codingPath, debugDescription: "Cannot decode JSONAny")
23 | return DecodingError.typeMismatch(JSONAny.self, context)
24 | }
25 |
26 | static func encodingError(forValue value: Any, codingPath: [CodingKey]) -> EncodingError {
27 | let context = EncodingError.Context(codingPath: codingPath, debugDescription: "Cannot encode JSONAny")
28 | return EncodingError.invalidValue(value, context)
29 | }
30 |
31 | static func decode(from container: SingleValueDecodingContainer) throws -> Any {
32 | if let value = try? container.decode(Bool.self) {
33 | return value
34 | }
35 | if let value = try? container.decode(Int64.self) {
36 | return value
37 | }
38 | if let value = try? container.decode(Double.self) {
39 | return value
40 | }
41 | if let value = try? container.decode(String.self) {
42 | return value
43 | }
44 | if container.decodeNil() {
45 | return JSONNull()
46 | }
47 | throw decodingError(forCodingPath: container.codingPath)
48 | }
49 |
50 | static func decode(from container: inout UnkeyedDecodingContainer) throws -> Any {
51 | if let value = try? container.decode(Bool.self) {
52 | return value
53 | }
54 | if let value = try? container.decode(Int64.self) {
55 | return value
56 | }
57 | if let value = try? container.decode(Double.self) {
58 | return value
59 | }
60 | if let value = try? container.decode(String.self) {
61 | return value
62 | }
63 | if let value = try? container.decodeNil() {
64 | if value {
65 | return JSONNull()
66 | }
67 | }
68 | if var container = try? container.nestedUnkeyedContainer() {
69 | return try decodeArray(from: &container)
70 | }
71 | if var container = try? container.nestedContainer(keyedBy: MyCodingKey.self) {
72 | return try decodeDictionary(from: &container)
73 | }
74 | throw decodingError(forCodingPath: container.codingPath)
75 | }
76 |
77 | static func decode(from container: inout KeyedDecodingContainer, forKey key: MyCodingKey) throws -> Any {
78 | if let value = try? container.decode(Bool.self, forKey: key) {
79 | return value
80 | }
81 | if let value = try? container.decode(Int64.self, forKey: key) {
82 | return value
83 | }
84 | if let value = try? container.decode(Double.self, forKey: key) {
85 | return value
86 | }
87 | if let value = try? container.decode(String.self, forKey: key) {
88 | return value
89 | }
90 | if let value = try? container.decodeNil(forKey: key) {
91 | if value {
92 | return JSONNull()
93 | }
94 | }
95 | if var container = try? container.nestedUnkeyedContainer(forKey: key) {
96 | return try decodeArray(from: &container)
97 | }
98 | if var container = try? container.nestedContainer(keyedBy: MyCodingKey.self, forKey: key) {
99 | return try decodeDictionary(from: &container)
100 | }
101 | throw decodingError(forCodingPath: container.codingPath)
102 | }
103 |
104 | static func decodeArray(from container: inout UnkeyedDecodingContainer) throws -> [Any] {
105 | var arr: [Any] = []
106 | while !container.isAtEnd {
107 | let value = try decode(from: &container)
108 | arr.append(value)
109 | }
110 | return arr
111 | }
112 |
113 | static func decodeDictionary(from container: inout KeyedDecodingContainer) throws -> [String: Any] {
114 | var dict = [String: Any]()
115 | for key in container.allKeys {
116 | let value = try decode(from: &container, forKey: key)
117 | dict[key.stringValue] = value
118 | }
119 | return dict
120 | }
121 |
122 | static func encode(to container: inout UnkeyedEncodingContainer, array: [Any]) throws {
123 | for value in array {
124 | if let value = value as? Bool {
125 | try container.encode(value)
126 | } else if let value = value as? Int64 {
127 | try container.encode(value)
128 | } else if let value = value as? Double {
129 | try container.encode(value)
130 | } else if let value = value as? String {
131 | try container.encode(value)
132 | } else if value is JSONNull {
133 | try container.encodeNil()
134 | } else if let value = value as? [Any] {
135 | var container = container.nestedUnkeyedContainer()
136 | try encode(to: &container, array: value)
137 | } else if let value = value as? [String: Any] {
138 | var container = container.nestedContainer(keyedBy: MyCodingKey.self)
139 | try encode(to: &container, dictionary: value)
140 | } else {
141 | throw encodingError(forValue: value, codingPath: container.codingPath)
142 | }
143 | }
144 | }
145 |
146 | static func encode(to container: inout KeyedEncodingContainer, dictionary: [String: Any]) throws {
147 | for (key, value) in dictionary {
148 | let key = MyCodingKey(stringValue: key)!
149 | if let value = value as? Bool {
150 | try container.encode(value, forKey: key)
151 | } else if let value = value as? Int64 {
152 | try container.encode(value, forKey: key)
153 | } else if let value = value as? Double {
154 | try container.encode(value, forKey: key)
155 | } else if let value = value as? String {
156 | try container.encode(value, forKey: key)
157 | } else if value is JSONNull {
158 | try container.encodeNil(forKey: key)
159 | } else if let value = value as? [Any] {
160 | var container = container.nestedUnkeyedContainer(forKey: key)
161 | try encode(to: &container, array: value)
162 | } else if let value = value as? [String: Any] {
163 | var container = container.nestedContainer(keyedBy: MyCodingKey.self, forKey: key)
164 | try encode(to: &container, dictionary: value)
165 | } else {
166 | throw encodingError(forValue: value, codingPath: container.codingPath)
167 | }
168 | }
169 | }
170 |
171 | static func encode(to container: inout SingleValueEncodingContainer, value: Any) throws {
172 | if let value = value as? Bool {
173 | try container.encode(value)
174 | } else if let value = value as? Int64 {
175 | try container.encode(value)
176 | } else if let value = value as? Double {
177 | try container.encode(value)
178 | } else if let value = value as? String {
179 | try container.encode(value)
180 | } else if value is JSONNull {
181 | try container.encodeNil()
182 | } else {
183 | throw encodingError(forValue: value, codingPath: container.codingPath)
184 | }
185 | }
186 |
187 | public required init(from decoder: Decoder) throws {
188 | if var arrayContainer = try? decoder.unkeyedContainer() {
189 | self.value = try JSONAny.decodeArray(from: &arrayContainer)
190 | } else if var container = try? decoder.container(keyedBy: MyCodingKey.self) {
191 | self.value = try JSONAny.decodeDictionary(from: &container)
192 | } else {
193 | let container = try decoder.singleValueContainer()
194 | self.value = try JSONAny.decode(from: container)
195 | }
196 | }
197 |
198 | public func encode(to encoder: Encoder) throws {
199 | if let arr = self.value as? [Any] {
200 | var container = encoder.unkeyedContainer()
201 | try JSONAny.encode(to: &container, array: arr)
202 | } else if let dict = self.value as? [String: Any] {
203 | var container = encoder.container(keyedBy: MyCodingKey.self)
204 | try JSONAny.encode(to: &container, dictionary: dict)
205 | } else {
206 | var container = encoder.singleValueContainer()
207 | try JSONAny.encode(to: &container, value: self.value)
208 | }
209 | }
210 | }
211 |
212 | public class JSONNull: Codable {
213 | public init() {
214 | }
215 |
216 | public required init(from decoder: Decoder) throws {
217 | let container = try decoder.singleValueContainer()
218 | if !container.decodeNil() {
219 | throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
220 | }
221 | }
222 |
223 | public func encode(to encoder: Encoder) throws {
224 | var container = encoder.singleValueContainer()
225 | try container.encodeNil()
226 | }
227 | }
228 |
229 | class MyCodingKey : CodingKey {
230 | let key: String
231 |
232 | required init?(intValue: Int) {
233 | return nil
234 | }
235 |
236 | required init?(stringValue: String) {
237 | key = stringValue
238 | }
239 |
240 | var intValue: Int? {
241 | return nil
242 | }
243 |
244 | var stringValue: String {
245 | return key
246 | }
247 | }
248 |
249 |
--------------------------------------------------------------------------------
/Sources/GoogleAPIRuntime/Service.swift:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import Foundation
16 | import OAuth2
17 |
18 | enum GoogleAPIRuntimeError: Error {
19 | case missingPathParameter(String)
20 | case invalidResponseFromServer
21 | }
22 |
23 | public protocol Parameterizable {
24 | func queryParameters() -> [String]
25 | func pathParameters() -> [String]
26 | func query() -> [String:String]
27 | func path(pattern: String) throws -> String
28 | }
29 |
30 | extension Parameterizable {
31 | public func query() -> [String:String] {
32 | var q : [String:String] = [:]
33 | let mirror = Mirror(reflecting:self)
34 | for p in queryParameters() {
35 | for child in mirror.children {
36 | if child.label == p {
37 | switch child.value {
38 | case let s as String:
39 | q[p] = s
40 | case let i as Int:
41 | q[p] = "\(i)"
42 | case Optional.none:
43 | continue
44 | default:
45 | print("failed to handle \(p) \(child.value)")
46 | }
47 |
48 | }
49 | }
50 | }
51 | print("query: \(q)")
52 | return q
53 | }
54 | public func path(pattern: String) throws -> String {
55 | var pattern = pattern
56 | let mirror = Mirror(reflecting:self)
57 | for p in pathParameters() {
58 | for child in mirror.children {
59 | if child.label == p {
60 | switch child.value {
61 | case let s as String:
62 | pattern = pattern.replacingOccurrences(of: "{"+p+"}", with: s)
63 | case Optional.none:
64 | throw GoogleAPIRuntimeError.missingPathParameter(p)
65 | default:
66 | print("failed to handle \(p) \(child.value)")
67 | }
68 | }
69 | }
70 | }
71 | print("path: \(pattern)")
72 | return pattern
73 | }
74 | }
75 |
76 | // general connection helper
77 | open class Service {
78 | var connection : Connection
79 | var base : String
80 |
81 | public init(_ tokenProvider : TokenProvider, _ base : String) {
82 | self.connection = Connection(provider:tokenProvider)
83 | self.base = base
84 | }
85 |
86 | func handleResponse(
87 | _ data : Data?,
88 | _ response : URLResponse?,
89 | _ error : Error?,
90 | _ completion : @escaping(Z?, Error?) -> ()) {
91 | if let error = error {
92 | completion(nil, error)
93 | } else if let data = data {
94 | print(String(data:data, encoding:.utf8)!)
95 | do {
96 | let json = try JSONSerialization.jsonObject(with: data, options: [])
97 | let decoder = JSONDecoder()
98 | if let json = json as? [String:Any] {
99 | if let errorPayload = json["error"] as? [String: Any],
100 | let code = errorPayload["code"] as? Int {
101 | return completion(nil, NSError(
102 | domain: "GoogleAPIRuntime",
103 | code: code,
104 | userInfo: errorPayload))
105 | } else if let payload = json["data"] {
106 | // remove the "data" wrapper that is used with some APIs (e.g. translate)
107 | let payloadData = try JSONSerialization.data(withJSONObject:payload)
108 | return completion(try decoder.decode(Z.self, from: payloadData), nil)
109 | }
110 | }
111 | completion(try decoder.decode(Z.self, from: data), nil)
112 | } catch {
113 | print(String(data:data, encoding:.utf8)!)
114 | completion(nil, error)
115 | }
116 | } else {
117 | completion(nil, GoogleAPIRuntimeError.invalidResponseFromServer)
118 | }
119 | }
120 |
121 | public func perform(
122 | method : String,
123 | path : String,
124 | completion : @escaping(Z?, Error?) -> ()) throws {
125 | let postData : Data? = nil
126 | try connection.performRequest(
127 | method:method,
128 | urlString:base + path,
129 | parameters: [:],
130 | body:postData) {(data, response, error) in
131 | self.handleResponse(data, response, error, completion)
132 | }
133 | }
134 |
135 | public func perform(
136 | method : String,
137 | path : String,
138 | request : X,
139 | completion : @escaping(Z?, Error?) -> ()) throws {
140 | let encoder = JSONEncoder()
141 | let postData = try encoder.encode(request)
142 | try connection.performRequest(
143 | method:method,
144 | urlString:base + path,
145 | parameters: [:],
146 | body:postData) {(data, response, error) in
147 | self.handleResponse(data, response, error, completion)
148 | }
149 | }
150 |
151 | public func perform(
152 | method : String,
153 | path : String,
154 | parameters : Y,
155 | completion : @escaping(Z?, Error?) -> ()) throws {
156 | let postData : Data? = nil
157 | try connection.performRequest(
158 | method:method,
159 | urlString:base + parameters.path(pattern:path),
160 | parameters: parameters.query(),
161 | body:postData) {(data, response, error) in
162 | self.handleResponse(data, response, error, completion)
163 | }
164 | }
165 |
166 | public func perform(
167 | method : String,
168 | path : String,
169 | request : X,
170 | parameters : Y,
171 | completion : @escaping(Z?, Error?) -> ()) throws {
172 | let encoder = JSONEncoder()
173 | let postData = try encoder.encode(request)
174 | try connection.performRequest(
175 | method:method,
176 | urlString:base + parameters.path(pattern:path),
177 | parameters: parameters.query(),
178 | body:postData) {(data, response, error) in
179 | self.handleResponse(data, response, error, completion)
180 | }
181 | }
182 |
183 | public func perform(
184 | method : String,
185 | path : String,
186 | request : X,
187 | parameters : Y,
188 | completion : @escaping(Error?) -> ()) throws {
189 | let encoder = JSONEncoder()
190 | let postData = try encoder.encode(request)
191 | try connection.performRequest(
192 | method:method,
193 | urlString:base + parameters.path(pattern:path),
194 | parameters: parameters.query(),
195 | body:postData) {(data, response, error) in
196 | self.handleResponse(data, response, error, completion)
197 | }
198 | }
199 |
200 | func handleResponse(
201 | _ data : Data?,
202 | _ response : URLResponse?,
203 | _ error : Error?,
204 | _ completion : @escaping(Error?) -> ()) {
205 | completion(error)
206 | }
207 |
208 | public func perform(
209 | method : String,
210 | path : String,
211 | completion : @escaping(Error?) -> ()) throws {
212 | let postData : Data? = nil
213 | try connection.performRequest(
214 | method:method,
215 | urlString:base + path,
216 | parameters: [:],
217 | body:postData) {(data, response, error) in
218 | self.handleResponse(data, response, error, completion)
219 | }
220 | }
221 |
222 | public func perform(
223 | method : String,
224 | path : String,
225 | request : X,
226 | completion : @escaping(Error?) -> ()) throws {
227 | let encoder = JSONEncoder()
228 | let postData = try encoder.encode(request)
229 | try connection.performRequest(
230 | method:method,
231 | urlString:base + path,
232 | parameters: [:],
233 | body:postData) {(data, response, error) in
234 | self.handleResponse(data, response, error, completion)
235 | }
236 | }
237 |
238 | public func perform(
239 | method : String,
240 | path : String,
241 | parameters : Y,
242 | completion : @escaping(Error?) -> ()) throws {
243 | let postData : Data? = nil
244 | try connection.performRequest(
245 | method:method,
246 | urlString:base + parameters.path(pattern:path),
247 | parameters: parameters.query(),
248 | body:postData) {(data, response, error) in
249 | self.handleResponse(data, response, error, completion)
250 | }
251 | }
252 | }
253 |
--------------------------------------------------------------------------------
/Sources/google-api-swift-generator/main.swift:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import Foundation
16 | import Discovery
17 |
18 | enum ParsingError: Error {
19 | case topLevelSchemaUnknownType(schemaName: String, type: String)
20 | case topLevelSchemaArrayDoesNotContainObjects(schemaName: String)
21 | case arrayDidNotIncludeItems(schemaName: String?)
22 | case arrayHadUnknownItems(schemaName: String)
23 | case schemaDidNotIncludeTypeOrRef(schemaName: String)
24 | case arrayContainedArray(schemaName: String)
25 | case unknown
26 | }
27 |
28 | func createInitLines(baseIndent: Int, parentName: String?, parameters: [String: Schema]) -> String {
29 | let inputs = parameters
30 | .sorted(by: { $0.key < $1.key })
31 | .map { (arg: (key: String, value: Schema)) -> (key: String, type: String) in
32 | let (key, value) = arg
33 | let typeName: String
34 | if let parentName = parentName {
35 | typeName = "\(parentName.upperCamelCased())_\(key.upperCamelCased())"
36 | } else {
37 | typeName = key.upperCamelCased()
38 | }
39 | var tmpKey = key.camelCased()
40 | if tmpKey == "self" {
41 | tmpKey = "selfRef"
42 | }
43 | return (key: "`\(tmpKey)`", type: "\(value.Type(objectName: typeName))?")
44 | }
45 | let inputSignature = inputs.map { "\($0.key): \($0.type)" }.joined(separator: ", ")
46 | let assignments = inputs.reduce("") { (prev: String, curr: (key: String, type: String)) -> String in
47 | let nextItem = String(repeating: " ", count: 12) + "self.\(curr.key) = \(curr.key)"
48 | if prev.isEmpty { return "\n" + nextItem }
49 | return """
50 | \(prev)
51 | \(nextItem)
52 | """
53 | }
54 | return """
55 | public init (\(inputSignature)) {\(assignments)
56 | }
57 | """
58 | }
59 |
60 | func createCodingKeys(baseIndent: Int, parentName: String?, parameters: [String: Schema]) -> String {
61 | let someKeyHasHyphen = parameters.keys.reduce(false) { (prev: Bool, curr: String) -> Bool in
62 | if prev { return prev }
63 | return curr.contains("-") || curr.contains(".") || curr.starts(with: "$") || curr.starts(with: "@") || curr == "self"
64 | }
65 | guard someKeyHasHyphen else { return "" }
66 | let cases = parameters
67 | .sorted(by: { $0.key < $1.key })
68 | .reduce("") { (prev: String, curr: (key: String, value: Schema)) -> String in
69 | let explicitValue = curr.key.contains("-") || curr.key.contains(".") || curr.key.starts(with: "$") || curr.key.starts(with: "@") || curr.key == "self"
70 | ? " = \"\(curr.key)\""
71 | : ""
72 | var key = curr.key.camelCased()
73 | if key == "self" {
74 | key = "selfRef"
75 | }
76 | let nextLine = "case `\(key)`\(explicitValue)"
77 | if prev.isEmpty { return String(repeating: " ", count: 12) + nextLine }
78 | return """
79 | \(prev)
80 | \(nextLine)
81 | """
82 | }
83 | return """
84 | enum CodingKeys : String, CodingKey {
85 | \(cases)
86 | }
87 | """
88 | }
89 |
90 | func createArrayType(nextName: String, schema: (key: String, value: Schema), stringUnderConstruction: inout String) throws -> String {
91 | guard let arrayItems = schema.value.items else {
92 | throw ParsingError.arrayDidNotIncludeItems(schemaName: schema.key)
93 | }
94 | let type: String
95 | if var ref = arrayItems.ref {
96 | let escapingNames = ["Type", "Error"]
97 | if escapingNames.contains(ref) {
98 | ref = "Custom_" + ref
99 | }
100 | type = "[\(ref)]"
101 | } else if let _ = arrayItems.properties {
102 | try createNestedObject(parentName: nextName, name: nextName, schema: arrayItems, stringUnderConstruction: &stringUnderConstruction)
103 | type = "\(schema.value.Type(objectName: nextName))"
104 | } else if let additionalProperties = arrayItems.additionalProperties {
105 | try createDynamicNestedObject(parentName: nextName, name: nextName, schema: additionalProperties, stringUnderConstruction: &stringUnderConstruction)
106 | type = "\(schema.value.Type(objectName: nextName))"
107 | }
108 | else if let arrayItemType = arrayItems.type {
109 | var arrayItemTypeName = ""
110 | switch arrayItemType {
111 | case "string": arrayItemTypeName = "String" // todo: perform check for enums.
112 | case "integer": arrayItemTypeName = "Int"
113 | case "number": arrayItemTypeName = "Double"
114 | case "boolean": arrayItemTypeName = "Bool"
115 | case "array":
116 | arrayItemTypeName = try createArrayType(nextName: nextName, schema: (key: "\(schema.key)ArrayItem", value: arrayItems), stringUnderConstruction: &stringUnderConstruction)
117 | default: arrayItemTypeName = "JSONAny"
118 | }
119 | type = "[\(arrayItemTypeName)]"
120 | } else {
121 | throw ParsingError.arrayHadUnknownItems(schemaName: schema.key)
122 | }
123 | return type
124 | }
125 |
126 | func createSchemaAssignment(parentName: String?, name: String, schema: (key: String, value: Schema), stringUnderConstruction: inout String) throws -> (key: String, type: String) {
127 | var key = schema.key.camelCased()
128 | if key == "self" { key = "selfRef" }
129 | let type: String
130 | let nextName = "\(name.upperCamelCased())_\(schema.key.upperCamelCased())"
131 | if let t = schema.value.type {
132 | switch t {
133 | case "object":
134 | // replace branchs with single route?
135 | if let additionalProperties = schema.value.additionalProperties {
136 | let dynamicNextName = schema.value.Type(objectName: nextName)
137 | try createDynamicNestedObject(parentName: nextName, name: dynamicNextName, schema: additionalProperties, stringUnderConstruction: &stringUnderConstruction)
138 | type = schema.value.Type(objectName: nextName)
139 | } else {
140 | try createNestedObject(parentName: nextName, name: nextName, schema: schema.value, stringUnderConstruction: &stringUnderConstruction)
141 | type = "\(schema.value.Type(objectName: nextName))"
142 | }
143 | case "array":
144 | type = try createArrayType(nextName: nextName, schema: schema, stringUnderConstruction: &stringUnderConstruction)
145 | default:
146 | type = schema.value.Type()
147 | }
148 | } else if let ref = schema.value.ref {
149 | let escapingNames = ["Type", "Error"]
150 | if escapingNames.contains(ref) {
151 | type = "Custom_" + ref
152 | } else {
153 | type = ref
154 | }
155 | } else {
156 | throw ParsingError.schemaDidNotIncludeTypeOrRef(schemaName: schema.key)
157 | }
158 | return (key: key, type: type)
159 | }
160 |
161 | func createDynamicNestedObject(parentName: String?, name: String, schema: Schema, stringUnderConstruction: inout String) throws {
162 | let aliasType: String
163 | if let type = schema.type, type == "object" {
164 | try createNestedObject(parentName: (parentName ?? "") + "Item", name: name + "Item", schema: schema, stringUnderConstruction: &stringUnderConstruction)
165 | aliasType = "[String: \(schema.Type(objectName: name + "Item"))]"
166 | } else if let type = schema.type, type == "array", let arrayItems = schema.items, arrayItems.type == "object" {
167 | try createNestedObject(parentName: (parentName ?? "") + "Item", name: name + "Item", schema: schema, stringUnderConstruction: &stringUnderConstruction)
168 | aliasType = "[String: \(schema.Type(objectName: name + "Item"))]"
169 | } else {
170 | aliasType = "[String: \(schema.Type(objectName: name))]"
171 | }
172 | // todo: check for string being an enum
173 | stringUnderConstruction.addLine(indent: 4, "public typealias \(name) = \(aliasType)\n")
174 | }
175 |
176 | func createStaticNestedObject(parentName: String?, name: String, schema: Schema, stringUnderConstruction: inout String) throws {
177 | let currentIndent = 2
178 | let initializer = createInitLines(baseIndent: currentIndent, parentName: parentName, parameters: schema.properties!)
179 | let codingKeys = createCodingKeys(baseIndent: currentIndent, parentName: parentName, parameters: schema.properties!)
180 | var assignments = ""
181 | for p in schema.properties!.sorted(by: { $0.key.camelCased() < $1.key.camelCased() }) {
182 | let assignment = try createSchemaAssignment(parentName: parentName, name: name, schema: p, stringUnderConstruction: &stringUnderConstruction)
183 | assignments.addLine(indent: 8, "public var `\(assignment.key)`: \(assignment.type)?")
184 | }
185 | //todo: add comments for class
186 | let escapingNames = ["Type", "Error"]
187 | let className = escapingNames.contains(name) ? "Custom_" + name : name
188 | let def = """
189 | public class \(className): Codable {
190 | \(initializer)\(!codingKeys.isEmpty ? "\n\(codingKeys)" : "")
191 | \(assignments) }
192 |
193 | """
194 | stringUnderConstruction.addLine(indent: currentIndent, def)
195 | }
196 |
197 | func createNestedObject(parentName: String?, name: String, schema: Schema, stringUnderConstruction: inout String) throws {
198 | if let additionalProperties = schema.additionalProperties {
199 | try createDynamicNestedObject(parentName: parentName, name: name, schema: additionalProperties, stringUnderConstruction: &stringUnderConstruction)
200 | } else if let _ = schema.properties {
201 | try createStaticNestedObject(parentName: parentName, name: name, schema: schema, stringUnderConstruction: &stringUnderConstruction)
202 | } else {
203 | // object has no dynamic properties, and no static properties. Can't infer what it is. Typealias to JSONAny
204 | let aliasDef = "public typealias \(name) = JSONAny\n"
205 | stringUnderConstruction.addLine(indent: 4, aliasDef)
206 | }
207 | }
208 |
209 | func createCodingKeys(baseIndent: Int, parameters: [String: Schema]) -> String {
210 | let someKeyHasHyphen = parameters.keys.reduce(false) { (prev: Bool, curr: String) -> Bool in
211 | if prev { return prev }
212 | return curr.contains("-")
213 | }
214 | guard someKeyHasHyphen else { return "" }
215 | var currentIndent = baseIndent
216 | var enumDeclaration = ""
217 | enumDeclaration.addLine(indent: currentIndent, "enum CodingKeys : String, CodingKey {")
218 | currentIndent += 2
219 | for p in parameters.sorted(by: { $0.key < $1.key }) {
220 | let explicitValue = p.key.contains("-") ? " = \"\(p.key)\"" : ""
221 | enumDeclaration.addLine(indent: currentIndent, "case `\(p.key.camelCased())`\(explicitValue)")
222 | }
223 | currentIndent -= 2
224 | enumDeclaration.addLine(indent: currentIndent, "}")
225 | return enumDeclaration
226 | }
227 |
228 | extension Discovery.Method {
229 |
230 | func ParametersTypeDeclaration(resource : String, method : String) -> String {
231 | var s = ""
232 | s.addLine()
233 | guard let parameters = parameters else { return "" } // todo: check: should this throw an error or return func with no args?
234 |
235 | let initializer = createInitLines(baseIndent: 4, parentName: nil, parameters: parameters)
236 | let codingKeys = createCodingKeys(baseIndent: 4, parentName: nil, parameters: parameters)
237 | var classProperties = ""
238 | for p in parameters.sorted(by: { $0.key < $1.key }) {
239 | classProperties.addLine(indent:8, "public var `\(p.key.camelCased())`: \(p.value.Type())?")
240 | }
241 |
242 | let queryParameterItems = parameters
243 | .sorted(by: { $0.key < $1.key })
244 | .filter { if let location = $0.value.location { return location == "query" } else { return false } }
245 | .map { return "\"\($0.key.camelCased())\"" }
246 | .joined(separator: ",")
247 | let queryParametersDef = """
248 | public func queryParameters() -> [String] {
249 | [\(queryParameterItems)]
250 | }
251 | """
252 |
253 | let pathParameterItems = parameters
254 | .sorted(by: { $0.key < $1.key })
255 | .filter { if let location = $0.value.location { return location == "path" } else { return false } }
256 | .map { return "\"\($0.key.camelCased())\"" }
257 | .joined(separator: ",")
258 | let pathParametersDef = """
259 | public func pathParameters() -> [String] {
260 | [\(pathParameterItems)]
261 | }
262 | """
263 |
264 | return """
265 | public class \(ParametersTypeName(resource:resource, method:method)): Parameterizable {
266 | \(initializer)
267 | \(codingKeys)
268 | \(classProperties)
269 | \(queryParametersDef)
270 | \(pathParametersDef)
271 | }
272 |
273 | """
274 | }
275 | }
276 |
277 | extension Discovery.Resource {
278 | func generate(name: String) -> String {
279 | var s = ""
280 | if let methods = self.methods {
281 | for m in methods.sorted(by: { $0.key < $1.key }) {
282 | s.addLine()
283 | if m.value.HasParameters() {
284 | s += m.value.ParametersTypeDeclaration(resource:name, method:m.key)
285 | }
286 | let methodName = name.camelCased() + "_" + m.key.upperCamelCased()
287 | s.addLine()
288 | s.addLine(indent:4, "public func \(methodName) (")
289 | if m.value.HasRequest() {
290 | s.addLine(indent:8, "request: \(m.value.RequestTypeName()),")
291 | }
292 | if m.value.HasParameters() {
293 | s.addLine(indent:8, "parameters: \(m.value.ParametersTypeName(resource:name, method:m.key)),")
294 | }
295 | if m.value.HasResponse() {
296 | s.addLine(indent:8, "completion: @escaping (\(m.value.ResponseTypeName())?, Error?) -> ()) throws {")
297 | } else {
298 | s.addLine(indent:8, "completion: @escaping (Error?) -> ()) throws {")
299 | }
300 | s.addLine(indent:12, "try perform(")
301 | s.addLine(indent:16, "method: \"\(m.value.httpMethod!)\",")
302 | var path = ""
303 | if m.value.path != nil {
304 | path = m.value.path!
305 | }
306 | s.addLine(indent:16, "path: \"\(path)\",")
307 | if m.value.HasRequest() {
308 | s.addLine(indent:16, "request: request,")
309 | }
310 | if m.value.HasParameters() {
311 | s.addLine(indent:16, "parameters: parameters,")
312 | }
313 | s.addLine(indent:16, "completion: completion)")
314 | s.addLine(indent:4, "}")
315 | s.addLine()
316 | }
317 | }
318 | if let resources = self.resources {
319 | for r in resources.sorted(by: { $0.key < $1.key }) {
320 | s += r.value.generate(name: name + "_" + r.key)
321 | }
322 | }
323 | return s
324 | }
325 | }
326 |
327 | extension Discovery.Service {
328 | func generate() throws -> String {
329 | guard let schemas = schemas else {
330 | return ""
331 | }
332 | var generatedSchemas = ""
333 | for schema in schemas.sorted(by: { $0.key < $1.key }) {
334 | switch schema.value.type {
335 | case "object":
336 | try createNestedObject(parentName: schema.key,
337 | name: schema.key.camelCased(),
338 | schema: schema.value,
339 | stringUnderConstruction: &generatedSchemas)
340 | case "array":
341 | guard let itemsSchema = schema.value.items else {
342 | throw ParsingError.topLevelSchemaArrayDoesNotContainObjects(schemaName: schema.key)
343 | }
344 | if let ref = itemsSchema.ref {
345 | generatedSchemas.addLine(indent: 4, "public typealias \(schema.key) = [\(ref)]")
346 | } else {
347 | generatedSchemas.addLine(indent:2, "public typealias \(schema.key) = [\(schema.key)Item]")
348 | generatedSchemas.addLine()
349 | generatedSchemas.addLine(indent:2, "public class \(schema.key)Item : Codable {")
350 | if let properties = itemsSchema.properties {
351 | let initializer = createInitLines(baseIndent: 4, parentName: nil, parameters: properties)
352 | generatedSchemas.addTextWithoutLinebreak(initializer)
353 | let codingKeys = createCodingKeys(baseIndent: 4, parentName: nil, parameters: properties)
354 | generatedSchemas.addLine(codingKeys)
355 | for p in properties.sorted(by: { $0.key < $1.key }) {
356 | generatedSchemas.addLine(indent:4, "public var `\(p.key.camelCased())` : \(p.value.Type())?")
357 | }
358 | }
359 | generatedSchemas.addLine("}")
360 | }
361 | case "any":
362 | generatedSchemas.addLine(indent: 2, "public typealias `\(schema.key)` = JSONAny")
363 | default:
364 | throw ParsingError.topLevelSchemaUnknownType(schemaName: schema.key, type: schema.value.type ?? "nil - unknown type")
365 | }
366 | }
367 |
368 | var generatesResources = ""
369 | if let resources = resources {
370 | for r in resources.sorted(by: { $0.key < $1.key }) {
371 | generatesResources += r.value.generate(name: r.key)
372 | }
373 | }
374 |
375 | return """
376 | \(Discovery.License)
377 |
378 | import Foundation
379 | import OAuth2
380 | import GoogleAPIRuntime
381 |
382 | public class \(self.name.capitalized()) : Service {
383 | public init(tokenProvider: TokenProvider) throws {
384 | try super.init(tokenProvider, "\(self.baseUrl)")
385 | }
386 |
387 | \(generatedSchemas)
388 |
389 | \(generatesResources)
390 | }
391 | """
392 | }
393 | }
394 |
395 | func main() throws {
396 | let arguments = CommandLine.arguments
397 | if (arguments.count > 1) {
398 | let discoveryFileURL = URL(fileURLWithPath: arguments[1])
399 | try processDiscoveryDocument(url: discoveryFileURL, name: discoveryFileURL.deletingPathExtension().lastPathComponent)
400 | } else {
401 | try interactiveServiceGeneration()
402 | }
403 | }
404 |
405 | func processDiscoveryDocument(url: URL, name: String) throws {
406 | let data = try Data(contentsOf: url)
407 | let decoder = JSONDecoder()
408 | do {
409 | let service = try decoder.decode(Service.self, from: data)
410 | let code = try service.generate()
411 | let fileURL = URL(fileURLWithPath: name).appendingPathExtension("swift")
412 | try code.write(to: fileURL, atomically: true, encoding: .utf8)
413 | print("wrote \(fileURL.path)")
414 | } catch {
415 | print("error \(error)\n")
416 | }
417 | }
418 |
419 | func interactiveServiceGeneration() throws {
420 | let data = try Data(contentsOf: URL(string: "https://www.googleapis.com/discovery/v1/apis")!)
421 | let decoder = JSONDecoder()
422 | let directoryList = try decoder.decode(DirectoryList.self, from: data)
423 | var map : [Int:DirectoryItem] = [:]
424 | var i = 1
425 | for item in directoryList.items.filter({ $0.preferred }) {
426 | map[i] = item
427 | let istr = String.init(describing: i)
428 | let padding = String(repeatElement(" ", count: 3 - istr.count))
429 | print("\(padding + istr)) \(item.title)")
430 | i += 1
431 | }
432 | var directoryItem: DirectoryItem?
433 | repeat {
434 | print("Please enter the number corresponding to the service or 0 to exit")
435 | print("> ", terminator: "")
436 | let input = readLine()
437 | if input == "0" {
438 | return
439 | }
440 | if let i = Int(input!), input != nil {
441 | directoryItem = map[i]
442 | }
443 | } while directoryItem == nil
444 | try processDiscoveryDocument(url: directoryItem!.discoveryRestUrl, name: directoryItem!.id.replacingOccurrences(of: ":", with: ""))
445 | }
446 |
447 | do {
448 | try main()
449 | } catch (let error) {
450 | print("ERROR: \(error)\n")
451 | }
452 |
--------------------------------------------------------------------------------
/Sources/google-cli-swift-generator/main.swift:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import Foundation
16 | import Discovery
17 |
18 | var stderr = FileHandle.standardError
19 | func printerr(_ s : String) {
20 | stderr.write(("google-cli-swift-generator WARNING: "+s+"\n").data(using:.utf8)!)
21 | }
22 |
23 | let ParameterPrefix = ""
24 | let RequestObjectPrefix = "request_"
25 |
26 | func optionDeclaration(_ prefix: String, _ name: String, _ schema: Schema) -> String {
27 | if schema.type == "string" {
28 | // https://github.com/kylef/Commander/issues/49
29 | var s = "Options(\""
30 | s += prefix + name
31 | s += "\", default: [], count: 1, description: \""
32 | if let d = schema.description {
33 | s += d.oneLine()
34 | }
35 | s += "\"),"
36 | return s
37 | } else if schema.type == "integer" {
38 | var s = "Options(\""
39 | s += prefix + name
40 | s += "\", default: [], count: 1, description: \""
41 | if let d = schema.description {
42 | s += d.oneLine()
43 | }
44 | s += "\"),"
45 | return s
46 | } else if let items = schema.items,
47 | schema.type == "array",
48 | items.type == "string" {
49 | var s = "VariadicOption(\""
50 | s += prefix + name
51 | s += "\", default: [], description: \""
52 | if let d = schema.description {
53 | s += d.oneLine()
54 | }
55 | s += "\"),"
56 | return s
57 | } else if let items = schema.items,
58 | schema.type == "array",
59 | items.type == "any" {
60 | var s = "VariadicOption(\""
61 | s += prefix + name
62 | s += "\", default: [], description: \""
63 | if let d = schema.description {
64 | s += d.oneLine()
65 | }
66 | s += "\"),"
67 | return s
68 | } else {
69 | let jsonData = try! JSONEncoder().encode(schema)
70 | let jsonString = String(data: jsonData, encoding: .utf8)!
71 | printerr("Unsupported schema for option \(prefix)\(name): \(jsonString)")
72 | return ""
73 | }
74 | }
75 |
76 | extension Discovery.Method {
77 | func parametersString() -> String {
78 | var s = ""
79 | if let parameters = parameters {
80 | for p in parameters.sorted(by: { $0.key < $1.key }) {
81 | if p.value.type == "string" || p.value.type == "integer" {
82 | if s != "" {
83 | s += ", "
84 | }
85 | s += ParameterPrefix + p.key
86 | }
87 | }
88 | }
89 | return s
90 | }
91 | func requestPropertiesString(requestSchema: Schema?) -> String {
92 | var s = ""
93 | if let requestSchema = requestSchema,
94 | let properties = requestSchema.properties {
95 | for p in properties.sorted(by: { $0.key < $1.key }) {
96 | if p.value.type == "string" || p.value.type == "integer" {
97 | if s != "" {
98 | s += ", "
99 | }
100 | s += RequestObjectPrefix + p.key
101 | } else if let items = p.value.items,
102 | p.value.type == "array",
103 | items.type == "string" {
104 | if s != "" {
105 | s += ", "
106 | }
107 | s += RequestObjectPrefix + p.key
108 | } else if let items = p.value.items,
109 | p.value.type == "array",
110 | items.type == "any" {
111 | if s != "" {
112 | s += ", "
113 | }
114 | s += RequestObjectPrefix + p.key
115 | }
116 | }
117 | }
118 | return s
119 | }
120 |
121 | func Invocation(serviceName: String,
122 | resourceName : String,
123 | resource: Discovery.Resource,
124 | methodName : String,
125 | method: Discovery.Method,
126 | requestSchema: Schema?) -> String {
127 | var s = "\n"
128 | s.addLine(indent:4, "$0.command(")
129 | s.addLine(indent:6, "\"" + resourceName + "." + methodName + "\",")
130 | if let parameters = parameters {
131 | for p in parameters.sorted(by: { $0.key < $1.key }) {
132 | let d = optionDeclaration(ParameterPrefix, p.key, p.value)
133 | if d.count > 0 {
134 | s.addLine(indent:6, d)
135 | }
136 | }
137 | }
138 | if let requestSchema = requestSchema,
139 | let properties = requestSchema.properties {
140 | for p in properties.sorted(by: { $0.key < $1.key }) {
141 | let d = optionDeclaration(RequestObjectPrefix, p.key, p.value)
142 | if d.count > 0 {
143 | s.addLine(indent:6, d)
144 | }
145 | }
146 | }
147 | if let description = method.description {
148 | s.addLine(indent:6, "description: \"" + description.oneLine() + "\") {")
149 | } else {
150 | s.addLine(indent:6, "description: \"\") {")
151 | }
152 | let p = self.parametersString()
153 | let r = self.requestPropertiesString(requestSchema: requestSchema)
154 | if p != "" && r != "" {
155 | s.addLine(indent:6, p + ", " + r + " in")
156 | } else if p != "" {
157 | s.addLine(indent:6, p + " in")
158 | } else if r != "" {
159 | s.addLine(indent:6, r + " in")
160 | }
161 | s.addLine(indent:6, "do {")
162 | if self.HasParameters() {
163 | s.addLine(indent:8, "var parameters = " + serviceName.capitalized() + "."
164 | + self.ParametersTypeName(resource:resourceName, method:methodName) + "()")
165 | if let parameters = parameters {
166 | for p in parameters.sorted(by: { $0.key < $1.key }) {
167 | if p.value.type == "string" || p.value.type == "integer" {
168 | s.addLine(indent:8, "if let " + ParameterPrefix + p.key + " = " + ParameterPrefix + p.key + ".first {")
169 | s.addLine(indent:10, "parameters." + p.key + " = " + ParameterPrefix + p.key)
170 | s.addLine(indent:8, "}")
171 | }
172 | }
173 | }
174 | }
175 | if self.HasRequest() {
176 | s.addLine(indent:8, "var request = " + serviceName.capitalized() + "."
177 | + self.RequestTypeName() + "()")
178 | if let requestSchema = requestSchema,
179 | let properties = requestSchema.properties {
180 | for p in properties.sorted(by: { $0.key < $1.key }) {
181 | if p.value.type == "string" || p.value.type == "integer" {
182 | s.addLine(indent:8, "if let " + RequestObjectPrefix + p.key + " = " + RequestObjectPrefix + p.key + ".first {")
183 | s.addLine(indent:10, "request." + p.key + " = " + RequestObjectPrefix + p.key)
184 | s.addLine(indent:8, "}")
185 | } else if let items = p.value.items,
186 | p.value.type == "array",
187 | items.type == "string" {
188 | s.addLine(indent:8, "if " + RequestObjectPrefix + p.key + ".count > 0 {")
189 | s.addLine(indent:10, "request." + p.key + " = " + RequestObjectPrefix + p.key)
190 | s.addLine(indent:8, "}")
191 | }
192 | }
193 | }
194 | }
195 | s.addLine(indent:8, "let sem = DispatchSemaphore(value: 0)")
196 | let fullMethodName = (resourceName + "_" + methodName)
197 |
198 | var invocation = "try " + serviceName + "." + fullMethodName + "("
199 | if self.HasRequest() {
200 | if self.HasParameters() {
201 | invocation += "request: request, parameters:parameters"
202 | } else {
203 | invocation += "request:request"
204 | }
205 | } else {
206 | if self.HasParameters() {
207 | invocation += "parameters:parameters"
208 | }
209 | }
210 | invocation += ") {"
211 | s.addLine(indent:8, invocation)
212 |
213 | var arguments = ""
214 | if self.HasResponse() {
215 | arguments += "response, "
216 | }
217 | arguments += "error in"
218 | s.addLine(indent:10, arguments)
219 | if self.HasResponse() {
220 | s.addLine(indent:10, "if let response = response { print (\"RESPONSE: \\(response)\") }")
221 | }
222 | s.addLine(indent:10, "if let error = error { print (\"ERROR: \\(error)\") }")
223 | s.addLine(indent:10, "sem.signal()")
224 | s.addLine(indent:8, "}")
225 | s.addLine(indent:8, "_ = sem.wait()")
226 | s.addLine(indent:6, "} catch let error {")
227 | s.addLine(indent:8, "print (\"Client error: \\(error)\")")
228 | s.addLine(indent:6, "}")
229 | s.addLine(indent:4, "}")
230 | return s
231 | }
232 | }
233 |
234 | extension Discovery.Resource {
235 | func generate(service: Service, name: String) -> String {
236 | var s = ""
237 | if let methods = self.methods {
238 | for m in methods.sorted(by: { $0.key < $1.key }) {
239 | let requestSchema = service.schema(name: m.value.RequestTypeName())
240 | s += m.value.Invocation(serviceName:service.serviceName(),
241 | resourceName:name,
242 | resource:self,
243 | methodName:m.key,
244 | method:m.value,
245 | requestSchema:requestSchema
246 | )
247 | }
248 | }
249 | if let resources = self.resources {
250 | for r in resources.sorted(by: { $0.key < $1.key }) {
251 | s += r.value.generate(service: service, name: name + "_" + r.key)
252 | }
253 | }
254 | return s
255 | }
256 | }
257 |
258 | extension Discovery.Service {
259 | func serviceTitle() -> String {
260 | return self.name.capitalized()
261 | }
262 | func serviceName() -> String {
263 | return self.name
264 | }
265 | func scopes() -> [String] {
266 | var scopeSet = Set()
267 | if let resources = resources {
268 | for r in resources {
269 | if let methods = r.value.methods {
270 | for m in methods {
271 | if let scopes = m.value.scopes {
272 | for scope in scopes {
273 | scopeSet.insert(scope)
274 | }
275 | }
276 | }
277 | }
278 | }
279 | }
280 | return scopeSet.sorted()
281 | }
282 | func generate() -> String {
283 | var s = Discovery.License
284 | s.addLine()
285 | for i in
286 | ["Foundation",
287 | "Dispatch",
288 | "OAuth2",
289 | "GoogleAPIRuntime",
290 | "Commander"] {
291 | s.addLine("import " + i)
292 | }
293 | s.addLine()
294 | s.addLine("let CLIENT_CREDENTIALS = \"" + serviceName() + ".json\"")
295 | s.addLine("let TOKEN = \"" + serviceName() + ".json\"")
296 | s.addLine()
297 | s.addLine("func main() throws {")
298 | let scopes = self.scopes()
299 | if scopes.count == 1 {
300 | s.addLine(indent:2, "let scopes = \(scopes)")
301 | } else {
302 | s.addLine(indent:2, "let scopes = [")
303 | s += " \"" + scopes.joined(separator:"\",\n \"") + "\"]\n"
304 | }
305 | s.addLine()
306 | s.addLine(indent:2, "guard let tokenProvider = BrowserTokenProvider(credentials:CLIENT_CREDENTIALS, token:TOKEN) else {")
307 | s.addLine(indent:4, "return")
308 | s.addLine(indent:2, "}")
309 | s.addLine(indent:2, "let \(self.serviceName()) = try \(self.serviceTitle())(tokenProvider:tokenProvider)")
310 | s.addLine()
311 | s.addLine(indent:2, "let group = Group {")
312 | s.addLine(indent:4, "$0.command(\"login\", description:\"Log in with browser-based authentication.\") {")
313 | s.addLine(indent:6, "try tokenProvider.signIn(scopes:scopes)")
314 | s.addLine(indent:6, "try tokenProvider.saveToken(TOKEN)")
315 | s.addLine(indent:4, "}")
316 | if let resources = resources {
317 | for r in resources.sorted(by: { $0.key < $1.key }) {
318 | s += r.value.generate(service: self, name: r.key)
319 | }
320 | }
321 | s.addLine(indent:2, "}")
322 | s.addLine(indent:2, "group.run()")
323 | s.addLine(indent:0, "}")
324 | s.addLine()
325 | s.addLine(indent:0, "do {")
326 | s.addLine(indent:2, "try main()")
327 | s.addLine(indent:0, "} catch (let error) {")
328 | s.addLine(indent:2, "print(\"Application error: \\(error)\")")
329 | s.addLine(indent:0, "}")
330 | return s
331 | }
332 | }
333 |
334 | func main() throws {
335 | let arguments = CommandLine.arguments
336 |
337 | let path = arguments[1]
338 | let data = try Data(contentsOf: URL(fileURLWithPath: path))
339 | let decoder = JSONDecoder()
340 | do {
341 | let service = try decoder.decode(Service.self, from: data)
342 | let code = service.generate()
343 | print(code)
344 | } catch {
345 | print("error \(error)\n")
346 | }
347 | }
348 |
349 | do {
350 | try main()
351 | } catch (let error) {
352 | print("ERROR: \(error)\n")
353 | }
354 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------