├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── stale.yml ├── .gitignore ├── CODEOWNERS ├── LICENSE ├── README.md ├── docker-compose.yml ├── docs ├── HHA-1606-rfeng.pptx ├── Polyglot Microservices.pptx ├── demo.png ├── docker-compose.png └── k8s.png ├── grpc-swagger ├── index.js ├── lib │ ├── proto2swagger.js │ └── swagger2proto.js ├── package.json ├── proto │ └── google │ │ └── protobuf │ │ └── any.proto └── test │ ├── note.proto │ ├── note.yaml │ ├── proto2swagger.test.js │ └── swagger2proto.test.js ├── kubernetes ├── create-k8s.sh ├── deploy.sh ├── note-java-deployment.yaml ├── note-java-service.yaml ├── note-loopback-deployment.yaml ├── note-loopback-service.yaml ├── note-mongo-deployment.yaml ├── note-mongo-service.yaml ├── note-swift-deployment.yaml ├── note-swift-service.yaml ├── zipkin-deployment.yaml └── zipkin-service.yaml ├── note-java ├── .dockerignore ├── .gitignore ├── Dockerfile ├── Dockerfile.ibmjdk ├── client.sh ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── ibm │ │ └── apiconnect │ │ └── demo │ │ └── polyglot │ │ ├── BraveUtil.java │ │ ├── JWEUtil.java │ │ ├── NoteClient.java │ │ └── NoteServer.java │ ├── proto │ └── note.proto │ └── resources │ ├── generate-grpc-keys.sh │ ├── generate-jwe-keys.sh │ ├── grpc.crt │ ├── grpc.key │ ├── grpc.srl │ ├── note-java.crt │ ├── note-java.csr │ ├── note-java.key │ ├── note-java.pfx │ ├── private_key.der │ ├── private_key.pem │ └── public_key.der ├── note-loopback ├── .apiconnect │ └── config ├── .dockerignore ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .yo-rc.json ├── Dockerfile ├── bin │ ├── generate-grpc-keys.sh │ └── grpc.crt ├── client.js ├── client │ └── README.md ├── common │ └── models │ │ ├── encryption-client.js │ │ ├── encryption-client.json │ │ ├── note.js │ │ ├── note.json │ │ ├── translation-client.js │ │ └── translation-client.json ├── lib │ └── zipkin-agent.js ├── package.json ├── proto │ ├── encryption.proto │ ├── note-model.proto │ ├── note.proto │ └── translation.proto └── server │ ├── boot │ ├── authentication.js │ ├── grpc-server.js │ └── root.js │ ├── component-config.json │ ├── config.json │ ├── datasources.json │ ├── middleware.development.json │ ├── middleware.json │ ├── middleware │ └── zipkin.js │ ├── model-config.json │ └── server.js └── note-swift ├── .dockerignore ├── .gitignore ├── Dockerfile ├── Makefile ├── Package.swift ├── Sources ├── NoteProvider.swift ├── main.swift ├── note.client.pb.swift ├── note.pb.swift └── note.server.pb.swift ├── codegen.sh ├── grpc.crt ├── grpc.key └── note.proto /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 9 | 10 | # Description/Steps to reproduce 11 | 12 | 16 | 17 | # Link to reproduction sandbox 18 | 19 | 24 | 25 | # Expected result 26 | 27 | 30 | 31 | # Additional information 32 | 33 | 38 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Description 2 | 3 | 4 | #### Related issues 5 | 6 | 12 | 13 | - connect to 14 | 15 | ### Checklist 16 | 17 | 22 | 23 | - [ ] New tests added or existing tests modified to cover all changes 24 | - [ ] Code conforms with the [style 25 | guide](http://loopback.io/doc/en/contrib/style-guide.html) 26 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 14 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | - critical 10 | - p1 11 | - major 12 | # Label to use when marking an issue as stale 13 | staleLabel: stale 14 | # Comment to post when marking an issue as stale. Set to `false` to disable 15 | markComment: > 16 | This issue has been automatically marked as stale because it has not had 17 | recent activity. It will be closed if no further activity occurs. Thank you 18 | for your contributions. 19 | # Comment to post when closing a stale issue. Set to `false` to disable 20 | closeComment: > 21 | This issue has been closed due to continued inactivity. Thank you for your understanding. 22 | If you believe this to be in error, please contact one of the code owners, 23 | listed in the `CODEOWNERS` file at the top-level of this repository. 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | .classpath 12 | .project 13 | target 14 | 15 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 16 | hs_err_pid* 17 | 18 | node_modules 19 | .idea 20 | 21 | 22 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Lines starting with '#' are comments. 2 | # Each line is a file pattern followed by one or more owners, 3 | # the last matching pattern has the most precedence. 4 | 5 | # Core team members from IBM 6 | * @raymondfeng 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) IBM Corp. 2017. All Rights Reserved. 2 | Node module: loopback-example-polyglot 3 | This project is licensed under the MIT License, full text below. 4 | 5 | -------- 6 | 7 | MIT license 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # loopback-example-polyglot 2 | 3 | **⚠️ This LoopBack 3 example project is no longer maintained. Please refer to [LoopBack 4 Examples](https://loopback.io/doc/en/lb4/Examples.html) instead. ⚠️** 4 | 5 | PoC project to illustrate how to create polyglot APIs and Microservices 6 | using [LoopBack](http://loopback.io) and [gRPC](http://grpc.io). 7 | 8 | # Modules 9 | - Apps 10 | - note-java: Java implementation of note encryption 11 | - note-swift: Swift implementation of note translation 12 | - note-loopback: Sample LoopBack application to demonstrate gRPC integration 13 | - Tools 14 | - grpc-swagger: Generate swagger spec from gRPC proto document and vice versa 15 | - [loopback-connector-grpc](https://github.com/strongloop/loopback-connector-grpc): LoopBack connector for gRPC services 16 | 17 | ![Demo](docs/demo.png) 18 | 19 | # Docker Containerization 20 | - note-loopback (Node.js) 21 | - note-java (Java) 22 | - note-swift (Swift) 23 | - note-mongo (MongoDB) 24 | - openzipkin/zipkin ([zipkin](http://zipkin.io/)) 25 | 26 | ![DockerCompose](docs/docker-compose.png) 27 | 28 | # Running with docker-compose 29 | 30 | ``` 31 | $ docker-compose up --build 32 | ``` 33 | 34 | Open your browser and point to http://localhost:3000/explorer to test drive: 35 | 36 | 1. Create a new note with `POST /notes`. 37 | 2. The `note-loopback` microservice will request the `note-swift` microservice to translate the content (mockup). 38 | 3. The `note-loopback` microservice will request the `note-java` microservice to encrypt the content using JWE. 39 | 4. Tracing metrics are sent to zipkin server. The dashboard is available at http://localhost:9411. 40 | 5. You can also run clients from the host, for example: 41 | ``` 42 | $ cd note-loopback 43 | $ node client.js 44 | 45 | or 46 | 47 | $ cd note-java 48 | $ java -cp ./target/note-1.0.0.jar com.ibm.apiconnect.demo.polyglot.NoteClient 49 | ``` 50 | 51 | # Running as local Kubernetes (minikube) 52 | 53 | Please follow instructions at https://github.com/kubernetes/minikube to install `minikube` and `kubectl` commands. 54 | 55 | ``` 56 | cd kubernetes 57 | ./create-k8s.sh 58 | ``` 59 | 60 | ![k8s](docs/k8s.png) 61 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | note-loopback: 4 | build: ./note-loopback 5 | image: raymondfeng/note-loopback 6 | ports: 7 | - "50051:50051" 8 | - "3000:3000" 9 | links: 10 | - note-java 11 | - note-mongo 12 | - zipkin 13 | networks: 14 | app-net: 15 | aliases: 16 | - note-loopback 17 | note-java: 18 | build: ./note-java 19 | image: raymondfeng/note-java 20 | ports: 21 | - "50052:50052" 22 | links: 23 | - zipkin 24 | networks: 25 | app-net: 26 | aliases: 27 | - note-java 28 | note-swift: 29 | build: ./note-swift 30 | image: raymondfeng/note-swift 31 | ports: 32 | - "50053:50053" 33 | links: 34 | - zipkin 35 | networks: 36 | app-net: 37 | aliases: 38 | - note-swift 39 | zipkin: 40 | image: openzipkin/zipkin 41 | ports: 42 | - "9411:9411" 43 | networks: 44 | app-net: 45 | aliases: 46 | - zipkin 47 | note-mongo: 48 | image: mongo:latest 49 | volumes: 50 | - mongo-data-volume:/data/db 51 | ports: 52 | - "27017:27017" 53 | networks: 54 | app-net: 55 | aliases: 56 | - note-mongo 57 | networks: 58 | app-net: 59 | driver: bridge 60 | volumes: 61 | mongo-data-volume: 62 | -------------------------------------------------------------------------------- /docs/HHA-1606-rfeng.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strongloop/loopback-example-polyglot/677edd9cc9fae067ec1be180c34b8659e5c000b6/docs/HHA-1606-rfeng.pptx -------------------------------------------------------------------------------- /docs/Polyglot Microservices.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strongloop/loopback-example-polyglot/677edd9cc9fae067ec1be180c34b8659e5c000b6/docs/Polyglot Microservices.pptx -------------------------------------------------------------------------------- /docs/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strongloop/loopback-example-polyglot/677edd9cc9fae067ec1be180c34b8659e5c000b6/docs/demo.png -------------------------------------------------------------------------------- /docs/docker-compose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strongloop/loopback-example-polyglot/677edd9cc9fae067ec1be180c34b8659e5c000b6/docs/docker-compose.png -------------------------------------------------------------------------------- /docs/k8s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strongloop/loopback-example-polyglot/677edd9cc9fae067ec1be180c34b8659e5c000b6/docs/k8s.png -------------------------------------------------------------------------------- /grpc-swagger/index.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016. All Rights Reserved. 2 | // Node module: grpc-swagger 3 | 4 | exports.proto2swagger = require('./lib/proto2swagger'); 5 | exports.swagger2proto = require('./lib/swagger2proto'); 6 | 7 | -------------------------------------------------------------------------------- /grpc-swagger/lib/proto2swagger.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: grpc-swagger 3 | 4 | var ProtoBuf = require("protobufjs"); 5 | var proto2json = require('protobufjs/cli/targets/json'); 6 | var debug = require('debug')('grpc:swagger'); 7 | var yaml = require('js-yaml'); 8 | 9 | function traverseTypes(current, metadata) { 10 | metadata.services = metadata.services || []; 11 | metadata.messages = metadata.messages || []; 12 | if (current instanceof ProtoBuf.Type) { 13 | metadata.messages.push(current); 14 | } 15 | 16 | if (current instanceof ProtoBuf.Service) { 17 | metadata.services.push(current); 18 | } 19 | if (current.nestedArray) 20 | current.nestedArray.forEach(function(nested) { 21 | traverseTypes(nested, metadata); 22 | }); 23 | } 24 | 25 | // https://developers.google.com/protocol-buffers/docs/proto3#json 26 | function parseProto(protoFile, cb) { 27 | ProtoBuf.load(protoFile, function(err, root) { 28 | if (err) return cb(err); 29 | var metadata = {}; 30 | traverseTypes(root, metadata); 31 | cb(null, metadata); 32 | }); 33 | } 34 | 35 | function proto2swagger(protoFile, format, cb) { 36 | parseProto(protoFile, function(err, json) { 37 | if (err) return cb(err); 38 | var paths = {}; 39 | json.services.forEach(function(service) { 40 | for (var m in service.methods) { 41 | var path = '/' + m; 42 | var op = service.methods[m]; 43 | paths[path] = { 44 | post: { 45 | tags: [service.name], 46 | summary: '', 47 | operationId: service.name + '.' + m, 48 | parameters: [ 49 | { 50 | name: 'request', 51 | in: 'body', 52 | description: '', 53 | required: false, 54 | schema: { 55 | $ref: '#/definitions/' + service.methods[m].requestType 56 | } 57 | } 58 | ], 59 | responses: { 60 | '200': { 61 | description: '', 62 | schema: { 63 | $ref: '#/definitions/' + service.methods[m].responseType 64 | } 65 | } 66 | } 67 | } 68 | }; 69 | } 70 | }); 71 | 72 | var definitions = {}; 73 | json.messages.forEach(function(message) { 74 | var properties = {}; 75 | definitions[message.name] = { 76 | properties: properties 77 | }; 78 | for (var f in message.fields) { 79 | let field = message.fields[f]; 80 | let type = field.type; 81 | if (/int|fixed|double|float/.test(field.type)) { 82 | type = 'number'; 83 | } 84 | if (field.type === 'bool') { 85 | type = 'boolean'; 86 | } 87 | if (field.type === 'bytes' || field.type === 'enum') { 88 | type = 'string'; 89 | } 90 | 91 | if (field.repeated) { 92 | type = [type]; 93 | } 94 | properties[field.name] = { 95 | type: type, 96 | format: field.type, 97 | required: field.field !== 'optional' 98 | } 99 | } 100 | }); 101 | 102 | var swagger = { 103 | version: '2.0', 104 | info: { 105 | version: '1.0.0', 106 | title: '', 107 | schemes: ['http', 'https'], 108 | host: '', 109 | basePath: '/api' 110 | }, 111 | consumes: ['application/json'], 112 | produces: ['application/json'], 113 | paths: paths, 114 | definitions: definitions 115 | }; 116 | 117 | debug('swagger.json: %j', swagger); 118 | 119 | if (format === 'yaml') { 120 | var spec = yaml.safeDump(swagger); 121 | debug('swagger.yaml: %s', spec); 122 | return cb(null, spec); 123 | } else { 124 | return cb(null, swagger); 125 | } 126 | }); 127 | } 128 | 129 | module.exports = proto2swagger; 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /grpc-swagger/lib/swagger2proto.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: grpc-swagger 3 | 4 | var swaggerParser = require('swagger-parser'); 5 | var debug = require('debug')('grpc:swagger'); 6 | var ProtoBuf = require('protobufjs'); 7 | var toProto = require('protobufjs/cli/targets/proto'); 8 | var path = require('path'); 9 | 10 | var OPTION_HTTP = '(loopback.http)'; 11 | 12 | /** 13 | * Describe a property type & rule 14 | * @param {object} p Property descriptor 15 | * @returns {{type: string, rule: string}} 16 | */ 17 | function describeProperty(p) { 18 | var type = p.type; 19 | var rule = 'optional'; 20 | if (p.schema) { 21 | var ref = p.schema.$ref; 22 | if (p.schema.type === 'array') { 23 | ref = p.schema.items.$ref; 24 | rule = 'repeated'; 25 | } else if (p.schema.type) { 26 | type = p.schema.type; 27 | } 28 | if (ref) { 29 | var parts = ref.split('/'); 30 | type = parts[parts.length - 1]; 31 | } 32 | } 33 | if (type === 'number') { 34 | type = p.format || 'float'; 35 | } else if (type === 'boolean') { 36 | type = 'bool'; 37 | } 38 | if (type === 'object' || type === 'x-any') { 39 | type = 'google.protobuf.Any'; 40 | } 41 | return {type: type, rule: rule}; 42 | } 43 | 44 | /** 45 | * Map Swagger model definitions into proto messages 46 | * @param {object} swagger Swagger spec 47 | * @param {object} protoObj JSON representation of proto 48 | * @returns {*} 49 | */ 50 | function mapDefinitions(swagger, protoObj) { 51 | for (var d in swagger.definitions) { 52 | if (d === 'x-any') continue; 53 | var type = new ProtoBuf.Type(d); 54 | 55 | var def = swagger.definitions[d]; 56 | var index = 0; 57 | for (var p in def.properties) { 58 | var prop = def.properties[p]; 59 | var desc = describeProperty(prop); 60 | var field = { 61 | name: p, 62 | type: desc.type, 63 | rule: desc.rule, 64 | id: ++index 65 | }; 66 | 67 | type.add(new ProtoBuf.Field.fromJSON(p, field)); 68 | } 69 | protoObj.add(type); 70 | } 71 | return prop; 72 | } 73 | 74 | /** 75 | * Map request & response to proto messages 76 | * @param {object} op Swagger operation object 77 | * @param {string} reqMsgName Request message name 78 | * @param {string} resMsgName Response message name 79 | * @param {object} protoObj JSON representation of proto 80 | */ 81 | function mapReqRes(op, reqMsgName, resMsgName, protoObj) { 82 | var reqType = new ProtoBuf.Type(reqMsgName); 83 | var id = 0; 84 | op.parameters.forEach(function(p) { 85 | var prop = describeProperty(p); 86 | var type = prop.type; 87 | var rule = prop.rule; 88 | var param = { 89 | name: p.name, 90 | id: ++id, 91 | rule: rule, 92 | type: type 93 | }; 94 | reqType.add(ProtoBuf.Field.fromJSON(p.name, param)); 95 | }); 96 | protoObj.add(reqType); 97 | 98 | var resType = new ProtoBuf.Type(resMsgName); 99 | var codes = Object.keys(op.responses); 100 | if (codes.length === 1) { 101 | // Single response mapped to a fixed message 102 | var prop = describeProperty(op.responses[codes[0]]); 103 | // Skip responses that don't have a type (void) 104 | if (prop.type) { 105 | let field = { 106 | name: 'response_' + codes[0], 107 | id: 1, 108 | type: prop.type, 109 | rule: prop.rule 110 | }; 111 | resType.add(ProtoBuf.Field.fromJSON(field.name, field)); 112 | } 113 | } else { 114 | // Multiple responses mapped to oneof (union) 115 | id = 0; 116 | let responsesUnion = ProtoBuf.OneOf.fromJSON('responses', {}); 117 | resType.add(responsesUnion); 118 | for (var c in op.responses) { 119 | prop = describeProperty(op.responses[c]); 120 | if (prop.type) { 121 | responsesUnion.add(ProtoBuf.Field.fromJSON('response_' + c, { 122 | name: 'response_' + c, 123 | id: ++id, 124 | type: prop.type, 125 | rule: prop.rule 126 | })); 127 | } 128 | } 129 | } 130 | protoObj.add(resType); 131 | } 132 | 133 | /** 134 | * Map a swagger operation to proto service rpc method 135 | * @param {object} op Swagger operation object 136 | * @param {object} protoObj JSON representation of proto 137 | */ 138 | function mapOperation(path, verb, op, protoObj) { 139 | var opId = op.operationId; 140 | var reqMsgName = (opId + 'Request').replace(/[\.\{\}]/g, '_'); 141 | var resMsgName = (opId + 'Response').replace(/[\.\{\}]/g, '_'); 142 | 143 | var services = []; 144 | if (Array.isArray(op.tags) && op.tags.length > 0) { 145 | services = op.tags; 146 | } else { 147 | services = ['Rest'] 148 | } 149 | 150 | services.forEach(function(s) { 151 | var serviceName = s + 'Service'; 152 | var service; 153 | // Match existing services 154 | if (protoObj.nestedArray) { 155 | for (var i = 0, n = protoObj.nestedArray.length; i < n; i++) { 156 | let item = protoObj.nestedArray[i]; 157 | if ((item instanceof ProtoBuf.Service) && item.name === serviceName) { 158 | service = item; 159 | break; 160 | } 161 | } 162 | } 163 | if (!service) { 164 | // Create a new one and add it to proto 165 | service = ProtoBuf.Service.fromJSON(serviceName, { 166 | name: serviceName, 167 | options: {} 168 | }); 169 | protoObj.add(service); 170 | } 171 | 172 | if (opId.indexOf(s + '.') === 0) { 173 | opId = opId.substring((s + '.').length); 174 | } 175 | var opName = opId.replace(/[\.\{\}]/g, '_'); 176 | var options = {}; 177 | 178 | options[OPTION_HTTP + '.' + verb] = path; 179 | // FIXME: protobuf doesn't allow object options 180 | // See https://github.com/googleapis/googleapis/blob/master/google/api/http.proto#L168-L171 181 | service.add(ProtoBuf.Method.fromJSON(opName, { 182 | requestType: reqMsgName, 183 | responseType: resMsgName, 184 | options: options 185 | })); 186 | mapReqRes(op, reqMsgName, resMsgName, protoObj); 187 | }); 188 | } 189 | 190 | function swagger2proto(swaggerFile, cb) { 191 | 192 | swaggerParser.bundle(swaggerFile, {}, function(err, swagger) { 193 | if (err) return cb(err); 194 | var root = ProtoBuf.Root.fromJSON({ 195 | syntax: 'proto3', 196 | package: null, 197 | options: {} 198 | }); 199 | for (let t in ProtoBuf.common) { 200 | root.addJSON(ProtoBuf.common[t].nested); 201 | } 202 | var prop = mapDefinitions(swagger, root); 203 | var services = {}; 204 | 205 | for (var p in swagger.paths) { 206 | var apiPath = swagger.paths[p]; 207 | for (var v in apiPath) { 208 | var op = apiPath[v]; 209 | mapOperation(p, v, op, root); 210 | } 211 | } 212 | toProto(root, {}, cb); 213 | }); 214 | } 215 | 216 | module.exports = swagger2proto; 217 | -------------------------------------------------------------------------------- /grpc-swagger/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grpc-swagger", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha test/*.js" 8 | }, 9 | "author": "Raymond Feng", 10 | "license": "MIT", 11 | "dependencies": { 12 | "debug": "^2.6.8", 13 | "js-yaml": "^3.8.4", 14 | "protobufjs": "^6.8.0", 15 | "swagger-parser": "^3.4.1" 16 | }, 17 | "devDependencies": { 18 | "chai": "^4.0.2", 19 | "mocha": "^3.4.2" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /grpc-swagger/proto/google/protobuf/any.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | syntax = "proto3"; 32 | 33 | package google.protobuf; 34 | 35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 36 | option go_package = "github.com/golang/protobuf/ptypes/any"; 37 | option java_package = "com.google.protobuf"; 38 | option java_outer_classname = "AnyProto"; 39 | option java_multiple_files = true; 40 | option java_generate_equals_and_hash = true; 41 | option objc_class_prefix = "GPB"; 42 | 43 | // `Any` contains an arbitrary serialized protocol buffer message along with a 44 | // URL that describes the type of the serialized message. 45 | // 46 | // Protobuf library provides support to pack/unpack Any values in the form 47 | // of utility functions or additional generated methods of the Any type. 48 | // 49 | // Example 1: Pack and unpack a message in C++. 50 | // 51 | // Foo foo = ...; 52 | // Any any; 53 | // any.PackFrom(foo); 54 | // ... 55 | // if (any.UnpackTo(&foo)) { 56 | // ... 57 | // } 58 | // 59 | // Example 2: Pack and unpack a message in Java. 60 | // 61 | // Foo foo = ...; 62 | // Any any = Any.pack(foo); 63 | // ... 64 | // if (any.is(Foo.class)) { 65 | // foo = any.unpack(Foo.class); 66 | // } 67 | // 68 | // Example 3: Pack and unpack a message in Python. 69 | // 70 | // foo = Foo(...) 71 | // any = Any() 72 | // any.Pack(foo) 73 | // ... 74 | // if any.Is(Foo.DESCRIPTOR): 75 | // any.Unpack(foo) 76 | // ... 77 | // 78 | // The pack methods provided by protobuf library will by default use 79 | // 'type.googleapis.com/full.type.name' as the type URL and the unpack 80 | // methods only use the fully qualified type name after the last '/' 81 | // in the type URL, for example "foo.bar.com/x/y.z" will yield type 82 | // name "y.z". 83 | // 84 | // 85 | // JSON 86 | // ==== 87 | // The JSON representation of an `Any` value uses the regular 88 | // representation of the deserialized, embedded message, with an 89 | // additional field `@type` which contains the type URL. Example: 90 | // 91 | // package google.profile; 92 | // message Person { 93 | // string first_name = 1; 94 | // string last_name = 2; 95 | // } 96 | // 97 | // { 98 | // "@type": "type.googleapis.com/google.profile.Person", 99 | // "firstName": , 100 | // "lastName": 101 | // } 102 | // 103 | // If the embedded message type is well-known and has a custom JSON 104 | // representation, that representation will be embedded adding a field 105 | // `value` which holds the custom JSON in addition to the `@type` 106 | // field. Example (for message [google.protobuf.Duration][]): 107 | // 108 | // { 109 | // "@type": "type.googleapis.com/google.protobuf.Duration", 110 | // "value": "1.212s" 111 | // } 112 | // 113 | message Any { 114 | // A URL/resource name whose content describes the type of the 115 | // serialized protocol buffer message. 116 | // 117 | // For URLs which use the scheme `http`, `https`, or no scheme, the 118 | // following restrictions and interpretations apply: 119 | // 120 | // * If no scheme is provided, `https` is assumed. 121 | // * The last segment of the URL's path must represent the fully 122 | // qualified name of the type (as in `path/google.protobuf.Duration`). 123 | // The name should be in a canonical form (e.g., leading "." is 124 | // not accepted). 125 | // * An HTTP GET on the URL must yield a [google.protobuf.Type][] 126 | // value in binary format, or produce an error. 127 | // * Applications are allowed to cache lookup results based on the 128 | // URL, or have them precompiled into a binary to avoid any 129 | // lookup. Therefore, binary compatibility needs to be preserved 130 | // on changes to types. (Use versioned type names to manage 131 | // breaking changes.) 132 | // 133 | // Schemes other than `http`, `https` (or the empty scheme) might be 134 | // used with implementation specific semantics. 135 | // 136 | string type_url = 1; 137 | 138 | // Must be a valid serialized protocol buffer of the above specified type. 139 | bytes value = 2; 140 | } 141 | -------------------------------------------------------------------------------- /grpc-swagger/test/note.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option java_multiple_files = true; 4 | option java_package = "com.ibm.apiconnect.demo.polyglot"; 5 | option java_outer_classname = "NoteMessage"; 6 | 7 | // See extensions at https://github.com/googleapis/googleapis/tree/master/google/api 8 | 9 | /* 10 | import "google/protobuf/any.proto"; 11 | message Filter { 12 | google.protobuf.Any where = 1; 13 | int32 limit = 2; 14 | int32 offset = 3; 15 | repeated string order = 4; 16 | google.protobuf.Any fields = 5; 17 | google.protobuf.Any include = 6; 18 | }; 19 | */ 20 | 21 | message Note { 22 | option(loopback.model) = { 23 | name: "Note" 24 | }; 25 | int32 id = 1; 26 | string title = 2; 27 | string content = 3; 28 | }; 29 | 30 | message FindByIdRequest { 31 | int32 id = 1; 32 | }; 33 | 34 | message FindRequest { 35 | // Filter filter = 1; 36 | }; 37 | 38 | message FindResponse { 39 | repeated Note notes = 1; 40 | }; 41 | 42 | service NoteService { 43 | rpc create (Note) returns (Note) { 44 | option (loopback.http) = { 45 | post: "/notes" 46 | body: "*" 47 | }; 48 | }; 49 | rpc findById (FindByIdRequest) returns (Note) { 50 | option (loopback.http) = { 51 | get: "/notes/{id}" 52 | }; 53 | }; 54 | rpc find (FindRequest) returns (FindResponse) { 55 | option (loopback.http) = { 56 | get: "/notes" 57 | }; 58 | }; 59 | option (loopback.http) = { 60 | description: "Note Service" 61 | }; 62 | }; 63 | 64 | -------------------------------------------------------------------------------- /grpc-swagger/test/note.yaml: -------------------------------------------------------------------------------- 1 | swagger: '2.0' 2 | 3 | info: 4 | x-ibm-name: note 5 | version: 1.0.0 6 | title: note 7 | 8 | schemes: 9 | - https 10 | host: $(catalog.host) 11 | basePath: /api 12 | 13 | consumes: 14 | - application/json 15 | produces: 16 | - application/json 17 | 18 | securityDefinitions: 19 | clientIdHeader: 20 | type: apiKey 21 | in: header 22 | name: X-IBM-Client-Id 23 | clientSecretHeader: 24 | in: "header" 25 | name: "X-IBM-Client-Secret" 26 | type: "apiKey" 27 | 28 | 29 | security: 30 | - 31 | clientIdHeader: [] 32 | clientSecretHeader: [] 33 | 34 | x-ibm-configuration: 35 | testable: true 36 | enforced: true 37 | cors: 38 | enabled: true 39 | catalogs: 40 | apic-dev: 41 | properties: 42 | runtime-url: $(TARGET_URL) 43 | sb: 44 | properties: 45 | runtime-url: 'http://localhost:4001' 46 | assembly: 47 | execute: 48 | - invoke: 49 | target-url: $(runtime-url)$(request.path)$(request.search) 50 | 51 | paths: 52 | '/Users/{id}/accessTokens/{fk}': 53 | get: 54 | tags: 55 | - User 56 | summary: Find a related item by id for accessTokens. 57 | operationId: User.prototype.__findById__accessTokens 58 | parameters: 59 | - name: fk 60 | in: path 61 | description: Foreign key for accessTokens 62 | required: true 63 | type: string 64 | format: JSON 65 | - name: id 66 | in: path 67 | description: User id 68 | required: true 69 | type: string 70 | format: JSON 71 | responses: 72 | '200': 73 | description: Request was successful 74 | schema: 75 | $ref: '#/definitions/AccessToken' 76 | '400': 77 | schema: 78 | $ref: '#/definitions/AccessToken' 79 | deprecated: false 80 | delete: 81 | tags: 82 | - User 83 | summary: Delete a related item by id for accessTokens. 84 | operationId: User.prototype.__destroyById__accessTokens 85 | parameters: 86 | - name: fk 87 | in: path 88 | description: Foreign key for accessTokens 89 | required: true 90 | type: string 91 | format: JSON 92 | - name: id 93 | in: path 94 | description: User id 95 | required: true 96 | type: string 97 | format: JSON 98 | responses: 99 | '204': 100 | description: Request was successful 101 | deprecated: false 102 | put: 103 | tags: 104 | - User 105 | summary: Update a related item by id for accessTokens. 106 | operationId: User.prototype.__updateById__accessTokens 107 | parameters: 108 | - name: fk 109 | in: path 110 | description: Foreign key for accessTokens 111 | required: true 112 | type: string 113 | format: JSON 114 | - name: data 115 | in: body 116 | required: false 117 | schema: 118 | $ref: '#/definitions/AccessToken' 119 | - name: id 120 | in: path 121 | description: User id 122 | required: true 123 | type: string 124 | format: JSON 125 | responses: 126 | '200': 127 | description: Request was successful 128 | schema: 129 | $ref: '#/definitions/AccessToken' 130 | deprecated: false 131 | '/Users/{id}/accessTokens': 132 | get: 133 | tags: 134 | - User 135 | summary: Queries accessTokens of User. 136 | operationId: User.prototype.__get__accessTokens 137 | parameters: 138 | - name: filter 139 | in: query 140 | required: false 141 | type: string 142 | format: JSON 143 | - name: id 144 | in: path 145 | description: User id 146 | required: true 147 | type: string 148 | format: JSON 149 | responses: 150 | '200': 151 | description: Request was successful 152 | schema: 153 | type: array 154 | items: 155 | $ref: '#/definitions/AccessToken' 156 | deprecated: false 157 | post: 158 | tags: 159 | - User 160 | summary: Creates a new instance in accessTokens of this model. 161 | operationId: User.prototype.__create__accessTokens 162 | parameters: 163 | - name: data 164 | in: body 165 | required: false 166 | schema: 167 | $ref: '#/definitions/AccessToken' 168 | - name: id 169 | in: path 170 | description: User id 171 | required: true 172 | type: string 173 | format: JSON 174 | responses: 175 | '200': 176 | description: Request was successful 177 | schema: 178 | $ref: '#/definitions/AccessToken' 179 | deprecated: false 180 | delete: 181 | tags: 182 | - User 183 | summary: Deletes all accessTokens of this model. 184 | operationId: User.prototype.__delete__accessTokens 185 | parameters: 186 | - name: id 187 | in: path 188 | description: User id 189 | required: true 190 | type: string 191 | format: JSON 192 | responses: 193 | '204': 194 | description: Request was successful 195 | deprecated: false 196 | '/Users/{id}/accessTokens/count': 197 | get: 198 | tags: 199 | - User 200 | summary: Counts accessTokens of User. 201 | operationId: User.prototype.__count__accessTokens 202 | parameters: 203 | - name: where 204 | in: query 205 | description: Criteria to match model instances 206 | required: false 207 | type: string 208 | format: JSON 209 | - name: id 210 | in: path 211 | description: User id 212 | required: true 213 | type: string 214 | format: JSON 215 | responses: 216 | '200': 217 | description: Request was successful 218 | schema: 219 | type: object 220 | properties: 221 | count: 222 | type: number 223 | format: double 224 | deprecated: false 225 | /Users: 226 | post: 227 | tags: 228 | - User 229 | summary: Create a new instance of the model and persist it into the data source. 230 | operationId: User.create 231 | parameters: 232 | - name: data 233 | in: body 234 | description: Model instance data 235 | required: false 236 | schema: 237 | $ref: '#/definitions/User' 238 | responses: 239 | '200': 240 | description: Request was successful 241 | schema: 242 | $ref: '#/definitions/User' 243 | deprecated: false 244 | patch: 245 | tags: 246 | - User 247 | summary: Patch an existing model instance or insert a new one into the data source. 248 | operationId: User.upsert__patch_Users 249 | parameters: 250 | - name: data 251 | in: body 252 | description: Model instance data 253 | required: false 254 | schema: 255 | $ref: '#/definitions/User' 256 | responses: 257 | '200': 258 | description: Request was successful 259 | schema: 260 | $ref: '#/definitions/User' 261 | deprecated: false 262 | put: 263 | tags: 264 | - User 265 | summary: Patch an existing model instance or insert a new one into the data source. 266 | operationId: User.upsert__put_Users 267 | parameters: 268 | - name: data 269 | in: body 270 | description: Model instance data 271 | required: false 272 | schema: 273 | $ref: '#/definitions/User' 274 | responses: 275 | '200': 276 | description: Request was successful 277 | schema: 278 | $ref: '#/definitions/User' 279 | deprecated: false 280 | get: 281 | tags: 282 | - User 283 | summary: Find all instances of the model matched by filter from the data source. 284 | operationId: User.find 285 | parameters: 286 | - name: filter 287 | in: query 288 | description: 'Filter defining fields, where, include, order, offset, and limit' 289 | required: false 290 | type: string 291 | format: JSON 292 | responses: 293 | '200': 294 | description: Request was successful 295 | schema: 296 | type: array 297 | items: 298 | $ref: '#/definitions/User' 299 | deprecated: false 300 | /Users/replaceOrCreate: 301 | post: 302 | tags: 303 | - User 304 | summary: Replace an existing model instance or insert a new one into the data source. 305 | operationId: User.replaceOrCreate 306 | parameters: 307 | - name: data 308 | in: body 309 | description: Model instance data 310 | required: false 311 | schema: 312 | $ref: '#/definitions/User' 313 | responses: 314 | '200': 315 | description: Request was successful 316 | schema: 317 | $ref: '#/definitions/User' 318 | deprecated: false 319 | '/Users/{id}/exists': 320 | get: 321 | tags: 322 | - User 323 | summary: Check whether a model instance exists in the data source. 324 | operationId: 'User.exists__get_Users_{id}_exists' 325 | parameters: 326 | - name: id 327 | in: path 328 | description: Model id 329 | required: true 330 | type: string 331 | format: JSON 332 | responses: 333 | '200': 334 | description: Request was successful 335 | schema: 336 | type: object 337 | properties: 338 | exists: 339 | type: boolean 340 | deprecated: false 341 | '/Users/{id}': 342 | head: 343 | tags: 344 | - User 345 | summary: Check whether a model instance exists in the data source. 346 | operationId: 'User.exists__head_Users_{id}' 347 | parameters: 348 | - name: id 349 | in: path 350 | description: Model id 351 | required: true 352 | type: string 353 | format: JSON 354 | responses: 355 | '200': 356 | description: Request was successful 357 | schema: 358 | type: object 359 | properties: 360 | exists: 361 | type: boolean 362 | deprecated: false 363 | get: 364 | tags: 365 | - User 366 | summary: 'Find a model instance by {{id}} from the data source.' 367 | operationId: User.findById 368 | parameters: 369 | - name: id 370 | in: path 371 | description: Model id 372 | required: true 373 | type: string 374 | format: JSON 375 | - name: filter 376 | in: query 377 | description: Filter defining fields and include 378 | required: false 379 | type: string 380 | format: JSON 381 | responses: 382 | '200': 383 | description: Request was successful 384 | schema: 385 | $ref: '#/definitions/User' 386 | deprecated: false 387 | delete: 388 | tags: 389 | - User 390 | summary: 'Delete a model instance by {{id}} from the data source.' 391 | operationId: User.deleteById 392 | parameters: 393 | - name: id 394 | in: path 395 | description: Model id 396 | required: true 397 | type: string 398 | format: JSON 399 | responses: 400 | '200': 401 | description: Request was successful 402 | schema: 403 | type: object 404 | deprecated: false 405 | patch: 406 | tags: 407 | - User 408 | summary: Patch attributes for a model instance and persist it into the data source. 409 | operationId: 'User.prototype.updateAttributes__patch_Users_{id}' 410 | parameters: 411 | - name: data 412 | in: body 413 | description: An object of model property name/value pairs 414 | required: false 415 | schema: 416 | $ref: '#/definitions/User' 417 | - name: id 418 | in: path 419 | description: User id 420 | required: true 421 | type: string 422 | format: JSON 423 | responses: 424 | '200': 425 | description: Request was successful 426 | schema: 427 | $ref: '#/definitions/User' 428 | deprecated: false 429 | put: 430 | tags: 431 | - User 432 | summary: Patch attributes for a model instance and persist it into the data source. 433 | operationId: 'User.prototype.updateAttributes__put_Users_{id}' 434 | parameters: 435 | - name: data 436 | in: body 437 | description: An object of model property name/value pairs 438 | required: false 439 | schema: 440 | $ref: '#/definitions/User' 441 | - name: id 442 | in: path 443 | description: User id 444 | required: true 445 | type: string 446 | format: JSON 447 | responses: 448 | '200': 449 | description: Request was successful 450 | schema: 451 | $ref: '#/definitions/User' 452 | deprecated: false 453 | '/Users/{id}/replace': 454 | post: 455 | tags: 456 | - User 457 | summary: Replace attributes for a model instance and persist it into the data source. 458 | operationId: User.replaceById 459 | parameters: 460 | - name: id 461 | in: path 462 | description: Model id 463 | required: true 464 | type: string 465 | format: JSON 466 | - name: data 467 | in: body 468 | description: Model instance data 469 | required: false 470 | schema: 471 | $ref: '#/definitions/User' 472 | responses: 473 | '200': 474 | description: Request was successful 475 | schema: 476 | $ref: '#/definitions/User' 477 | deprecated: false 478 | /Users/findOne: 479 | get: 480 | tags: 481 | - User 482 | summary: Find first instance of the model matched by filter from the data source. 483 | operationId: User.findOne 484 | parameters: 485 | - name: filter 486 | in: query 487 | description: 'Filter defining fields, where, include, order, offset, and limit' 488 | required: false 489 | type: string 490 | format: JSON 491 | responses: 492 | '200': 493 | description: Request was successful 494 | schema: 495 | $ref: '#/definitions/User' 496 | deprecated: false 497 | /Users/update: 498 | post: 499 | tags: 500 | - User 501 | summary: 'Update instances of the model matched by {{where}} from the data source.' 502 | operationId: User.updateAll 503 | parameters: 504 | - name: where 505 | in: query 506 | description: Criteria to match model instances 507 | required: false 508 | type: string 509 | format: JSON 510 | - name: data 511 | in: body 512 | description: An object of model property name/value pairs 513 | required: false 514 | schema: 515 | $ref: '#/definitions/User' 516 | responses: 517 | '200': 518 | description: Request was successful 519 | schema: 520 | description: The number of instances updated 521 | type: object 522 | deprecated: false 523 | /Users/count: 524 | get: 525 | tags: 526 | - User 527 | summary: Count instances of the model matched by where from the data source. 528 | operationId: User.count 529 | parameters: 530 | - name: where 531 | in: query 532 | description: Criteria to match model instances 533 | required: false 534 | type: string 535 | format: JSON 536 | responses: 537 | '200': 538 | description: Request was successful 539 | schema: 540 | type: object 541 | properties: 542 | count: 543 | type: number 544 | format: double 545 | deprecated: false 546 | /Users/login: 547 | post: 548 | tags: 549 | - User 550 | summary: Login a user with username/email and password. 551 | operationId: User.login 552 | parameters: 553 | - name: credentials 554 | in: body 555 | required: true 556 | schema: 557 | type: object 558 | - name: include 559 | in: query 560 | description: Related objects to include in the response. See the description of return value for more details. 561 | required: false 562 | type: string 563 | format: JSON 564 | responses: 565 | '200': 566 | description: Request was successful 567 | schema: 568 | description: >+ 569 | The response body contains properties of the AccessToken created on 570 | login. 571 | 572 | Depending on the value of `include` parameter, the body may contain 573 | additional properties: 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | - `user` - `U+007BUserU+007D` - Data of the currently logged in 582 | 583 | user. (`include=user`) 584 | 585 | 586 | type: object 587 | deprecated: false 588 | /Users/logout: 589 | post: 590 | tags: 591 | - User 592 | summary: Logout a user with access token. 593 | operationId: User.logout 594 | parameters: [] 595 | responses: 596 | '204': 597 | description: Request was successful 598 | deprecated: false 599 | /Users/confirm: 600 | get: 601 | tags: 602 | - User 603 | summary: Confirm a user registration with email verification token. 604 | operationId: User.confirm 605 | parameters: 606 | - name: uid 607 | in: query 608 | required: true 609 | type: string 610 | - name: token 611 | in: query 612 | required: true 613 | type: string 614 | - name: redirect 615 | in: query 616 | required: false 617 | type: string 618 | responses: 619 | '204': 620 | description: Request was successful 621 | deprecated: false 622 | /Users/reset: 623 | post: 624 | tags: 625 | - User 626 | summary: Reset password for a user with email. 627 | operationId: User.resetPassword 628 | parameters: 629 | - name: options 630 | in: body 631 | required: true 632 | schema: 633 | type: object 634 | responses: 635 | '204': 636 | description: Request was successful 637 | deprecated: false 638 | /Notes: 639 | post: 640 | tags: 641 | - Note 642 | summary: Create a new instance of the model and persist it into the data source. 643 | operationId: Note.create 644 | parameters: 645 | - name: data 646 | in: body 647 | description: Model instance data 648 | required: false 649 | schema: 650 | $ref: '#/definitions/Note' 651 | responses: 652 | '200': 653 | description: Request was successful 654 | schema: 655 | $ref: '#/definitions/Note' 656 | deprecated: false 657 | patch: 658 | tags: 659 | - Note 660 | summary: Patch an existing model instance or insert a new one into the data source. 661 | operationId: Note.upsert__patch_Notes 662 | parameters: 663 | - name: data 664 | in: body 665 | description: Model instance data 666 | required: false 667 | schema: 668 | $ref: '#/definitions/Note' 669 | responses: 670 | '200': 671 | description: Request was successful 672 | schema: 673 | $ref: '#/definitions/Note' 674 | deprecated: false 675 | put: 676 | tags: 677 | - Note 678 | summary: Patch an existing model instance or insert a new one into the data source. 679 | operationId: Note.upsert__put_Notes 680 | parameters: 681 | - name: data 682 | in: body 683 | description: Model instance data 684 | required: false 685 | schema: 686 | $ref: '#/definitions/Note' 687 | responses: 688 | '200': 689 | description: Request was successful 690 | schema: 691 | $ref: '#/definitions/Note' 692 | deprecated: false 693 | get: 694 | tags: 695 | - Note 696 | summary: Find all instances of the model matched by filter from the data source. 697 | operationId: Note.find 698 | parameters: 699 | - name: filter 700 | in: query 701 | description: 'Filter defining fields, where, include, order, offset, and limit' 702 | required: false 703 | type: string 704 | format: JSON 705 | responses: 706 | '200': 707 | description: Request was successful 708 | schema: 709 | type: array 710 | items: 711 | $ref: '#/definitions/Note' 712 | deprecated: false 713 | /Notes/replaceOrCreate: 714 | post: 715 | tags: 716 | - Note 717 | summary: Replace an existing model instance or insert a new one into the data source. 718 | operationId: Note.replaceOrCreate 719 | parameters: 720 | - name: data 721 | in: body 722 | description: Model instance data 723 | required: false 724 | schema: 725 | $ref: '#/definitions/Note' 726 | responses: 727 | '200': 728 | description: Request was successful 729 | schema: 730 | $ref: '#/definitions/Note' 731 | deprecated: false 732 | '/Notes/{id}/exists': 733 | get: 734 | tags: 735 | - Note 736 | summary: Check whether a model instance exists in the data source. 737 | operationId: 'Note.exists__get_Notes_{id}_exists' 738 | parameters: 739 | - name: id 740 | in: path 741 | description: Model id 742 | required: true 743 | type: string 744 | format: JSON 745 | responses: 746 | '200': 747 | description: Request was successful 748 | schema: 749 | type: object 750 | properties: 751 | exists: 752 | type: boolean 753 | deprecated: false 754 | '/Notes/{id}': 755 | head: 756 | tags: 757 | - Note 758 | summary: Check whether a model instance exists in the data source. 759 | operationId: 'Note.exists__head_Notes_{id}' 760 | parameters: 761 | - name: id 762 | in: path 763 | description: Model id 764 | required: true 765 | type: string 766 | format: JSON 767 | responses: 768 | '200': 769 | description: Request was successful 770 | schema: 771 | type: object 772 | properties: 773 | exists: 774 | type: boolean 775 | deprecated: false 776 | get: 777 | tags: 778 | - Note 779 | summary: 'Find a model instance by {{id}} from the data source.' 780 | operationId: Note.findById 781 | parameters: 782 | - name: id 783 | in: path 784 | description: Model id 785 | required: true 786 | type: string 787 | format: JSON 788 | - name: filter 789 | in: query 790 | description: Filter defining fields and include 791 | required: false 792 | type: string 793 | format: JSON 794 | responses: 795 | '200': 796 | description: Request was successful 797 | schema: 798 | $ref: '#/definitions/Note' 799 | deprecated: false 800 | delete: 801 | tags: 802 | - Note 803 | summary: 'Delete a model instance by {{id}} from the data source.' 804 | operationId: Note.deleteById 805 | parameters: 806 | - name: id 807 | in: path 808 | description: Model id 809 | required: true 810 | type: string 811 | format: JSON 812 | responses: 813 | '200': 814 | description: Request was successful 815 | schema: 816 | type: object 817 | deprecated: false 818 | patch: 819 | tags: 820 | - Note 821 | summary: Patch attributes for a model instance and persist it into the data source. 822 | operationId: 'Note.prototype.updateAttributes__patch_Notes_{id}' 823 | parameters: 824 | - name: data 825 | in: body 826 | description: An object of model property name/value pairs 827 | required: false 828 | schema: 829 | $ref: '#/definitions/Note' 830 | - name: id 831 | in: path 832 | description: PersistedModel id 833 | required: true 834 | type: string 835 | format: JSON 836 | responses: 837 | '200': 838 | description: Request was successful 839 | schema: 840 | $ref: '#/definitions/Note' 841 | deprecated: false 842 | put: 843 | tags: 844 | - Note 845 | summary: Patch attributes for a model instance and persist it into the data source. 846 | operationId: 'Note.prototype.updateAttributes__put_Notes_{id}' 847 | parameters: 848 | - name: data 849 | in: body 850 | description: An object of model property name/value pairs 851 | required: false 852 | schema: 853 | $ref: '#/definitions/Note' 854 | - name: id 855 | in: path 856 | description: PersistedModel id 857 | required: true 858 | type: string 859 | format: JSON 860 | responses: 861 | '200': 862 | description: Request was successful 863 | schema: 864 | $ref: '#/definitions/Note' 865 | deprecated: false 866 | '/Notes/{id}/replace': 867 | post: 868 | tags: 869 | - Note 870 | summary: Replace attributes for a model instance and persist it into the data source. 871 | operationId: Note.replaceById 872 | parameters: 873 | - name: id 874 | in: path 875 | description: Model id 876 | required: true 877 | type: string 878 | format: JSON 879 | - name: data 880 | in: body 881 | description: Model instance data 882 | required: false 883 | schema: 884 | $ref: '#/definitions/Note' 885 | responses: 886 | '200': 887 | description: Request was successful 888 | schema: 889 | $ref: '#/definitions/Note' 890 | deprecated: false 891 | /Notes/findOne: 892 | get: 893 | tags: 894 | - Note 895 | summary: Find first instance of the model matched by filter from the data source. 896 | operationId: Note.findOne 897 | parameters: 898 | - name: filter 899 | in: query 900 | description: 'Filter defining fields, where, include, order, offset, and limit' 901 | required: false 902 | type: string 903 | format: JSON 904 | responses: 905 | '200': 906 | description: Request was successful 907 | schema: 908 | $ref: '#/definitions/Note' 909 | deprecated: false 910 | /Notes/update: 911 | post: 912 | tags: 913 | - Note 914 | summary: 'Update instances of the model matched by {{where}} from the data source.' 915 | operationId: Note.updateAll 916 | parameters: 917 | - name: where 918 | in: query 919 | description: Criteria to match model instances 920 | required: false 921 | type: string 922 | format: JSON 923 | - name: data 924 | in: body 925 | description: An object of model property name/value pairs 926 | required: false 927 | schema: 928 | $ref: '#/definitions/Note' 929 | responses: 930 | '200': 931 | description: Request was successful 932 | schema: 933 | description: The number of instances updated 934 | type: object 935 | deprecated: false 936 | /Notes/count: 937 | get: 938 | tags: 939 | - Note 940 | summary: Count instances of the model matched by where from the data source. 941 | operationId: Note.count 942 | parameters: 943 | - name: where 944 | in: query 945 | description: Criteria to match model instances 946 | required: false 947 | type: string 948 | format: JSON 949 | responses: 950 | '200': 951 | description: Request was successful 952 | schema: 953 | type: object 954 | properties: 955 | count: 956 | type: number 957 | format: double 958 | deprecated: false 959 | 960 | 961 | definitions: 962 | x-any: 963 | properties: {} 964 | AccessToken: 965 | properties: 966 | id: 967 | type: string 968 | ttl: 969 | default: 1209600 970 | description: time to live in seconds (2 weeks by default) 971 | type: number 972 | format: double 973 | created: 974 | type: string 975 | format: date 976 | userId: 977 | type: number 978 | format: double 979 | required: 980 | - id 981 | additionalProperties: false 982 | User: 983 | properties: 984 | realm: 985 | type: string 986 | username: 987 | type: string 988 | credentials: 989 | type: object 990 | challenges: 991 | type: object 992 | email: 993 | type: string 994 | emailVerified: 995 | type: boolean 996 | status: 997 | type: string 998 | created: 999 | type: string 1000 | format: date 1001 | lastUpdated: 1002 | type: string 1003 | format: date 1004 | id: 1005 | type: number 1006 | format: double 1007 | required: 1008 | - email 1009 | additionalProperties: false 1010 | Note: 1011 | properties: 1012 | title: 1013 | type: string 1014 | content: 1015 | type: string 1016 | id: 1017 | type: number 1018 | format: double 1019 | required: 1020 | - title 1021 | additionalProperties: false 1022 | 1023 | -------------------------------------------------------------------------------- /grpc-swagger/test/proto2swagger.test.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: grpc-swagger 3 | 4 | var path = require('path'); 5 | var proto2swagger = require('../index').proto2swagger; 6 | 7 | describe('proto2swagger', function() { 8 | it('transform proto to swagger', function(done) { 9 | proto2swagger(path.join(__dirname, './note.proto'), 'yaml', 10 | function(err, spec) { 11 | console.log(spec) 12 | done(err, spec); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /grpc-swagger/test/swagger2proto.test.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016. All Rights Reserved. 2 | // Node module: grpc-swagger 3 | 4 | var path = require('path'); 5 | var swagger2proto = require('../index').swagger2proto; 6 | 7 | describe('swagger2proto', function() { 8 | it('transform swagger to proto', function(done) { 9 | swagger2proto(path.join(__dirname, './note.yaml'), 10 | function(err, proto) { 11 | console.log(proto); 12 | done(); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /kubernetes/create-k8s.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Start minikube 3 | minikube start 4 | 5 | # Create deployments 6 | kubectl create -f note-swift-deployment.yaml 7 | kubectl create -f note-java-deployment.yaml 8 | kubectl create -f note-loopback-deployment.yaml 9 | kubectl create -f note-mongo-deployment.yaml 10 | kubectl create -f zipkin-deployment.yaml 11 | 12 | # Create services 13 | kubectl create -f note-swift-service.yaml 14 | kubectl create -f note-java-service.yaml 15 | kubectl create -f note-loopback-service.yaml 16 | kubectl create -f note-mongo-service.yaml 17 | kubectl create -f zipkin-service.yaml 18 | 19 | # Start dashboard 20 | minikube dashboard 21 | 22 | # Open note-loopback 23 | minikube service note-loopback 24 | 25 | -------------------------------------------------------------------------------- /kubernetes/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Create deployments 4 | kubectl apply -f note-swift-deployment.yaml 5 | kubectl apply -f note-java-deployment.yaml 6 | kubectl apply -f note-loopback-deployment.yaml 7 | kubectl apply -f note-mongo-deployment.yaml 8 | kubectl apply -f zipkin-deployment.yaml 9 | 10 | # Create services 11 | kubectl apply -f note-swift-service.yaml 12 | kubectl apply -f note-java-service.yaml 13 | kubectl apply -f note-loopback-service.yaml 14 | kubectl apply -f note-mongo-service.yaml 15 | kubectl apply -f zipkin-service.yaml 16 | 17 | 18 | -------------------------------------------------------------------------------- /kubernetes/note-java-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | creationTimestamp: null 5 | name: note-java 6 | spec: 7 | replicas: 1 8 | strategy: {} 9 | template: 10 | metadata: 11 | creationTimestamp: null 12 | labels: 13 | service: note-java 14 | spec: 15 | containers: 16 | - image: raymondfeng/note-java 17 | name: note-java 18 | ports: 19 | - containerPort: 50052 20 | protocol: TCP 21 | resources: {} 22 | restartPolicy: Always 23 | status: {} 24 | -------------------------------------------------------------------------------- /kubernetes/note-java-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | service: note-java 7 | name: note-java 8 | spec: 9 | ports: 10 | - name: "50052" 11 | port: 50052 12 | protocol: TCP 13 | targetPort: 50052 14 | selector: 15 | service: note-java 16 | status: 17 | loadBalancer: {} 18 | -------------------------------------------------------------------------------- /kubernetes/note-loopback-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | creationTimestamp: null 5 | name: note-loopback 6 | spec: 7 | replicas: 1 8 | strategy: {} 9 | template: 10 | metadata: 11 | creationTimestamp: null 12 | labels: 13 | service: note-loopback 14 | spec: 15 | containers: 16 | - image: raymondfeng/note-loopback 17 | name: note-loopback 18 | ports: 19 | - containerPort: 50051 20 | protocol: TCP 21 | - containerPort: 3000 22 | protocol: TCP 23 | resources: {} 24 | restartPolicy: Always 25 | status: {} 26 | -------------------------------------------------------------------------------- /kubernetes/note-loopback-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | service: note-loopback 7 | name: note-loopback 8 | spec: 9 | type: NodePort 10 | ports: 11 | - name: "50051" 12 | port: 50051 13 | protocol: TCP 14 | targetPort: 50051 15 | - name: "3000" 16 | port: 3000 17 | protocol: TCP 18 | targetPort: 3000 19 | selector: 20 | service: note-loopback 21 | status: 22 | loadBalancer: {} 23 | -------------------------------------------------------------------------------- /kubernetes/note-mongo-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | creationTimestamp: null 5 | name: note-mongo 6 | spec: 7 | replicas: 1 8 | strategy: {} 9 | template: 10 | metadata: 11 | creationTimestamp: null 12 | labels: 13 | service: note-mongo 14 | spec: 15 | containers: 16 | - image: mongo 17 | name: note-mongo 18 | # volumeMounts: 19 | # - name: mongo-persistent-storage 20 | # mountPath: /data/db 21 | ports: 22 | - containerPort: 27017 23 | protocol: TCP 24 | resources: {} 25 | restartPolicy: Always 26 | # volumes: 27 | # - name: mongo-persistent-storage 28 | # hostPath: 29 | # path: /tmp/mongodb 30 | status: {} 31 | -------------------------------------------------------------------------------- /kubernetes/note-mongo-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | service: note-mongo 7 | name: note-mongo 8 | spec: 9 | type: NodePort 10 | ports: 11 | - name: "27017" 12 | port: 27017 13 | protocol: TCP 14 | targetPort: 27017 15 | selector: 16 | service: note-mongo 17 | status: 18 | loadBalancer: {} 19 | -------------------------------------------------------------------------------- /kubernetes/note-swift-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | creationTimestamp: null 5 | name: note-swift 6 | spec: 7 | replicas: 1 8 | strategy: {} 9 | template: 10 | metadata: 11 | creationTimestamp: null 12 | labels: 13 | service: note-swift 14 | spec: 15 | containers: 16 | - image: raymondfeng/note-swift 17 | name: note-swift 18 | ports: 19 | - containerPort: 50053 20 | protocol: TCP 21 | resources: {} 22 | restartPolicy: Always 23 | status: {} 24 | -------------------------------------------------------------------------------- /kubernetes/note-swift-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | service: note-swift 7 | name: note-swift 8 | spec: 9 | ports: 10 | - name: "50053" 11 | port: 50053 12 | protocol: TCP 13 | targetPort: 50053 14 | selector: 15 | service: note-swift 16 | status: 17 | loadBalancer: {} 18 | -------------------------------------------------------------------------------- /kubernetes/zipkin-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | creationTimestamp: null 5 | name: zipkin 6 | spec: 7 | replicas: 1 8 | strategy: {} 9 | template: 10 | metadata: 11 | creationTimestamp: null 12 | labels: 13 | service: zipkin 14 | spec: 15 | containers: 16 | - image: openzipkin/zipkin 17 | name: zipkin 18 | ports: 19 | - containerPort: 9411 20 | protocol: TCP 21 | resources: {} 22 | restartPolicy: Always 23 | status: {} 24 | -------------------------------------------------------------------------------- /kubernetes/zipkin-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | service: zipkin 7 | name: zipkin 8 | spec: 9 | ports: 10 | - name: "9411" 11 | port: 9411 12 | protocol: TCP 13 | targetPort: 9411 14 | selector: 15 | service: zipkin 16 | status: 17 | loadBalancer: {} 18 | -------------------------------------------------------------------------------- /note-java/.dockerignore: -------------------------------------------------------------------------------- 1 | dependency-reduced-pom.xml 2 | target/ 3 | META-INF/ 4 | .classpath 5 | .project 6 | -------------------------------------------------------------------------------- /note-java/.gitignore: -------------------------------------------------------------------------------- 1 | dependency-reduced-pom.xml 2 | target/ 3 | META-INF/ 4 | .classpath 5 | .project 6 | .settings 7 | 8 | -------------------------------------------------------------------------------- /note-java/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM maven:3.3.9-jdk-8 2 | 3 | # Create app directory 4 | RUN mkdir -p /usr/src/note-java 5 | WORKDIR /usr/src/note-java 6 | 7 | # Bundle app source 8 | COPY . /usr/src/note-java 9 | 10 | RUN mvn clean install 11 | 12 | EXPOSE 50052 13 | CMD [ "java", "-jar", "./target/note-1.0.0.jar" ] 14 | -------------------------------------------------------------------------------- /note-java/Dockerfile.ibmjdk: -------------------------------------------------------------------------------- 1 | FROM ibmjava:8-sfj 2 | 3 | # Create app directory 4 | RUN mkdir -p /usr/src/note-java/lib 5 | WORKDIR /usr/src/note-java 6 | 7 | # Bundle app source 8 | COPY ./target/note-1.0.0.jar /usr/src/note-java 9 | COPY ./target/lib/*.jar /usr/src/note-java/lib/ 10 | 11 | EXPOSE 50052 12 | CMD [ "java", "-jar", "note-1.0.0.jar" ] 13 | -------------------------------------------------------------------------------- /note-java/client.sh: -------------------------------------------------------------------------------- 1 | DIR=`dirname $0` 2 | java -cp $DIR/target/note-1.0.0.jar com.ibm.apiconnect.demo.polyglot.NoteClient $@ 3 | -------------------------------------------------------------------------------- /note-java/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.ibm.apiconnect.demo.ployglot 5 | note 6 | jar 7 | 1.0.0 8 | note 9 | http://maven.apache.org 10 | 11 | 1.4.0 12 | 13 | 14 | 15 | junit 16 | junit 17 | 4.12 18 | test 19 | 20 | 21 | io.grpc 22 | grpc-netty 23 | ${grpc.version} 24 | 25 | 26 | io.grpc 27 | grpc-protobuf 28 | ${grpc.version} 29 | 30 | 31 | io.grpc 32 | grpc-stub 33 | ${grpc.version} 34 | 35 | 36 | com.nimbusds 37 | nimbus-jose-jwt 38 | 4.39.1 39 | 40 | 41 | org.bouncycastle 42 | bcprov-jdk15on 43 | 1.57 44 | 45 | 46 | io.zipkin.brave 47 | brave-grpc 48 | 4.4.0 49 | 50 | 51 | io.grpc 52 | grpc-core 53 | 54 | 55 | 56 | 57 | io.zipkin.brave 58 | brave-http 59 | 4.4.0 60 | 61 | 62 | io.zipkin.brave 63 | brave-spancollector-http 64 | 4.4.0 65 | 66 | 67 | io.netty 68 | netty-tcnative-boringssl-static 69 | 2.0.3.Final 70 | ${os.detected.classifier} 71 | 72 | 73 | 74 | 75 | 76 | kr.motd.maven 77 | os-maven-plugin 78 | 1.4.1.Final 79 | 80 | 81 | 82 | 83 | 84 | org.apache.maven.plugins 85 | maven-antrun-plugin 86 | 87 | 88 | initialize 89 | 90 | true 91 | 92 | 95 | 96 | 97 | 98 | 99 | 100 | run 101 | 102 | 103 | 104 | 105 | 106 | org.xolstice.maven.plugins 107 | protobuf-maven-plugin 108 | 0.5.0 109 | 110 | 113 | 114 | com.google.protobuf:protoc:3.0.0:exe:${os.detected.classifier} 115 | 116 | grpc-java 117 | 118 | io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} 119 | 120 | 121 | 122 | 123 | 124 | compile 125 | compile-custom 126 | 127 | 128 | 129 | 130 | 131 | org.apache.maven.plugins 132 | maven-dependency-plugin 133 | 2.10 134 | 135 | 136 | copy-dependencies 137 | package 138 | 139 | copy-dependencies 140 | 141 | 142 | 143 | runtime 144 | 145 | ${project.build.directory}/lib/ 146 | 147 | 148 | 149 | 150 | 151 | org.apache.maven.plugins 152 | 153 | maven-jar-plugin 154 | 155 | 156 | 157 | true 158 | com.ibm.apiconnect.demo.polyglot.NoteServer 159 | lib/ 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /note-java/src/main/java/com/ibm/apiconnect/demo/polyglot/BraveUtil.java: -------------------------------------------------------------------------------- 1 | package com.ibm.apiconnect.demo.polyglot; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.Collections; 6 | import java.util.Iterator; 7 | 8 | import com.github.kristofa.brave.Brave; 9 | import com.github.kristofa.brave.EmptySpanCollectorMetricsHandler; 10 | import com.github.kristofa.brave.Sampler; 11 | import com.github.kristofa.brave.grpc.BraveGrpcServerInterceptor; 12 | import com.github.kristofa.brave.http.HttpSpanCollector; 13 | 14 | import io.grpc.BindableService; 15 | import io.grpc.ServerInterceptors; 16 | import io.grpc.ServerServiceDefinition; 17 | 18 | public class BraveUtil { 19 | public static final String ZIPKIN_SERVER_URL = "http://zipkin:9411"; 20 | 21 | private static Collection collectors = Collections 22 | .synchronizedList(new ArrayList()); 23 | 24 | public static Brave brave(String serviceName, String zipkinBaseUrl) { 25 | HttpSpanCollector spanCollector = HttpSpanCollector.create(zipkinBaseUrl, 26 | new EmptySpanCollectorMetricsHandler()); 27 | collectors.add(spanCollector); 28 | return new Brave.Builder(serviceName).traceSampler(Sampler.ALWAYS_SAMPLE).spanCollector(spanCollector).build(); 29 | } 30 | 31 | public static ServerServiceDefinition intercept(BindableService service, String name, String zipkinBaseUrl) { 32 | if (zipkinBaseUrl == null) { 33 | zipkinBaseUrl = ZIPKIN_SERVER_URL; 34 | } 35 | return ServerInterceptors.intercept(service, new BraveGrpcServerInterceptor(brave(name, zipkinBaseUrl))); 36 | } 37 | 38 | public static void shutdown() { 39 | Iterator it = collectors.iterator(); 40 | while (it.hasNext()) { 41 | it.next().close(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /note-java/src/main/java/com/ibm/apiconnect/demo/polyglot/JWEUtil.java: -------------------------------------------------------------------------------- 1 | package com.ibm.apiconnect.demo.polyglot; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.security.KeyFactory; 7 | import java.security.interfaces.RSAPrivateKey; 8 | import java.security.interfaces.RSAPublicKey; 9 | import java.security.spec.PKCS8EncodedKeySpec; 10 | import java.security.spec.X509EncodedKeySpec; 11 | import java.text.ParseException; 12 | import java.util.ArrayList; 13 | import java.util.Date; 14 | import java.util.List; 15 | import java.util.logging.Logger; 16 | 17 | import com.nimbusds.jose.EncryptionMethod; 18 | import com.nimbusds.jose.JOSEException; 19 | import com.nimbusds.jose.JWEAlgorithm; 20 | import com.nimbusds.jose.JWEHeader; 21 | import com.nimbusds.jose.crypto.RSADecrypter; 22 | import com.nimbusds.jose.crypto.RSAEncrypter; 23 | import com.nimbusds.jose.crypto.bc.BouncyCastleProviderSingleton; 24 | import com.nimbusds.jwt.EncryptedJWT; 25 | import com.nimbusds.jwt.JWTClaimsSet; 26 | 27 | public class JWEUtil { 28 | private static final Logger logger = Logger.getLogger(JWEUtil.class.getName()); 29 | 30 | /** 31 | * Load the private key 32 | * @return private key 33 | * @throws Exception 34 | */ 35 | public static RSAPrivateKey loadPrivateKey() throws Exception { 36 | byte[] keyBytes = loadResource("/private_key.der"); 37 | PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); 38 | KeyFactory kf = KeyFactory.getInstance("RSA"); 39 | return (RSAPrivateKey) kf.generatePrivate(spec); 40 | } 41 | 42 | /** 43 | * Load the public key 44 | * @return public key 45 | * @throws Exception 46 | */ 47 | public static RSAPublicKey loadPublicKey() throws Exception { 48 | byte[] keyBytes = loadResource("/public_key.der"); 49 | X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes); 50 | KeyFactory kf = KeyFactory.getInstance("RSA"); 51 | return (RSAPublicKey) kf.generatePublic(spec); 52 | } 53 | 54 | /** 55 | * Load resources into bytes 56 | * @param name Name of the resource 57 | * @return Resource content 58 | * @throws IOException 59 | */ 60 | private static byte[] loadResource(String name) throws IOException { 61 | InputStream is = JWEUtil.class.getResourceAsStream(name); 62 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 63 | byte[] buffer = new byte[4096]; 64 | int length = 0; 65 | while (length != -1) { 66 | length = is.read(buffer); 67 | if (length > 0) { 68 | bos.write(buffer, 0, length); 69 | } 70 | } 71 | is.close(); 72 | byte[] keyBytes = bos.toByteArray(); 73 | return keyBytes; 74 | } 75 | 76 | /** 77 | * Generate an encrypted JWE 78 | * @param jti 79 | * @param publicKey 80 | * @return 81 | * @throws Exception 82 | */ 83 | public static String generateJWE(String jti, RSAPublicKey publicKey) throws Exception { 84 | // Compose the JWT claims set 85 | String iss = "https://openid.net"; 86 | String sub = "apiconnect"; 87 | List aud = new ArrayList(); 88 | aud.add("https://app-one.com"); 89 | aud.add("https://app-two.com"); 90 | final Date NOW = new Date(new Date().getTime() / 1000 * 1000); 91 | Date exp = new Date(NOW.getTime() + 1000 * 60 * 10); 92 | Date nbf = NOW; 93 | Date iat = NOW; 94 | 95 | JWTClaimsSet jwtClaims = new JWTClaimsSet.Builder().issuer(iss).subject(sub).audience(aud).expirationTime(exp) 96 | .notBeforeTime(nbf).issueTime(iat).jwtID(jti).build(); 97 | 98 | // Request JWT encrypted with RSA-OAEP and 128-bit AES/GCM 99 | JWEHeader header = new JWEHeader(JWEAlgorithm.RSA_OAEP, EncryptionMethod.A128GCM); 100 | 101 | // Create the encrypted JWT object 102 | EncryptedJWT jwt = new EncryptedJWT(header, jwtClaims); 103 | 104 | // Create an encrypter with the specified public RSA key 105 | RSAEncrypter encrypter = new RSAEncrypter(publicKey); 106 | encrypter.getJCAContext().setProvider(BouncyCastleProviderSingleton.getInstance()); 107 | 108 | // Do the actual encryption 109 | jwt.encrypt(encrypter); 110 | 111 | // Serialize to JWT compact form 112 | String jwtString = jwt.serialize(); 113 | 114 | return jwtString; 115 | } 116 | 117 | /** 118 | * Decrypt the JWT 119 | * @param jwtString 120 | * @param privateKey 121 | * @throws ParseException 122 | * @throws JOSEException 123 | */ 124 | public static String decrypt(String jwtString, RSAPrivateKey privateKey) throws ParseException, JOSEException { 125 | EncryptedJWT jwt; 126 | // Parse back 127 | jwt = EncryptedJWT.parse(jwtString); 128 | 129 | // Create an decrypter with the specified private RSA key 130 | RSADecrypter decrypter = new RSADecrypter(privateKey); 131 | decrypter.getJCAContext().setProvider(BouncyCastleProviderSingleton.getInstance()); 132 | 133 | // Decrypt 134 | jwt.decrypt(decrypter); 135 | 136 | // Retrieve JWT claims 137 | logger.info(jwt.getJWTClaimsSet().getIssuer()); 138 | logger.info(jwt.getJWTClaimsSet().getSubject()); 139 | logger.info(jwt.getJWTClaimsSet().getAudience().toString()); 140 | logger.info(jwt.getJWTClaimsSet().getExpirationTime().toString()); 141 | logger.info(jwt.getJWTClaimsSet().getNotBeforeTime().toString()); 142 | logger.info(jwt.getJWTClaimsSet().getIssueTime().toString()); 143 | logger.info(jwt.getJWTClaimsSet().getJWTID()); 144 | 145 | return jwt.getJWTClaimsSet().getJWTID(); 146 | } 147 | 148 | public static void main(String[] args) throws Exception { 149 | RSAPrivateKey privateKey = JWEUtil.loadPrivateKey(); 150 | RSAPublicKey publicKey = JWEUtil.loadPublicKey(); 151 | String jwe = JWEUtil.generateJWE("Hello", publicKey); 152 | logger.info(jwe); 153 | JWEUtil.decrypt(jwe, privateKey); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /note-java/src/main/java/com/ibm/apiconnect/demo/polyglot/NoteClient.java: -------------------------------------------------------------------------------- 1 | package com.ibm.apiconnect.demo.polyglot; 2 | 3 | import static com.ibm.apiconnect.demo.polyglot.BraveUtil.ZIPKIN_SERVER_URL; 4 | import static com.ibm.apiconnect.demo.polyglot.BraveUtil.brave; 5 | 6 | import java.io.InputStream; 7 | import java.util.concurrent.TimeUnit; 8 | import java.util.logging.Level; 9 | import java.util.logging.Logger; 10 | 11 | import javax.net.ssl.SSLException; 12 | 13 | import com.github.kristofa.brave.grpc.BraveGrpcClientInterceptor; 14 | 15 | import io.grpc.ManagedChannel; 16 | import io.grpc.ManagedChannelBuilder; 17 | import io.grpc.StatusRuntimeException; 18 | import io.grpc.netty.GrpcSslContexts; 19 | import io.grpc.netty.NettyChannelBuilder; 20 | 21 | /** 22 | * A simple client that requests a note from the {@link NoteServer}. 23 | */ 24 | public class NoteClient { 25 | 26 | private static final Logger logger = Logger.getLogger(NoteClient.class.getName()); 27 | 28 | private final ManagedChannel noteChannel; 29 | private final ManagedChannel encryptionChannel; 30 | private final NoteServiceGrpc.NoteServiceBlockingStub noteServiceStub; 31 | private final EncryptionServiceGrpc.EncryptionServiceBlockingStub encryptionServiceStub; 32 | 33 | /** Construct client connecting to Note server at {@code host:port}. 34 | * @throws SSLException */ 35 | public NoteClient(String noteHost, int notePort, String encryptionHost, int encryptionPort) throws SSLException { 36 | InputStream keyCertChainInputStream = NoteClient.class.getResourceAsStream("/grpc.crt"); 37 | noteChannel = ManagedChannelBuilder.forAddress(noteHost, notePort) 38 | .intercept(new BraveGrpcClientInterceptor(brave("note-client", ZIPKIN_SERVER_URL))) 39 | // Channels are secure by default (via SSL/TLS). For the example 40 | // we disable TLS to avoid 41 | // needing certificates. 42 | .usePlaintext(true).build(); 43 | encryptionChannel = NettyChannelBuilder.forAddress(encryptionHost, encryptionPort) 44 | .intercept(new BraveGrpcClientInterceptor(brave("encryption-client", ZIPKIN_SERVER_URL))) 45 | // Channels are secure by default (via SSL/TLS). For the example 46 | // we disable TLS to avoid 47 | // needing certificates. 48 | .sslContext(GrpcSslContexts.forClient().trustManager(keyCertChainInputStream).build()) 49 | .build(); 50 | noteServiceStub = NoteServiceGrpc.newBlockingStub(noteChannel); 51 | encryptionServiceStub = EncryptionServiceGrpc.newBlockingStub(encryptionChannel); 52 | } 53 | 54 | public void shutdown() throws InterruptedException { 55 | noteChannel.shutdown().awaitTermination(5, TimeUnit.SECONDS); 56 | encryptionChannel.shutdown().awaitTermination(5, TimeUnit.SECONDS); 57 | BraveUtil.shutdown(); 58 | } 59 | 60 | public Note create(int id) { 61 | Note request = Note.newBuilder().setId(id) 62 | .setTitle("Note" + id).setContent("My Note: " + id).build(); 63 | try { 64 | Note response = noteServiceStub.create(request); 65 | logger.info("Note created: " + response); 66 | return response; 67 | } catch (StatusRuntimeException e) { 68 | logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus()); 69 | return null; 70 | } 71 | } 72 | 73 | /** 74 | * Greet server. If provided, the first element of {@code args} is the name 75 | * to use in the note. 76 | */ 77 | public static void main(String[] args) throws Exception { 78 | NoteClient client = new NoteClient("note-loopback", 50051, "note-java", 50052); 79 | try { 80 | int id = 1; 81 | if (args.length >= 1) { 82 | id = Integer.parseInt(args[0]); 83 | } 84 | Note note = client.create(id); 85 | System.out.println("Created: " + note); 86 | if (note != null) { 87 | if (note.getContent().equals("My Note")) { 88 | note = client.encryptionServiceStub.encrypt(note); 89 | System.out.println("Encrypted: " + note); 90 | } 91 | note = client.encryptionServiceStub.decrypt(note); 92 | System.out.println("Decrypted:" + note); 93 | } 94 | } finally { 95 | client.shutdown(); 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /note-java/src/main/java/com/ibm/apiconnect/demo/polyglot/NoteServer.java: -------------------------------------------------------------------------------- 1 | package com.ibm.apiconnect.demo.polyglot; 2 | 3 | import java.io.InputStream; 4 | import java.security.interfaces.RSAPrivateKey; 5 | import java.security.interfaces.RSAPublicKey; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.logging.Logger; 9 | 10 | import javax.net.ssl.SSLException; 11 | 12 | import io.grpc.Server; 13 | import io.grpc.ServerServiceDefinition; 14 | import io.grpc.netty.GrpcSslContexts; 15 | import io.grpc.netty.NettyServerBuilder; 16 | import io.grpc.stub.StreamObserver; 17 | import io.netty.handler.ssl.ClientAuth; 18 | import io.netty.handler.ssl.SslContext; 19 | import io.netty.handler.ssl.SslContextBuilder; 20 | 21 | /** 22 | * Server that manages startup/shutdown of a {@code NoteService} server. 23 | */ 24 | public class NoteServer { 25 | 26 | public NoteServer() { 27 | this(50052, BraveUtil.ZIPKIN_SERVER_URL); 28 | } 29 | 30 | public NoteServer(int port, String zipkinServerUrl) { 31 | super(); 32 | this.port = port; 33 | this.zipkinServerUrl = zipkinServerUrl; 34 | } 35 | 36 | private static final Logger logger = Logger.getLogger(NoteServer.class.getName()); 37 | 38 | /* The port on which the server should run */ 39 | private int port = 50052; 40 | 41 | private String zipkinServerUrl = BraveUtil.ZIPKIN_SERVER_URL; 42 | private Server server; 43 | private RSAPrivateKey privateKey; 44 | private RSAPublicKey publicKey; 45 | 46 | private void start() throws Exception { 47 | this.privateKey = JWEUtil.loadPrivateKey(); 48 | this.publicKey = JWEUtil.loadPublicKey(); 49 | // Please note that Brave does NOT create a child span at the moment 50 | ServerServiceDefinition noteService = BraveUtil.intercept(new NoteServiceImpl(), "note-service", 51 | zipkinServerUrl); 52 | ServerServiceDefinition encryptionService = BraveUtil.intercept(new EncryptioneServiceImpl(), 53 | "encryption-service", zipkinServerUrl); 54 | SslContext sslContext = configureSSLContext(); 55 | server = NettyServerBuilder.forPort(port).sslContext(sslContext).addService(noteService) 56 | .addService(encryptionService).build().start(); 57 | logger.info("Server started, listening on " + port); 58 | Runtime.getRuntime().addShutdownHook(new Thread() { 59 | @Override 60 | public void run() { 61 | // Use stderr here since the logger may have been reset by its 62 | // JVM shutdown hook. 63 | System.err.println("*** shutting down gRPC server since JVM is shutting down"); 64 | NoteServer.this.stop(); 65 | System.err.println("*** server shut down"); 66 | } 67 | }); 68 | } 69 | 70 | public SslContext configureSSLContext() throws SSLException { 71 | InputStream keyCertChainInputStream = NoteServer.class.getResourceAsStream("/note-java.crt"); 72 | // Netty requires pkcs#8 73 | InputStream keyInputStream = NoteServer.class.getResourceAsStream("/note-java.pfx"); 74 | InputStream trustCertCollectionInputStream = NoteServer.class.getResourceAsStream("/gprc.crt"); 75 | 76 | SslContext sslContext = GrpcSslContexts 77 | .configure(SslContextBuilder.forServer(keyCertChainInputStream, keyInputStream)) 78 | .trustManager(trustCertCollectionInputStream) 79 | .clientAuth(ClientAuth.OPTIONAL) 80 | .build(); 81 | return sslContext; 82 | } 83 | 84 | private void stop() { 85 | if (server != null) { 86 | server.shutdown(); 87 | BraveUtil.shutdown(); 88 | } 89 | } 90 | 91 | /** 92 | * Await termination on the main thread since the grpc library uses daemon 93 | * threads. 94 | */ 95 | private void blockUntilShutdown() throws InterruptedException { 96 | if (server != null) { 97 | server.awaitTermination(); 98 | } 99 | } 100 | 101 | /** 102 | * Main launches the server from the command line. 103 | */ 104 | public static void main(String[] args) throws Exception { 105 | final NoteServer server = new NoteServer(); 106 | server.start(); 107 | server.blockUntilShutdown(); 108 | } 109 | 110 | private static List notes = new ArrayList(); 111 | private static volatile int index = 0; 112 | 113 | private class NoteServiceImpl extends NoteServiceGrpc.NoteServiceImplBase { 114 | 115 | @Override 116 | public void create(Note req, StreamObserver responseObserver) { 117 | Note reply = Note.newBuilder(req).setId(++index).build(); 118 | notes.add(reply); 119 | responseObserver.onNext(reply); 120 | responseObserver.onCompleted(); 121 | } 122 | 123 | public void findById(com.ibm.apiconnect.demo.polyglot.FindByIdRequest request, 124 | io.grpc.stub.StreamObserver responseObserver) { 125 | Note found = null; 126 | for (Note n : notes) { 127 | if (n.getId() == request.getId()) { 128 | found = n; 129 | break; 130 | } 131 | } 132 | responseObserver.onNext(found); 133 | responseObserver.onCompleted(); 134 | } 135 | 136 | /** 137 | */ 138 | public void find(com.ibm.apiconnect.demo.polyglot.FindRequest request, 139 | io.grpc.stub.StreamObserver responseObserver) { 140 | FindResponse reply = FindResponse.newBuilder().addAllNotes(notes).build(); 141 | responseObserver.onNext(reply); 142 | responseObserver.onCompleted(); 143 | } 144 | } 145 | 146 | private class EncryptioneServiceImpl extends EncryptionServiceGrpc.EncryptionServiceImplBase { 147 | 148 | @Override 149 | public void encrypt(Note req, StreamObserver responseObserver) { 150 | String content = req.getContent(); 151 | try { 152 | logger.info("Encrypting: " + content); 153 | content = JWEUtil.generateJWE(content, publicKey); 154 | logger.info("Result: " + content); 155 | } catch (Throwable e) { 156 | System.err.println(e); 157 | logger.throwing("com.ibm.apiconnect.demo.polyglot.JWEUtil", "generateJWE", e); 158 | responseObserver.onError(e); 159 | return; 160 | } 161 | 162 | Note reply = Note.newBuilder(req).setContent(content).build(); 163 | responseObserver.onNext(reply); 164 | responseObserver.onCompleted(); 165 | } 166 | 167 | @Override 168 | public void decrypt(Note req, StreamObserver responseObserver) { 169 | String content = req.getContent(); 170 | try { 171 | logger.info("Decrypting: " + content); 172 | content = JWEUtil.decrypt(content, privateKey); 173 | logger.info("Result: " + content); 174 | } catch (Throwable e) { 175 | logger.throwing("com.ibm.apiconnect.demo.polyglot.JWEUtil", "decrypt", e); 176 | responseObserver.onError(e); 177 | return; 178 | } 179 | 180 | Note reply = Note.newBuilder(req).setContent(content).build(); 181 | responseObserver.onNext(reply); 182 | responseObserver.onCompleted(); 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /note-java/src/main/proto/note.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package note; 4 | 5 | option java_multiple_files = true; 6 | option java_package = "com.ibm.apiconnect.demo.polyglot"; 7 | option java_outer_classname = "NoteMessage"; 8 | 9 | message Note { 10 | int32 id = 1; 11 | string title = 2; 12 | string content = 3; 13 | }; 14 | 15 | message FindByIdRequest { 16 | int32 id = 1; 17 | }; 18 | 19 | message FindRequest { 20 | }; 21 | 22 | message FindResponse { 23 | repeated Note notes = 1; 24 | }; 25 | 26 | service NoteService { 27 | rpc create (Note) returns (Note); 28 | rpc findById (FindByIdRequest) returns (Note); 29 | rpc find (FindRequest) returns (FindResponse); 30 | }; 31 | 32 | service EncryptionService { 33 | rpc encrypt (Note) returns (Note); 34 | rpc decrypt (Note) returns (Note); 35 | } 36 | 37 | -------------------------------------------------------------------------------- /note-java/src/main/resources/generate-grpc-keys.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | if [ ! -f grpc.crt ]; then 3 | # Create a root CA certificate 4 | openssl req -newkey rsa:2048 -nodes -keyout grpc.key -x509 -days 365 -out grpc.crt 5 | fi 6 | 7 | # Create a certificate for note-java 8 | openssl genrsa -out note-java.key 2048 9 | openssl req -new -key note-java.key -out note-java.csr 10 | openssl x509 -req -in note-java.csr -CA grpc.crt -CAkey grpc.key -CAcreateserial -out note-java.crt -days 365 11 | 12 | # Netty requires pkcs8 13 | openssl pkcs8 -topk8 -nocrypt -out note-java.pfx -in note-java.key 14 | -------------------------------------------------------------------------------- /note-java/src/main/resources/generate-jwe-keys.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Generate a 2048-bit RSA private key 3 | openssl genrsa -out private_key.pem 2048 4 | # 5 | # Convert private Key to PKCS#8 format (so Java can read it) 6 | # 7 | openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key.pem -out private_key.der -nocrypt 8 | # 9 | # Output public key portion in DER format (so Java can read it) 10 | # 11 | openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der 12 | -------------------------------------------------------------------------------- /note-java/src/main/resources/grpc.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIERTCCAy2gAwIBAgIJALCZULSgjiqEMA0GCSqGSIb3DQEBBQUAMHQxCzAJBgNV 3 | BAYTAlVTMQswCQYDVQQIEwJDQTEUMBIGA1UEBxMLRm9zdGVyIENpdHkxDDAKBgNV 4 | BAoTA0lCTTETMBEGA1UECxMKU3Ryb25nTG9vcDEfMB0GCSqGSIb3DQEJARYQcmZl 5 | bmdAdXMuaWJtLmNvbTAeFw0xNjA5MjIxNTI5MTBaFw0xNzA5MjIxNTI5MTBaMHQx 6 | CzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEUMBIGA1UEBxMLRm9zdGVyIENpdHkx 7 | DDAKBgNVBAoTA0lCTTETMBEGA1UECxMKU3Ryb25nTG9vcDEfMB0GCSqGSIb3DQEJ 8 | ARYQcmZlbmdAdXMuaWJtLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC 9 | ggEBAK/6zOF/pgH1P9T7RzCJHp7Yz7oPU29TTamXPjHx2M2HcT7MWP0rmMYqmsfh 10 | gIHZqGYOfQ4ZZIJD9j1bh5ttClMrLTCmRYLh7I1bswtP29xCoNdx/gtTKKUS6dQB 11 | Di+VjlUOEZCp4xEVELf0jTNt+zOpzwxKIXuMaogyTne1xHitLSra+8BwIP+KnSET 12 | zfjblAhgZVtqzbs2qr5WRxr7nhPZLjVw568fqIHHqS9I6n2PrAWZDPf8Go5sibi0 13 | kF++0Foh6erJ5S5nMfDO+aqY7N33tq0aHZ7wvuAmocCn/1viWcyOupF4L0A9+7Zi 14 | LnQcLQQRrqVaI08BcfKVCIMIDIUCAwEAAaOB2TCB1jAdBgNVHQ4EFgQUwzuQS7Js 15 | JyInHKG+LTGP4AnBoGEwgaYGA1UdIwSBnjCBm4AUwzuQS7JsJyInHKG+LTGP4AnB 16 | oGGheKR2MHQxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEUMBIGA1UEBxMLRm9z 17 | dGVyIENpdHkxDDAKBgNVBAoTA0lCTTETMBEGA1UECxMKU3Ryb25nTG9vcDEfMB0G 18 | CSqGSIb3DQEJARYQcmZlbmdAdXMuaWJtLmNvbYIJALCZULSgjiqEMAwGA1UdEwQF 19 | MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADO4j94rjuRcapFw9zeRuqjltVniV7Hj 20 | D7e7gy/WtFesTY2x+lraJaMStUKnYlBf2haQRTuGxqXllQAOAKzLQmAjvOTc84dB 21 | C7C9IwI3/BanKXCmntxQOFblqo8UpK0gM2loNYU7s3Q5H8G2JnNUMOfcesMMyhLY 22 | aqPdhbTSYeK9BJAXvPao3478JTk2uQVEmSzHWydOZCxGNn8uH8mInd4QJnoOWB91 23 | Olprw+4I9A3FXRnAC5asVwzQGpKFfX7v+OH6dY4WG38ZfCpHc1qq4solkKgGBR6y 24 | aJEWDtVCM2mxaCXHOz6WN74DRGNE19qQ//dbBdUvqhz7OLmx6GKdUSc= 25 | -----END CERTIFICATE----- 26 | -------------------------------------------------------------------------------- /note-java/src/main/resources/grpc.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAr/rM4X+mAfU/1PtHMIkentjPug9Tb1NNqZc+MfHYzYdxPsxY 3 | /SuYxiqax+GAgdmoZg59DhlkgkP2PVuHm20KUystMKZFguHsjVuzC0/b3EKg13H+ 4 | C1MopRLp1AEOL5WOVQ4RkKnjERUQt/SNM237M6nPDEohe4xqiDJOd7XEeK0tKtr7 5 | wHAg/4qdIRPN+NuUCGBlW2rNuzaqvlZHGvueE9kuNXDnrx+ogcepL0jqfY+sBZkM 6 | 9/wajmyJuLSQX77QWiHp6snlLmcx8M75qpjs3fe2rRodnvC+4CahwKf/W+JZzI66 7 | kXgvQD37tmIudBwtBBGupVojTwFx8pUIgwgMhQIDAQABAoIBAQCLZjYpYn5UCrvX 8 | sHzB+7xfxAs4ra8//lMExiOkWJmOpX2VazYKxiAsyc72CrFLKul0foGdS4wMjF1g 9 | WULgYc3N9+PCJ9PkS6agi0UW7tmQbs8OGuRMgEEwEf1bzMI5+1RWF+DeIVUXUQJ/ 10 | VZ8MYFCeqiKy7336ak5xOLhjp37Mv7+5RzByNsL9AYRskowmv4q5qwswYk8hS+aL 11 | YcyQuQAW7SNKhxlaPr7k6pOcdZ2/iBx+osjso8B+PKx5XzGWF+qTustXnyXgvcxV 12 | YtN7kwsXbTFQWLASGC7bfkQtte3re8Kk7P2hgTZdjsbglMptEMqVsLXbS+44daVQ 13 | QE2dbTA5AoGBAOmaid3/Ueny64m579is5ODjB018aEvuJefsYv67gh51Vk/bwniu 14 | GpFDY9HkcZeaij5YfKYbpthdTWsfX9Uzp6awq/oxAl0J/HTbW83epczHP6bE8GAp 15 | w8MgeR5ejSsu4Ri6t5XD4mrUny7SgMUJWdYMT4AW71GiaVCFaryWeswzAoGBAMDZ 16 | 9xpVxN1zMEOSGz7+F1ZYD9w3RX7LgIsAifYyQeFMIkO/etVy2hrEKsYvi616TV/+ 17 | SFdcFowkBLlcSzgQazoiTv5/ZCZ0gHNRCwToMXZzuHuJygNPSGqEA6JFx34ohODE 18 | 6jwYhBCVm+saIC5wsLoh7I2hZMkclU0ZEeM8PIxnAoGAUedvIzjbvY0AbF+W6n9k 19 | GD1BWDegUc8D11cYNnwD2S4GvyUsACf7BKd+Hh9cfG0gv09DFPJpAz9jX4W2kgf0 20 | ZgtXoPcB/yD7NPWcMIg51ZyegphWN0EtdAK0tKMuF8/t+D+vEoGFpzM5RK9lmq/2 21 | oYfbb+uaqSKqjiLZE5onH+UCgYB45qzRZ7/ZjSNO2UQXg7ghu2eGCWiaCv51JktX 22 | ez5t/grlKh/ZvP0bFqwyPxB0G15ytbmoeuTvyozjoAbQCQsQEP4w8rBYo2T75mzg 23 | EKkht36KhGGPHZ8ql8SncNOWNdTIDOtD7aKtuv1asLBILQG+TxI74FiM9ExtXzAl 24 | o1faNQKBgGdXe3H2AQOdLwg74JAL/GyHskajvU2FwmbX/w1fJVQaTnkioxM8EYzn 25 | EnapCnLFqI9PwkQBBwqt8tkG+9jU2MUmD/AUO7IKTSWWvxRRQAIqVfJCx43+hm42 26 | Llhaypua2MCUJRElwFyhnfDDFSZ+tPV+gbr07R6/UMJyAKUnL/v6 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /note-java/src/main/resources/grpc.srl: -------------------------------------------------------------------------------- 1 | F348DD0220A52F15 2 | -------------------------------------------------------------------------------- /note-java/src/main/resources/note-java.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDYzCCAksCCQDzSN0CIKUvFTANBgkqhkiG9w0BAQUFADB0MQswCQYDVQQGEwJV 3 | UzELMAkGA1UECBMCQ0ExFDASBgNVBAcTC0Zvc3RlciBDaXR5MQwwCgYDVQQKEwNJ 4 | Qk0xEzARBgNVBAsTClN0cm9uZ0xvb3AxHzAdBgkqhkiG9w0BCQEWEHJmZW5nQHVz 5 | LmlibS5jb20wHhcNMTYwOTIyMTg0MDMwWhcNMTcwOTIyMTg0MDMwWjBzMQswCQYD 6 | VQQGEwJVUzELMAkGA1UECBMCQ0ExFDASBgNVBAcTC0Zvc3RlciBDaXR5MQwwCgYD 7 | VQQKEwNJQk0xEjAQBgNVBAMTCW5vdGUtamF2YTEfMB0GCSqGSIb3DQEJARYQcmZl 8 | bmdAdXMuaWJtLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAO72 9 | kYxqc9Wuf9foc04o1Fi6BkvgYhPhR/p2p9rczMoNFE2wANWVCn4NzPZw9/DA/edV 10 | 5v2uF/4tnODye4WpfOqEbWwGcSOr3IFkJcpJxJPFDaeUz1LU+732WUFRbUO2j5BO 11 | rI7F/TvCk6r97Gfi2YQKj4kYt2vMYFBq9pLAvCjLqQk6djuxJGKFJYJ2zJdx8gQ3 12 | DfkJtr7Rm6mcSpS8+mYrirlFSty6KykcZDODr8ZX7m56Bd5cV9uIyKal1YXkL+CH 13 | UJ7mB68N18yeyR+Vz+IuSr1812iCSMBj1b7l6dtgsC94Q45SLJzZYhooTyYxXboZ 14 | b2RiK1YAq7bVweO2dk0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAIxYT7Vy1F1ni 15 | X+77M3TE5B8MVoyCFCnf1uitQtyjtIp5JCej49P8nZz5QY708e9lD0BOYEHHEgZz 16 | sjLWG/PEc+U40wsv7v5dM8mK3B6FjxE7XO3eeUL+VXCqTyYXhLhx0K6lPJ6eR8zg 17 | ik4bslJyeXw4QFWzlaNz+OfhUz5GitRtOQfm53BK0ery5zyTRGDRuX8n1B4/CIk/ 18 | 8Sow3f0fZyCejCw849S1pKcGLYknBgMkw9WQksShvngw6ybluZT0FBAlcEBD/FCX 19 | qf5JIYelIAV+KtAWcD0ivMuT4eDWiK0D3LAzm6elDhjNWk/CUK76s14JmXiMXaf5 20 | 7XA41+mMTA== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /note-java/src/main/resources/note-java.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICuDCCAaACAQAwczELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRQwEgYDVQQH 3 | EwtGb3N0ZXIgQ2l0eTEMMAoGA1UEChMDSUJNMRIwEAYDVQQDEwlub3RlLWphdmEx 4 | HzAdBgkqhkiG9w0BCQEWEHJmZW5nQHVzLmlibS5jb20wggEiMA0GCSqGSIb3DQEB 5 | AQUAA4IBDwAwggEKAoIBAQDu9pGManPVrn/X6HNOKNRYugZL4GIT4Uf6dqfa3MzK 6 | DRRNsADVlQp+Dcz2cPfwwP3nVeb9rhf+LZzg8nuFqXzqhG1sBnEjq9yBZCXKScST 7 | xQ2nlM9S1Pu99llBUW1Dto+QTqyOxf07wpOq/exn4tmECo+JGLdrzGBQavaSwLwo 8 | y6kJOnY7sSRihSWCdsyXcfIENw35Cba+0ZupnEqUvPpmK4q5RUrcuispHGQzg6/G 9 | V+5uegXeXFfbiMimpdWF5C/gh1Ce5gevDdfMnskflc/iLkq9fNdogkjAY9W+5enb 10 | YLAveEOOUiyc2WIaKE8mMV26GW9kYitWAKu21cHjtnZNAgMBAAGgADANBgkqhkiG 11 | 9w0BAQUFAAOCAQEAK8L6t1UjeZHYfir3otwImxtjbBILFSC1FcEq80gY9xV6hkwc 12 | iNjjcfOn1CeqHg7SxPCaaFX2NpxIyxVz3lUJO4kTVXir7IurvkKYdW5nY3uLHkMO 13 | 1/x5zHfGLHqyC/VGtRHShXCQ7tMV28WFrDygkxRoBtk9cZUfdExMBB7t3N7V4du7 14 | ZhfSVdZnpRMX5Jzlkg2HM/wJr4FluNdCLkXatJ59AZbtsuIM3PMf0H8pvNQHMvLC 15 | udGE2wiuC1FglzkKHJMMV/3sAjxVeFMdYUIxYSs1NOty/GrNzklIAtg44Y1S8H/W 16 | 0CKvB0gD6515LvUD9v6gG49DIDshNnx6ZfKzFg== 17 | -----END CERTIFICATE REQUEST----- 18 | -------------------------------------------------------------------------------- /note-java/src/main/resources/note-java.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEA7vaRjGpz1a5/1+hzTijUWLoGS+BiE+FH+nan2tzMyg0UTbAA 3 | 1ZUKfg3M9nD38MD951Xm/a4X/i2c4PJ7hal86oRtbAZxI6vcgWQlyknEk8UNp5TP 4 | UtT7vfZZQVFtQ7aPkE6sjsX9O8KTqv3sZ+LZhAqPiRi3a8xgUGr2ksC8KMupCTp2 5 | O7EkYoUlgnbMl3HyBDcN+Qm2vtGbqZxKlLz6ZiuKuUVK3LorKRxkM4OvxlfubnoF 6 | 3lxX24jIpqXVheQv4IdQnuYHrw3XzJ7JH5XP4i5KvXzXaIJIwGPVvuXp22CwL3hD 7 | jlIsnNliGihPJjFduhlvZGIrVgCrttXB47Z2TQIDAQABAoIBAHl/8N0+OwtIoEP9 8 | MrNo2x8J5q2ptvR4tQpKTmpfndiV8d7WQHZYQ294QOCE6+wDlJWM/de6LwTT2FIf 9 | uGJrnNH451HnrJsAhStc6kICY+rmZFzysr9g/2bE3PjzF+U366Lp/Z7SWEGMW1yz 10 | owHnO8WBQR3p7UjoL4hz+k7/pCvw9bEFObJ+MrLYrR2/qJEjj8LxO9zOrza8PLRr 11 | 02+iNJ/PK/BGn9ahTwYtQ92/qL2nJt0ubwEY9vD3VLjItV/fty5w45oadiJkNKJL 12 | Jkc9CjCjsac53HkmdXWrP3KpnPWuIFRYgWGVb34rxWmz1euLXfj1qFxnoFRvdO5j 13 | 1KK6L4ECgYEA/n33NSi0l+h3Nb675a18SnKuLjZqgSNmFXB7QxTcHSkpXTixFRmi 14 | 9K7puU4IycIsSn+S/QK5bha8YxVtHmbDMxamWWWw4dJsiL7gEkC1JV2PUXsSKQR/ 15 | WpFiF/7dwTzy3oN2qr4Gkvclz3qt09V44g+v32tH7HHbVOPuUkAvEL0CgYEA8GEM 16 | IjUk611HubjIunDZk0xCtdQGyUV4nqoHUwoDtec53P4jpLxGatyAnXGtD+wruoR9 17 | A1xd0MEzbEfvDZ+S4bqumSJzg3XRm/Pq7ln5QTztgMn84jW1eDlLywLQ/Cw1/0HD 18 | wQYVKxUHNXe4EYPgf3FpA7QVAB+wD68c0oZrvNECgYEAr9eTzyxQFEJw1Db1F47W 19 | uY4h4haBYrd12sx8ru4j5RZC2YHJgb3UBeMx5XMQyW+cMOGTS7TtMS8OfhwY6I+y 20 | woMc79o+7Nn7WkUhCBujzqyl0XQLKUJNSOP5doEI8nNNkQgfH3zrh/KzMnge0abH 21 | vtN9/w7ehHZrSW2why6IFrUCgYATtREmmM7PERW5T0H9rxTXc7AgciKgS7bI4024 22 | bCDDwBJJYPiRH/Jx/mP5BIHYxVcMcqJBLUhvvfc2UGtz5XpIUWLRITbxNy6ZJ5VC 23 | N1QtGg6quWSnMxoOojvph/CUTBedNsoSwCnqH044/vizTumP6T9f01rJaM+paYTm 24 | aajZkQKBgHF6RtKqySz4nBYhY+I6vUV6TTKnaKPMn08G3+QCTGWS4omZulMX6ZAT 25 | bosnpwLl+Nj0xGIgArTrupZ08DGGm7RUDYnzkU7/MrOTe5xNymJPmf7eI4oz32xU 26 | qDCJDmv5cIAzLKpvRkhv0jghNmoaRQqNgBmB6cb+m7z2cbPMECsS 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /note-java/src/main/resources/note-java.pfx: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDu9pGManPVrn/X 3 | 6HNOKNRYugZL4GIT4Uf6dqfa3MzKDRRNsADVlQp+Dcz2cPfwwP3nVeb9rhf+LZzg 4 | 8nuFqXzqhG1sBnEjq9yBZCXKScSTxQ2nlM9S1Pu99llBUW1Dto+QTqyOxf07wpOq 5 | /exn4tmECo+JGLdrzGBQavaSwLwoy6kJOnY7sSRihSWCdsyXcfIENw35Cba+0Zup 6 | nEqUvPpmK4q5RUrcuispHGQzg6/GV+5uegXeXFfbiMimpdWF5C/gh1Ce5gevDdfM 7 | nskflc/iLkq9fNdogkjAY9W+5enbYLAveEOOUiyc2WIaKE8mMV26GW9kYitWAKu2 8 | 1cHjtnZNAgMBAAECggEAeX/w3T47C0igQ/0ys2jbHwnmram29Hi1CkpOal+d2JXx 9 | 3tZAdlhDb3hA4ITr7AOUlYz917ovBNPYUh+4Ymuc0fjnUeesmwCFK1zqQgJj6uZk 10 | XPKyv2D/ZsTc+PMX5Tfroun9ntJYQYxbXLOjAec7xYFBHentSOgviHP6Tv+kK/D1 11 | sQU5sn4ystitHb+okSOPwvE73M6vNrw8tGvTb6I0n88r8Eaf1qFPBi1D3b+ovacm 12 | 3S5vARj28PdUuMi1X9+3LnDjmhp2ImQ0oksmRz0KMKOxpznceSZ1das/cqmc9a4g 13 | VFiBYZVvfivFabPV64td+PWoXGegVG907mPUorovgQKBgQD+ffc1KLSX6Hc1vrvl 14 | rXxKcq4uNmqBI2YVcHtDFNwdKSldOLEVGaL0rum5TgjJwixKf5L9ArluFrxjFW0e 15 | ZsMzFqZZZbDh0myIvuASQLUlXY9RexIpBH9akWIX/t3BPPLeg3aqvgaS9yXPeq3T 16 | 1XjiD6/fa0fscdtU4+5SQC8QvQKBgQDwYQwiNSTrXUe5uMi6cNmTTEK11AbJRXie 17 | qgdTCgO15znc/iOkvEZq3ICdca0P7Cu6hH0DXF3QwTNsR+8Nn5Lhuq6ZInODddGb 18 | 8+ruWflBPO2AyfziNbV4OUvLAtD8LDX/QcPBBhUrFQc1d7gRg+B/cWkDtBUAH7AP 19 | rxzShmu80QKBgQCv15PPLFAUQnDUNvUXjta5jiHiFoFit3XazHyu7iPlFkLZgcmB 20 | vdQF4zHlcxDJb5ww4ZNLtO0xLw5+HBjoj7LCgxzv2j7s2ftaRSEIG6POrKXRdAsp 21 | Qk1I4/l2gQjyc02RCB8ffOuH8rMyeB7Rpse+033/Dt6EdmtJbbCHLogWtQKBgBO1 22 | ESaYzs8RFblPQf2vFNdzsCByIqBLtsjjTbhsIMPAEklg+JEf8nH+Y/kEgdjFVwxy 23 | okEtSG+99zZQa3PlekhRYtEhNvE3LpknlUI3VC0aDqq5ZKczGg6iO+mH8JRMF502 24 | yhLAKeofTjj++LNO6Y/pP1/TWsloz6lphOZpqNmRAoGAcXpG0qrJLPicFiFj4jq9 25 | RXpNMqdoo8yfTwbf5AJMZZLiiZm6UxfpkBNuiyenAuX42PTEYiACtOu6lnTwMYab 26 | tFQNifORTv8ys5N7nE3KYk+Z/t4jijPfbFSoMIkOa/lwgDMsqm9GSG/SOCE2ahpF 27 | Co2AGYHpxv6bvPZxs8wQKxI= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /note-java/src/main/resources/private_key.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strongloop/loopback-example-polyglot/677edd9cc9fae067ec1be180c34b8659e5c000b6/note-java/src/main/resources/private_key.der -------------------------------------------------------------------------------- /note-java/src/main/resources/private_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpQIBAAKCAQEA4M2wJA9FC8RxmSGIShU2AFszeondEmbl4SW9hVEnHEX+5sUo 3 | JbzQOf8UCXzpZGmZVmW+BNjiS37T85/XUpdALmxy/+oC7GynBQd+y2xpfZnkyR7q 4 | IHSXb+YBdCTcZduX6U6JjcZqiuUp7sYrsGWWARQu3H/1keIoUcobi5WYwXzMNauu 5 | yWrQ9WL/eFleE1rPp4AaTHrdT0SjEq0rIOvEfJSlCNAQrEzKZ38NUoaxE/s+RnfW 6 | Xuqn/GnELwPujKGWEqIiNNnmS7+bwoiSAiauZnU2mplxbR/Ga5/AR5Aj/v+xjlTj 7 | eHZVw/UwU8b29tSTnVQYzNedKAgOAk/A+pgcWQIDAQABAoIBAQCQX2jU772PHh06 8 | lA98THz+45N/ChUhYVYXL260tPaiMM3QlSQAgG9WH9xVb5RQeYURuIu9fzBt/cjP 9 | OINHtn1wkxOUCUzRW8Iz96lVqY1iLbiB9cm1CEvNgHDEj3vAKrQ4EMlEdP4tr5kr 10 | kLe0NLmOfdDOqZsjkpQEWwEvtuaAs5or3m1hfT4zg1CBJJbZusxTzMrbM5Q4dz3m 11 | GcsAlpHG9/VC4dATDnPVdMK5MTDN0McrMMoBuOSjlmjNG3tcq1s9hLkiDwX+YhIy 12 | /2eA0xVZ02cnSLugtTFj0RUKMT84yeYnjDMRuagnikY55nkZQouvOuRI2zA7kGiZ 13 | IuCY96FVAoGBAPCFauSj+S8Lf1SzMmx+2uSsn0icODuNcjnEdOnzkw6oNFO1AyWf 14 | CCBSfbQL1n3+TKT99MzNpBzR4wQPXYiPST2aQWJyRDNJ674QCYhorqoK3IqzNFRY 15 | EW2NQGiM2YrRyFO/jl/cri2CHsZtsDx2cfJ1qxRmeVsKSg0mCR7J0FQ3AoGBAO9F 16 | Ul25ClsnLyFzxQxdY8AMEPHaHcnblwufhK9NRPC+/Hl6WAcg9axvvPa67bdpiSyV 17 | 9Wu7xgcHvbn6MXNBGqvOJTYIX2hqtIUmZddjZ8OQJj0Bnd5Yoc0KnNFf6gpTi+fF 18 | W5nT6uNhJAtK+PMIUNbHPUPmsNNbrMPWyWfbDevvAoGBAM2UO9lRjhr36M2snNy/ 19 | ULlEEqEtZaMBMzDNCmEtH/TXMGKbxSTS2U9HMspBlAP85+Xeau7ilfNMMo211Gx0 20 | jFVrE5fhswljlWvOqVc+oowU3Ixwv7mXzM2mffbe7NIKnP94K2kkZnvfTA6ukoQb 21 | eSmUCbZWIEQAd2rlp8X5GWfhAoGBAMVX8pA0fxMhZWD84CfLmusZyJwBHxKPF6xR 22 | fBkt7M93Zjs3KgVvLNF1M3B4Sno9BBOO92VM8Q55VwVeEo0RxFKxasV+8FnpsEwl 23 | mFemuD121eL5Q2DQz1qTBzY1Go47zCd0ApIEVDYHt7fMtTmVwT70A1bOnUcl1H+N 24 | 1n3fEUNnAoGALS5jWww8amXQHV79iTNAdEXrUSZcna3BpDNG1JqGdr2OvPxmHsAR 25 | CVveC2EeucV0NEPm5uSEWlnSjIcF/o8GaqHEyacWHafVeUR2BzBrevVt04GKo0GP 26 | Hl+HmrCjaQvJ+zIZasxIq+UdBwCjNxwwOTlsK5x5GNUKkCkukQuSqpw= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /note-java/src/main/resources/public_key.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strongloop/loopback-example-polyglot/677edd9cc9fae067ec1be180c34b8659e5c000b6/note-java/src/main/resources/public_key.der -------------------------------------------------------------------------------- /note-loopback/.apiconnect/config: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /note-loopback/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /note-loopback/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # http://editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | indent_style = space 9 | indent_size = 2 10 | end_of_line = lf 11 | charset = utf-8 12 | trim_trailing_whitespace = true 13 | insert_final_newline = true 14 | -------------------------------------------------------------------------------- /note-loopback/.eslintignore: -------------------------------------------------------------------------------- 1 | /client/ -------------------------------------------------------------------------------- /note-loopback/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "loopback" 3 | } -------------------------------------------------------------------------------- /note-loopback/.gitignore: -------------------------------------------------------------------------------- 1 | *.csv 2 | *.dat 3 | *.iml 4 | *.log 5 | *.out 6 | *.pid 7 | *.seed 8 | *.sublime-* 9 | *.swo 10 | *.swp 11 | *.tgz 12 | *.xml 13 | .DS_Store 14 | .idea 15 | .project 16 | .strong-pm 17 | coverage 18 | node_modules 19 | npm-debug.log 20 | -------------------------------------------------------------------------------- /note-loopback/.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-loopback": {} 3 | } -------------------------------------------------------------------------------- /note-loopback/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:6.9 2 | 3 | # Create app directory 4 | RUN mkdir -p /usr/src/note-loopback 5 | WORKDIR /usr/src/note-loopback 6 | 7 | # Install app dependencies 8 | COPY package.json /usr/src/note-loopback 9 | RUN npm install 10 | 11 | # Bundle app source 12 | COPY . /usr/src/note-loopback 13 | 14 | EXPOSE 3000 50051 15 | CMD [ "node", "." ] 16 | -------------------------------------------------------------------------------- /note-loopback/bin/generate-grpc-keys.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | if [ ! -f grpc.crt ]; then 3 | # Create a root CA certificate 4 | openssl req -newkey rsa:2048 -nodes -keyout grpc.key -x509 -days 365 -out grpc.crt 5 | fi 6 | 7 | # Create a certificate for note-loopback 8 | openssl genrsa -out note-loopback.key 2048 9 | openssl req -new -key note-loopback.key -out note-loopback.csr 10 | openssl x509 -req -in note-loopback.csr -CA grpc.crt -CAkey grpc.key -CAcreateserial -out note-loopback.crt -days 365 11 | 12 | # Netty requires pkcs8 13 | openssl pkcs8 -topk8 -nocrypt -out note-loopback.pfx -in note-loopback.key 14 | -------------------------------------------------------------------------------- /note-loopback/bin/grpc.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIERTCCAy2gAwIBAgIJALCZULSgjiqEMA0GCSqGSIb3DQEBBQUAMHQxCzAJBgNV 3 | BAYTAlVTMQswCQYDVQQIEwJDQTEUMBIGA1UEBxMLRm9zdGVyIENpdHkxDDAKBgNV 4 | BAoTA0lCTTETMBEGA1UECxMKU3Ryb25nTG9vcDEfMB0GCSqGSIb3DQEJARYQcmZl 5 | bmdAdXMuaWJtLmNvbTAeFw0xNjA5MjIxNTI5MTBaFw0xNzA5MjIxNTI5MTBaMHQx 6 | CzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEUMBIGA1UEBxMLRm9zdGVyIENpdHkx 7 | DDAKBgNVBAoTA0lCTTETMBEGA1UECxMKU3Ryb25nTG9vcDEfMB0GCSqGSIb3DQEJ 8 | ARYQcmZlbmdAdXMuaWJtLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC 9 | ggEBAK/6zOF/pgH1P9T7RzCJHp7Yz7oPU29TTamXPjHx2M2HcT7MWP0rmMYqmsfh 10 | gIHZqGYOfQ4ZZIJD9j1bh5ttClMrLTCmRYLh7I1bswtP29xCoNdx/gtTKKUS6dQB 11 | Di+VjlUOEZCp4xEVELf0jTNt+zOpzwxKIXuMaogyTne1xHitLSra+8BwIP+KnSET 12 | zfjblAhgZVtqzbs2qr5WRxr7nhPZLjVw568fqIHHqS9I6n2PrAWZDPf8Go5sibi0 13 | kF++0Foh6erJ5S5nMfDO+aqY7N33tq0aHZ7wvuAmocCn/1viWcyOupF4L0A9+7Zi 14 | LnQcLQQRrqVaI08BcfKVCIMIDIUCAwEAAaOB2TCB1jAdBgNVHQ4EFgQUwzuQS7Js 15 | JyInHKG+LTGP4AnBoGEwgaYGA1UdIwSBnjCBm4AUwzuQS7JsJyInHKG+LTGP4AnB 16 | oGGheKR2MHQxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEUMBIGA1UEBxMLRm9z 17 | dGVyIENpdHkxDDAKBgNVBAoTA0lCTTETMBEGA1UECxMKU3Ryb25nTG9vcDEfMB0G 18 | CSqGSIb3DQEJARYQcmZlbmdAdXMuaWJtLmNvbYIJALCZULSgjiqEMAwGA1UdEwQF 19 | MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADO4j94rjuRcapFw9zeRuqjltVniV7Hj 20 | D7e7gy/WtFesTY2x+lraJaMStUKnYlBf2haQRTuGxqXllQAOAKzLQmAjvOTc84dB 21 | C7C9IwI3/BanKXCmntxQOFblqo8UpK0gM2loNYU7s3Q5H8G2JnNUMOfcesMMyhLY 22 | aqPdhbTSYeK9BJAXvPao3478JTk2uQVEmSzHWydOZCxGNn8uH8mInd4QJnoOWB91 23 | Olprw+4I9A3FXRnAC5asVwzQGpKFfX7v+OH6dY4WG38ZfCpHc1qq4solkKgGBR6y 24 | aJEWDtVCM2mxaCXHOz6WN74DRGNE19qQ//dbBdUvqhz7OLmx6GKdUSc= 25 | -----END CERTIFICATE----- 26 | -------------------------------------------------------------------------------- /note-loopback/client.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: note-loopback 3 | 4 | var path = require('path'); 5 | var PROTO_PATH = path.join(__dirname, './proto/note.proto'); 6 | 7 | var grpc = require('grpc'); 8 | var proto = grpc.load(PROTO_PATH); 9 | var fs = require('fs'); 10 | 11 | var zipkinAgent = require('./lib/zipkin-agent'); 12 | 13 | function main() { 14 | var noteClient = new proto.note.NoteService('localhost:50051', 15 | grpc.credentials.createInsecure()); 16 | 17 | var rootCerts = fs.readFileSync(path.join(__dirname, './bin/grpc.crt')); 18 | var ssl = grpc.credentials.createSsl(rootCerts); 19 | 20 | var encryptionClient = new proto.note.NoteService('localhost:50052', ssl); 21 | var id = parseInt(process.argv[2]) || 1; 22 | 23 | zipkinAgent.traceClient('note-loopback-client', 24 | {zipkinServerUrl: 'http://localhost:9411'}, {}, 25 | function(metadata, done) { 26 | var note = { 27 | id: id, 28 | title: 'note1', 29 | content: 'my note' 30 | }; 31 | noteClient.create(note, metadata, done); 32 | }, function(err, response) { 33 | if (err) { 34 | console.error(err); 35 | } else { 36 | console.log('Response:', response); 37 | noteClient.findById({id: response.id}, function(err, note) { 38 | if (err) { 39 | console.error(err); 40 | return; 41 | } 42 | console.log('Note found: ', note); 43 | noteClient.find({}, function(err, notes) { 44 | if (err) { 45 | console.error(err); 46 | return; 47 | } 48 | console.log('Notes found: ', notes); 49 | setTimeout(function() { 50 | // Wait for 2 seconds so that tracing information can be written 51 | // out as the timer has 1 sec interval 52 | console.log('Done'); 53 | }, 2000); 54 | }); 55 | }); 56 | } 57 | }); 58 | } 59 | 60 | main(); 61 | -------------------------------------------------------------------------------- /note-loopback/client/README.md: -------------------------------------------------------------------------------- 1 | ## Client 2 | 3 | This is the place for your application front-end files. 4 | -------------------------------------------------------------------------------- /note-loopback/common/models/encryption-client.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2017. All Rights Reserved. 2 | // Node module: note-loopback 3 | 4 | 'use strict'; 5 | 6 | module.exports = function(Encryptionclient) { 7 | 8 | }; 9 | -------------------------------------------------------------------------------- /note-loopback/common/models/encryption-client.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "EncryptionClient", 3 | "base": "Model", 4 | "idInjection": true, 5 | "options": { 6 | "validateUpsert": true 7 | }, 8 | "properties": {}, 9 | "validations": [], 10 | "relations": {}, 11 | "acls": [], 12 | "methods": {} 13 | } 14 | -------------------------------------------------------------------------------- /note-loopback/common/models/note.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: note-loopback 3 | 4 | module.exports = function(Note) { 5 | var fs = require('fs'); 6 | 7 | var zipkinAgent = require('../../lib/zipkin-agent'); 8 | 9 | /* 10 | var path = require('path'); 11 | var grpc = require('grpc'); 12 | var PROTO_PATH = path.join(__dirname, '../../proto/note.proto'); 13 | 14 | var proto = grpc.load(PROTO_PATH); 15 | var rootCerts = fs.readFileSync(path.join(__dirname, '../../bin/grpc.crt')); 16 | var ssl = grpc.credentials.createSsl(rootCerts); 17 | var address = Note.settings.encryptionServiceAddress || 'localhost:50052'; 18 | console.log('Encryption service address: %s', address); 19 | var encryptionClient = new proto.note.EncryptionService(address, 20 | ssl // grpc.credentials.createInsecure() 21 | ); 22 | 23 | var translationServiceAddress = Note.settings.translationServiceAddress || 24 | 'localhost:50053'; 25 | console.log('Translation service address: %s', translationServiceAddress); 26 | var translationClient = new proto.note.TranslationService( 27 | translationServiceAddress, 28 | grpc.credentials.createInsecure() 29 | ); 30 | */ 31 | 32 | Note.observe('before save', function encryptContent(ctx, next) { 33 | console.log('Requesting to encrypt content: %s', ctx.instance.content); 34 | 35 | zipkinAgent.traceClient('note-loopback.encrypt', 36 | {zipkinServerUrl: Note.settings.zipkinServerUrl}, ctx.options, 37 | function(metadata, done) { 38 | Note.app.models.TranslationClient.translate(ctx.instance.toJSON(), metadata, 39 | function(err, note) { 40 | if (err) { 41 | return done(err); 42 | } 43 | console.log('Content is now translated: %s', note.content); 44 | Note.app.models.EncryptionClient.encrypt(note, metadata, 45 | function(err, note) { 46 | if (err) { 47 | return done(err); 48 | } 49 | console.log('Content is now encrypted: %s', note.content); 50 | ctx.instance.content = note.content; 51 | done(); 52 | }); 53 | }); 54 | }, function(err, note) { 55 | if (err) { 56 | console.error(err); 57 | return next(err); 58 | } 59 | next(); 60 | }); 61 | }); 62 | }; 63 | -------------------------------------------------------------------------------- /note-loopback/common/models/note.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Note", 3 | "options": { 4 | "zipkinServerUrl": "http://zipkin:9411", 5 | "forceId": false 6 | }, 7 | "properties": { 8 | "title": { 9 | "type": "string", 10 | "required": true 11 | }, 12 | "content": { 13 | "type": "string" 14 | } 15 | }, 16 | "validations": [], 17 | "relations": {}, 18 | "acls": [], 19 | "methods": {} 20 | } 21 | -------------------------------------------------------------------------------- /note-loopback/common/models/translation-client.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2017. All Rights Reserved. 2 | // Node module: note-loopback 3 | 4 | 'use strict'; 5 | 6 | module.exports = function(Translationclient) { 7 | 8 | }; 9 | -------------------------------------------------------------------------------- /note-loopback/common/models/translation-client.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TranslationClient", 3 | "base": "Model", 4 | "idInjection": true, 5 | "options": { 6 | "validateUpsert": true 7 | }, 8 | "properties": {}, 9 | "validations": [], 10 | "relations": {}, 11 | "acls": [], 12 | "methods": {} 13 | } 14 | -------------------------------------------------------------------------------- /note-loopback/lib/zipkin-agent.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: note-loopback 3 | 4 | var zipkin = require('zipkin'); 5 | const { 6 | ExplicitContext, 7 | Tracer, 8 | Annotation, 9 | BatchRecorder, 10 | HttpHeaders: Header, 11 | option: {Some, None}, 12 | TraceId 13 | } = zipkin; 14 | 15 | var grpc = require('grpc'); 16 | 17 | var HttpLogger = require('zipkin-transport-http').HttpLogger; 18 | 19 | function createTracer(options) { 20 | var serverUrl = options.zipkinServerUrl || 'http://localhost:9411'; 21 | console.log('Zipkin server: %s', serverUrl); 22 | var recorder = new BatchRecorder({ 23 | logger: new HttpLogger({ 24 | endpoint: serverUrl + '/api/v1/spans' 25 | }), 26 | timeout: options.timeout == null ? 60 * 1000000 : options.timeout 27 | }); 28 | 29 | var ctxImpl = new ExplicitContext(); 30 | var tracer = new Tracer({ctxImpl, recorder}); // configure your tracer properly here 31 | return tracer; 32 | } 33 | 34 | function containsRequiredHeaders(metadata) { 35 | return metadata[Header.TraceId.toLowerCase()] !== undefined && 36 | metadata[Header.SpanId.toLowerCase()] !== undefined; 37 | } 38 | 39 | function readHeader(metadata, header) { 40 | const val = metadata[header.toLowerCase()]; 41 | if (val != null) { 42 | return new Some(val); 43 | } else { 44 | return None; 45 | } 46 | } 47 | 48 | function stringToBoolean(str) { 49 | return str === '1'; 50 | } 51 | 52 | function stringToIntOption(str) { 53 | try { 54 | var val = parseInt(str); 55 | if (isNaN(val)) { 56 | return None; 57 | } 58 | return new Some(val); 59 | } catch (err) { 60 | return None; 61 | } 62 | } 63 | 64 | function beginTrace(tracer, metadata) { 65 | console.log('Metadata for tracing: ', metadata); 66 | if (containsRequiredHeaders(metadata)) { 67 | const spanId = readHeader(metadata, Header.SpanId); 68 | spanId.ifPresent(sid => { 69 | const traceId = readHeader(metadata, Header.TraceId); 70 | const parentSpanId = readHeader(metadata, Header.ParentSpanId); 71 | const sampled = readHeader(metadata, Header.Sampled); 72 | const flags = readHeader(metadata, Header.Flags).flatMap(stringToIntOption).getOrElse(0); 73 | const id = new TraceId({ 74 | traceId, 75 | parentId: spanId, 76 | spanId: tracer.createChildId().spanId, 77 | sampled: sampled.map(stringToBoolean), 78 | flags 79 | }); 80 | tracer.setId(id); 81 | }); 82 | } else { 83 | tracer.setId(tracer.createRootId()); 84 | if (metadata[Header.Flags]) { 85 | const currentId = tracer.id; 86 | const idWithFlags = new TraceId({ 87 | traceId: currentId.traceId, 88 | parentId: currentId.parentId, 89 | spanId: currentId.spanId, 90 | sampled: currentId.sampled, 91 | flags: readHeader(metadata, Header.Flags) 92 | }); 93 | tracer.setId(idWithFlags); 94 | } 95 | } 96 | return tracer.id; 97 | } 98 | 99 | function serverInterceptorFactory(options) { 100 | options = options || {}; 101 | 102 | var tracer = createTracer(options); 103 | 104 | return function intercept(serviceName, fn) { 105 | return function wrappedServiceMethod(call, cb) { 106 | var metadata = call.metadata.getMap(); 107 | 108 | tracer.scoped(() => { 109 | beginTrace(tracer, metadata); 110 | const id = tracer.id; 111 | tracer.recordServiceName(serviceName); 112 | // tracer.recordRpc(req.method); 113 | // tracer.recordBinary('http.url', formatRequestUrl(req)); 114 | tracer.recordAnnotation(new Annotation.ServerRecv()); 115 | // tracer.recordAnnotation(new Annotation.LocalAddr({port})); 116 | 117 | if (id.flags !== 0 && id.flags != null) { 118 | tracer.recordBinary(Header.Flags, id.flags.toString()); 119 | } 120 | 121 | fn(call, function() { 122 | var args = [].slice.call(arguments); 123 | tracer.scoped(() => { 124 | tracer.setId(id); 125 | // tracer.recordBinary('http.status_code', res.statusCode.toString()); 126 | tracer.recordAnnotation(new Annotation.ServerSend()); 127 | cb.apply(this, args); 128 | }); 129 | }); 130 | }); 131 | }; 132 | }; 133 | } 134 | 135 | function traceClient(serviceName, options, headers, fn, cb) { 136 | 137 | options = options || {zipkinServerUrl: 'http://zipkin:9411'}; 138 | var metadata = new grpc.Metadata(); 139 | 140 | var tracer = createTracer(options); 141 | beginTrace(tracer, headers); 142 | var id = tracer.id; 143 | 144 | metadata.set(Header.TraceId, id.traceId); 145 | metadata.set(Header.SpanId, id.spanId); 146 | metadata.set(Header.ParentSpanId, id.parentId); 147 | if (id.sampled.present) { 148 | metadata.set(Header.Sampled, '1'); 149 | } else { 150 | metadata.set(Header.Sampled, '0'); 151 | } 152 | metadata.set(Header.Flags, id.flags.toString()); 153 | 154 | tracer.scoped(function() { 155 | tracer.recordServiceName(serviceName); 156 | tracer.recordAnnotation(new Annotation.ClientSend()); 157 | 158 | if (id.flags !== 0 && id.flags != null) { 159 | tracer.recordBinary(Header.Flags, id.flags.toString()); 160 | } 161 | 162 | fn(metadata, function() { 163 | var args = [].slice.call(arguments); 164 | tracer.scoped(() => { 165 | tracer.setId(id); 166 | tracer.recordAnnotation(new Annotation.ClientRecv()); 167 | cb.apply(this, args); 168 | }); 169 | }); 170 | }); 171 | } 172 | 173 | module.exports = { 174 | createTracer, 175 | beginTrace, 176 | serverInterceptorFactory, 177 | HttpHeaders: Header, 178 | zipkin, 179 | traceClient 180 | }; 181 | -------------------------------------------------------------------------------- /note-loopback/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "note-loopback", 3 | "version": "1.0.0", 4 | "main": "server/server.js", 5 | "scripts": { 6 | "lint": "eslint .", 7 | "start": "node .", 8 | "posttest": "npm run lint && nsp check" 9 | }, 10 | "dependencies": { 11 | "compression": "^1.6.2", 12 | "cors": "^2.8.3", 13 | "grpc": "^1.3.9", 14 | "helmet": "^3.6.1", 15 | "loopback": "^3.8.0", 16 | "loopback-boot": "^2.24.1", 17 | "loopback-component-explorer": "^4.2.0", 18 | "loopback-connector-grpc": "github:strongloop/loopback-connector-grpc", 19 | "loopback-connector-mongodb": "^3.1.0", 20 | "loopback-datasource-juggler": "^3.9.2", 21 | "serve-favicon": "^2.4.3", 22 | "strong-error-handler": "^2.1.0", 23 | "zipkin": "^0.7.1", 24 | "zipkin-instrumentation-express": "^0.7.1", 25 | "zipkin-transport-http": "^0.7.1" 26 | }, 27 | "devDependencies": { 28 | "eslint": "^3.15.0", 29 | "eslint-config-loopback": "^8.0.0", 30 | "nsp": "^2.6.3" 31 | }, 32 | "repository": { 33 | "type": "", 34 | "url": "" 35 | }, 36 | "license": "MIT", 37 | "description": "note-loopback" 38 | } 39 | -------------------------------------------------------------------------------- /note-loopback/proto/encryption.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package note; 4 | 5 | import "note-model.proto"; 6 | 7 | service EncryptionService { 8 | rpc encrypt (Note) returns (Note); 9 | rpc decrypt (Note) returns (Note); 10 | }; 11 | 12 | -------------------------------------------------------------------------------- /note-loopback/proto/note-model.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package note; 4 | 5 | option java_multiple_files = true; 6 | option java_package = "com.ibm.apiconnect.demo.polyglot"; 7 | option java_outer_classname = "NoteMessage"; 8 | 9 | message Note { 10 | option(loopback.model) = { 11 | name: "Note" 12 | }; 13 | int32 id = 1; 14 | string title = 2; 15 | string content = 3; 16 | }; 17 | 18 | -------------------------------------------------------------------------------- /note-loopback/proto/note.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package note; 4 | 5 | option java_multiple_files = true; 6 | option java_package = "com.ibm.apiconnect.demo.polyglot"; 7 | option java_outer_classname = "NoteMessage"; 8 | 9 | // See extensions at https://github.com/googleapis/googleapis/tree/master/google/api 10 | 11 | /* 12 | import "google/protobuf/any.proto"; 13 | message Filter { 14 | google.protobuf.Any where = 1; 15 | int32 limit = 2; 16 | int32 offset = 3; 17 | repeated string order = 4; 18 | google.protobuf.Any fields = 5; 19 | google.protobuf.Any include = 6; 20 | }; 21 | */ 22 | 23 | import "note-model.proto"; 24 | 25 | message FindByIdRequest { 26 | int32 id = 1; 27 | }; 28 | 29 | message FindRequest { 30 | // Filter filter = 1; 31 | }; 32 | 33 | message FindResponse { 34 | repeated Note notes = 1; 35 | }; 36 | 37 | service NoteService { 38 | rpc create (Note) returns (Note) { 39 | option (loopback.http) = { 40 | post: "/notes" 41 | body: "*" 42 | }; 43 | }; 44 | rpc findById (FindByIdRequest) returns (Note) { 45 | option (loopback.http) = { 46 | get: "/notes/{id}" 47 | }; 48 | }; 49 | rpc find (FindRequest) returns (FindResponse) { 50 | option (loopback.http) = { 51 | get: "/notes" 52 | }; 53 | }; 54 | option (loopback.http) = { 55 | description: "Note Service" 56 | }; 57 | }; 58 | 59 | -------------------------------------------------------------------------------- /note-loopback/proto/translation.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package note; 4 | 5 | import "note-model.proto"; 6 | 7 | service TranslationService { 8 | rpc translate (Note) returns (Note); 9 | }; 10 | 11 | -------------------------------------------------------------------------------- /note-loopback/server/boot/authentication.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016. All Rights Reserved. 2 | // Node module: note-loopback 3 | 4 | 'use strict'; 5 | 6 | module.exports = function enableAuthentication(server) { 7 | // enable authentication 8 | server.enableAuth(); 9 | }; 10 | -------------------------------------------------------------------------------- /note-loopback/server/boot/grpc-server.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016,2017. All Rights Reserved. 2 | // Node module: note-loopback 3 | 4 | 'use strict'; 5 | 6 | module.exports = function(app) { 7 | var path = require('path'); 8 | var PROTO_PATH = path.join(__dirname, '../../proto/note.proto'); 9 | 10 | var grpc = require('grpc'); 11 | var proto = grpc.load(PROTO_PATH); 12 | 13 | var zipkinAgent = require('../../lib/zipkin-agent'); 14 | 15 | /** 16 | * Implements the SayHello RPC method. 17 | */ 18 | function create(call, callback) { 19 | var note = call.request; 20 | var options = call.metadata.getMap(); 21 | app.models.Note.create(note, options, function(err, result) { 22 | if (err) return callback(err); 23 | callback(null, result.toJSON()); 24 | }); 25 | } 26 | 27 | function findById(call, callback) { 28 | var id = call.request.id; 29 | var options = call.metadata.getMap(); 30 | app.models.Note.findById(id, options, function(err, result) { 31 | if (err) return callback(err); 32 | callback(null, result.toJSON()); 33 | }); 34 | } 35 | 36 | function find(call, callback) { 37 | var options = call.metadata.getMap(); 38 | var filter = (call.request && call.request.filter) || {}; 39 | app.models.Note.find(filter, options, function(err, values) { 40 | callback(err, { 41 | notes: values.map(function(v) { 42 | var obj = v.toJSON(); 43 | obj.id = parseInt(obj.id); 44 | return obj; 45 | }), 46 | }); 47 | }); 48 | } 49 | 50 | /** 51 | * Starts an RPC server that receives requests for the Greeter service at the 52 | * sample server port 53 | */ 54 | function main() { 55 | var server = new grpc.Server(); 56 | var remotingConfig = app.get('remoting') || {}; 57 | var grpcConfig = remotingConfig.grpc || {}; 58 | var host = grpcConfig.host || '0.0.0.0'; 59 | var port = grpcConfig.port || 50051; 60 | var address = host + ':' + port; 61 | var zipkinServerUrl = grpcConfig.zipkinServerUrl || 'http://localhost:9411'; 62 | 63 | var zipkinFactory = zipkinAgent.serverInterceptorFactory( 64 | {zipkinServerUrl: zipkinServerUrl}); 65 | server.addService(proto.note.NoteService.service, { 66 | create: zipkinFactory('note-loopback.create', create), 67 | findById: zipkinFactory('note-loopback.findById', findById), 68 | find: zipkinFactory('note-loopback.find', find), 69 | }); 70 | 71 | server.bind(address, grpc.ServerCredentials.createInsecure()); 72 | server.start(); 73 | console.log('Note gRPC service is running at %s', address); 74 | } 75 | 76 | main(); 77 | }; 78 | 79 | -------------------------------------------------------------------------------- /note-loopback/server/boot/root.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016. All Rights Reserved. 2 | // Node module: note-loopback 3 | 4 | 'use strict'; 5 | 6 | module.exports = function(server) { 7 | // Install a `/` route that returns server status 8 | var router = server.loopback.Router(); 9 | router.get('/', server.loopback.status()); 10 | server.use(router); 11 | }; 12 | -------------------------------------------------------------------------------- /note-loopback/server/component-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "loopback-component-explorer": { 3 | "mountPath": "/explorer" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /note-loopback/server/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "restApiRoot": "/api", 3 | "host": "0.0.0.0", 4 | "port": 3000, 5 | "remoting": { 6 | "context": false, 7 | "rest": { 8 | "normalizeHttpPath": false, 9 | "xml": false 10 | }, 11 | "json": { 12 | "strict": false, 13 | "limit": "100kb" 14 | }, 15 | "urlencoded": { 16 | "extended": true, 17 | "limit": "100kb" 18 | }, 19 | "cors": false, 20 | "handleErrors": false, 21 | "grpc": { 22 | "host": "0.0.0.0", 23 | "port": 50051, 24 | "zipkinServerUrl": "http://zipkin:9411" 25 | } 26 | }, 27 | "legacyExplorer": false 28 | } 29 | -------------------------------------------------------------------------------- /note-loopback/server/datasources.json: -------------------------------------------------------------------------------- 1 | { 2 | "db": { 3 | "name": "db", 4 | "connector": "memory" 5 | }, 6 | "note-mongo": { 7 | "name": "note-mongo", 8 | "host": "note-mongo", 9 | "port": 27017, 10 | "database": "notedb", 11 | "connector": "mongodb" 12 | }, 13 | "grpc-encryption": { 14 | "url": "note-java:50052", 15 | "name": "grpc-encryption", 16 | "connector": "loopback-connector-grpc", 17 | "lazyConnect": false, 18 | "spec": "proto/encryption.proto", 19 | "security": { 20 | "rootCerts": "bin/grpc.crt" 21 | } 22 | }, 23 | "grpc-translation": { 24 | "url": "note-swift:50053", 25 | "name": "grpc-translation", 26 | "connector": "loopback-connector-grpc", 27 | "lazyConnect": false, 28 | "spec": "proto/translation.proto" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /note-loopback/server/middleware.development.json: -------------------------------------------------------------------------------- 1 | { 2 | "final:after": { 3 | "strong-error-handler": { 4 | "params": { 5 | "debug": true, 6 | "log": true 7 | } 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /note-loopback/server/middleware.json: -------------------------------------------------------------------------------- 1 | { 2 | "initial:before": { 3 | "loopback#favicon": {} 4 | }, 5 | "initial": { 6 | "./middleware/zipkin": { 7 | "params": { 8 | "zipkinServerUrl": "http://zipkin:9411" 9 | } 10 | }, 11 | "compression": {}, 12 | "cors": { 13 | "params": { 14 | "origin": true, 15 | "credentials": true, 16 | "maxAge": 86400 17 | } 18 | }, 19 | "helmet#xssFilter": {}, 20 | "helmet#frameguard": { 21 | "params": [ 22 | "deny" 23 | ] 24 | }, 25 | "helmet#hsts": { 26 | "params": { 27 | "maxAge": 0, 28 | "includeSubdomains": true 29 | } 30 | }, 31 | "helmet#hidePoweredBy": {}, 32 | "helmet#ieNoOpen": {}, 33 | "helmet#noSniff": {}, 34 | "helmet#noCache": { 35 | "enabled": false 36 | } 37 | }, 38 | "session": {}, 39 | "auth": {}, 40 | "parse": {}, 41 | "routes": { 42 | "loopback#rest": { 43 | "paths": [ 44 | "${restApiRoot}" 45 | ] 46 | } 47 | }, 48 | "files": {}, 49 | "final": { 50 | "loopback#urlNotFound": {} 51 | }, 52 | "final:after": { 53 | "strong-error-handler": {} 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /note-loopback/server/middleware/zipkin.js: -------------------------------------------------------------------------------- 1 | // Copyright Owner 2016,2017. All Rights Reserved. 2 | // Node module: 3 | 4 | module.exports = function(options) { 5 | var zipkinAgent = require('../../lib/zipkin-agent'); 6 | var zipkinMiddleware = require('zipkin-instrumentation-express').expressMiddleware; 7 | var tracer = zipkinAgent.createTracer(options); 8 | 9 | // Add the Zipkin middleware 10 | return zipkinMiddleware({ 11 | tracer, 12 | serviceName: 'note-loopback' // name of this application 13 | }); 14 | }; 15 | -------------------------------------------------------------------------------- /note-loopback/server/model-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "sources": [ 4 | "loopback/common/models", 5 | "loopback/server/models", 6 | "../common/models", 7 | "./models" 8 | ], 9 | "mixins": [ 10 | "loopback/common/mixins", 11 | "loopback/server/mixins", 12 | "../common/mixins", 13 | "./mixins" 14 | ] 15 | }, 16 | "User": { 17 | "dataSource": "db" 18 | }, 19 | "AccessToken": { 20 | "dataSource": "db", 21 | "public": false 22 | }, 23 | "ACL": { 24 | "dataSource": "db", 25 | "public": false 26 | }, 27 | "RoleMapping": { 28 | "dataSource": "db", 29 | "public": false 30 | }, 31 | "Role": { 32 | "dataSource": "db", 33 | "public": false 34 | }, 35 | "Note": { 36 | "dataSource": "note-mongo" 37 | }, 38 | "EncryptionClient": { 39 | "dataSource": "grpc-encryption", 40 | "public": false 41 | }, 42 | "TranslationClient": { 43 | "dataSource": "grpc-translation", 44 | "public": false 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /note-loopback/server/server.js: -------------------------------------------------------------------------------- 1 | // Copyright IBM Corp. 2016. All Rights Reserved. 2 | // Node module: note-loopback 3 | 4 | 'use strict'; 5 | 6 | var loopback = require('loopback'); 7 | var boot = require('loopback-boot'); 8 | 9 | var app = module.exports = loopback(); 10 | 11 | app.start = function() { 12 | // start the web server 13 | return app.listen(function() { 14 | app.emit('started'); 15 | var baseUrl = app.get('url').replace(/\/$/, ''); 16 | console.log('Web server listening at: %s', baseUrl); 17 | if (app.get('loopback-component-explorer')) { 18 | var explorerPath = app.get('loopback-component-explorer').mountPath; 19 | console.log('Browse your REST API at %s%s', baseUrl, explorerPath); 20 | } 21 | }); 22 | }; 23 | 24 | // Bootstrap the application, configure models, datasources and middleware. 25 | // Sub-apps like REST API are mounted via boot scripts. 26 | boot(app, __dirname, function(err) { 27 | if (err) throw err; 28 | 29 | // start the server if `$ node server.js` 30 | if (require.main === module) 31 | app.start(); 32 | }); 33 | -------------------------------------------------------------------------------- /note-swift/.dockerignore: -------------------------------------------------------------------------------- 1 | .build 2 | Packages 3 | -------------------------------------------------------------------------------- /note-swift/.gitignore: -------------------------------------------------------------------------------- 1 | .build 2 | Packages 3 | -------------------------------------------------------------------------------- /note-swift/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ibmcom/swift-ubuntu 2 | 3 | # Create app directory 4 | RUN mkdir -p /usr/src/note-swift 5 | WORKDIR /usr/src/note-swift 6 | 7 | # Bundle app source 8 | COPY . /usr/src/note-swift 9 | 10 | RUN make 11 | 12 | EXPOSE 50053 13 | CMD [ ".build/debug/note-swift", "serve" ] 14 | -------------------------------------------------------------------------------- /note-swift/Makefile: -------------------------------------------------------------------------------- 1 | # Build 2 | LDFLAGS = -Xlinker -lz 3 | 4 | all: 5 | swift build -v $(LDFLAGS) 6 | 7 | clean: 8 | rm -rf Packages 9 | rm -rf .build 10 | 11 | run: all 12 | .build/debug/note-swift serve & 13 | 14 | run-ssl: all 15 | .build/debug/note-swift serve -ssl & 16 | -------------------------------------------------------------------------------- /note-swift/Package.swift: -------------------------------------------------------------------------------- 1 | import PackageDescription 2 | let package = Package ( 3 | name: "note-swift", 4 | dependencies: [ 5 | .Package(url: "https://github.com/grpc/grpc-swift.git", Version(0,1,10)), 6 | .Package(url: "https://github.com/apple/swift-protobuf.git", Version(0,9,24)), 7 | ] 8 | ) 9 | -------------------------------------------------------------------------------- /note-swift/Sources/NoteProvider.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class NoteProvider : Note_TranslationServiceProvider { 4 | 5 | // Translate the note to Chinese 6 | func translate(request : Note_Note, session : Note_TranslationServicetranslateSession) throws -> Note_Note { 7 | print("Request: " + String(describing:request)); 8 | var content = request.content; 9 | if (content == "string") { 10 | content = "字符串"; 11 | } 12 | return Note_Note( 13 | id: request.id, 14 | title: request.title, 15 | content: "Swift<" + content + ">" 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /note-swift/Sources/main.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import gRPC 3 | import CgRPC 4 | import Dispatch 5 | 6 | print("\(CommandLine.arguments)") 7 | 8 | // server options 9 | var server : Bool = false 10 | 11 | // general configuration 12 | var useSSL : Bool = false 13 | 14 | var i : Int = 0 15 | while i < Int(CommandLine.argc) { 16 | let arg = CommandLine.arguments[i] 17 | i = i + 1 18 | if i == 1 { 19 | continue // skip the first argument 20 | } 21 | 22 | if arg == "serve" { 23 | server = true 24 | } else if arg == "-ssl" { 25 | useSSL = true 26 | } 27 | } 28 | 29 | var latch = CountDownLatch(1) 30 | 31 | gRPC.initialize() 32 | 33 | if server { 34 | let noteProvider = NoteProvider() 35 | var noteServer: Note_TranslationServiceServer! 36 | 37 | if useSSL { 38 | print("Starting secure server") 39 | let certificateURL = URL(fileURLWithPath:"grpc.crt") 40 | let keyURL = URL(fileURLWithPath:"grpc.key") 41 | noteServer = Note_TranslationServiceServer(address:"0.0.0.0:50053", 42 | certificateURL:certificateURL, 43 | keyURL:keyURL, 44 | provider:noteProvider) 45 | } else { 46 | print("Starting insecure server") 47 | noteServer = Note_TranslationServiceServer(address:"0.0.0.0:50053", 48 | provider:noteProvider) 49 | } 50 | noteServer.start() 51 | // Block to keep the main thread from finishing while the server runs. 52 | // This server never exits. Kill the process to stop it. 53 | latch.wait() 54 | } 55 | 56 | -------------------------------------------------------------------------------- /note-swift/Sources/note.client.pb.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * DO NOT EDIT. 3 | * 4 | * Generated by the protocol buffer compiler. 5 | * Source: note.proto 6 | * 7 | */ 8 | 9 | /* 10 | * 11 | * Copyright 2016, Google Inc. 12 | * All rights reserved. 13 | * 14 | * Redistribution and use in source and binary forms, with or without 15 | * modification, are permitted provided that the following conditions are 16 | * met: 17 | * 18 | * * Redistributions of source code must retain the above copyright 19 | * notice, this list of conditions and the following disclaimer. 20 | * * Redistributions in binary form must reproduce the above 21 | * copyright notice, this list of conditions and the following disclaimer 22 | * in the documentation and/or other materials provided with the 23 | * distribution. 24 | * * Neither the name of Google Inc. nor the names of its 25 | * contributors may be used to endorse or promote products derived from 26 | * this software without specific prior written permission. 27 | * 28 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 29 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 30 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 31 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 32 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 33 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 34 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 35 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 36 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 37 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 38 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39 | * 40 | */ 41 | 42 | import Foundation 43 | import gRPC 44 | 45 | /// Type for errors thrown from generated client code. 46 | public enum Note_TranslationServiceClientError : Error { 47 | case endOfStream 48 | case invalidMessageReceived 49 | case error(c: CallResult) 50 | } 51 | // translate (Unary) 52 | public class Note_TranslationServicetranslateCall { 53 | private var call : Call 54 | 55 | /// Create a call. 56 | fileprivate init(_ channel: Channel) { 57 | self.call = channel.makeCall("/note.TranslationService/translate") 58 | } 59 | 60 | /// Run the call. Blocks until the reply is received. 61 | fileprivate func run(request: Note_Note, 62 | metadata: Metadata) throws -> Note_Note { 63 | let latch = CountDownLatch(1) 64 | var callResult : CallResult! 65 | var response : Note_Note? 66 | let requestData = try request.serializeProtobuf() 67 | try call.start(.unary, 68 | metadata:metadata, 69 | message:requestData) 70 | {(_callResult) in 71 | callResult = _callResult 72 | if let responseData = callResult.resultData { 73 | response = try? Note_Note(protobuf:responseData) 74 | } 75 | latch.signal() 76 | } 77 | latch.wait() 78 | if let response = response { 79 | return response 80 | } else { 81 | throw Note_TranslationServiceClientError.error(c: callResult) 82 | } 83 | } 84 | } 85 | 86 | 87 | // Call methods of this class to make API calls. 88 | public class Note_TranslationServiceService { 89 | private var channel: Channel 90 | 91 | /// This metadata will be sent with all requests. 92 | public var metadata : Metadata 93 | 94 | /// This property allows the service host name to be overridden. 95 | /// For example, it can be used to make calls to "localhost:8080" 96 | /// appear to be to "example.com". 97 | public var host : String { 98 | get { 99 | return self.channel.host 100 | } 101 | set { 102 | self.channel.host = newValue 103 | } 104 | } 105 | 106 | /// Create a client that makes insecure connections. 107 | public init(address: String) { 108 | gRPC.initialize() 109 | channel = Channel(address:address) 110 | metadata = Metadata() 111 | } 112 | 113 | /// Create a client that makes secure connections. 114 | public init(address: String, certificates: String?, host: String?) { 115 | gRPC.initialize() 116 | channel = Channel(address:address, certificates:certificates, host:host) 117 | metadata = Metadata() 118 | } 119 | 120 | // Synchronous. Unary. 121 | public func translate(_ request: Note_Note) throws -> Note_Note { 122 | return try Note_TranslationServicetranslateCall(channel).run(request:request, metadata:metadata) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /note-swift/Sources/note.pb.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * DO NOT EDIT. 3 | * 4 | * Generated by the protocol buffer compiler. 5 | * Source: note.proto 6 | * 7 | */ 8 | 9 | import Foundation 10 | import SwiftProtobuf 11 | 12 | 13 | public struct Note_Note: ProtobufGeneratedMessage { 14 | public var swiftClassName: String {return "Note_Note"} 15 | public var protoMessageName: String {return "Note"} 16 | public var protoPackageName: String {return "note"} 17 | public var jsonFieldNames: [String: Int] {return [ 18 | "id": 1, 19 | "title": 2, 20 | "content": 3, 21 | ]} 22 | public var protoFieldNames: [String: Int] {return [ 23 | "id": 1, 24 | "title": 2, 25 | "content": 3, 26 | ]} 27 | 28 | public var id: Int32 = 0 29 | 30 | public var title: String = "" 31 | 32 | public var content: String = "" 33 | 34 | public init() {} 35 | 36 | public init(id: Int32? = nil, 37 | title: String? = nil, 38 | content: String? = nil) 39 | { 40 | if let v = id { 41 | self.id = v 42 | } 43 | if let v = title { 44 | self.title = v 45 | } 46 | if let v = content { 47 | self.content = v 48 | } 49 | } 50 | 51 | public mutating func _protoc_generated_decodeField(setter: inout ProtobufFieldDecoder, protoFieldNumber: Int) throws -> Bool { 52 | let handled: Bool 53 | switch protoFieldNumber { 54 | case 1: handled = try setter.decodeSingularField(fieldType: ProtobufInt32.self, value: &id) 55 | case 2: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &title) 56 | case 3: handled = try setter.decodeSingularField(fieldType: ProtobufString.self, value: &content) 57 | default: 58 | handled = false 59 | } 60 | return handled 61 | } 62 | 63 | public func _protoc_generated_traverse(visitor: inout ProtobufVisitor) throws { 64 | if id != 0 { 65 | try visitor.visitSingularField(fieldType: ProtobufInt32.self, value: id, protoFieldNumber: 1, protoFieldName: "id", jsonFieldName: "id", swiftFieldName: "id") 66 | } 67 | if title != "" { 68 | try visitor.visitSingularField(fieldType: ProtobufString.self, value: title, protoFieldNumber: 2, protoFieldName: "title", jsonFieldName: "title", swiftFieldName: "title") 69 | } 70 | if content != "" { 71 | try visitor.visitSingularField(fieldType: ProtobufString.self, value: content, protoFieldNumber: 3, protoFieldName: "content", jsonFieldName: "content", swiftFieldName: "content") 72 | } 73 | } 74 | 75 | public func _protoc_generated_isEqualTo(other: Note_Note) -> Bool { 76 | if id != other.id {return false} 77 | if title != other.title {return false} 78 | if content != other.content {return false} 79 | return true 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /note-swift/Sources/note.server.pb.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * DO NOT EDIT. 3 | * 4 | * Generated by the protocol buffer compiler. 5 | * Source: note.proto 6 | * 7 | */ 8 | 9 | /* 10 | * 11 | * Copyright 2016, Google Inc. 12 | * All rights reserved. 13 | * 14 | * Redistribution and use in source and binary forms, with or without 15 | * modification, are permitted provided that the following conditions are 16 | * met: 17 | * 18 | * * Redistributions of source code must retain the above copyright 19 | * notice, this list of conditions and the following disclaimer. 20 | * * Redistributions in binary form must reproduce the above 21 | * copyright notice, this list of conditions and the following disclaimer 22 | * in the documentation and/or other materials provided with the 23 | * distribution. 24 | * * Neither the name of Google Inc. nor the names of its 25 | * contributors may be used to endorse or promote products derived from 26 | * this software without specific prior written permission. 27 | * 28 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 29 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 30 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 31 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 32 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 33 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 34 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 35 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 36 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 37 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 38 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39 | * 40 | */ 41 | 42 | import Foundation 43 | import gRPC 44 | import Dispatch 45 | 46 | /// Type for errors thrown from generated server code. 47 | public enum Note_TranslationServiceServerError : Error { 48 | case endOfStream 49 | } 50 | 51 | /// To build a server, implement a class that conforms to this protocol. 52 | public protocol Note_TranslationServiceProvider { 53 | func translate(request : Note_Note, session : Note_TranslationServicetranslateSession) throws -> Note_Note 54 | } 55 | 56 | /// Common properties available in each service session. 57 | public class Note_TranslationServiceSession { 58 | fileprivate var handler : gRPC.Handler 59 | public var requestMetadata : Metadata { return handler.requestMetadata } 60 | 61 | public var statusCode : Int = 0 62 | public var statusMessage : String = "OK" 63 | public var initialMetadata : Metadata = Metadata() 64 | public var trailingMetadata : Metadata = Metadata() 65 | 66 | fileprivate init(handler:gRPC.Handler) { 67 | self.handler = handler 68 | } 69 | } 70 | 71 | // translate (Unary) 72 | public class Note_TranslationServicetranslateSession : Note_TranslationServiceSession { 73 | private var provider : Note_TranslationServiceProvider 74 | 75 | /// Create a session. 76 | fileprivate init(handler:gRPC.Handler, provider: Note_TranslationServiceProvider) { 77 | self.provider = provider 78 | super.init(handler:handler) 79 | } 80 | 81 | /// Run the session. Internal. 82 | fileprivate func run(queue:DispatchQueue) throws { 83 | try handler.receiveMessage(initialMetadata:initialMetadata) {(requestData) in 84 | if let requestData = requestData { 85 | let requestMessage = try Note_Note(protobuf:requestData) 86 | let replyMessage = try self.provider.translate(request:requestMessage, session: self) 87 | try self.handler.sendResponse(message:replyMessage.serializeProtobuf(), 88 | statusCode:self.statusCode, 89 | statusMessage:self.statusMessage, 90 | trailingMetadata:self.trailingMetadata) 91 | } 92 | } 93 | } 94 | } 95 | 96 | 97 | /// Main server for generated service 98 | public class Note_TranslationServiceServer { 99 | private var address: String 100 | private var server: gRPC.Server 101 | private var provider: Note_TranslationServiceProvider? 102 | 103 | /// Create a server that accepts insecure connections. 104 | public init(address:String, 105 | provider:Note_TranslationServiceProvider) { 106 | gRPC.initialize() 107 | self.address = address 108 | self.provider = provider 109 | self.server = gRPC.Server(address:address) 110 | } 111 | 112 | /// Create a server that accepts secure connections. 113 | public init?(address:String, 114 | certificateURL:URL, 115 | keyURL:URL, 116 | provider:Note_TranslationServiceProvider) { 117 | gRPC.initialize() 118 | self.address = address 119 | self.provider = provider 120 | guard 121 | let certificate = try? String(contentsOf: certificateURL), 122 | let key = try? String(contentsOf: keyURL) 123 | else { 124 | return nil 125 | } 126 | self.server = gRPC.Server(address:address, key:key, certs:certificate) 127 | } 128 | 129 | /// Start the server. 130 | public func start(queue:DispatchQueue = DispatchQueue.global()) { 131 | guard let provider = self.provider else { 132 | assert(false) // the server requires a provider 133 | } 134 | server.run {(handler) in 135 | print("Server received request to " + handler.host 136 | + " calling " + handler.method 137 | + " from " + handler.caller 138 | + " with " + String(describing:handler.requestMetadata) ) 139 | 140 | do { 141 | switch handler.method { 142 | case "/note.TranslationService/translate": 143 | try Note_TranslationServicetranslateSession(handler:handler, provider:provider).run(queue:queue) 144 | default: 145 | break // handle unknown requests 146 | } 147 | } catch (let error) { 148 | print("Server error: \(error)") 149 | } 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /note-swift/codegen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Generate gprc and protobuf code for Swift 3 | protoc --swift_out=Sources --swiftgrpc_out=Sources note.proto 4 | 5 | -------------------------------------------------------------------------------- /note-swift/grpc.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIERTCCAy2gAwIBAgIJALCZULSgjiqEMA0GCSqGSIb3DQEBBQUAMHQxCzAJBgNV 3 | BAYTAlVTMQswCQYDVQQIEwJDQTEUMBIGA1UEBxMLRm9zdGVyIENpdHkxDDAKBgNV 4 | BAoTA0lCTTETMBEGA1UECxMKU3Ryb25nTG9vcDEfMB0GCSqGSIb3DQEJARYQcmZl 5 | bmdAdXMuaWJtLmNvbTAeFw0xNjA5MjIxNTI5MTBaFw0xNzA5MjIxNTI5MTBaMHQx 6 | CzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEUMBIGA1UEBxMLRm9zdGVyIENpdHkx 7 | DDAKBgNVBAoTA0lCTTETMBEGA1UECxMKU3Ryb25nTG9vcDEfMB0GCSqGSIb3DQEJ 8 | ARYQcmZlbmdAdXMuaWJtLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC 9 | ggEBAK/6zOF/pgH1P9T7RzCJHp7Yz7oPU29TTamXPjHx2M2HcT7MWP0rmMYqmsfh 10 | gIHZqGYOfQ4ZZIJD9j1bh5ttClMrLTCmRYLh7I1bswtP29xCoNdx/gtTKKUS6dQB 11 | Di+VjlUOEZCp4xEVELf0jTNt+zOpzwxKIXuMaogyTne1xHitLSra+8BwIP+KnSET 12 | zfjblAhgZVtqzbs2qr5WRxr7nhPZLjVw568fqIHHqS9I6n2PrAWZDPf8Go5sibi0 13 | kF++0Foh6erJ5S5nMfDO+aqY7N33tq0aHZ7wvuAmocCn/1viWcyOupF4L0A9+7Zi 14 | LnQcLQQRrqVaI08BcfKVCIMIDIUCAwEAAaOB2TCB1jAdBgNVHQ4EFgQUwzuQS7Js 15 | JyInHKG+LTGP4AnBoGEwgaYGA1UdIwSBnjCBm4AUwzuQS7JsJyInHKG+LTGP4AnB 16 | oGGheKR2MHQxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEUMBIGA1UEBxMLRm9z 17 | dGVyIENpdHkxDDAKBgNVBAoTA0lCTTETMBEGA1UECxMKU3Ryb25nTG9vcDEfMB0G 18 | CSqGSIb3DQEJARYQcmZlbmdAdXMuaWJtLmNvbYIJALCZULSgjiqEMAwGA1UdEwQF 19 | MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADO4j94rjuRcapFw9zeRuqjltVniV7Hj 20 | D7e7gy/WtFesTY2x+lraJaMStUKnYlBf2haQRTuGxqXllQAOAKzLQmAjvOTc84dB 21 | C7C9IwI3/BanKXCmntxQOFblqo8UpK0gM2loNYU7s3Q5H8G2JnNUMOfcesMMyhLY 22 | aqPdhbTSYeK9BJAXvPao3478JTk2uQVEmSzHWydOZCxGNn8uH8mInd4QJnoOWB91 23 | Olprw+4I9A3FXRnAC5asVwzQGpKFfX7v+OH6dY4WG38ZfCpHc1qq4solkKgGBR6y 24 | aJEWDtVCM2mxaCXHOz6WN74DRGNE19qQ//dbBdUvqhz7OLmx6GKdUSc= 25 | -----END CERTIFICATE----- 26 | -------------------------------------------------------------------------------- /note-swift/grpc.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAr/rM4X+mAfU/1PtHMIkentjPug9Tb1NNqZc+MfHYzYdxPsxY 3 | /SuYxiqax+GAgdmoZg59DhlkgkP2PVuHm20KUystMKZFguHsjVuzC0/b3EKg13H+ 4 | C1MopRLp1AEOL5WOVQ4RkKnjERUQt/SNM237M6nPDEohe4xqiDJOd7XEeK0tKtr7 5 | wHAg/4qdIRPN+NuUCGBlW2rNuzaqvlZHGvueE9kuNXDnrx+ogcepL0jqfY+sBZkM 6 | 9/wajmyJuLSQX77QWiHp6snlLmcx8M75qpjs3fe2rRodnvC+4CahwKf/W+JZzI66 7 | kXgvQD37tmIudBwtBBGupVojTwFx8pUIgwgMhQIDAQABAoIBAQCLZjYpYn5UCrvX 8 | sHzB+7xfxAs4ra8//lMExiOkWJmOpX2VazYKxiAsyc72CrFLKul0foGdS4wMjF1g 9 | WULgYc3N9+PCJ9PkS6agi0UW7tmQbs8OGuRMgEEwEf1bzMI5+1RWF+DeIVUXUQJ/ 10 | VZ8MYFCeqiKy7336ak5xOLhjp37Mv7+5RzByNsL9AYRskowmv4q5qwswYk8hS+aL 11 | YcyQuQAW7SNKhxlaPr7k6pOcdZ2/iBx+osjso8B+PKx5XzGWF+qTustXnyXgvcxV 12 | YtN7kwsXbTFQWLASGC7bfkQtte3re8Kk7P2hgTZdjsbglMptEMqVsLXbS+44daVQ 13 | QE2dbTA5AoGBAOmaid3/Ueny64m579is5ODjB018aEvuJefsYv67gh51Vk/bwniu 14 | GpFDY9HkcZeaij5YfKYbpthdTWsfX9Uzp6awq/oxAl0J/HTbW83epczHP6bE8GAp 15 | w8MgeR5ejSsu4Ri6t5XD4mrUny7SgMUJWdYMT4AW71GiaVCFaryWeswzAoGBAMDZ 16 | 9xpVxN1zMEOSGz7+F1ZYD9w3RX7LgIsAifYyQeFMIkO/etVy2hrEKsYvi616TV/+ 17 | SFdcFowkBLlcSzgQazoiTv5/ZCZ0gHNRCwToMXZzuHuJygNPSGqEA6JFx34ohODE 18 | 6jwYhBCVm+saIC5wsLoh7I2hZMkclU0ZEeM8PIxnAoGAUedvIzjbvY0AbF+W6n9k 19 | GD1BWDegUc8D11cYNnwD2S4GvyUsACf7BKd+Hh9cfG0gv09DFPJpAz9jX4W2kgf0 20 | ZgtXoPcB/yD7NPWcMIg51ZyegphWN0EtdAK0tKMuF8/t+D+vEoGFpzM5RK9lmq/2 21 | oYfbb+uaqSKqjiLZE5onH+UCgYB45qzRZ7/ZjSNO2UQXg7ghu2eGCWiaCv51JktX 22 | ez5t/grlKh/ZvP0bFqwyPxB0G15ytbmoeuTvyozjoAbQCQsQEP4w8rBYo2T75mzg 23 | EKkht36KhGGPHZ8ql8SncNOWNdTIDOtD7aKtuv1asLBILQG+TxI74FiM9ExtXzAl 24 | o1faNQKBgGdXe3H2AQOdLwg74JAL/GyHskajvU2FwmbX/w1fJVQaTnkioxM8EYzn 25 | EnapCnLFqI9PwkQBBwqt8tkG+9jU2MUmD/AUO7IKTSWWvxRRQAIqVfJCx43+hm42 26 | Llhaypua2MCUJRElwFyhnfDDFSZ+tPV+gbr07R6/UMJyAKUnL/v6 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /note-swift/note.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package note; 4 | 5 | message Note { 6 | int32 id = 1; 7 | string title = 2; 8 | string content = 3; 9 | }; 10 | 11 | service TranslationService { 12 | rpc translate (Note) returns (Note); 13 | } 14 | 15 | --------------------------------------------------------------------------------