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
 
 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. 
 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)")
+}