@balena/pinejs 17.1.0-build-model-based-typings-437bb06f44567532aec78e550f3d545732466411-1 → 17.1.0-build-joshbwlng-tasks-61ce10e444abec6afea3fec43e9a5c37c7cedea6-1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. package/.pinejs-cache.json +1 -1
  2. package/.versionbot/CHANGELOG.yml +13 -239
  3. package/CHANGELOG.md +5 -69
  4. package/out/config-loader/env.d.ts +4 -0
  5. package/out/config-loader/env.js +5 -1
  6. package/out/config-loader/env.js.map +1 -1
  7. package/out/data-server/sbvr-server.js +2 -3
  8. package/out/data-server/sbvr-server.js.map +1 -1
  9. package/out/database-layer/db.d.ts +3 -0
  10. package/out/database-layer/db.js +17 -0
  11. package/out/database-layer/db.js.map +1 -1
  12. package/out/migrator/sync.d.ts +0 -17
  13. package/out/migrator/sync.js +40 -39
  14. package/out/migrator/sync.js.map +1 -1
  15. package/out/sbvr-api/hooks.d.ts +33 -33
  16. package/out/sbvr-api/hooks.js.map +1 -1
  17. package/out/sbvr-api/odata-response.d.ts +2 -1
  18. package/out/sbvr-api/odata-response.js +4 -4
  19. package/out/sbvr-api/odata-response.js.map +1 -1
  20. package/out/sbvr-api/permissions.d.ts +2 -26
  21. package/out/sbvr-api/permissions.js +40 -39
  22. package/out/sbvr-api/permissions.js.map +1 -1
  23. package/out/sbvr-api/sbvr-utils.d.ts +6 -46
  24. package/out/sbvr-api/sbvr-utils.js +76 -73
  25. package/out/sbvr-api/sbvr-utils.js.map +1 -1
  26. package/out/server-glue/module.d.ts +1 -0
  27. package/out/server-glue/module.js +4 -1
  28. package/out/server-glue/module.js.map +1 -1
  29. package/out/tasks/common.d.ts +4 -0
  30. package/out/tasks/common.js +13 -0
  31. package/out/tasks/common.js.map +1 -0
  32. package/out/tasks/index.d.ts +8 -0
  33. package/out/tasks/index.js +142 -0
  34. package/out/tasks/index.js.map +1 -0
  35. package/out/tasks/tasks.sbvr +60 -0
  36. package/out/tasks/types.d.ts +38 -0
  37. package/out/tasks/types.js +10 -0
  38. package/out/tasks/types.js.map +1 -0
  39. package/out/tasks/worker.d.ts +16 -0
  40. package/out/tasks/worker.js +228 -0
  41. package/out/tasks/worker.js.map +1 -0
  42. package/package.json +20 -19
  43. package/src/config-loader/env.ts +6 -1
  44. package/src/data-server/sbvr-server.js +2 -3
  45. package/src/database-layer/db.ts +25 -0
  46. package/src/migrator/sync.ts +41 -46
  47. package/src/sbvr-api/hooks.ts +20 -21
  48. package/src/sbvr-api/odata-response.ts +13 -3
  49. package/src/sbvr-api/permissions.ts +48 -54
  50. package/src/sbvr-api/sbvr-utils.ts +92 -133
  51. package/src/server-glue/module.ts +3 -0
  52. package/src/tasks/common.ts +14 -0
  53. package/src/tasks/index.ts +158 -0
  54. package/src/tasks/tasks.sbvr +60 -0
  55. package/src/tasks/types.ts +58 -0
  56. package/src/tasks/worker.ts +278 -0
  57. package/out/migrator/migrations.d.ts +0 -58
  58. package/out/migrator/migrations.js +0 -3
  59. package/out/migrator/migrations.js.map +0 -1
  60. package/out/sbvr-api/dev.d.ts +0 -22
  61. package/out/sbvr-api/dev.js +0 -3
  62. package/out/sbvr-api/dev.js.map +0 -1
  63. package/out/sbvr-api/user.d.ts +0 -236
  64. package/out/sbvr-api/user.js +0 -3
  65. package/out/sbvr-api/user.js.map +0 -1
  66. package/src/migrator/migrations.ts +0 -64
  67. package/src/sbvr-api/dev.ts +0 -26
  68. package/src/sbvr-api/user.ts +0 -216
