Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ts: add way to load sequelize models programmatically as ts script #8

Merged
merged 8 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,34 @@ jobs:
- name: Lint check
run: npm run fmt-check
working-directory: ./${{ matrix.language }}
build-ts:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18.16.0
- name: Cache node modules
uses: actions/cache@v3
with:
path: ./ts/node_modules
key: ${{ runner.os }}-build-${{ hashFiles('**/package-lock.json') }}
restore-keys: node_modules-
- name: Install
run: npm install
working-directory: ./ts
- name: Build Typescript Files
run: npm run build
working-directory: ./ts
- name: Verify migrations generated
run: |
status=$(git status --porcelain)
if [ -n "$status" ]; then
echo "you need to run 'npm run build' and commit the changes"
echo "$status"
git --no-pager diff
exit 1
fi
integration-tests:
strategy:
matrix:
Expand Down Expand Up @@ -67,8 +95,6 @@ jobs:
fi
- name: Run Test as ${{ matrix.language }} Script
working-directory: ./${{ matrix.language }}/testdata
# TODO: remove the check if file exists once we support loading models from ts script
if: ${{ hashFiles('atlas-script.hcl') != '' }}
run: |
atlas migrate diff --env sequelize -c "file://atlas-script.hcl" --var dialect=${{ matrix.dialect }}
- name: Verify migrations generated
Expand Down
1 change: 0 additions & 1 deletion js/testdata/load-models.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,4 @@ const loadModels = require("../index");
// parse the second argument as the dialect
const dialect = process.argv[2];

// load the models
console.log(loadModels(dialect, ingredient, recipe, recipeIngredient));
2 changes: 2 additions & 0 deletions ts/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
src/sequelize_schema.d.ts
src/sequelize_schema.js
3 changes: 2 additions & 1 deletion ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"license": "ISC",
"scripts": {
"fmt": "eslint src/**/*.ts --fix && prettier --write .",
"fmt-check": "npx eslint ./src --max-warnings=0 && prettier --check ."
"fmt-check": "eslint src/**/*.ts --max-warnings=0 && prettier --check .",
"build": "tsc -d src/sequelize_schema.ts"
},
"dependencies": {
"@types/node": "^20.5.7",
Expand Down
2 changes: 2 additions & 0 deletions ts/src/sequelize_schema.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { ModelCtor } from "sequelize-typescript/dist/model/model/model";
export declare const loadModels: (dialect: string, models: ModelCtor[]) => string;
42 changes: 42 additions & 0 deletions ts/src/sequelize_schema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"use strict";
exports.__esModule = true;
exports.loadModels = void 0;
var sequelize_typescript_1 = require("sequelize-typescript");
var validDialects = ["mysql", "postgres", "sqlite", "mariadb", "mssql"];
// load sql state of sequelize models
var loadModels = function (dialect, models) {
var _a;
if (!validDialects.includes(dialect)) {
throw new Error("Invalid dialect ".concat(dialect));
}
var sequelize = new sequelize_typescript_1.Sequelize({
dialect: dialect,
models: models
});
var orderedModels = (_a = sequelize.modelManager
.getModelsTopoSortedByForeignKey()) === null || _a === void 0 ? void 0 : _a.reverse();
if (!orderedModels) {
throw new Error("no models found");
}
var sql = "";
for (var _i = 0, orderedModels_1 = orderedModels; _i < orderedModels_1.length; _i++) {
var model = orderedModels_1[_i];
var def = sequelize.modelManager.getModel(model.name);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
var attr = sequelize.getQueryInterface().queryGenerator.attributesToSQL(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
def.getAttributes(), Object.assign({}, def.options));
sql +=
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
sequelize
.getQueryInterface()
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
.queryGenerator.createTableQuery(def.tableName, attr, Object.assign({}, def.options)) + "\n";
}
return sql;
};
exports.loadModels = loadModels;
46 changes: 46 additions & 0 deletions ts/src/sequelize_schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { ModelCtor } from "sequelize-typescript/dist/model/model/model";
import { Sequelize, SequelizeOptions } from "sequelize-typescript";

const validDialects = ["mysql", "postgres", "sqlite", "mariadb", "mssql"];

// load sql state of sequelize models
export const loadModels = (dialect: string, models: ModelCtor[]) => {
if (!validDialects.includes(dialect)) {
throw new Error(`Invalid dialect ${dialect}`);
}
const sequelize = new Sequelize({
dialect: dialect,
models: models,
} as SequelizeOptions);
const orderedModels = sequelize.modelManager
.getModelsTopoSortedByForeignKey()
?.reverse();
if (!orderedModels) {
throw new Error("no models found");
}
let sql = "";
for (const model of orderedModels) {
const def = sequelize.modelManager.getModel(model.name);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const attr = sequelize.getQueryInterface().queryGenerator.attributesToSQL(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
def.getAttributes(),
Object.assign({}, def.options),
);
sql +=
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
sequelize
.getQueryInterface()
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
.queryGenerator.createTableQuery(
def.tableName,
attr,
Object.assign({}, def.options),
) + "\n";
}
return sql;
};
33 changes: 33 additions & 0 deletions ts/testdata/atlas-script.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
variable "dialect" {
type = string
}

locals {
dev_url = {
mysql = "docker://mysql/8/dev"
postgres = "docker://postgres/15"
sqlite = "sqlite://file::memory:?cache=shared"
}[var.dialect]
}

data "external_schema" "sequelize" {
program = [
"npx",
"ts-node",
"load-models.ts",
var.dialect,
]
}

env "sequelize" {
src = data.external_schema.sequelize.url
dev = local.dev_url
migration {
dir = "file://migrations/${var.dialect}"
}
format {
migrate {
diff = "{{ sql . \" \" }}"
}
}
}
11 changes: 11 additions & 0 deletions ts/testdata/load-models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#! /usr/bin/env ts-node-script

import Phone from "./models/Phone";
import Email from "./models/Email";
import Contact from "./models/Contact";
import { loadModels } from "../src/sequelize_schema";

// parse the second argument as the dialect
const dialect = process.argv[2];

console.log(loadModels(dialect, [Phone, Email, Contact]));
4 changes: 3 additions & 1 deletion ts/testdata/models/Email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ class Email extends Model {
@Column(DataType.INTEGER)
contact_id!: number;

@BelongsTo(() => Contact)
@BelongsTo(() => Contact, {
onDelete: "CASCADE",
})
contact!: Contact;

@CreatedAt
Expand Down
4 changes: 3 additions & 1 deletion ts/testdata/models/Phone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ class Phone extends Model {
@Column(DataType.INTEGER)
contact_id!: number;

@BelongsTo(() => Contact)
@BelongsTo(() => Contact, {
onDelete: "CASCADE",
})
contact!: Contact;

@CreatedAt
Expand Down