Browse Source

Add sample Google API call using SwiftGRPCNIO. (#448)

* Add sample Google API call using SwiftGRPCNIO.

* Fix copyright dates, remove unnecessary file.

* address tools version and formatting concerns in NaturalLanguageNIO sample.

* Reduce threadcount to 1 in NaturalLanguageNIO sample.

* Make getAuthToken return a future in NIO API sample client.

* Updated NaturalLanguageNIO sample with suggestions from MrMage and improved overall structure.

* Fix formatting in NaturalLanguageNIO/Package.swift

* cascade to promise inside makeServiceClient + minor consistency improvements
Tim Burks 6 years ago
parent
commit
882b91eedf

+ 1 - 1
Examples/Google/NaturalLanguage/README.md

@@ -1,7 +1,7 @@
 # Calling the Google Cloud Natural Language API
 # Calling the Google Cloud Natural Language API
 
 
 This directory contains a very simple sample that calls the 
 This directory contains a very simple sample that calls the 
-[Google Cloud Natural Language API](https://cloud.google.com/datastore/docs/reference/rpc/google.datastore.v1).
+[Google Cloud Natural Language API](https://cloud.google.com/natural-language/docs/reference/rpc/).
 Calls are made directly to the Cloud Natural Language RPC interface. 
 Calls are made directly to the Cloud Natural Language RPC interface. 
 In practice, these would be wrapped in idiomatic code.
 In practice, these would be wrapped in idiomatic code.
 
 

+ 9 - 0
Examples/Google/NaturalLanguageNIO/Makefile

@@ -0,0 +1,9 @@
+
+all:
+	swift build -c release
+	cp .build/release/NaturalLanguage .
+
+clean :
+	rm -rf Packages googleapis .build
+	rm -f Package.pins NaturalLanguage Sources/*.pb.swift Sources/*.grpc.swift google.json
+	rm -rf Package.resolved

+ 36 - 0
Examples/Google/NaturalLanguageNIO/Package.swift

@@ -0,0 +1,36 @@
+// swift-tools-version:5.0
+
+/*
+ * Copyright 2019, gRPC Authors All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import PackageDescription
+
+let package = Package(
+  name: "NaturalLanguage",
+  dependencies: [
+    .package(url: "../../..", .branch("HEAD")),
+    .package(url: "https://github.com/apple/swift-protobuf.git", from: "1.0.2"),
+    .package(url: "https://github.com/kylef/Commander.git", from: "0.8.0"),
+    .package(url: "https://github.com/googleapis/google-auth-library-swift.git", from: "0.5.0")
+  ],
+  targets: [
+    .target(name: "NaturalLanguage",
+            dependencies: [
+              "SwiftGRPCNIO",
+              "SwiftProtobuf",
+              "Commander",
+              "OAuth2"],
+            path: "Sources")
+  ])

+ 17 - 0
Examples/Google/NaturalLanguageNIO/README.md

@@ -0,0 +1,17 @@
+# Calling the Google Cloud Natural Language API
+
+This directory contains a very simple sample that calls the 
+[Google Cloud Natural Language API](https://cloud.google.com/natural-language/docs/reference/rpc/).
+Calls are made directly to the Cloud Natural Language RPC interface. 
+In practice, these would be wrapped in idiomatic code.
+
+Use [RUNME](RUNME) to generate the necessary Protocol Buffer
+and gRPC support code. It uses protoc and the Swift Protocol
+Buffer and gRPC plugins, so please be sure these are in your
+path. The plugins can be built by running `make` in the 
+top-level Plugins directory.
+
+## Prerequisites
+
+Please be sure to perform the preliminary steps in
+[Examples/Google/README](../README.md).

+ 29 - 0
Examples/Google/NaturalLanguageNIO/RUNME

@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+# Use this script to regenerate the Protocol Buffer and gRPC files
+# needed to build the example.
+#
+# Note that it requires updated protoc, protoc-gen-swift, and
+# protoc-gen-swiftgrpc binaries and assumes that protoc-gen-swift 
+# is installed in $HOME/local/bin.
+
+if [ ! -d "googleapis" ]; then
+  curl -L -O https://github.com/googleapis/googleapis/archive/master.zip
+  unzip master.zip
+  rm -f master.zip
+  mv googleapis-master googleapis
+fi
+
+protoc \
+ 	google/cloud/language/v1/language_service.proto \
+	google/api/annotations.proto \
+	google/api/http.proto \
+	google/protobuf/descriptor.proto \
+ 	-Igoogleapis \
+	-I../common/include \
+	--swift_out=googleapis \
+	--swiftgrpc_out=googleapis \
+	--swiftgrpc_opt=NIO=true
+
+# move Swift files to the Sources directory
+find googleapis -name "*.swift" -exec mv {} Sources \;

+ 135 - 0
Examples/Google/NaturalLanguageNIO/Sources/main.swift

@@ -0,0 +1,135 @@
+/*
+ * Copyright 2019, gRPC Authors All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import Dispatch
+import Foundation
+import SwiftGRPCNIO
+import OAuth2
+import NIO
+import NIOHTTP1
+import NIOSSL
+
+/// Prepare a TLSMode for a general SSL client that supports HTTP/2.
+func makeClientTLS() throws -> GRPCClientConnection.TLSMode {
+  let configuration = TLSConfiguration.forClient(applicationProtocols: ["h2"])
+  let context = try NIOSSLContext(configuration: configuration)
+  return .custom(context)
+}
+
+/// Create a client and return a future to provide its value.
+func makeServiceClient(host: String,
+                       port: Int,
+                       eventLoopGroup: MultiThreadedEventLoopGroup)
+  -> EventLoopFuture<Google_Cloud_Language_V1_LanguageServiceService_NIOClient> {
+    let promise = eventLoopGroup.next().makePromise(of: Google_Cloud_Language_V1_LanguageServiceService_NIOClient.self)
+    do {
+      try GRPCClientConnection.start(host: host,
+                                     port: port,
+                                     eventLoopGroup: eventLoopGroup,
+                                     tls: makeClientTLS())
+        .map { client in
+          Google_Cloud_Language_V1_LanguageServiceService_NIOClient(connection: client)
+        }.cascade(to: promise)
+    } catch {
+      promise.fail(error)
+    }
+    return promise.futureResult
+}
+
+enum AuthError: Error {
+  case noTokenProvider
+  case tokenProviderFailed
+}
+
+/// Get an auth token and return a future to provide its value.
+func getAuthToken(scopes: [String],
+                  eventLoop: EventLoop)
+  -> EventLoopFuture<String> {
+    let promise = eventLoop.makePromise(of: String.self)
+    guard let provider = DefaultTokenProvider(scopes: scopes) else {
+      promise.fail(AuthError.noTokenProvider)
+      return promise.futureResult
+    }
+    do {
+      try provider.withToken { (token, error) in
+        if let token = token,
+          let accessToken = token.AccessToken {
+          promise.succeed(accessToken)
+        } else if let error = error {
+          promise.fail(error)
+        } else {
+          promise.fail(AuthError.tokenProviderFailed)
+        }
+      }
+    } catch {
+      promise.fail(error)
+    }
+    return promise.futureResult
+}
+
+/// Main program. Make a sample API request.
+do {
+  let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
+  
+  // Get an auth token.
+  let scopes = ["https://www.googleapis.com/auth/cloud-language"]
+  let authToken = try getAuthToken(
+    scopes: scopes,
+    eventLoop: eventLoopGroup.next()).wait()
+  
+  // Create a service client.
+  let service = try makeServiceClient(
+    host: "language.googleapis.com",
+    port: 443,
+    eventLoopGroup: eventLoopGroup).wait()
+  
+  // Use CallOptions to send the auth token (necessary) and set a custom timeout (optional).
+  let headers = HTTPHeaders([("authorization", "Bearer " + authToken)])
+  let timeout = try! GRPCTimeout.seconds(30)
+  let callOptions = CallOptions(customMetadata: headers, timeout: timeout)
+  print("CALL OPTIONS\n\(callOptions)\n")
+  
+  // Construct the API request.
+  var document = Google_Cloud_Language_V1_Document()
+  document.type = .plainText
+  document.content = "The Caterpillar and Alice looked at each other for some time in silence: at last the Caterpillar took the hookah out of its mouth, and addressed her in a languid, sleepy voice. `Who are you?' said the Caterpillar."
+  
+  var features = Google_Cloud_Language_V1_AnnotateTextRequest.Features()
+  features.extractSyntax = true
+  features.extractEntities = true
+  features.extractDocumentSentiment = true
+  features.extractEntitySentiment = true
+  features.classifyText = true
+  
+  var request = Google_Cloud_Language_V1_AnnotateTextRequest()
+  request.document = document
+  request.features = features
+  print("REQUEST MESSAGE\n\(request)")
+  
+  // Create/start the API call.
+  let call = service.annotateText(request, callOptions: callOptions)
+  call.response.whenSuccess { response in
+    print("CALL SUCCEEDED WITH RESPONSE\n\(response)")
+  }
+  call.response.whenFailure { error in
+    print("CALL FAILED WITH ERROR\n\(error)")
+  }
+  
+  // wait() on the status to stop the program from exiting.
+  let status = try call.status.wait()
+  print("CALL STATUS\n\(status)")
+} catch {
+  print("EXAMPLE FAILED WITH ERROR\n\(error)")
+}