Skip to content

Commit d40d1cb

Browse files
authored
Merge pull request #7 from ariga/sequelize_schema
js: add way to load sequelize models programmatically as js script
2 parents 243e2ff + 6d6c0e8 commit d40d1cb

File tree

8 files changed

+118
-38
lines changed

8 files changed

+118
-38
lines changed

.github/workflows/ci.yaml

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
- name: Lint check
3232
run: npm run fmt-check
3333
working-directory: ./${{ matrix.language }}
34-
integration-test:
34+
integration-tests:
3535
strategy:
3636
matrix:
3737
dialect: [ mysql, postgres, sqlite ]
@@ -52,9 +52,25 @@ jobs:
5252
run: npm install
5353
working-directory: ./${{ matrix.language }}
5454
- uses: ariga/setup-atlas@master
55-
- working-directory: ./${{ matrix.language }}/testdata
55+
- name: Run Test as Standalone
56+
working-directory: ./${{ matrix.language }}/testdata
5657
run: |
57-
atlas migrate diff --env sequelize --var dialect=${{ matrix.dialect }}
58+
atlas migrate diff --env sequelize -c "file://atlas-standalone.hcl" --var dialect=${{ matrix.dialect }}
59+
- name: Verify migrations generated
60+
run: |
61+
status=$(git status --porcelain)
62+
if [ -n "$status" ]; then
63+
echo "you need to run 'atlas migrate diff --env sequelize' and commit the changes"
64+
echo "$status"
65+
git --no-pager diff
66+
exit 1
67+
fi
68+
- name: Run Test as ${{ matrix.language }} Script
69+
working-directory: ./${{ matrix.language }}/testdata
70+
# TODO: remove the check if file exists once we support loading models from ts script
71+
if: ${{ hashFiles('atlas-script.hcl') != '' }}
72+
run: |
73+
atlas migrate diff --env sequelize -c "file://atlas-script.hcl" --var dialect=${{ matrix.dialect }}
5874
- name: Verify migrations generated
5975
run: |
6076
status=$(git status --porcelain)

js/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const loadSequelizeModels = require("./src/sequelize_schema");
2+
3+
module.exports = loadSequelizeModels;

js/src/index.js

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,29 @@
11
#!/usr/bin/env node
22

3-
const Sequelize = require("sequelize");
4-
const DataTypes = require("sequelize/lib/data-types");
53
const yargs = require("yargs");
64
const fs = require("fs");
75
const { hideBin } = require("yargs/helpers");
86
const process = require("process");
97
const { resolve } = require("path");
8+
const loadSequelizeModels = require("./sequelize_schema");
109

1110
// get sql state of sequelize models
1211
const loadSQL = (relativePath, driver) => {
13-
let sequelize = new Sequelize({
14-
dialect: driver,
15-
});
1612
if (!fs.existsSync(relativePath)) {
1713
throw new Error("path does not exist");
1814
}
1915
const absolutePath = resolve(relativePath);
2016
// get all models from files in models folder
2117
const files = fs.readdirSync(absolutePath);
22-
const db = {};
18+
const models = [];
2319
for (const file of files) {
2420
if (file.match(/\.js$/) !== null && file !== "index.js") {
2521
const name = file.replace(".js", "");
26-
const m = require(absolutePath + "/" + name)(sequelize, DataTypes);
27-
if (m?.name) {
28-
db[m.name] = m;
29-
}
30-
}
31-
}
32-
// create associations between models
33-
for (const modelName of Object.keys(db)) {
34-
if (db[modelName]?.associate) {
35-
db[modelName].associate(db);
22+
const m = require(absolutePath + "/" + name);
23+
models.push(m);
3624
}
3725
}
38-
39-
const models = sequelize.modelManager
40-
.getModelsTopoSortedByForeignKey()
41-
.reverse();
42-
43-
let sql = "";
44-
for (const model of models) {
45-
const def = sequelize.modelManager.getModel(model.name);
46-
const attr = sequelize
47-
.getQueryInterface()
48-
.queryGenerator.attributesToSQL(def.getAttributes(), { ...def.options });
49-
sql +=
50-
sequelize
51-
.getQueryInterface()
52-
.queryGenerator.createTableQuery(def.tableName, attr, {
53-
...def.options,
54-
}) + "\n";
55-
}
56-
return sql;
26+
return loadSequelizeModels(driver, ...models);
5727
};
5828

5929
yargs(hideBin(process.argv))

js/src/sequelize_schema.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
const Sequelize = require("sequelize");
2+
const DataTypes = require("sequelize/lib/data-types");
3+
4+
const validDialects = ["mysql", "postgres", "sqlite", "mariadb", "mssql"];
5+
6+
// gets dialect and models that are functions of the form (sequelize, DataTypes) => Model
7+
// returns DDL string describing the models.
8+
const loadSequelizeModels = (dialect, ...models) => {
9+
if (!validDialects.includes(dialect)) {
10+
throw new Error("invalid dialect: " + dialect);
11+
}
12+
let sequelize = new Sequelize({
13+
dialect: dialect,
14+
});
15+
const db = {};
16+
for (const model of models) {
17+
const m = model(sequelize, DataTypes);
18+
if (m?.name) {
19+
db[m.name] = m;
20+
}
21+
}
22+
// create associations between models
23+
for (const modelName of Object.keys(db)) {
24+
if (db[modelName]?.associate) {
25+
db[modelName].associate(db);
26+
}
27+
}
28+
const modelsOrdered = sequelize.modelManager
29+
.getModelsTopoSortedByForeignKey()
30+
.reverse();
31+
let sql = "";
32+
for (const model of modelsOrdered) {
33+
const def = sequelize.modelManager.getModel(model.name);
34+
const attr = sequelize
35+
.getQueryInterface()
36+
.queryGenerator.attributesToSQL(def.getAttributes(), { ...def.options });
37+
sql +=
38+
sequelize
39+
.getQueryInterface()
40+
.queryGenerator.createTableQuery(def.tableName, attr, {
41+
...def.options,
42+
}) + "\n";
43+
}
44+
return sql;
45+
};
46+
47+
module.exports = loadSequelizeModels;

js/testdata/atlas-script.hcl

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
variable "dialect" {
2+
type = string
3+
}
4+
5+
locals {
6+
dev_url = {
7+
mysql = "docker://mysql/8/dev"
8+
postgres = "docker://postgres/15"
9+
sqlite = "sqlite://file::memory:?cache=shared"
10+
}[var.dialect]
11+
}
12+
13+
data "external_schema" "sequelize" {
14+
program = [
15+
"node",
16+
"load-models.js",
17+
var.dialect,
18+
]
19+
}
20+
21+
env "sequelize" {
22+
src = data.external_schema.sequelize.url
23+
dev = local.dev_url
24+
migration {
25+
dir = "file://migrations/${var.dialect}"
26+
}
27+
format {
28+
migrate {
29+
diff = "{{ sql . \" \" }}"
30+
}
31+
}
32+
}
File renamed without changes.

js/testdata/load-models.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/usr/bin/env node
2+
3+
const ingredient = require("./models/ingredient");
4+
const recipe = require("./models/recipe");
5+
const recipeIngredient = require("./models/recipe-ingredient");
6+
const loadModels = require("../index");
7+
8+
// parse the second argument as the dialect
9+
const dialect = process.argv[2];
10+
11+
// load the models
12+
console.log(loadModels(dialect, ingredient, recipe, recipeIngredient));
File renamed without changes.

0 commit comments

Comments
 (0)