@@ -0,0 +1,158 @@
1
+ import type { Schema } from 'ajv';
2
+ import * as cronParser from 'cron-parser';
3
+ import { tasks as tasksEnv } from '../config-loader/env';
4
+ import type * as Db from '../database-layer/db';
5
+ import { BadRequestError } from '../sbvr-api/errors';
6
+ import { addPureHook } from '../sbvr-api/hooks';
7
+ import * as sbvrUtils from '../sbvr-api/sbvr-utils';
8
+ import type { ConfigLoader } from '../server-glue/module';
9
+ import { ajv, apiRoot, channel } from './common';
10
+ import type { TaskHandler } from './types';
11
+ import { Worker } from './worker';
12
+
13
+ export * from './types';
14
+
15
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
16
+ const modelText: string = require('./tasks.sbvr');
17
+
18
+ // Create trigger for handling new tasks
19
+ // Create index for polling tasks table
20
+ const initSql = `
21
+ CREATE OR REPLACE FUNCTION notify_task_insert()
22
+ RETURNS TRIGGER AS $$
23
+ BEGIN
24
+ PERFORM pg_notify('${channel}', NEW.id::text);
25
+ RETURN NEW;
26
+ END;
27
+ $$ LANGUAGE plpgsql;
28
+
29
+ CREATE OR REPLACE TRIGGER task_insert_trigger
30
+ AFTER INSERT ON task
31
+ FOR EACH ROW WHEN (NEW.status = 'queued' AND NEW."is scheduled to execute on-time" IS NULL)
32
+ EXECUTE FUNCTION notify_task_insert();
33
+
34
+ CREATE INDEX IF NOT EXISTS idx_task_poll ON task USING btree (
35
+ "is executed by-handler",
36
+ "is scheduled to execute on-time" ASC,
37
+ "priority" DESC,
38
+ "id" ASC
39
+ ) WHERE status = 'queued';
40
+ `;
41
+
42
+ export const config: ConfigLoader.Config = {
43
+ models: [
44
+ {
45
+ modelName: apiRoot,
46
+ apiRoot,
47
+ modelText,
48
+ customServerCode: exports,
49
+ initSql,
50
+ },
51
+ ],
52
+ };
53
+
54
+ let worker: Worker | null = null;
55
+ export async function setup(db: Db.Database): Promise<void> {
56
+ // Async task functionality is only supported on Postgres
57
+ if (db.engine !== 'postgres') {
58
+ console.warn('Skipping task setup as database not supported');
59
+ return;
60
+ }
61
+
62
+ const client = sbvrUtils.api[apiRoot];
63
+ worker = new Worker(client);
64
+
65
+ // Add resource hooks
66
+ addPureHook('POST', apiRoot, 'task', {
67
+ POSTPARSE: async ({ req, request }) => {
68
+ // Set the actor
69
+ request.values.is_created_by__actor =
70
+ req.user?.actor ?? req.apiKey?.actor;
71
+ if (request.values.is_created_by__actor == null) {
72
+ throw new BadRequestError(
73
+ 'Creating tasks with missing actor on req is not allowed',
74
+ );
75
+ }
76
+
77
+ // Set defaults
78
+ request.values.status = 'queued';
79
+ request.values.attempt_count = 0;
80
+ request.values.attempt_limit ??= 1;
81
+ // TODO: Implement a balancer to better enqueue tasks based on actor usage
82
+ request.values.priority ??= 1;
83
+
84
+ // Set scheduled start time using cron expression if provided
85
+ if (
86
+ request.values.is_scheduled_with__cron_expression != null &&
87
+ request.values.is_scheduled_to_execute_on__time == null
88
+ ) {
89
+ try {
90
+ request.values.is_scheduled_to_execute_on__time = cronParser
91
+ .parseExpression(request.values.is_scheduled_with__cron_expression)
92
+ .next()
93
+ .toDate()
94
+ .toISOString();
95
+ } catch {
96
+ throw new BadRequestError(
97
+ `Invalid cron expression: ${request.values.is_scheduled_with__cron_expression}`,
98
+ );
99
+ }
100
+ }
101
+
102
+ // Assert that the provided start time is far enough in the future
103
+ if (request.values.is_scheduled_to_execute_on__time != null) {
104
+ const now = new Date(Date.now() + tasksEnv.queueIntervalMS);
105
+ const startTime = new Date(
106
+ request.values.is_scheduled_to_execute_on__time,
107
+ );
108
+ if (startTime < now) {
109
+ throw new BadRequestError(
110
+ `Task scheduled start time must be greater than ${tasksEnv.queueIntervalMS} milliseconds in the future`,
111
+ );
112
+ }
113
+ }
114
+
115
+ // Assert that the requested handler exists
116
+ const handlerName = request.values.is_executed_by__handler;
117
+ if (handlerName == null) {
118
+ throw new BadRequestError(`Must specify a task handler to execute`);
119
+ }
120
+ const handler = worker?.handlers[handlerName];
121
+ if (handler == null) {
122
+ throw new BadRequestError(
123
+ `No task handler with name '${handlerName}' registered`,
124
+ );
125
+ }
126
+
127
+ // Assert that the provided parameter set is valid
128
+ if (handler.validate != null) {
129
+ if (!handler.validate(request.values.is_executed_with__parameter_set)) {
130
+ throw new BadRequestError(
131
+ `Invalid parameter set: ${ajv.errorsText(handler.validate.errors)}`,
132
+ );
133
+ }
134
+ }
135
+ },
136
+ });
137
+ worker.start();
138
+ }
139
+
140
+ // Register a task handler
141
+ export function addTaskHandler(
142
+ name: string,
143
+ fn: TaskHandler['fn'],
144
+ schema?: Schema,
145
+ ): void {
146
+ if (worker == null) {
147
+ throw new Error('Database does not support tasks');
148
+ }
149
+
150
+ if (worker.handlers[name] != null) {
151
+ throw new Error(`Task handler with name '${name}' already registered`);
152
+ }
153
+ worker.handlers[name] = {
154
+ name,
155
+ fn,
156
+ validate: schema != null ? ajv.compile(schema) : undefined,
157
+ };
158
+ }
@@ -0,0 +1,60 @@
1
+ Vocabulary: tasks
2
+
3
+ Term: id
4
+ Concept Type: Big Serial (Type)
5
+ Term: actor
6
+ Concept Type: Integer (Type)
7
+ Term: attempt count
8
+ Concept Type: Integer (Type)
9
+ Term: attempt limit
10
+ Concept Type: Integer (Type)
11
+ Term: cron expression
12
+ Concept Type: Short Text (Type)
13
+ Term: error message
14
+ Concept Type: Short Text (Type)
15
+ Term: handler
16
+ Concept Type: Short Text (Type)
17
+ Term: key
18
+ Concept Type: Short Text (Type)
19
+ Term: parameter set
20
+ Concept Type: JSON (Type)
21
+ Term: priority
22
+ Concept Type: Integer (Type)
23
+ Term: status
24
+ Concept Type: Short Text (Type)
25
+ Term: time
26
+ Concept Type: Date Time (Type)
27
+
28
+ Term: task
29
+ Fact type: task has id
30
+ Necessity: each task has exactly one id
31
+ Fact type: task has key
32
+ Necessity: each task has at most one key
33
+ Fact type: task is created by actor
34
+ Necessity: each task is created by exactly one actor
35
+ Fact type: task is executed by handler
36
+ Necessity: each task is executed by exactly one handler
37
+ Fact type: task is executed with parameter set
38
+ Necessity: each task is executed with at most one parameter set
39
+ Fact type: task has priority
40
+ Necessity: each task has exactly one priority
41
+ Necessity: each task has a priority that is greater than or equal to 0
42
+ Fact type: task is scheduled with cron expression
43
+ Necessity: each task is scheduled with at most one cron expression
44
+ Fact type: task is scheduled to execute on time
45
+ Necessity: each task is scheduled to execute on at most one time
46
+ Fact type: task has status
47
+ Necessity: each task has exactly one status
48
+ Definition: "queued" or "cancelled" or "succeeded" or "failed"
49
+ Fact type: task started on time
50
+ Necessity: each task started on at most one time
51
+ Fact type: task ended on time
52
+ Necessity: each task ended on at most one time
53
+ Fact type: task has error message
54
+ Necessity: each task has at most one error message
55
+ Fact type: task has attempt count
56
+ Necessity: each task has exactly one attempt count
57
+ Fact type: task has attempt limit
58
+ Necessity: each task has exactly one attempt limit
59
+ Necessity: each task has an attempt limit that is greater than or equal to 1
60
+
@@ -0,0 +1,58 @@
1
+ import type { ValidateFunction } from 'ajv';
2
+ import type { AnyObject } from 'pinejs-client-core';
3
+ import type * as Db from '../database-layer/db';
4
+ import type { PinejsClient } from '../sbvr-api/sbvr-utils';
5
+
6
+ export const taskStatuses = [
7
+ 'queued',
8
+ 'cancelled',
9
+ 'succeeded',
10
+ 'failed',
11
+ ] as const;
12
+ export type TaskStatus = (typeof taskStatuses)[number];
13
+ export interface Task {
14
+ id: number;
15
+ created_at: Date;
16
+ modified_at: Date;
17
+ is_created_by__actor: number;
18
+ is_executed_by__handler: string;
19
+ is_executed_with__parameter_set: object | null;
20
+ is_scheduled_with__cron_expression: string | null;
21
+ is_scheduled_to_execute_on__time: Date | null;
22
+ priority: number;
23
+ status: TaskStatus;
24
+ started_on__time: Date | null;
25
+ ended_on__time: Date | null;
26
+ error_message: string | null;
27
+ attempt_count: number;
28
+ attempt_limit: number;
29
+ }
30
+
31
+ export type PartialTask = Pick<
32
+ Task,
33
+ | 'id'
34
+ | 'is_created_by__actor'
35
+ | 'is_executed_by__handler'
36
+ | 'is_executed_with__parameter_set'
37
+ | 'is_scheduled_with__cron_expression'
38
+ | 'priority'
39
+ | 'attempt_count'
40
+ | 'attempt_limit'
41
+ >;
42
+
43
+ export interface TaskArgs {
44
+ api: PinejsClient;
45
+ params: AnyObject;
46
+ tx: Db.Tx;
47
+ }
48
+
49
+ export type TaskResponse = Promise<{
50
+ status: TaskStatus;
51
+ error?: string;
52
+ }>;
53
+
54
+ export interface TaskHandler {
55
+ name: string;
56
+ fn: (options: TaskArgs) => TaskResponse;
57
+ validate?: ValidateFunction;
58
+ }
@@ -0,0 +1,278 @@
1
+ import { setTimeout } from 'node:timers/promises';
2
+ import type { AnyObject } from 'pinejs-client-core';
3
+ import { tasks as tasksEnv } from '../config-loader/env';
4
+ import type * as Db from '../database-layer/db';
5
+ import { TransactionClosedError } from '../database-layer/db';
6
+ import * as permissions from '../sbvr-api/permissions';
7
+ import { PinejsClient } from '../sbvr-api/sbvr-utils';
8
+ import { sbvrUtils } from '../server-glue/module';
9
+ import { ajv, channel } from './common';
10
+ import type { PartialTask, TaskHandler, TaskStatus } from './types';
11
+
12
+ // Map of column names with SBVR names used in SELECT queries
13
+ const selectColumns = Object.entries({
14
+ id: 'id',
15
+ 'is executed by-handler': 'is_executed_by__handler',
16
+ 'is executed with-parameter set': 'is_executed_with__parameter_set',
17
+ 'is scheduled with-cron expression': 'is_scheduled_with__cron_expression',
18
+ 'attempt count': 'attempt_count',
19
+ 'attempt limit': 'attempt_limit',
20
+ priority: 'priority',
21
+ 'is created by-actor': 'is_created_by__actor',
22
+ })
23
+ .map(([key, value]) => `t."${key}" AS "${value}"`)
24
+ .join(', ');
25
+
26
+ // The worker is responsible for executing tasks in the queue. It listens for
27
+ // notifications and polls the database for tasks to execute. It will execute
28
+ // tasks in parallel up to a certain concurrency limit.
29
+ export class Worker {
30
+ public handlers: Record<string, TaskHandler> = {};
31
+ private readonly concurrency: number;
32
+ private readonly interval: number;
33
+ private executing = 0;
34
+
35
+ constructor(private readonly client: PinejsClient) {
36
+ this.concurrency = tasksEnv.queueConcurrency;
37
+ this.interval = tasksEnv.queueIntervalMS;
38
+ }
39
+
40
+ // Check if instance can execute more tasks
41
+ private canExecute(): boolean {
42
+ return (
43
+ this.executing < this.concurrency && Object.keys(this.handlers).length > 0
44
+ );
45
+ }
46
+
47
+ private async execute(task: PartialTask, tx: Db.Tx): Promise<void> {
48
+ this.executing++;
49
+ try {
50
+ // Get specified handler
51
+ const handler = this.handlers[task.is_executed_by__handler];
52
+ const startedOnTime = new Date();
53
+ if (handler == null) {
54
+ await this.finalize(
55
+ tx,
56
+ task,
57
+ startedOnTime,
58
+ 'failed',
59
+ 'Matching task handler not found',
60
+ );
61
+ return;
62
+ }
63
+
64
+ // Validate parameters before execution so we can fail early if
65
+ // the parameter set is invalid. This can happen if the handler
66
+ // definition changes after a task is added to the queue.
67
+ if (
68
+ handler.validate != null &&
69
+ !handler.validate(task.is_executed_with__parameter_set)
70
+ ) {
71
+ await this.finalize(
72
+ tx,
73
+ task,
74
+ startedOnTime,
75
+ 'failed',
76
+ `Invalid parameter set: ${ajv.errorsText(handler.validate.errors)}`,
77
+ );
78
+ return;
79
+ }
80
+
81
+ // Execute handler
82
+ let status: TaskStatus = 'queued';
83
+ let error: string | undefined;
84
+ try {
85
+ await sbvrUtils.db.transaction(async (handlerTx) => {
86
+ const results = await handler.fn({
87
+ api: new PinejsClient({
88
+ passthrough: {
89
+ tx: handlerTx,
90
+ },
91
+ }),
92
+ params: task.is_executed_with__parameter_set ?? {},
93
+ tx: handlerTx,
94
+ });
95
+ status = results.status;
96
+ error = results.error;
97
+ if (results.status !== 'succeeded' && !handlerTx.isClosed()) {
98
+ await handlerTx.rollback();
99
+ }
100
+ });
101
+ } catch (err) {
102
+ // Ignore closed/rollback errors
103
+ if (!(err instanceof TransactionClosedError)) {
104
+ throw err;
105
+ }
106
+ } finally {
107
+ // Update task with results
108
+ await this.finalize(tx, task, startedOnTime, status, error);
109
+ }
110
+ } catch (err) {
111
+ // This shouldn't happen, but if it does we want to log and kill the process
112
+ console.error(
113
+ `Failed to execute task ${task.id} with handler ${task.is_executed_by__handler}:`,
114
+ err,
115
+ );
116
+ process.exit(1);
117
+ } finally {
118
+ this.executing--;
119
+ }
120
+ }
121
+
122
+ // Update task and schedule next attempt if needed
123
+ private async finalize(
124
+ tx: Db.Tx,
125
+ task: PartialTask,
126
+ startedOnTime: Date,
127
+ status: TaskStatus,
128
+ errorMessage?: string,
129
+ ): Promise<void> {
130
+ const attemptCount = task.attempt_count + 1;
131
+ const body: AnyObject = {
132
+ started_on__time: startedOnTime,
133
+ ended_on__time: new Date(),
134
+ status,
135
+ attempt_count: attemptCount,
136
+ ...(errorMessage != null && { error_message: errorMessage }),
137
+ };
138
+
139
+ // Re-enqueue if the task failed but has retries left, remember that
140
+ // attemptCount includes the initial attempt while attempt_limit does not
141
+ if (status === 'failed' && attemptCount < task.attempt_limit) {
142
+ body.status = 'queued';
143
+
144
+ // Schedule next attempt using exponential backoff
145
+ body.is_scheduled_to_execute_on__time =
146
+ this.getNextAttemptTime(attemptCount);
147
+ }
148
+
149
+ // Patch current task
150
+ await this.client.patch({
151
+ resource: 'task',
152
+ passthrough: {
153
+ tx,
154
+ req: permissions.root,
155
+ },
156
+ id: task.id,
157
+ body,
158
+ });
159
+
160
+ // Create new task with same configuration if previous
161
+ // iteration completed and has a cron expression
162
+ if (
163
+ ['failed', 'succeeded'].includes(body.status) &&
164
+ task.is_scheduled_with__cron_expression != null
165
+ ) {
166
+ await this.client.post({
167
+ resource: 'task',
168
+ passthrough: {
169
+ tx,
170
+ req: permissions.root,
171
+ },
172
+ options: {
173
+ returnResource: false,
174
+ },
175
+ body: {
176
+ attempt_limit: task.attempt_limit,
177
+ is_created_by__actor: task.is_created_by__actor,
178
+ is_executed_by__handler: task.is_executed_by__handler,
179
+ is_executed_with__parameter_set: task.is_executed_with__parameter_set,
180
+ is_scheduled_with__cron_expression:
181
+ task.is_scheduled_with__cron_expression,
182
+ priority: task.priority,
183
+ },
184
+ });
185
+ }
186
+ }
187
+
188
+ // Calculate next attempt time using exponential backoff
189
+ private getNextAttemptTime(attempt: number): Date | null {
190
+ const delay = Math.ceil(Math.exp(Math.min(10, attempt)));
191
+ return new Date(Date.now() + delay);
192
+ }
193
+
194
+ // Poll for tasks to execute
195
+ private poll(): void {
196
+ let executed = false;
197
+ void (async () => {
198
+ try {
199
+ const handlerNames = Object.keys(this.handlers);
200
+ const binds = handlerNames
201
+ .map((_, index) => `$${index + 1}`)
202
+ .join(', ');
203
+ if (!this.canExecute()) {
204
+ return;
205
+ }
206
+ await sbvrUtils.db.transaction(async (tx) => {
207
+ const result = await tx.executeSql(
208
+ `SELECT ${selectColumns}
209
+ FROM task AS t
210
+ WHERE
211
+ t."is executed by-handler" IN (${binds}) AND
212
+ t."status" = 'queued' AND
213
+ t."attempt count" <= t."attempt limit" AND
214
+ (
215
+ t."is scheduled to execute on-time" IS NULL OR
216
+ t."is scheduled to execute on-time" <= CURRENT_TIMESTAMP + INTERVAL '${Math.ceil(this.interval / 1000)} second'
217
+ )
218
+ ORDER BY
219
+ t."is scheduled to execute on-time" ASC,
220
+ t."priority" DESC,
221
+ t."id" ASC
222
+ LIMIT ${Math.max(this.concurrency - this.executing, 0)}
223
+ FOR UPDATE SKIP LOCKED`,
224
+ handlerNames,
225
+ );
226
+ if (result.rows.length === 0) {
227
+ return;
228
+ }
229
+
230
+ // Tasks found, execute them in parallel
231
+ await Promise.all(
232
+ result.rows.map(async (row) => {
233
+ await this.execute(row as PartialTask, tx);
234
+ }),
235
+ );
236
+ executed = true;
237
+ });
238
+ } catch (err) {
239
+ console.error('Failed polling for tasks:', err);
240
+ } finally {
241
+ if (!executed) {
242
+ await setTimeout(this.interval);
243
+ }
244
+ this.poll();
245
+ }
246
+ })();
247
+ }
248
+
249
+ // Start listening and polling for tasks
250
+ public start(): void {
251
+ // Tasks only support postgres for now
252
+ if (sbvrUtils.db.engine !== 'postgres' || sbvrUtils.db.on == null) {
253
+ throw new Error(
254
+ 'Database does not support tasks, giving up on starting worker',
255
+ );
256
+ }
257
+ sbvrUtils.db.on(
258
+ 'notification',
259
+ async (msg) => {
260
+ if (this.canExecute()) {
261
+ await sbvrUtils.db.transaction(async (tx) => {
262
+ const result = await tx.executeSql(
263
+ `SELECT ${selectColumns} FROM task AS t WHERE id = $1 FOR UPDATE SKIP LOCKED`,
264
+ [msg.payload],
265
+ );
266
+ if (result.rows.length > 0) {
267
+ await this.execute(result.rows[0] as PartialTask, tx);
268
+ }
269
+ });
270
+ }
271
+ },
272
+ {
273
+ channel,
274
+ },
275
+ );
276
+ this.poll();
277
+ }
278
+ }
@@ -1,58 +0,0 @@
1
- import type { Types } from '@balena/abstract-sql-to-typescript';
2
- export interface Migration {
3
- Read: {
4
- created_at: Types['Date Time']['Read'];
5
- modified_at: Types['Date Time']['Read'];
6
- model_name: Types['Short Text']['Read'];
7
- executed_migrations: Types['JSON']['Read'];
8
- };
9
- Write: {
10
- created_at: Types['Date Time']['Write'];
11
- modified_at: Types['Date Time']['Write'];
12
- model_name: Types['Short Text']['Write'];
13
- executed_migrations: Types['JSON']['Write'];
14
- };
15
- }
16
- export interface MigrationLock {
17
- Read: {
18
- created_at: Types['Date Time']['Read'];
19
- modified_at: Types['Date Time']['Read'];
20
- model_name: Types['Short Text']['Read'];
21
- };
22
- Write: {
23
- created_at: Types['Date Time']['Write'];
24
- modified_at: Types['Date Time']['Write'];
25
- model_name: Types['Short Text']['Write'];
26
- };
27
- }
28
- export interface MigrationStatus {
29
- Read: {
30
- created_at: Types['Date Time']['Read'];
31
- modified_at: Types['Date Time']['Read'];
32
- migration_key: Types['Short Text']['Read'];
33
- start_time: Types['Date Time']['Read'] | null;
34
- last_run_time: Types['Date Time']['Read'] | null;
35
- run_count: Types['Integer']['Read'];
36
- migrated_row_count: Types['Integer']['Read'] | null;
37
- error_count: Types['Integer']['Read'] | null;
38
- is_backing_off: Types['Boolean']['Read'];
39
- converged_time: Types['Date Time']['Read'] | null;
40
- };
41
- Write: {
42
- created_at: Types['Date Time']['Write'];
43
- modified_at: Types['Date Time']['Write'];
44
- migration_key: Types['Short Text']['Write'];
45
- start_time: Types['Date Time']['Write'] | null;
46
- last_run_time: Types['Date Time']['Write'] | null;
47
- run_count: Types['Integer']['Write'];
48
- migrated_row_count: Types['Integer']['Write'] | null;
49
- error_count: Types['Integer']['Write'] | null;
50
- is_backing_off: Types['Boolean']['Write'];
51
- converged_time: Types['Date Time']['Write'] | null;
52
- };
53
- }
54
- export default interface $Model {
55
- migration: Migration;
56
- migration_lock: MigrationLock;
57
- migration_status: MigrationStatus;
58
- }
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=migrations.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../src/migrator/migrations.ts"],"names":[],"mappings":""}
@@ -1,22 +0,0 @@
1
- import type { Types } from '@balena/abstract-sql-to-typescript';
2
- export interface Model {
3
- Read: {
4
- created_at: Types['Date Time']['Read'];
5
- modified_at: Types['Date Time']['Read'];
6
- id: Types['Serial']['Read'];
7
- is_of__vocabulary: Types['Short Text']['Read'];
8
- model_type: Types['Short Text']['Read'];
9
- model_value: Types['JSON']['Read'];
10
- };
11
- Write: {
12
- created_at: Types['Date Time']['Write'];
13
- modified_at: Types['Date Time']['Write'];
14
- id: Types['Serial']['Write'];
15
- is_of__vocabulary: Types['Short Text']['Write'];
16
- model_type: Types['Short Text']['Write'];
17
- model_value: Types['JSON']['Write'];
18
- };
19
- }
20
- export default interface $Model {
21
- model: Model;
22
- }
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=dev.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/sbvr-api/dev.ts"],"names":[],"mappings":""}