-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathindex.js
158 lines (149 loc) · 6.7 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
const grpc = require("grpc");
const path = require("path")
const fs = require("fs");
const protoLoader = require("@grpc/proto-loader");
const bunyan = require("bunyan");
const packagejson =require("./package.json");
let log, config, client;
const VECTOR_CLIENT_VERSION = 2;
const VECTOR_MIN_HOST_VERSION = 0;
var LoggerInterceptor = function(options, nextCall) {
return new grpc.InterceptingCall(nextCall(options), {
start(passedMetadata, listener, nnext) {
nnext(passedMetadata, {
onReceiveMetadata (mdata, next) {
log.trace("onReceiveMetadata", mdata);
next(mdata);
},
onReceiveMessage (message, next) {
log.trace("onReceiveMessage", message);
next(message);
},
onReceiveStatus (status, next) {
log.trace("onReceiveStatus", status);
next(status);
},
});
},
sendMessage(message, next) {
log.trace("sendMessage", message);
next(message);
},
halfClose(next) {
log.trace("halfClose");
next();
},
cancel(message, next) {
log.trace("cancel", message);
next(message);
}
});
};
var VectorAPI = function(configObj){
if (fs.existsSync("./config.js")){
config = require("./config.js");
}
if (!configObj){
console.error("Missing config.js file or no config object passed in.");
throw new Error("Missing config.js file or no config object passed in.");
}else{
config = configObj;
}
// Have the gRPC libs spit out more debug data
if (configObj && configObj.gRPCDebug){
grpc.setLogVerbosity(0);
}
// Configure bunyan logging
log = bunyan.createLogger({
name: packagejson.name + " " + packagejson.version,
streams: [{
level: config.DEBUG_LEVEL, // Priority of levels looks like this: Trace -> Debug -> Info -> Warn -> Error -> Fatal
stream: process.stdout
}]
});
// TODO improve detection of these values
if (!config || !config.VECTOR_NAME || !config.VECTOR_SN || !config.VECTOR_BEARER_TOKEN || !config.VECTOR_IP || !config.VECTOR_CRT){
log.error("Missing config.js values. Please edit values in config.sample.js and rename it 'config.js'");
process.exit(-1);
}
var proto = grpc.loadPackageDefinition(
protoLoader.loadSync(path.join(__dirname, "./protobufs/anki_vector/messaging/external_interface.proto"), {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
includeDirs: ["node_modules/google-proto-files", path.join(__dirname, "protobufs")]
})
);
// build meta data credentials
var metadata = new grpc.Metadata();
metadata.add('authorization', `Bearer ${config.VECTOR_BEARER_TOKEN}`);
var headerCreds = grpc.credentials.createFromMetadataGenerator((_args, callback) => callback(null, metadata));
// build ssl credentials using the cert
if (!fs.existsSync(config.VECTOR_CRT)){
log.error("Missing Vector certificate file. Make sure you've got the proper file location specified in your config.js");
process.exit(-1);
}
var sslCreds = grpc.credentials.createSsl(fs.readFileSync(config.VECTOR_CRT));
// combine so that every call is properly encrypted and authenticated
var credentials = grpc.credentials.combineChannelCredentials(sslCreds, headerCreds);
// Pass the crendentials when creating a channel
// ExternalInterface is the only exposed gRPC service right now, so we hardcode the client to it.
// The idea is if Anki opens other gRPC services later you can call client.SERVICE.METHOD -- but for now since there's only 1 service we call: client.METHOD
client = new proto.Anki.Vector.external_interface.ExternalInterface(config.VECTOR_IP, credentials, {"grpc.ssl_target_name_override": config.VECTOR_NAME, interceptors: [LoggerInterceptor]});
this.client = client;
this.VECTOR_CLIENT_VERSION = VECTOR_CLIENT_VERSION;
this.VECTOR_MIN_HOST_VERSION = VECTOR_MIN_HOST_VERSION;
};
VectorAPI.prototype.listMethods = function(){
const proto = grpc.loadPackageDefinition(
protoLoader.loadSync(path.join(__dirname, "./protobufs/anki_vector/messaging/external_interface.proto"), {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
includeDirs: ["node_modules/google-proto-files", path.join(__dirname, "protobufs")]
})
);
let finalRoutes = {};
const rootLevelRoutes = Object.keys(proto.Anki.Vector.external_interface);
rootLevelRoutes.forEach((rootLevelRoute) => {
const service = proto.Anki.Vector.external_interface[rootLevelRoute].service || false;
if (service){
if (!finalRoutes[rootLevelRoute]){
finalRoutes[rootLevelRoute] = [];
}
const secondLevelRoutes = Object.keys(proto.Anki.Vector.external_interface[rootLevelRoute].service);
secondLevelRoutes.forEach((secondLevelRoute) => {
const reqFields = [];
proto.Anki.Vector.external_interface[rootLevelRoute].service[secondLevelRoute].requestType.type.field.forEach((field) => {
reqFields.push({
name: field.name,
type: String(field.type).replace("TYPE_", "")
});
});
const resFields = [];
proto.Anki.Vector.external_interface[rootLevelRoute].service[secondLevelRoute].responseType.type.field.forEach((field) => {
reqFields.push({
name: field.name,
type: String(field.type).replace("TYPE_", "")
});
});
finalRoutes[rootLevelRoute].push({
name: secondLevelRoute,
responseStream: proto.Anki.Vector.external_interface[rootLevelRoute].service[secondLevelRoute].responseStream,
requestStream: proto.Anki.Vector.external_interface[rootLevelRoute].service[secondLevelRoute].requestStream,
requestFields: reqFields,
responseFields: resFields
});
});
}
});
// Syntactic sugar: ExternalInterface is currently the only exposed gRPC service API, we need not nest it until there's more
finalRoutes = finalRoutes.ExternalInterface;
return finalRoutes;
};
VectorAPI.prototype.getLogger = () => log;
module.exports = VectorAPI;