@selfage/spanner_schema_update_cli
Version:
Update Spanner schema.
267 lines • 34.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.SchemaState = void 0;
exports.markSchemaImageUpdateDone = markSchemaImageUpdateDone;
exports.updateSchemaFromDdlFile = updateSchemaFromDdlFile;
exports.updateSchema = updateSchema;
const fs = require("fs");
const schema_ddl_1 = require("./schema_ddl");
const spanner_1 = require("@google-cloud/spanner");
const comparator_1 = require("@selfage/message/comparator");
const parser_1 = require("@selfage/message/parser");
const serializer_1 = require("@selfage/message/serializer");
var SchemaState;
(function (SchemaState) {
SchemaState[SchemaState["PENDING"] = 1] = "PENDING";
SchemaState[SchemaState["DONE"] = 2] = "DONE";
})(SchemaState || (exports.SchemaState = SchemaState = {}));
async function createSchemaImageTableIfNotExists(databaseAdminClient, projectId, instanceId, databaseId) {
let createSchemaImageTable = `
CREATE TABLE IF NOT EXISTS SchemaImage (
versionId FLOAT64 NOT NULL,
schema BYTES(MAX) NOT NULL,
state FLOAT64 NOT NULL,
) PRIMARY KEY(versionId)`;
let [operation] = await databaseAdminClient.updateDatabaseDdl({
database: databaseAdminClient.databasePath(projectId, instanceId, databaseId),
statements: [createSchemaImageTable],
});
console.log(`Waiting for creation of table SchemaImage to complete...`);
await operation.promise();
console.log(`Created table SchemaImage.`);
}
async function insertNewSchemaImage(databaseClient, versionId, schemaDdl) {
await databaseClient.runTransactionAsync(async (transction) => {
await transction.run({
sql: `INSERT SchemaImage (versionId, schema, state) VALUES (@versionId, @schema, @state)`,
params: {
versionId: spanner_1.Spanner.float(versionId),
schema: Buffer.from((0, serializer_1.serializeMessage)(schemaDdl, schema_ddl_1.SCHEMA_DDL).buffer),
state: spanner_1.Spanner.float(SchemaState.PENDING),
},
types: {
versionId: {
type: "float64",
},
schema: {
type: "bytes",
},
state: {
type: "float64",
},
},
});
await transction.commit();
console.log(`Inserted new schema with version ${versionId}.`);
});
}
async function insertNewSchemaDdlIfNotExists(databaseClient, newSchemaDdl) {
let [rows] = await databaseClient.run({
sql: `SELECT versionId, schema FROM SchemaImage ORDER BY versionId DESC LIMIT 1;`,
});
if (rows.length === 0) {
await insertNewSchemaImage(databaseClient, 1, newSchemaDdl);
return 1;
}
else {
let latestVersionId = rows[0].at(0).value + 0;
let latestSchemaDdl = (0, serializer_1.deserializeMessage)(rows[0].at(1).value, schema_ddl_1.SCHEMA_DDL);
if (!(0, comparator_1.equalMessage)(latestSchemaDdl, newSchemaDdl, schema_ddl_1.SCHEMA_DDL)) {
await insertNewSchemaImage(databaseClient, latestVersionId + 1, newSchemaDdl);
return latestVersionId + 1;
}
else {
return latestVersionId;
}
}
}
async function markSchemaImageUpdateDone(databaseClient, databaseId, versionId) {
await databaseClient.runTransactionAsync(async (transction) => {
await transction.run({
sql: `UPDATE SchemaImage SET state = @state WHERE versionId = @versionId`,
params: {
versionId: spanner_1.Spanner.float(versionId),
state: spanner_1.Spanner.float(SchemaState.DONE),
},
types: {
versionId: {
type: "float64",
},
state: {
type: "float64",
},
},
});
await transction.commit();
console.log(`Marked database ${databaseId} version ${versionId} update done.`);
});
}
async function updateSchemaFromDdlFile(projectId, instanceId, databaseId, ddlFile) {
let newSchemaDdl = (0, parser_1.parseMessage)(JSON.parse(fs.readFileSync(ddlFile).toString()), schema_ddl_1.SCHEMA_DDL);
await updateSchema(projectId, instanceId, databaseId, newSchemaDdl);
}
async function updateSchema(projectId, instanceId, databaseId, newSchemaDdl) {
let spanner = new spanner_1.Spanner({
projectId,
});
let databaseAdminClient = spanner.getDatabaseAdminClient();
await createSchemaImageTableIfNotExists(databaseAdminClient, projectId, instanceId, databaseId);
let instance = spanner.instance(instanceId);
let databaseClient = instance.database(databaseId);
let versionId = await insertNewSchemaDdlIfNotExists(databaseClient, newSchemaDdl);
let [rows] = await databaseClient.run({
sql: `
WITH
t AS (
SELECT
t.table_name AS table_name,
t.spanner_state AS table_spanner_state,
ARRAY_AGG(c.column_name) AS column_names,
ARRAY_AGG(c.spanner_state) AS column_spanner_states,
FROM
information_schema.tables AS t
JOIN
information_schema.columns AS c
ON
t.table_name = c.table_name
WHERE
t.table_catalog = ''
AND t.table_schema = ''
AND t.table_name != 'SchemaImage'
GROUP BY
1,
2),
i AS (
SELECT
t.table_name,
ARRAY_AGG(i.index_name) AS index_names,
ARRAY_AGG(i.index_state) AS index_states,
FROM
information_schema.tables AS t
JOIN
information_schema.indexes AS i
ON
t.table_name = i.table_name
WHERE
t.table_catalog = ''
AND t.table_schema = ''
AND t.table_name != 'SchemaImage'
AND i.index_type != 'PRIMARY_KEY'
GROUP BY
1)
SELECT
t.table_name,
t.table_spanner_state,
t.column_names,
t.column_spanner_states,
i.index_names,
i.index_states,
FROM
t
LEFT JOIN
i
ON
t.table_name = i.table_name;`,
});
let notCommittedErrors = new Array();
let createdTables = new Map();
for (let row of rows) {
let tableName = row.at(0).value;
let tableSpannerState = row.at(1).value;
if (tableSpannerState !== "COMMITTED") {
notCommittedErrors.push(`Table ${tableName} not committed yet. Current state: ${tableSpannerState}.`);
}
let columns = new Set();
for (let i = 0; i < row.at(2).value.length; i++) {
let columnName = row.at(2).value[i];
let columnSpannerState = row.at(3).value[i];
if (columnSpannerState !== "COMMITTED") {
notCommittedErrors.push(`Column ${tableName}.${columnName} not comitted yet. Current state: ${columnSpannerState}.`);
}
columns.add(columnName);
}
let indexes = new Set();
if (row.at(4).value) {
for (let i = 0; i < row.at(4).value.length; i++) {
let indexName = row.at(4).value[i];
let indexState = row.at(5).value[i];
if (indexState !== "READ_WRITE") {
notCommittedErrors.push(`Index ${indexName} is not ready yet. Current state: ${indexState}.`);
}
indexes.add(row.at(4).value[i]);
}
}
createdTables.set(tableName, {
name: tableName,
columns,
indexes,
});
}
if (notCommittedErrors.length > 0) {
throw new Error(`Give up updating schema due to existing tables/columns not committed yet.\n${notCommittedErrors.join("\n")}`);
}
let statements = new Array();
for (let table of newSchemaDdl.tables) {
let createdTable = createdTables.get(table.name);
if (!createdTable) {
statements.push(table.createTableDdl);
if (table.indexes) {
for (let index of table.indexes) {
statements.push(index.createIndexDdl);
}
}
}
else {
let addIndexesStatement = new Array();
if (table.indexes) {
for (let index of table.indexes) {
if (!createdTable.indexes.has(index.name)) {
addIndexesStatement.push(index.createIndexDdl);
}
else {
createdTable.indexes.delete(index.name);
}
}
}
// Drop indexes before dropping columns.
for (let excessiveIndex of createdTable.indexes) {
statements.push(`DROP INDEX ${excessiveIndex}`);
}
for (let column of table.columns) {
if (!createdTable.columns.has(column.name)) {
statements.push(column.addColumnDdl);
}
else {
createdTable.columns.delete(column.name);
}
}
for (let excessiveColumn of createdTable.columns) {
statements.push(`ALTER TABLE ${table.name} DROP COLUMN ${excessiveColumn}`);
}
// Wait until columns are added before adding indexes.
statements.push(...addIndexesStatement);
}
createdTables.delete(table.name);
}
for (let excessiveTable of createdTables.values()) {
for (let excessiveIndex of excessiveTable.indexes) {
statements.push(`DROP INDEX ${excessiveIndex}`);
}
statements.push(`DROP TABLE ${excessiveTable.name}`);
}
if (statements.length === 0) {
console.log(`Database ${databaseId} version ${versionId} already up-to-date.`);
}
else {
console.log(`Executing the following statements:\n${statements.join("\n")}`);
let [operation] = await databaseAdminClient.updateDatabaseDdl({
database: databaseAdminClient.databasePath(projectId, instanceId, databaseId),
statements,
});
console.log(`Waiting for updating database ${databaseId} to complete...`);
await operation.promise();
console.log(`Updated database ${databaseId} version ${versionId}.`);
await markSchemaImageUpdateDone(databaseClient, databaseId, versionId);
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBkYXRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInVwZGF0ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBdUdBLDhEQTBCQztBQUVELDBEQVdDO0FBRUQsb0NBdU1DO0FBdlZELHlCQUEwQjtBQUMxQiw2Q0FBcUQ7QUFDckQsbURBQTBEO0FBRTFELDREQUEyRDtBQUMzRCxvREFBdUQ7QUFDdkQsNERBR3FDO0FBRXJDLElBQVksV0FHWDtBQUhELFdBQVksV0FBVztJQUNyQixtREFBVyxDQUFBO0lBQ1gsNkNBQVEsQ0FBQTtBQUNWLENBQUMsRUFIVyxXQUFXLDJCQUFYLFdBQVcsUUFHdEI7QUFRRCxLQUFLLFVBQVUsaUNBQWlDLENBQzlDLG1CQUF3QyxFQUN4QyxTQUFpQixFQUNqQixVQUFrQixFQUNsQixVQUFrQjtJQUVsQixJQUFJLHNCQUFzQixHQUFHOzs7OzsyQkFLSixDQUFDO0lBQzFCLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxNQUFNLG1CQUFtQixDQUFDLGlCQUFpQixDQUFDO1FBQzVELFFBQVEsRUFBRSxtQkFBbUIsQ0FBQyxZQUFZLENBQ3hDLFNBQVMsRUFDVCxVQUFVLEVBQ1YsVUFBVSxDQUNYO1FBQ0QsVUFBVSxFQUFFLENBQUMsc0JBQXNCLENBQUM7S0FDckMsQ0FBQyxDQUFDO0lBQ0gsT0FBTyxDQUFDLEdBQUcsQ0FBQywwREFBMEQsQ0FBQyxDQUFDO0lBQ3hFLE1BQU0sU0FBUyxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBQzFCLE9BQU8sQ0FBQyxHQUFHLENBQUMsNEJBQTRCLENBQUMsQ0FBQztBQUM1QyxDQUFDO0FBRUQsS0FBSyxVQUFVLG9CQUFvQixDQUNqQyxjQUF3QixFQUN4QixTQUFpQixFQUNqQixTQUFvQjtJQUVwQixNQUFNLGNBQWMsQ0FBQyxtQkFBbUIsQ0FBQyxLQUFLLEVBQUUsVUFBVSxFQUFFLEVBQUU7UUFDNUQsTUFBTSxVQUFVLENBQUMsR0FBRyxDQUFDO1lBQ25CLEdBQUcsRUFBRSxvRkFB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Q0FBQyxTQUFTLENBQUMsR0FBRyxNQUFNLG1CQUFtQixDQUFDLGlCQUFpQixDQUFDO1lBQzVELFFBQVEsRUFBRSxtQkFBbUIsQ0FBQyxZQUFZLENBQ3hDLFNBQVMsRUFDVCxVQUFVLEVBQ1YsVUFBVSxDQUNYO1lBQ0QsVUFBVTtTQUNYLENBQUMsQ0FBQztRQUNILE9BQU8sQ0FBQyxHQUFHLENBQUMsaUNBQWlDLFVBQVUsaUJBQWlCLENBQUMsQ0FBQztRQUMxRSxNQUFNLFNBQVMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUMxQixPQUFPLENBQUMsR0FBRyxDQUFDLG9CQUFvQixVQUFVLFlBQVksU0FBUyxHQUFHLENBQUMsQ0FBQztRQUVwRSxNQUFNLHlCQUF5QixDQUFDLGNBQWMsRUFBRSxVQUFVLEVBQUUsU0FBUyxDQUFDLENBQUM7SUFDekUsQ0FBQztBQUNILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgZnMgPSByZXF1aXJlKFwiZnNcIik7XG5pbXBvcnQgeyBTQ0hFTUFfRERMLCBTY2hlbWFEZGwgfSBmcm9tIFwiLi9zY2hlbWFfZGRsXCI7XG5pbXBvcnQgeyBEYXRhYmFzZSwgU3Bhbm5lciB9IGZyb20gXCJAZ29vZ2xlLWNsb3VkL3NwYW5uZXJcIjtcbmltcG9ydCB7IERhdGFiYXNlQWRtaW5DbGllbnQgfSBmcm9tIFwiQGdvb2dsZS1jbG91ZC9zcGFubmVyL2J1aWxkL3NyYy92MVwiO1xuaW1wb3J0IHsgZXF1YWxNZXNzYWdlIH0gZnJvbSBcIkBzZWxmYWdlL21lc3NhZ2UvY29tcGFyYXRvclwiO1xuaW1wb3J0IHsgcGFyc2VNZXNzYWdlIH0gZnJvbSBcIkBzZWxmYWdlL21lc3NhZ2UvcGFyc2VyXCI7XG5pbXBvcnQge1xuICBkZXNlcmlhbGl6ZU1lc3NhZ2UsXG4gIHNlcmlhbGl6ZU1lc3NhZ2UsXG59IGZyb20gXCJAc2VsZmFnZS9tZXNzYWdlL3NlcmlhbGl6ZXJcIjtcblxuZXhwb3J0IGVudW0gU2NoZW1hU3RhdGUge1xuICBQRU5ESU5HID0gMSxcbiAgRE9ORSA9IDIsXG59XG5cbmludGVyZmFjZSBDcmVhdGVkVGFibGUge1xuICBuYW1lOiBzdHJpbmc7XG4gIGNvbHVtbnM6IFNldDxzdHJpbmc+O1xuICBpbmRleGVzOiBTZXQ8c3RyaW5nPjtcbn1cblxuYXN5bmMgZnVuY3Rpb24gY3JlYXRlU2NoZW1hSW1hZ2VUYWJsZUlmTm90RXhpc3RzKFxuICBkYXRhYmFzZUFkbWluQ2xpZW50OiBEYXRhYmFzZUFkbWluQ2xpZW50LFxuICBwcm9qZWN0SWQ6IHN0cmluZyxcbiAgaW5zdGFuY2VJZDogc3RyaW5nLFxuICBkYXRhYmFzZUlkOiBzdHJpbmcsXG4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgbGV0IGNyZWF0ZVNjaGVtYUltYWdlVGFibGUgPSBgXG4gIENSRUFURSBUQUJMRSBJRiBOT1QgRVhJU1RTIFNjaGVtYUltYWdlIChcbiAgICB2ZXJzaW9uSWQgRkxPQVQ2NCBOT1QgTlVMTCxcbiAgICBzY2hlbWEgQllURVMoTUFYKSBOT1QgTlVMTCxcbiAgICBzdGF0ZSBGTE9BVDY0IE5PVCBOVUxMLFxuICApIFBSSU1BUlkgS0VZKHZlcnNpb25JZClgO1xuICBsZXQgW29wZXJhdGlvbl0gPSBhd2FpdCBkYXRhYmFzZUFkbWluQ2xpZW50LnVwZGF0ZURhdGFiYXNlRGRsKHtcbiAgICBkYXRhYmFzZTogZGF0YWJhc2VBZG1pbkNsaWVudC5kYXRhYmFzZVBhdGgoXG4gICAgICBwcm9qZWN0SWQsXG4gICAgICBpbnN0YW5jZUlkLFxuICAgICAgZGF0YWJhc2VJZCxcbiAgICApLFxuICAgIHN0YXRlbWVudHM6IFtjcmVhdGVTY2hlbWFJbWFnZVRhYmxlXSxcbiAgfSk7XG4gIGNvbnNvbGUubG9nKGBXYWl0aW5nIGZvciBjcmVhdGlvbiBvZiB0YWJsZSBTY2hlbWFJbWFnZSB0byBjb21wbGV0ZS4uLmApO1xuICBhd2FpdCBvcGVyYXRpb24ucHJvbWlzZSgpO1xuICBjb25zb2xlLmxvZyhgQ3JlYXRlZCB0YWJsZSBTY2hlbWFJbWFnZS5gKTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gaW5zZXJ0TmV3U2NoZW1hSW1hZ2UoXG4gIGRhdGFiYXNlQ2xpZW50OiBEYXRhYmFzZSxcbiAgdmVyc2lvbklkOiBudW1iZXIsXG4gIHNjaGVtYURkbDogU2NoZW1hRGRsLFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IGRhdGFiYXNlQ2xpZW50LnJ1blRyYW5zYWN0aW9uQXN5bmMoYXN5bmMgKHRyYW5zY3Rpb24pID0+IHtcbiAgICBhd2FpdCB0cmFuc2N0aW9uLnJ1bih7XG4gICAgICBzcWw6IGBJTlNFUlQgU2NoZW1hSW1hZ2UgKHZlcnNpb25JZCwgc2NoZW1hLCBzdGF0ZSkgVkFMVUVTIChAdmVyc2lvbklkLCBAc2NoZW1hLCBAc3RhdGUpYCxcbiAgICAgIHBhcmFtczoge1xuICAgICAgICB2ZXJzaW9uSWQ6IFNwYW5uZXIuZmxvYXQodmVyc2lvbklkKSxcbiAgICAgICAgc2NoZW1hOiBCdWZmZXIuZnJvbShzZXJpYWxpemVNZXNzYWdlKHNjaGVtYURkbCwgU0NIRU1BX0RETCkuYnVmZmVyKSxcbiAgICAgICAgc3RhdGU6IFNwYW5uZXIuZmxvYXQoU2NoZW1hU3RhdGUuUEVORElORyksXG4gICAgICB9LFxuICAgICAgdHlwZXM6IHtcbiAgICAgICAgdmVyc2lvbklkOiB7XG4gICAgICAgICAgdHlwZTogXCJmbG9hdDY0XCIsXG4gICAgICAgIH0sXG4gICAgICAgIHNjaGVtYToge1xuICAgICAgICAgIHR5cGU6IFwiYnl0ZXNcIixcbiAgICAgICAgfSxcbiAgICAgICAgc3RhdGU6IHtcbiAgICAgICAgICB0eXBlOiBcImZsb2F0NjRcIixcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgfSk7XG4gICAgYXdhaXQgdHJhbnNjdGlvbi5jb21taXQoKTtcbiAgICBjb25zb2xlLmxvZyhgSW5zZXJ0ZWQgbmV3IHNjaGVtYSB3aXRoIHZlcnNpb24gJHt2ZXJzaW9uSWR9LmApO1xuICB9KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gaW5zZXJ0TmV3U2NoZW1hRGRsSWZOb3RFeGlzdHMoXG4gIGRhdGFiYXNlQ2xpZW50OiBEYXRhYmFzZSxcbiAgbmV3U2NoZW1hRGRsOiBTY2hlbWFEZGwsXG4pOiBQcm9taXNlPG51bWJlcj4ge1xuICBsZXQgW3Jvd3NdID0gYXdhaXQgZGF0YWJhc2VDbGllbnQucnVuKHtcbiAgICBzcWw6IGBTRUxFQ1QgdmVyc2lvbklkLCBzY2hlbWEgRlJPTSBTY2hlbWFJbWFnZSBPUkRFUiBCWSB2ZXJzaW9uSWQgREVTQyBMSU1JVCAxO2AsXG4gIH0pO1xuICBpZiAocm93cy5sZW5ndGggPT09IDApIHtcbiAgICBhd2FpdCBpbnNlcnROZXdTY2hlbWFJbWFnZShkYXRhYmFzZUNsaWVudCwgMSwgbmV3U2NoZW1hRGRsKTtcbiAgICByZXR1cm4gMTtcbiAgfSBlbHNlIHtcbiAgICBsZXQgbGF0ZXN0VmVyc2lvbklkID0gcm93c1swXS5hdCgwKS52YWx1ZSArIDA7XG4gICAgbGV0IGxhdGVzdFNjaGVtYURkbCA9IGRlc2VyaWFsaXplTWVzc2FnZShyb3dzWzBdLmF0KDEpLnZhbHVlLCBTQ0hFTUFfRERMKTtcbiAgICBpZiAoIWVxdWFsTWVzc2FnZShsYXRlc3RTY2hlbWFEZGwsIG5ld1NjaGVtYURkbCwgU0NIRU1BX0RETCkpIHtcbiAgICAgIGF3YWl0IGluc2VydE5ld1NjaGVtYUltYWdlKFxuICAgICAgICBkYXRhYmFzZUNsaWVudCxcbiAgICAgICAgbGF0ZXN0VmVyc2lvbklkICsgMSxcbiAgICAgICAgbmV3U2NoZW1hRGRsLFxuICAgICAgKTtcbiAgICAgIHJldHVybiBsYXRlc3RWZXJzaW9uSWQgKyAxO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gbGF0ZXN0VmVyc2lvbklkO1xuICAgIH1cbiAgfVxufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbWFya1NjaGVtYUltYWdlVXBkYXRlRG9uZShcbiAgZGF0YWJhc2VDbGllbnQ6IERhdGFiYXNlLFxuICBkYXRhYmFzZUlkOiBzdHJpbmcsXG4gIHZlcnNpb25JZDogbnVtYmVyLFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IGRhdGFiYXNlQ2xpZW50LnJ1blRyYW5zYWN0aW9uQXN5bmMoYXN5bmMgKHRyYW5zY3Rpb24pID0+IHtcbiAgICBhd2FpdCB0cmFuc2N0aW9uLnJ1bih7XG4gICAgICBzcWw6IGBVUERBVEUgU2NoZW1hSW1hZ2UgU0VUIHN0YXRlID0gQHN0YXRlIFdIRVJFIHZlcnNpb25JZCA9IEB2ZXJzaW9uSWRgLFxuICAgICAgcGFyYW1zOiB7XG4gICAgICAgIHZlcnNpb25JZDogU3Bhbm5lci5mbG9hdCh2ZXJzaW9uSWQpLFxuICAgICAgICBzdGF0ZTogU3Bhbm5lci5mbG9hdChTY2hlbWFTdGF0ZS5ET05FKSxcbiAgICAgIH0sXG4gICAgICB0eXBlczoge1xuICAgICAgICB2ZXJzaW9uSWQ6IHtcbiAgICAgICAgICB0eXBlOiBcImZsb2F0NjRcIixcbiAgICAgICAgfSxcbiAgICAgICAgc3RhdGU6IHtcbiAgICAgICAgICB0eXBlOiBcImZsb2F0NjRcIixcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgfSk7XG4gICAgYXdhaXQgdHJhbnNjdGlvbi5jb21taXQoKTtcbiAgICBjb25zb2xlLmxvZyhcbiAgICAgIGBNYXJrZWQgZGF0YWJhc2UgJHtkYXRhYmFzZUlkfSB2ZXJzaW9uICR7dmVyc2lvbklkfSB1cGRhdGUgZG9uZS5gLFxuICAgICk7XG4gIH0pO1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gdXBkYXRlU2NoZW1hRnJvbURkbEZpbGUoXG4gIHByb2plY3RJZDogc3RyaW5nLFxuICBpbnN0YW5jZUlkOiBzdHJpbmcsXG4gIGRhdGFiYXNlSWQ6IHN0cmluZyxcbiAgZGRsRmlsZTogc3RyaW5nLFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGxldCBuZXdTY2hlbWFEZGwgPSBwYXJzZU1lc3NhZ2UoXG4gICAgSlNPTi5wYXJzZShmcy5yZWFkRmlsZVN5bmMoZGRsRmlsZSkudG9TdHJpbmcoKSksXG4gICAgU0NIRU1BX0RETCxcbiAgKTtcbiAgYXdhaXQgdXBkYXRlU2NoZW1hKHByb2plY3RJZCwgaW5zdGFuY2VJZCwgZGF0YWJhc2VJZCwgbmV3U2NoZW1hRGRsKTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHVwZGF0ZVNjaGVtYShcbiAgcHJvamVjdElkOiBzdHJpbmcsXG4gIGluc3RhbmNlSWQ6IHN0cmluZyxcbiAgZGF0YWJhc2VJZDogc3RyaW5nLFxuICBuZXdTY2hlbWFEZGw6IFNjaGVtYURkbCxcbik6IFByb21pc2U8dm9pZD4ge1xuICBsZXQgc3Bhbm5lciA9IG5ldyBTcGFubmVyKHtcbiAgICBwcm9qZWN0SWQsXG4gIH0pO1xuICBsZXQgZGF0YWJhc2VBZG1pbkNsaWVudCA9IHNwYW5uZXIuZ2V0RGF0YWJhc2VBZG1pbkNsaWVudCgpO1xuICBhd2FpdCBjcmVhdGVTY2hlbWFJbWFnZVRhYmxlSWZOb3RFeGlzdHMoXG4gICAgZGF0YWJhc2VBZG1pbkNsaWVudCxcbiAgICBwcm9qZWN0SWQsXG4gICAgaW5zdGFuY2VJZCxcbiAgICBkYXRhYmFzZUlkLFxuICApO1xuXG4gIGxldCBpbnN0YW5jZSA9IHNwYW5uZXIuaW5zdGFuY2UoaW5zdGFuY2VJZCk7XG4gIGxldCBkYXRhYmFzZUNsaWVudCA9IGluc3RhbmNlLmRhdGFiYXNlKGRhdGFiYXNlSWQpO1xuICBsZXQgdmVyc2lvbklkID0gYXdhaXQgaW5zZXJ0TmV3U2NoZW1hRGRsSWZOb3RFeGlzdHMoXG4gICAgZGF0YWJhc2VDbGllbnQsXG4gICAgbmV3U2NoZW1hRGRsLFxuICApO1xuXG4gIGxldCBbcm93c10gPSBhd2FpdCBkYXRhYmFzZUNsaWVudC5ydW4oe1xuICAgIHNxbDogYFxuICAgICAgV0lUSFxuICAgICAgICB0IEFTIChcbiAgICAgICAgU0VMRUNUXG4gICAgICAgICAgdC50YWJsZV9uYW1lIEFTIHRhYmxlX25hbWUsXG4gICAgICAgICAgdC5zcGFubmVyX3N0YXRlIEFTIHRhYmxlX3NwYW5uZXJfc3RhdGUsXG4gICAgICAgICAgQVJSQVlfQUdHKGMuY29sdW1uX25hbWUpIEFTIGNvbHVtbl9uYW1lcyxcbiAgICAgICAgICBBUlJBWV9BR0coYy5zcGFubmVyX3N0YXRlKSBBUyBjb2x1bW5fc3Bhbm5lcl9zdGF0ZXMsXG4gICAgICAgIEZST01cbiAgICAgICAgICBpbmZvcm1hdGlvbl9zY2hlbWEudGFibGVzIEFTIHRcbiAgICAgICAgSk9JTlxuICAgICAgICAgIGluZm9ybWF0aW9uX3NjaGVtYS5jb2x1bW5zIEFTIGNcbiAgICAgICAgT05cbiAgICAgICAgICB0LnRhYmxlX25hbWUgPSBjLnRhYmxlX25hbWVcbiAgICAgICAgV0hFUkVcbiAgICAgICAgICB0LnRhYmxlX2NhdGFsb2cgPSAnJ1xuICAgICAgICAgIEFORCB0LnRhYmxlX3NjaGVtYSA9ICcnXG4gICAgICAgICAgQU5EIHQudGFibGVfbmFtZSAhPSAnU2NoZW1hSW1hZ2UnXG4gICAgICAgIEdST1VQIEJZXG4gICAgICAgICAgMSxcbiAgICAgICAgICAyKSxcbiAgICAgICAgaSBBUyAoXG4gICAgICAgIFNFTEVDVFxuICAgICAgICAgIHQudGFibGVfbmFtZSxcbiAgICAgICAgICBBUlJBWV9BR0coaS5pbmRleF9uYW1lKSBBUyBpbmRleF9uYW1lcyxcbiAgICAgICAgICBBUlJBWV9BR0coaS5pbmRleF9zdGF0ZSkgQVMgaW5kZXhfc3RhdGVzLFxuICAgICAgICBGUk9NXG4gICAgICAgICAgaW5mb3JtYXRpb25fc2NoZW1hLnRhYmxlcyBBUyB0XG4gICAgICAgIEpPSU5cbiAgICAgICAgICBpbmZvcm1hdGlvbl9zY2hlbWEuaW5kZXhlcyBBUyBpXG4gICAgICAgIE9OXG4gICAgICAgICAgdC50YWJsZV9uYW1lID0gaS50YWJsZV9uYW1lXG4gICAgICAgIFdIRVJFXG4gICAgICAgICAgdC50YWJsZV9jYXRhbG9nID0gJydcbiAgICAgICAgICBBTkQgdC50YWJsZV9zY2hlbWEgPSAnJ1xuICAgICAgICAgIEFORCB0LnRhYmxlX25hbWUgIT0gJ1NjaGVtYUltYWdlJ1xuICAgICAgICAgIEFORCBpLmluZGV4X3R5cGUgIT0gJ1BSSU1BUllfS0VZJ1xuICAgICAgICBHUk9VUCBCWVxuICAgICAgICAgIDEpXG4gICAgICBTRUxFQ1RcbiAgICAgICAgdC50YWJsZV9uYW1lLFxuICAgICAgICB0LnRhYmxlX3NwYW5uZXJfc3RhdGUsXG4gICAgICAgIHQuY29sdW1uX25hbWVzLFxuICAgICAgICB0LmNvbHVtbl9zcGFubmVyX3N0YXRlcyxcbiAgICAgICAgaS5pbmRleF9uYW1lcyxcbiAgICAgICAgaS5pbmRleF9zdGF0ZXMsXG4gICAgICBGUk9NXG4gICAgICAgIHRcbiAgICAgIExFRlQgSk9JTlxuICAgICAgICBpXG4gICAgICBPTlxuICAgICAgICB0LnRhYmxlX25hbWUgPSBpLnRhYmxlX25hbWU7YCxcbiAgfSk7XG4gIGxldCBub3RDb21taXR0ZWRFcnJvcnMgPSBuZXcgQXJyYXk8c3RyaW5nPigpO1xuICBsZXQgY3JlYXRlZFRhYmxlcyA9IG5ldyBNYXA8c3RyaW5nLCBDcmVhdGVkVGFibGU+KCk7XG4gIGZvciAobGV0IHJvdyBvZiByb3dzKSB7XG4gICAgbGV0IHRhYmxlTmFtZSA9IHJvdy5hdCgwKS52YWx1ZTtcbiAgICBsZXQgdGFibGVTcGFubmVyU3RhdGUgPSByb3cuYXQoMSkudmFsdWU7XG4gICAgaWYgKHRhYmxlU3Bhbm5lclN0YXRlICE9PSBcIkNPTU1JVFRFRFwiKSB7XG4gICAgICBub3RDb21taXR0ZWRFcnJvcnMucHVzaChcbiAgICAgICAgYFRhYmxlICR7dGFibGVOYW1lfSBub3QgY29tbWl0dGVkIHlldC4gQ3VycmVudCBzdGF0ZTogJHt0YWJsZVNwYW5uZXJTdGF0ZX0uYCxcbiAgICAgICk7XG4gICAgfVxuXG4gICAgbGV0IGNvbHVtbnMgPSBuZXcgU2V0PHN0cmluZz4oKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHJvdy5hdCgyKS52YWx1ZS5sZW5ndGg7IGkrKykge1xuICAgICAgbGV0IGNvbHVtbk5hbWUgPSByb3cuYXQoMikudmFsdWVbaV07XG4gICAgICBsZXQgY29sdW1uU3Bhbm5lclN0YXRlID0gcm93LmF0KDMpLnZhbHVlW2ldO1xuICAgICAgaWYgKGNvbHVtblNwYW5uZXJTdGF0ZSAhPT0gXCJDT01NSVRURURcIikge1xuICAgICAgICBub3RDb21taXR0ZWRFcnJvcnMucHVzaChcbiAgICAgICAgICBgQ29sdW1uICR7dGFibGVOYW1lfS4ke2NvbHVtbk5hbWV9IG5vdCBjb21pdHRlZCB5ZXQuIEN1cnJlbnQgc3RhdGU6ICR7Y29sdW1uU3Bhbm5lclN0YXRlfS5gLFxuICAgICAgICApO1xuICAgICAgfVxuICAgICAgY29sdW1ucy5hZGQoY29sdW1uTmFtZSk7XG4gICAgfVxuXG4gICAgbGV0IGluZGV4ZXMgPSBuZXcgU2V0PHN0cmluZz4oKTtcbiAgICBpZiAocm93LmF0KDQpLnZhbHVlKSB7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHJvdy5hdCg0KS52YWx1ZS5sZW5ndGg7IGkrKykge1xuICAgICAgICBsZXQgaW5kZXhOYW1lID0gcm93LmF0KDQpLnZhbHVlW2ldO1xuICAgICAgICBsZXQgaW5kZXhTdGF0ZSA9IHJvdy5hdCg1KS52YWx1ZVtpXTtcbiAgICAgICAgaWYgKGluZGV4U3RhdGUgIT09IFwiUkVBRF9XUklURVwiKSB7XG4gICAgICAgICAgbm90Q29tbWl0dGVkRXJyb3JzLnB1c2goXG4gICAgICAgICAgICBgSW5kZXggJHtpbmRleE5hbWV9IGlzIG5vdCByZWFkeSB5ZXQuIEN1cnJlbnQgc3RhdGU6ICR7aW5kZXhTdGF0ZX0uYCxcbiAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIGluZGV4ZXMuYWRkKHJvdy5hdCg0KS52YWx1ZVtpXSk7XG4gICAgICB9XG4gICAgfVxuICAgIGNyZWF0ZWRUYWJsZXMuc2V0KHRhYmxlTmFtZSwge1xuICAgICAgbmFtZTogdGFibGVOYW1lLFxuICAgICAgY29sdW1ucyxcbiAgICAgIGluZGV4ZXMsXG4gICAgfSk7XG4gIH1cbiAgaWYgKG5vdENvbW1pdHRlZEVycm9ycy5sZW5ndGggPiAwKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYEdpdmUgdXAgdXBkYXRpbmcgc2NoZW1hIGR1ZSB0byBleGlzdGluZyB0YWJsZXMvY29sdW1ucyBub3QgY29tbWl0dGVkIHlldC5cXG4ke25vdENvbW1pdHRlZEVycm9ycy5qb2luKFwiXFxuXCIpfWAsXG4gICAgKTtcbiAgfVxuXG4gIGxldCBzdGF0ZW1lbnRzID0gbmV3IEFycmF5PHN0cmluZz4oKTtcbiAgZm9yIChsZXQgdGFibGUgb2YgbmV3U2NoZW1hRGRsLnRhYmxlcykge1xuICAgIGxldCBjcmVhdGVkVGFibGUgPSBjcmVhdGVkVGFibGVzLmdldCh0YWJsZS5uYW1lKTtcbiAgICBpZiAoIWNyZWF0ZWRUYWJsZSkge1xuICAgICAgc3RhdGVtZW50cy5wdXNoKHRhYmxlLmNyZWF0ZVRhYmxlRGRsKTtcbiAgICAgIGlmICh0YWJsZS5pbmRleGVzKSB7XG4gICAgICAgIGZvciAobGV0IGluZGV4IG9mIHRhYmxlLmluZGV4ZXMpIHtcbiAgICAgICAgICBzdGF0ZW1lbnRzLnB1c2goaW5kZXguY3JlYXRlSW5kZXhEZGwpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGxldCBhZGRJbmRleGVzU3RhdGVtZW50ID0gbmV3IEFycmF5PHN0cmluZz4oKTtcbiAgICAgIGlmICh0YWJsZS5pbmRleGVzKSB7XG4gICAgICAgIGZvciAobGV0IGluZGV4IG9mIHRhYmxlLmluZGV4ZXMpIHtcbiAgICAgICAgICBpZiAoIWNyZWF0ZWRUYWJsZS5pbmRleGVzLmhhcyhpbmRleC5uYW1lKSkge1xuICAgICAgICAgICAgYWRkSW5kZXhlc1N0YXRlbWVudC5wdXNoKGluZGV4LmNyZWF0ZUluZGV4RGRsKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY3JlYXRlZFRhYmxlLmluZGV4ZXMuZGVsZXRlKGluZGV4Lm5hbWUpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgLy8gRHJvcCBpbmRleGVzIGJlZm9yZSBkcm9wcGluZyBjb2x1bW5zLlxuICAgICAgZm9yIChsZXQgZXhjZXNzaXZlSW5kZXggb2YgY3JlYXRlZFRhYmxlLmluZGV4ZXMpIHtcbiAgICAgICAgc3RhdGVtZW50cy5wdXNoKGBEUk9QIElOREVYICR7ZXhjZXNzaXZlSW5kZXh9YCk7XG4gICAgICB9XG5cbiAgICAgIGZvciAobGV0IGNvbHVtbiBvZiB0YWJsZS5jb2x1bW5zKSB7XG4gICAgICAgIGlmICghY3JlYXRlZFRhYmxlLmNvbHVtbnMuaGFzKGNvbHVtbi5uYW1lKSkge1xuICAgICAgICAgIHN0YXRlbWVudHMucHVzaChjb2x1bW4uYWRkQ29sdW1uRGRsKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjcmVhdGVkVGFibGUuY29sdW1ucy5kZWxldGUoY29sdW1uLm5hbWUpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBmb3IgKGxldCBleGNlc3NpdmVDb2x1bW4gb2YgY3JlYXRlZFRhYmxlLmNvbHVtbnMpIHtcbiAgICAgICAgc3RhdGVtZW50cy5wdXNoKFxuICAgICAgICAgIGBBTFRFUiBUQUJMRSAke3RhYmxlLm5hbWV9IERST1AgQ09MVU1OICR7ZXhjZXNzaXZlQ29sdW1ufWAsXG4gICAgICAgICk7XG4gICAgICB9XG5cbiAgICAgIC8vIFdhaXQgdW50aWwgY29sdW1ucyBhcmUgYWRkZWQgYmVmb3JlIGFkZGluZyBpbmRleGVzLlxuICAgICAgc3RhdGVtZW50cy5wdXNoKC4uLmFkZEluZGV4ZXNTdGF0ZW1lbnQpO1xuICAgIH1cbiAgICBjcmVhdGVkVGFibGVzLmRlbGV0ZSh0YWJsZS5uYW1lKTtcbiAgfVxuICBmb3IgKGxldCBleGNlc3NpdmVUYWJsZSBvZiBjcmVhdGVkVGFibGVzLnZhbHVlcygpKSB7XG4gICAgZm9yIChsZXQgZXhjZXNzaXZlSW5kZXggb2YgZXhjZXNzaXZlVGFibGUuaW5kZXhlcykge1xuICAgICAgc3RhdGVtZW50cy5wdXNoKGBEUk9QIElOREVYICR7ZXhjZXNzaXZlSW5kZXh9YCk7XG4gICAgfVxuICAgIHN0YXRlbWVudHMucHVzaChgRFJPUCBUQUJMRSAke2V4Y2Vzc2l2ZVRhYmxlLm5hbWV9YCk7XG4gIH1cblxuICBpZiAoc3RhdGVtZW50cy5sZW5ndGggPT09IDApIHtcbiAgICBjb25zb2xlLmxvZyhcbiAgICAgIGBEYXRhYmFzZSAke2RhdGFiYXNlSWR9IHZlcnNpb24gJHt2ZXJzaW9uSWR9IGFscmVhZHkgdXAtdG8tZGF0ZS5gLFxuICAgICk7XG4gIH0gZWxzZSB7XG4gICAgY29uc29sZS5sb2coXG4gICAgICBgRXhlY3V0aW5nIHRoZSBmb2xsb3dpbmcgc3RhdGVtZW50czpcXG4ke3N0YXRlbWVudHMuam9pbihcIlxcblwiKX1gLFxuICAgICk7XG4gICAgbGV0IFtvcGVyYXRpb25dID0gYXdhaXQgZGF0YWJhc2VBZG1pbkNsaWVudC51cGRhdGVEYXRhYmFzZURkbCh7XG4gICAgICBkYXRhYmFzZTogZGF0YWJhc2VBZG1pbkNsaWVudC5kYXRhYmFzZVBhdGgoXG4gICAgICAgIHByb2plY3RJZCxcbiAgICAgICAgaW5zdGFuY2VJZCxcbiAgICAgICAgZGF0YWJhc2VJZCxcbiAgICAgICksXG4gICAgICBzdGF0ZW1lbnRzLFxuICAgIH0pO1xuICAgIGNvbnNvbGUubG9nKGBXYWl0aW5nIGZvciB1cGRhdGluZyBkYXRhYmFzZSAke2RhdGFiYXNlSWR9IHRvIGNvbXBsZXRlLi4uYCk7XG4gICAgYXdhaXQgb3BlcmF0aW9uLnByb21pc2UoKTtcbiAgICBjb25zb2xlLmxvZyhgVXBkYXRlZCBkYXRhYmFzZSAke2RhdGFiYXNlSWR9IHZlcnNpb24gJHt2ZXJzaW9uSWR9LmApO1xuXG4gICAgYXdhaXQgbWFya1NjaGVtYUltYWdlVXBkYXRlRG9uZShkYXRhYmFzZUNsaWVudCwgZGF0YWJhc2VJZCwgdmVyc2lvbklkKTtcbiAgfVxufVxuIl19