Browse Source

Merge pull request #97 from grpc/datastore

Datastore example improvements
Tim Burks 8 years ago
parent
commit
119676f712

+ 1 - 0
Examples/Datastore/PackageManager/Makefile

@@ -6,3 +6,4 @@ all:
 clean :
 	rm -rf Packages googleapis .build
 	rm -f Package.pins Datastore Sources/*.pb.swift Sources/swiftgrpc.log google.json
+	rm -rf Package.resolved

+ 1 - 0
Examples/Datastore/PackageManager/Package.swift

@@ -20,5 +20,6 @@ let package = Package (
         .Package(url: "https://github.com/grpc/grpc-swift.git", Version(0,2,4)),
         .Package(url: "https://github.com/apple/swift-protobuf.git", Version(1,0,2)),
         .Package(url: "https://github.com/google/auth-library-swift.git", Version(0,3,4)),
+	.Package(url: "https://github.com/kylef/Commander.git", Version(0,8,0)),
     ]
 )

+ 6 - 12
Examples/Datastore/PackageManager/README.md

@@ -11,20 +11,14 @@ 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.
 
-Calls require a Google project ID and an OAuth token.
+Calls require a Google project ID and service account credentials.
 
 To create a project ID, visit the 
 [Google Cloud Console](https://cloud.google.com/console).
-Then edit [Sources/main.swift](Sources/main.swift) to 
-replace "YOUR PROJECT ID" with your project ID.
 
-OAuth tokens are obtained using Google's 
+Service account support is provided by Google's 
 [Auth Library for Swift](https://github.com/google/auth-library-swift).
-On OS X, this package uses a locally-installed browser and
-a temporary web server to take a user through an OAuth signin flow.
-On Linux, it gets an OAuth token from the
-[Instance Metadata Service](https://cloud.google.com/compute/docs/storing-retrieving-metadata)
-that is available in Google Cloud instances, such as 
-[Google Compute Engine](https://cloud.google.com/compute/)
-or 
-[Google Cloud Shell](https://cloud.google.com/shell/docs/).
+After enabling the Cloud Natural Language API for your project,
+create and download service account credentials. Then set the
+GOOGLE_APPLICATION_CREDENTIALS environment variable to point to 
+the file containing these credentials.

+ 143 - 37
Examples/Datastore/PackageManager/Sources/main.swift

@@ -16,55 +16,161 @@
 import Foundation
 import gRPC
 import OAuth2
+import Commander
 
+let projectID = "your-project-identifier"
 let scopes = ["https://www.googleapis.com/auth/datastore"]
 
-if let provider = DefaultTokenProvider(scopes:scopes) {
-  let sem = DispatchSemaphore(value: 0)
-  try provider.withToken() {(token, error) -> Void in
-    if let token = token {
-      gRPC.initialize()
-
-      guard let authToken = token.AccessToken else {
-        print("ERROR: No OAuth token is available.")
-        exit(-1)
-      }
-
-      let projectID = "your-project-identifier"
+struct Thing : Codable {
+  var name: String
+  var number: Int
+}
 
-      let certificateURL = URL(fileURLWithPath:"roots.pem")
-      let certificates = try! String(contentsOf: certificateURL, encoding: .utf8)
-      let service = Google_Datastore_V1_DatastoreService(address:"datastore.googleapis.com",
-                                                         certificates:certificates,
-                                                         host:nil)
+// Convert Encodable objects to dictionaries of property-value pairs.
+class PropertiesEncoder {
+  static func encode<T : Encodable>(_ value : T) throws -> [String:Any]? {
+    let plist = try PropertyListEncoder().encode(value)
+    let properties = try PropertyListSerialization.propertyList(from:plist, options:[], format:nil)
+    return properties as? [String:Any]
+  }
+}
 
-      service.metadata = Metadata(["authorization":"Bearer " + authToken])
+// Create Decodable objects from dictionaries of property-value pairs.
+class PropertiesDecoder {
+  static func decode<T: Decodable>(_ type: T.Type, from: [String:Any]) throws -> T {
+    let plist = try PropertyListSerialization.data(fromPropertyList: from,
+                                                   format: .binary, options:0)
+    return try PropertyListDecoder().decode(type, from: plist)
+  }
+}
 
-      var request = Google_Datastore_V1_RunQueryRequest()
-      request.projectID = projectID
+func prepareService() throws -> Google_Datastore_V1_DatastoreService? {
+  // Get an OAuth token
+  var authToken : String!
+  if let provider = DefaultTokenProvider(scopes:scopes) {
+    let sem = DispatchSemaphore(value: 0)
+    try provider.withToken() {(token, error) -> Void in
+      if let token = token {
+        authToken = token.AccessToken
+      }
+      sem.signal()
+    }
+    sem.wait()
+  }
+  if authToken == nil {
+    print("ERROR: No OAuth token is available. Did you set GOOGLE_APPLICATION_CREDENTIALS?")
+    exit(-1)
+  }
+  // Initialize gRPC service
+  gRPC.initialize()
+  let certificateURL = URL(fileURLWithPath:"/roots.pem")
+  let certificates = try! String(contentsOf: certificateURL, encoding: .utf8)
+  let service = Google_Datastore_V1_DatastoreService(address:"datastore.googleapis.com",
+                                                     certificates:certificates,
+                                                     host:nil)
+  service.metadata = Metadata(["authorization":"Bearer " + authToken])
+  return service
+}
 
-      var query = Google_Datastore_V1_GqlQuery()
-      query.queryString = "select *"
+func performList(service: Google_Datastore_V1_DatastoreService) throws {
+  var request = Google_Datastore_V1_RunQueryRequest()
+  request.projectID = projectID
+  var query = Google_Datastore_V1_GqlQuery()
+  query.queryString = "select * from Thing"
+  request.gqlQuery = query
+  let result = try service.runquery(request)
+  var entities : [Int64 : Thing] = [:]
+  for entityResult in result.batch.entityResults {
+    var properties : [String:Any] = [:]
+    for property in entityResult.entity.properties {
+      let key = property.key
+      switch property.value.valueType! {
+      case .integerValue(let v):
+        properties[key] = v
+      case .stringValue(let v):
+        properties[key] = v
+      default:
+        print("?")
+      }
+    }
+    let entity = try PropertiesDecoder.decode(Thing.self, from:properties)
+    entities[entityResult.entity.key.path[0].id] = entity
+  }
+  print("\(entities)")
+}
 
-      request.gqlQuery = query
+func performInsert(service: Google_Datastore_V1_DatastoreService,
+               number: Int) throws {
+  var request = Google_Datastore_V1_CommitRequest()
+  request.projectID = projectID
+  request.mode = .nonTransactional
+  var pathElement = Google_Datastore_V1_Key.PathElement()
+  pathElement.kind = "Thing"
+  var key = Google_Datastore_V1_Key()
+  key.path = [pathElement]
+  var entity = Google_Datastore_V1_Entity()
+  entity.key = key
+  let thing = Thing(name:"Thing", number:number)
+  let properties = try PropertiesEncoder.encode(thing)!
+  for (k,v) in properties {
+    var value = Google_Datastore_V1_Value()
+    switch v {
+    case let v as String:
+      value.stringValue = v
+    case let v as Int:
+      value.integerValue = Int64(v)
+    default:
+      break
+    }
+    entity.properties[k] = value
+  }
+  var mutation = Google_Datastore_V1_Mutation()
+  mutation.insert = entity
+  request.mutations.append(mutation)
+  let result = try service.commit(request)
+  for mutationResult in result.mutationResults {
+    print("\(mutationResult)")
+  }
+}
 
-      print("\(request)")
+func performDelete(service: Google_Datastore_V1_DatastoreService,
+               kind: String,
+               id: Int64) throws {
+  var request = Google_Datastore_V1_CommitRequest()
+  request.projectID = projectID
+  request.mode = .nonTransactional
+  var pathElement = Google_Datastore_V1_Key.PathElement()
+  pathElement.kind = kind
+  pathElement.id = id
+  var key = Google_Datastore_V1_Key()
+  key.path = [pathElement]
+  var mutation = Google_Datastore_V1_Mutation()
+  mutation.delete = key
+  request.mutations.append(mutation)
+  let result = try service.commit(request)
+  for mutationResult in result.mutationResults {
+    print("\(mutationResult)")
+  }
+}
 
-      do {
-        let result = try service.runquery(request)
-        print("\(result)")
-      } catch (let error) {
-        print("ERROR: \(error)")
-      }
+Group {
+  $0.command("insert") { (number:Int) in
+    if let service = try prepareService() {
+      try performInsert(service:service, number:number)
+    }
+  }
 
+  $0.command("delete") { (id:Int) in
+    if let service = try prepareService() {
+      try performDelete(service:service, kind:"Thing", id:Int64(id))
     }
-    if let error = error {
-      print("ERROR \(error)")
+  }
+  
+  $0.command("list") {
+    if let service = try prepareService() {
+      try performList(service:service)
     }
-    sem.signal()
   }
-  _ = sem.wait(timeout: DispatchTime.distantFuture)
-} else {
-  print("Unable to create default token provider.")
-}
+  
+  }.run()
 

+ 1 - 0
Examples/NaturalLanguage/PackageManager/Makefile

@@ -6,3 +6,4 @@ all:
 clean :
 	rm -rf Packages googleapis .build
 	rm -f Package.pins NaturalLanguage Sources/*.pb.swift Sources/swiftgrpc.log google.json
+	rm -rf Package.resolved

+ 1 - 0
Plugin/Makefile

@@ -22,3 +22,4 @@ clear :
 
 clean : clear
 	rm -rf protoc-gen-swiftgrpc Packages .build protoc-gen-swift Package.pins
+	rm -rf Package.resolved