update dependencies and devDependencies, address lint issues
[squeep-indie-auther] / src / db / schema-version-helper.js
1 'use strict';
2
3 const fs = require('fs');
4 const path = require('path');
5
6 /**
7 * Utility functions for wrangling schema migrations.
8 * This mostly just deals with sorting and comparing 'x.y.z' version
9 * strings, with some presumptions about directory layouts and whatnot.
10 */
11
12 /**
13 * @typedef {object} SchemaVersionObject
14 * @property {number} major major
15 * @property {number} minor minor
16 * @property {number} patch patch
17 */
18
19
20 /**
21 * Split a dotted version string into parts.
22 * @param {string} v version string
23 * @returns {SchemaVersionObject} version object
24 */
25 function schemaVersionStringToObject(v) {
26 const [ major, minor, patch ] = v.split('.', 3).map((x) => parseInt(x, 10));
27 return { major, minor, patch };
28 }
29
30
31 /**
32 * Render a version object numerically.
33 * Assumes no part will be greater than 1000.
34 * @param {SchemaVersionObject} v version object
35 * @returns {number} number
36 */
37 function schemaVersionObjectToNumber(v) {
38 const vScale = 1000;
39 return parseInt(v.major) * vScale * vScale + parseInt(v.minor) * vScale + parseInt(v.patch);
40 }
41
42
43 /**
44 * Convert dotted version string into number.
45 * @param {string} v version string
46 * @returns {number} number
47 */
48 function schemaVersionStringToNumber(v) {
49 return schemaVersionObjectToNumber(schemaVersionStringToObject(v));
50 }
51
52
53 /**
54 * Version string comparison, for sorting.
55 * @param {string} a version string
56 * @param {string} b version string
57 * @returns {number} cmp
58 */
59 function schemaVersionStringCmp(a, b) {
60 return schemaVersionStringToNumber(a) - schemaVersionStringToNumber(b);
61 }
62
63
64 /**
65 * Check if an entry in a directory is a directory containing a migration file.
66 * @param {string} schemaDir schema dir
67 * @param {string} name name
68 * @param {string} migrationFile migration file
69 * @returns {boolean} is
70 */
71 function isSchemaMigrationDirectory(schemaDir, name, migrationFile = 'apply.sql') {
72 // eslint-disable-next-line security/detect-non-literal-fs-filename
73 const nameStat = fs.statSync(path.join(schemaDir, name));
74 if (nameStat.isDirectory()) {
75 let applyStat;
76 try {
77 // eslint-disable-next-line security/detect-non-literal-fs-filename
78 applyStat = fs.statSync(path.join(schemaDir, name, migrationFile));
79 return applyStat.isFile();
80 } catch (e) { // eslint-disable-line no-unused-vars
81 return false;
82 }
83 }
84 return false;
85 }
86
87
88 /**
89 * Return an array of schema migration directory names within engineDir,
90 * sorted in increasing order.
91 * @param {string} engineDir path to implementation
92 * @returns {string[]} versions
93 */
94 function allSchemaVersions(engineDir) {
95 const schemaDir = path.join(engineDir, 'sql', 'schema');
96 // eslint-disable-next-line security/detect-non-literal-fs-filename
97 const availableVersions = fs.readdirSync(schemaDir).filter((d) => isSchemaMigrationDirectory(schemaDir, d));
98 availableVersions.sort(schemaVersionStringCmp);
99 return availableVersions;
100 }
101
102
103 /**
104 * Return an array of schema migration directory names within engineDir,
105 * which are within supported range, and are greater than the current
106 * @param {string} engineDir path to implementation
107 * @param {SchemaVersionObject} current version
108 * @param {object} supported range of supported versions
109 * @param {SchemaVersionObject} supported.min minimum version
110 * @param {SchemaVersionObject} supported.max maximum version
111 * @returns {string[]} applicable versions
112 */
113 function unappliedSchemaVersions(engineDir, current, supported) {
114 const min = schemaVersionObjectToNumber(supported.min);
115 const max = schemaVersionObjectToNumber(supported.max);
116 const cur = schemaVersionObjectToNumber(current);
117 const available = allSchemaVersions(engineDir);
118 return available.filter((a) => {
119 a = schemaVersionStringToNumber(a);
120 return a >= min && a <= max && a > cur;
121 });
122 }
123
124
125 module.exports = {
126 schemaVersionStringToObject,
127 schemaVersionObjectToNumber,
128 schemaVersionStringToNumber,
129 schemaVersionStringCmp,
130 isSchemaMigrationDirectory,
131 allSchemaVersions,
132 unappliedSchemaVersions,
133 };