Skip to content

Commit 22f67e0

Browse files
committed
add "Apply script to instance"
1 parent 75d180d commit 22f67e0

File tree

3 files changed

+208
-0
lines changed

3 files changed

+208
-0
lines changed

forward_engineering/api.js

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const _ = require('lodash');
2+
const applyToInstanceHelper = require('./applyToInstanceHelper');
23

34
const DEFAULT_INDENT = ' ';
45
let graphName = 'g';
@@ -45,6 +46,83 @@ module.exports = {
4546
}, 150);
4647
return;
4748
}
49+
},
50+
51+
async applyToInstance(data, logger, cb, app) {
52+
try {
53+
logger.clear();
54+
logger.log('info', data, data.hiddenKeys);
55+
56+
if (!data.script) {
57+
return cb({ message: 'Empty script' });
58+
}
59+
60+
if (!data.containerData) {
61+
return cb({ message: 'Graph wasn\'t specified' });
62+
}
63+
const containerProps = _.get(data.containerData, '[0]', {});
64+
if (!containerProps.dbId) {
65+
return cb({ message: 'Database id wasn\'t specified' });
66+
}
67+
const graphName = containerProps.code || containerProps.name;
68+
if (!graphName) {
69+
return cb({ message: 'Graph name wasn\'t specified' });
70+
}
71+
72+
const cosmosClient = applyToInstanceHelper.setUpDocumentClient(data);
73+
await cosmosClient.databases.createIfNotExists({
74+
id: containerProps.dbId
75+
});
76+
const containerResponse = await cosmosClient
77+
.database(containerProps.dbId)
78+
.containers.createIfNotExists({
79+
id: graphName,
80+
partitionKey: containerProps.partitionKey,
81+
...(containerProps.autopilot
82+
? { maxThroughput: containerProps.throughput || 400 }
83+
: { throughput: containerProps.throughput || 400 }),
84+
defaultTtl: applyToInstanceHelper.getTTL(containerProps),
85+
});
86+
if (containerResponse.statusCode === 201) {
87+
const containerInstance = cosmosClient.database(containerProps.dbId).container(graphName);
88+
89+
const storedProcs = _.get(data.containerData, '[2].storedProcs', []);
90+
if (storedProcs.length) {
91+
await applyToInstanceHelper.createStoredProcs(storedProcs, containerInstance);
92+
}
93+
94+
const udfs = _.get(data.containerData, '[3].udfs', []);
95+
if (udfs.length) {
96+
await applyToInstanceHelper.createUDFs(udfs, containerInstance);
97+
}
98+
const triggers = _.get(data.containerData, '[4].triggers', []);
99+
if (triggers.length) {
100+
await applyToInstanceHelper.createTriggers(triggers, containerInstance);
101+
}
102+
}
103+
104+
const { labels, edges } = applyToInstanceHelper.parseScriptStatements(data.script);
105+
const gremlinClient = await applyToInstanceHelper.getGremlinClient(data, containerProps.dbId, graphName);
106+
await applyToInstanceHelper.runGremlinQueries(gremlinClient, labels);
107+
await applyToInstanceHelper.runGremlinQueries(gremlinClient, edges);
108+
109+
cb();
110+
} catch(err) {
111+
cb(mapError(err));
112+
}
113+
},
114+
115+
async testConnection(connectionInfo, logger, cb, app) {
116+
logger.clear();
117+
logger.log('info', connectionInfo, 'Test connection', connectionInfo.hiddenKeys);
118+
try {
119+
const client = applyToInstanceHelper.setUpDocumentClient(connectionInfo);
120+
await applyToInstanceHelper.testConnection(client);
121+
return cb();
122+
} catch(err) {
123+
logger.log('error', mapError(err), 'Connection failed');
124+
return cb(mapError(err));
125+
}
48126
}
49127
};
50128

@@ -479,3 +557,10 @@ const generateIndexes = indexesData => {
479557

480558
return script + ';';
481559
};
560+
561+
const mapError = (error) => {
562+
return {
563+
message: error.message,
564+
stack: error.stack
565+
};
566+
};
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
const _ = require('lodash');
2+
const { CosmosClient } = require('../reverse_engineering/node_modules/@azure/cosmos');
3+
const gremlin = require('../reverse_engineering/node_modules/gremlin');
4+
5+
6+
const applyToInstanceHelper = {
7+
setUpDocumentClient(connectionInfo) {
8+
const dbNameRegExp = /wss:\/\/(\S*).gremlin\.cosmos\./i;
9+
const dbName = dbNameRegExp.exec(connectionInfo.gremlinEndpoint)[1];
10+
const endpoint = `https://${dbName}.documents.azure.com:443/`;
11+
const key = connectionInfo.accountKey;
12+
13+
return new CosmosClient({ endpoint, key });
14+
},
15+
16+
async getGremlinClient(connectionInfo, databaseId, collection) {
17+
const traversalSource = 'g';
18+
19+
const authenticator = new gremlin.driver.auth.PlainTextSaslAuthenticator(
20+
`/dbs/${databaseId}/colls/${collection}`,
21+
connectionInfo.accountKey
22+
);
23+
24+
const client = new gremlin.driver.Client(connectionInfo.gremlinEndpoint, {
25+
authenticator,
26+
traversalSource,
27+
rejectUnauthorized : true,
28+
mimeType : 'application/vnd.gremlin-v2.0+json',
29+
});
30+
31+
await client.open();
32+
return client;
33+
},
34+
35+
runGremlinQueries(gremlinClient, queries) {
36+
return Promise.all(queries.map(query => {
37+
return gremlinClient.submit(query);
38+
}));
39+
},
40+
41+
testConnection(client) {
42+
return this.getDatabases(client);
43+
},
44+
45+
async getDatabases(client) {
46+
const dbResponse = await client.databases.readAll().fetchAll();
47+
return dbResponse.resources;
48+
49+
},
50+
51+
async getContainers(databaseId, client) {
52+
const { resources: containers } = await client.database(databaseId).containers.readAll().fetchAll();
53+
return containers;
54+
},
55+
56+
parseScriptStatements(script) {
57+
const scriptStatements = script.split('\n\n').map(item => item.replace(/\.\s+/g, '.'));
58+
const [labels, edges] = _.partition(scriptStatements, statement => statement.startsWith('g.addV'));
59+
60+
return { labels, edges };
61+
},
62+
63+
mapStoredProcs(storedPropcs) {
64+
return storedPropcs.map(proc => {
65+
return {
66+
id: proc.storedProcID,
67+
body: proc.storedProcFunction,
68+
};
69+
});
70+
},
71+
72+
createStoredProcs(storedProcs, containerInstance) {
73+
return Promise.all(this.mapStoredProcs(storedProcs).map(proc => {
74+
return containerInstance.scripts.storedProcedures.create(proc);
75+
}));
76+
},
77+
78+
mapUDFs(udfs) {
79+
return udfs.map(udf => {
80+
return {
81+
id: udf.udfID,
82+
body: udf.udfFunction,
83+
};
84+
});
85+
},
86+
87+
createUDFs(udfs, containerInstance) {
88+
return Promise.all(this.mapUDFs(udfs).map(udf => {
89+
return containerInstance.scripts.userDefinedFunctions.create(udf);
90+
}));
91+
},
92+
93+
mapTriggers(triggers) {
94+
return triggers.map(trigger => {
95+
return {
96+
id: trigger.triggerID,
97+
body: trigger.triggerFunction,
98+
triggerOperation: trigger.triggerOperation,
99+
triggerType: trigger.prePostTrigger === 'Pre-Trigger' ? 'Pre' : 'Post'
100+
};
101+
});
102+
},
103+
104+
createTriggers(triggers, containerInstance) {
105+
return Promise.all(this.mapTriggers(triggers).map(trigger => {
106+
return containerInstance.scripts.triggers.create(trigger);
107+
}));
108+
},
109+
110+
getTTL(containerData) {
111+
switch (containerData.TTL) {
112+
case 'On (no default)':
113+
return -1;
114+
case 'On':
115+
return _.parseInt(TTLseconds) || 0;
116+
default:
117+
return -1;
118+
}
119+
},
120+
};
121+
122+
module.exports = applyToInstanceHelper;

forward_engineering/config.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"filterName": "Gremlin script",
44
"namePrefix": "Gremlin",
55
"hasUpdateScript": false,
6+
"applyScriptToInstance": true,
67
"level": {
78
"container": true,
89
"entity": false

0 commit comments

Comments
 (0)