kopia lustrzana https://github.com/FacilMap/facilmap
Change migration code into async functions
rodzic
2fac2bd04e
commit
12b8e737bd
|
@ -19,59 +19,58 @@ export default class DatabaseMigrations {
|
|||
}
|
||||
|
||||
async _runMigrations(): Promise<void> {
|
||||
await this._renameColMigrations();
|
||||
await this._changeColMigrations();
|
||||
await this._addColMigrations();
|
||||
await this._dropdownKeyMigration();
|
||||
await this._elevationMigration();
|
||||
await this._legendMigration();
|
||||
await this._bboxMigration();
|
||||
}
|
||||
|
||||
|
||||
/** Run any migrations that rename columns */
|
||||
async _renameColMigrations(): Promise<void> {
|
||||
const queryInterface = this._db._conn.getQueryInterface();
|
||||
|
||||
const renameColMigrations = Promise.all([
|
||||
queryInterface.describeTable('Lines').then((attributes: any) => {
|
||||
const promises: Promise<any>[] = [ ];
|
||||
const lineAttrs = await queryInterface.describeTable('Lines');
|
||||
|
||||
// Rename Line.points to Line.routePoints
|
||||
if(attributes.points) {
|
||||
promises.push(queryInterface.renameColumn('Lines', 'points', 'routePoints'));
|
||||
}
|
||||
// Rename Line.points to Line.routePoints
|
||||
if(lineAttrs.points) {
|
||||
await queryInterface.renameColumn('Lines', 'points', 'routePoints');
|
||||
}
|
||||
|
||||
// Change routing type "shortest" / "fastest" to "car", add type "track"
|
||||
if(attributes.mode.type.indexOf("shortest") != -1)
|
||||
promises.push(this._db.lines.LineModel.update({ mode: "car" }, { where: { mode: { [Op.in]: [ "fastest", "shortest" ] } } }));
|
||||
// Change routing type "shortest" / "fastest" to "car", add type "track"
|
||||
if(lineAttrs.mode.type.indexOf("shortest") != -1)
|
||||
await this._db.lines.LineModel.update({ mode: "car" }, { where: { mode: { [Op.in]: [ "fastest", "shortest" ] } } });
|
||||
|
||||
return Promise.all(promises);
|
||||
}),
|
||||
|
||||
queryInterface.describeTable('Pads').then((attributes: any) => {
|
||||
// Rename writeId to adminId
|
||||
const padAttrs = await queryInterface.describeTable('Pads');
|
||||
|
||||
if(!attributes.adminId) {
|
||||
const Pad = this._db.pads.PadModel;
|
||||
return queryInterface.renameColumn('Pads', 'writeId', 'adminId').then(() => {
|
||||
return queryInterface.addColumn('Pads', 'writeId', Pad.rawAttributes.writeId);
|
||||
}).then(() => {
|
||||
return Pad.findAll<PadModel>();
|
||||
}).then((pads) => {
|
||||
let promise: Promise<any> = Promise.resolve();
|
||||
for(const pad of pads) {
|
||||
const genId = (): Promise<string> => {
|
||||
const writeId = generateRandomId(14);
|
||||
return this._db.pads.padIdExists(writeId).then((exists) => {
|
||||
if(exists)
|
||||
return genId();
|
||||
else
|
||||
return writeId;
|
||||
});
|
||||
};
|
||||
// Rename writeId to adminId
|
||||
if(!padAttrs.adminId) {
|
||||
const Pad = this._db.pads.PadModel;
|
||||
await queryInterface.renameColumn('Pads', 'writeId', 'adminId');
|
||||
await queryInterface.addColumn('Pads', 'writeId', Pad.rawAttributes.writeId);
|
||||
|
||||
promise = promise.then(() => {
|
||||
return genId();
|
||||
}).then((writeId) => {
|
||||
return Pad.update({writeId}, { where: { id: pad.id } });
|
||||
});
|
||||
}
|
||||
return promise;
|
||||
});
|
||||
}
|
||||
})
|
||||
]);
|
||||
const pads = await Pad.findAll<PadModel>();
|
||||
for(const pad of pads) {
|
||||
let writeId;
|
||||
do {
|
||||
writeId = generateRandomId(14);
|
||||
} while (await this._db.pads.padIdExists(writeId));
|
||||
|
||||
const changeColMigrations = Promise.all([ 'Pads', 'Markers', 'Lines', 'Types' ].map(async (table) => {
|
||||
await Pad.update({writeId}, { where: { id: pad.id } });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Run any migrations that change column types */
|
||||
async _changeColMigrations(): Promise<void> {
|
||||
const queryInterface = this._db._conn.getQueryInterface();
|
||||
|
||||
for (const table of [ 'Pads', 'Markers', 'Lines', 'Types' ]) {
|
||||
const attributes: any = await queryInterface.describeTable(table);
|
||||
|
||||
// allow null on Pad.name, Marker.name, Line.name
|
||||
|
@ -83,162 +82,148 @@ export default class DatabaseMigrations {
|
|||
await queryInterface.changeColumn(table, "mode", { type: Sequelize.TEXT, allowNull: false, defaultValue: "" });
|
||||
if(table == "Types" && attributes.defaultMode.type != "TEXT")
|
||||
await queryInterface.changeColumn(table, "defaultMode", { type: Sequelize.TEXT, allowNull: true });
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
const addColMigrations = renameColMigrations.then(() => {
|
||||
return Promise.all([ 'Pad', 'Marker', 'Type', 'View', 'Line', 'LinePoint' ].map((table) => {
|
||||
const model = this._db._conn.model(table);
|
||||
return queryInterface.describeTable(model.getTableName()).then((attributes: any) => {
|
||||
const promises = [ ];
|
||||
for(const attribute in model.rawAttributes) {
|
||||
if(!attributes[attribute])
|
||||
promises.push(queryInterface.addColumn(model.getTableName(), attribute, model.rawAttributes[attribute]));
|
||||
|
||||
/** Add all missing columns */
|
||||
async _addColMigrations(): Promise<void> {
|
||||
const queryInterface = this._db._conn.getQueryInterface();
|
||||
|
||||
for (const table of [ 'Pad', 'Marker', 'Type', 'View', 'Line', 'LinePoint' ]) {
|
||||
const model = this._db._conn.model(table);
|
||||
const attributes = await queryInterface.describeTable(model.getTableName());
|
||||
for(const attribute in model.rawAttributes) {
|
||||
if(!attributes[attribute])
|
||||
await queryInterface.addColumn(model.getTableName(), attribute, model.rawAttributes[attribute]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Get rid of the dropdown key, save the value in the data instead */
|
||||
async _dropdownKeyMigration(): Promise<void> {
|
||||
const dropdownKeysMigrated = await this._db.meta.getMeta("dropdownKeysMigrated");
|
||||
if(dropdownKeysMigrated == "1")
|
||||
return;
|
||||
|
||||
const types = await this._db.types.TypeModel.findAll();
|
||||
for(const type of types) {
|
||||
const newFields = type.fields; // type.fields is a getter, we cannot modify the object directly
|
||||
const dropdowns = newFields.filter((field) => field.type == "dropdown");
|
||||
if(dropdowns.length > 0) {
|
||||
const objectStream = (type.type == "line" ? this._db.lines.getPadLinesByType(type.padId, type.id) : this._db.markers.getPadMarkersByType(type.padId, type.id)) as Highland.Stream<Marker | Line>;
|
||||
|
||||
await streamEachPromise(objectStream, (object) => {
|
||||
const newData = clone(object.data);
|
||||
for(const dropdown of dropdowns) {
|
||||
const newVal = (dropdown.options || []).filter((option: any) => option.key == newData[dropdown.name])[0];
|
||||
if(newVal)
|
||||
newData[dropdown.name] = newVal.value;
|
||||
else if(newData[dropdown.name])
|
||||
console.log(`Warning: Dropdown key ${newData[dropdown.name]} for field ${dropdown.name} of type ${type.name} of pad ${type.padId} does not exist.`);
|
||||
}
|
||||
return Promise.all(promises);
|
||||
|
||||
if(!isEqual(newData, object.data))
|
||||
return this._db.helpers._updatePadObject(type.type == "line" ? "Line" : "Marker", object.padId, object.id, {data: newData}, true);
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
// Get rid of the dropdown key, save the value in the data instead
|
||||
const dropdownKeyMigration = this._db.meta.getMeta("dropdownKeysMigrated").then((dropdownKeysMigrated) => {
|
||||
if(dropdownKeysMigrated == "1")
|
||||
return;
|
||||
|
||||
return this._db.types.TypeModel.findAll().then((types) => {
|
||||
let operations = Promise.resolve();
|
||||
for(const type of types) {
|
||||
const newFields = type.fields; // type.fields is a getter, we cannot modify the object directly
|
||||
const dropdowns = newFields.filter((field) => field.type == "dropdown");
|
||||
if(dropdowns.length > 0) {
|
||||
operations = operations.then(() => {
|
||||
const objectStream = (type.type == "line" ? this._db.lines.getPadLinesByType(type.padId, type.id) : this._db.markers.getPadMarkersByType(type.padId, type.id)) as Highland.Stream<Marker | Line>;
|
||||
|
||||
return streamEachPromise(objectStream, (object) => {
|
||||
const newData = clone(object.data);
|
||||
for(const dropdown of dropdowns) {
|
||||
const newVal = (dropdown.options || []).filter((option: any) => option.key == newData[dropdown.name])[0];
|
||||
if(newVal)
|
||||
newData[dropdown.name] = newVal.value;
|
||||
else if(newData[dropdown.name])
|
||||
console.log(`Warning: Dropdown key ${newData[dropdown.name]} for field ${dropdown.name} of type ${type.name} of pad ${type.padId} does not exist.`);
|
||||
}
|
||||
|
||||
if(!isEqual(newData, object.data))
|
||||
return this._db.helpers._updatePadObject(type.type == "line" ? "Line" : "Marker", object.padId, object.id, {data: newData}, true);
|
||||
});
|
||||
}).then(() => {
|
||||
dropdowns.forEach((dropdown) => {
|
||||
if(dropdown.default) {
|
||||
const newDefault = dropdown.options?.filter((option: any) => (option.key == dropdown.default))[0];
|
||||
if(newDefault)
|
||||
dropdown.default = newDefault.value;
|
||||
else
|
||||
console.log(`Warning: Default dropdown key ${dropdown.default} for field ${dropdown.name} of type ${type.name} of pad ${type.padId} does not exist.`);
|
||||
}
|
||||
|
||||
dropdown.options?.forEach((option: any) => {
|
||||
delete option.key;
|
||||
});
|
||||
});
|
||||
return this._db.helpers._updatePadObject("Type", type.padId, type.id, {fields: newFields}, true);
|
||||
});
|
||||
dropdowns.forEach((dropdown) => {
|
||||
if(dropdown.default) {
|
||||
const newDefault = dropdown.options?.filter((option: any) => (option.key == dropdown.default))[0];
|
||||
if(newDefault)
|
||||
dropdown.default = newDefault.value;
|
||||
else
|
||||
console.log(`Warning: Default dropdown key ${dropdown.default} for field ${dropdown.name} of type ${type.name} of pad ${type.padId} does not exist.`);
|
||||
}
|
||||
}
|
||||
return operations;
|
||||
}).then(() => {
|
||||
return this._db.meta.setMeta("dropdownKeysMigrated", "1");
|
||||
});
|
||||
});
|
||||
|
||||
// Get elevation data for all lines/markers that don't have any yet
|
||||
const elevationMigration = addColMigrations.then(() => {
|
||||
return this._db.meta.getMeta("hasElevation");
|
||||
}).then((hasElevation) => {
|
||||
if(hasElevation == "1")
|
||||
return;
|
||||
|
||||
return Promise.all([
|
||||
this._db.lines.LineModel.findAll().then((lines) => {
|
||||
let operations = Promise.resolve();
|
||||
for(const line of lines) {
|
||||
operations = operations.then(() => {
|
||||
return this._db.lines.LineModel.build({ id: line.id }).getLinePoints().then((trackPoints) => {
|
||||
return this._db.lines._setLinePoints(line.padId, line.id, trackPoints, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
return operations;
|
||||
}),
|
||||
this._db.markers.MarkerModel.findAll({where: {ele: null}}).then((markers) => {
|
||||
return getElevationForPoints(markers).then((elevations) => {
|
||||
let operations = Promise.resolve();
|
||||
markers.forEach((marker, i) => {
|
||||
operations = operations.then(() => {
|
||||
return this._db.helpers._updatePadObject("Marker", marker.padId, marker.id, {ele: elevations[i]}, true);
|
||||
});
|
||||
});
|
||||
return operations;
|
||||
dropdown.options?.forEach((option: any) => {
|
||||
delete option.key;
|
||||
});
|
||||
})
|
||||
]).then(() => {
|
||||
return this._db.meta.setMeta("hasElevation", "1");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Add showInLegend field to types
|
||||
const legendMigration = addColMigrations.then(() => (this._db.meta.getMeta("hasLegendOption"))).then((hasLegendOption) => {
|
||||
if(hasLegendOption == "1")
|
||||
return;
|
||||
|
||||
return this._db.types.TypeModel.findAll().then((types) => {
|
||||
let operations = Promise.resolve();
|
||||
for(const type of types) {
|
||||
let showInLegend = false;
|
||||
|
||||
if(type.colourFixed || (type.type == "marker" && type.symbolFixed && type.defaultSymbol) || (type.type == "marker" && type.shapeFixed) || (type.type == "line" && type.widthFixed))
|
||||
showInLegend = true;
|
||||
|
||||
if(!showInLegend) {
|
||||
for(const field of type.fields) {
|
||||
if((field.type == "dropdown" || field.type == "checkbox") && (field.controlColour || (type.type == "marker" && field.controlSymbol) || (type.type == "marker" && field.controlShape) || (type.type == "line" && field.controlWidth))) {
|
||||
showInLegend = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
operations = operations.then(() => (this._db.helpers._updatePadObject("Type", type.padId, type.id, { showInLegend }, true)));
|
||||
}
|
||||
return operations;
|
||||
}).then(() => (this._db.meta.setMeta("hasLegendOption", "1")));
|
||||
});
|
||||
|
||||
|
||||
// Calculate bounding box for lines
|
||||
const bboxMigration = addColMigrations.then(async () => {
|
||||
if(await this._db.meta.getMeta("hasBboxes") == "1")
|
||||
return;
|
||||
|
||||
const LinePoint = this._db.lines.LinePointModel;
|
||||
|
||||
for(const line of await this._db.lines.LineModel.findAll()) {
|
||||
const bbox = await promiseProps({
|
||||
top: LinePoint.min<number, LinePointModel>("lat", { where: { lineId: line.id } }),
|
||||
bottom: LinePoint.max<number, LinePointModel>("lat", { where: { lineId: line.id } }),
|
||||
left: LinePoint.min<number, LinePointModel>("lon", { where: { lineId: line.id } }),
|
||||
right: LinePoint.max<number, LinePointModel>("lon", { where: { lineId: line.id } })
|
||||
});
|
||||
|
||||
if(isNaN(bbox.top) || isNaN(bbox.left) || isNaN(bbox.bottom) || isNaN(bbox.right)) // This is a broken line without track points
|
||||
await this._db.helpers._deletePadObject("Line", line.padId, line.id);
|
||||
else
|
||||
await this._db.helpers._updatePadObject("Line", line.padId, line.id, bbox, true);
|
||||
await this._db.helpers._updatePadObject("Type", type.padId, type.id, {fields: newFields}, true);
|
||||
}
|
||||
}
|
||||
|
||||
await this._db.meta.setMeta("dropdownKeysMigrated", "1");
|
||||
}
|
||||
|
||||
|
||||
/* Get elevation data for all lines/markers that don't have any yet */
|
||||
async _elevationMigration(): Promise<void> {
|
||||
const hasElevation = await this._db.meta.getMeta("hasElevation");
|
||||
if(hasElevation == "1")
|
||||
return;
|
||||
|
||||
const lines = await this._db.lines.LineModel.findAll();
|
||||
for(const line of lines) {
|
||||
const trackPoints = await this._db.lines.LineModel.build({ id: line.id }).getLinePoints();
|
||||
await this._db.lines._setLinePoints(line.padId, line.id, trackPoints, true);
|
||||
}
|
||||
|
||||
const markers = await this._db.markers.MarkerModel.findAll({where: {ele: null}});
|
||||
const elevations = await getElevationForPoints(markers);
|
||||
|
||||
for (let i = 0; i < markers.length; i++) {
|
||||
await this._db.helpers._updatePadObject("Marker", markers[i].padId, markers[i].id, {ele: elevations[i]}, true);
|
||||
}
|
||||
|
||||
await this._db.meta.setMeta("hasElevation", "1");
|
||||
}
|
||||
|
||||
|
||||
/* Add showInLegend field to types */
|
||||
async _legendMigration(): Promise<void> {
|
||||
const hasLegendOption = await this._db.meta.getMeta("hasLegendOption");
|
||||
if(hasLegendOption == "1")
|
||||
return;
|
||||
|
||||
const types = await this._db.types.TypeModel.findAll();
|
||||
for(const type of types) {
|
||||
let showInLegend = false;
|
||||
|
||||
if(type.colourFixed || (type.type == "marker" && type.symbolFixed && type.defaultSymbol) || (type.type == "marker" && type.shapeFixed) || (type.type == "line" && type.widthFixed))
|
||||
showInLegend = true;
|
||||
|
||||
if(!showInLegend) {
|
||||
for(const field of type.fields) {
|
||||
if((field.type == "dropdown" || field.type == "checkbox") && (field.controlColour || (type.type == "marker" && field.controlSymbol) || (type.type == "marker" && field.controlShape) || (type.type == "line" && field.controlWidth))) {
|
||||
showInLegend = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await this._db.meta.setMeta("hasBboxes", "1");
|
||||
});
|
||||
await this._db.helpers._updatePadObject("Type", type.padId, type.id, { showInLegend }, true);
|
||||
}
|
||||
|
||||
await Promise.all([ renameColMigrations, changeColMigrations, addColMigrations, dropdownKeyMigration, elevationMigration, legendMigration, bboxMigration ]);
|
||||
await this._db.meta.setMeta("hasLegendOption", "1");
|
||||
}
|
||||
|
||||
|
||||
/* Calculate bounding box for lines */
|
||||
async _bboxMigration(): Promise<void> {
|
||||
if(await this._db.meta.getMeta("hasBboxes") == "1")
|
||||
return;
|
||||
|
||||
const LinePoint = this._db.lines.LinePointModel;
|
||||
|
||||
for(const line of await this._db.lines.LineModel.findAll()) {
|
||||
const bbox = await promiseProps({
|
||||
top: LinePoint.min<number, LinePointModel>("lat", { where: { lineId: line.id } }),
|
||||
bottom: LinePoint.max<number, LinePointModel>("lat", { where: { lineId: line.id } }),
|
||||
left: LinePoint.min<number, LinePointModel>("lon", { where: { lineId: line.id } }),
|
||||
right: LinePoint.max<number, LinePointModel>("lon", { where: { lineId: line.id } })
|
||||
});
|
||||
|
||||
if(isNaN(bbox.top) || isNaN(bbox.left) || isNaN(bbox.bottom) || isNaN(bbox.right)) // This is a broken line without track points
|
||||
await this._db.helpers._deletePadObject("Line", line.padId, line.id);
|
||||
else
|
||||
await this._db.helpers._updatePadObject("Line", line.padId, line.id, bbox, true);
|
||||
}
|
||||
|
||||
await this._db.meta.setMeta("hasBboxes", "1");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue