@loomcore/api 0.1.21 → 0.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/__tests__/common-test.utils.js +13 -23
  2. package/dist/__tests__/models/test-item.model.d.ts +1 -1
  3. package/dist/__tests__/postgres-test-migrations/004-create-test-users-table.migration.js +0 -1
  4. package/dist/__tests__/postgres.test-database.js +6 -2
  5. package/dist/__tests__/test-objects.d.ts +9 -41
  6. package/dist/__tests__/test-objects.js +96 -63
  7. package/dist/controllers/auth.controller.js +5 -6
  8. package/dist/databases/postgres/migrations/001-create-migrations-table.migration.js +18 -20
  9. package/dist/databases/postgres/migrations/002-create-organizations-table.migration.d.ts +1 -3
  10. package/dist/databases/postgres/migrations/002-create-organizations-table.migration.js +41 -48
  11. package/dist/databases/postgres/migrations/003-create-users-table.migration.js +42 -47
  12. package/dist/databases/postgres/migrations/004-create-refresh-tokens-table.migration.js +34 -37
  13. package/dist/databases/postgres/migrations/005-create-meta-org.migration.js +24 -23
  14. package/dist/databases/postgres/migrations/006-create-admin-user.migration.d.ts +1 -2
  15. package/dist/databases/postgres/migrations/006-create-admin-user.migration.js +16 -7
  16. package/dist/databases/postgres/migrations/007-create-roles-table.migration.d.ts +21 -0
  17. package/dist/databases/postgres/migrations/007-create-roles-table.migration.js +62 -0
  18. package/dist/databases/postgres/migrations/008-create-user-roles-table.migration.d.ts +21 -0
  19. package/dist/databases/postgres/migrations/008-create-user-roles-table.migration.js +65 -0
  20. package/dist/databases/postgres/migrations/009-create-features-table.migration.d.ts +21 -0
  21. package/dist/databases/postgres/migrations/009-create-features-table.migration.js +62 -0
  22. package/dist/databases/postgres/migrations/010-create-authorizations-table.migration.d.ts +21 -0
  23. package/dist/databases/postgres/migrations/010-create-authorizations-table.migration.js +74 -0
  24. package/dist/databases/postgres/migrations/setup-for-auth.migration.js +14 -5
  25. package/dist/databases/postgres/migrations/setup-for-multitenant.migration.d.ts +1 -1
  26. package/dist/databases/postgres/migrations/setup-for-multitenant.migration.js +16 -6
  27. package/dist/models/refresh-token.model.d.ts +1 -1
  28. package/dist/services/auth.service.d.ts +3 -3
  29. package/dist/services/auth.service.js +13 -10
  30. package/dist/services/multi-tenant-api.service.js +1 -1
  31. package/package.json +6 -6
@@ -1,16 +1,14 @@
1
1
  import crypto from 'crypto';
2
2
  import jwt from 'jsonwebtoken';
3
- import { getSystemUserContext } from '@loomcore/common/models';
4
3
  import { Type } from '@sinclair/typebox';
5
4
  import { JwtService } from '../services/jwt.service.js';
6
5
  import { ApiController } from '../controllers/api.controller.js';
7
6
  import { MultiTenantApiService } from '../services/multi-tenant-api.service.js';
8
7
  import { Join } from '../databases/operations/join.operation.js';
9
8
  import { OrganizationService } from '../services/organization.service.js';
10
- import { IdNotFoundError } from '../errors/index.js';
11
9
  import { AuthService, GenericApiService } from '../services/index.js';
12
10
  import { ObjectId } from 'mongodb';
13
- import { testMetaOrg, testOrg, getTestMetaOrgUser, testMetaOrgUserContext } from './test-objects.js';
11
+ import { getTestMetaOrg, getTestOrg, getTestMetaOrgUser, getTestMetaOrgUserContext } from './test-objects.js';
14
12
  import { CategorySpec } from './models/category.model.js';
15
13
  import { ProductSpec } from './models/product.model.js';
16
14
  let deviceIdCookie;
@@ -33,9 +31,9 @@ async function createMetaOrg() {
33
31
  throw new Error('OrganizationService not initialized. Call initialize() first.');
34
32
  }
35
33
  try {
36
- const existingMetaOrg = await organizationService.findOne(testMetaOrgUserContext, { filters: { isMetaOrg: { eq: true } } });
34
+ const existingMetaOrg = await organizationService.getMetaOrg(getTestMetaOrgUserContext());
37
35
  if (!existingMetaOrg) {
38
- const metaOrgInsertResult = await organizationService.create(testMetaOrgUserContext, testMetaOrg);
36
+ const metaOrgInsertResult = await organizationService.create(getTestMetaOrgUserContext(), getTestMetaOrg());
39
37
  }
40
38
  }
41
39
  catch (error) {
@@ -48,7 +46,7 @@ async function deleteMetaOrg() {
48
46
  return Promise.resolve();
49
47
  }
50
48
  try {
51
- await organizationService.deleteMany(testMetaOrgUserContext, { filters: { isMetaOrg: { eq: true } } });
49
+ await organizationService.deleteMany(getTestMetaOrgUserContext(), { filters: { isMetaOrg: { eq: true } } });
52
50
  }
53
51
  catch (error) {
54
52
  console.log('Error deleting meta org:', error);
@@ -69,23 +67,15 @@ async function createTestUser() {
69
67
  throw new Error('Database not initialized. Call initialize() first.');
70
68
  }
71
69
  try {
72
- let existingMetaOrg;
73
- try {
74
- existingMetaOrg = await organizationService.getMetaOrg(testMetaOrgUserContext);
75
- }
76
- catch (error) {
77
- if (error instanceof IdNotFoundError) {
78
- existingMetaOrg = null;
79
- }
80
- else {
81
- throw error;
82
- }
83
- }
70
+ const existingMetaOrg = await organizationService.getMetaOrg(getTestMetaOrgUserContext());
84
71
  if (!existingMetaOrg) {
85
- await organizationService.create(testMetaOrgUserContext, testMetaOrg);
72
+ await organizationService.create(getTestMetaOrgUserContext(), getTestMetaOrg());
73
+ }
74
+ const existingTestOrg = await organizationService.findOne(getTestMetaOrgUserContext(), { filters: { _id: { eq: getTestOrg()._id } } });
75
+ if (!existingTestOrg) {
76
+ await organizationService.create(getTestMetaOrgUserContext(), getTestOrg());
86
77
  }
87
- const systemUserContext = getSystemUserContext();
88
- const createdUser = await authService.createUser(systemUserContext, getTestMetaOrgUser());
78
+ const createdUser = await authService.createUser(getTestMetaOrgUserContext(), getTestMetaOrgUser());
89
79
  if (!createdUser) {
90
80
  throw new Error('Failed to create test user');
91
81
  }
@@ -97,10 +87,10 @@ async function createTestUser() {
97
87
  }
98
88
  }
99
89
  async function deleteTestUser() {
100
- await authService.deleteById(testMetaOrgUserContext, getTestMetaOrgUser()._id).catch((error) => {
90
+ await authService.deleteById(getTestMetaOrgUserContext(), getTestMetaOrgUser()._id).catch((error) => {
101
91
  return null;
102
92
  });
103
- await organizationService.deleteById(testMetaOrgUserContext, testOrg._id).catch((error) => {
93
+ await organizationService.deleteById(getTestMetaOrgUserContext(), getTestOrg()._id).catch((error) => {
104
94
  return null;
105
95
  });
106
96
  }
@@ -7,6 +7,6 @@ export interface ITestItem extends IEntity, IAuditable {
7
7
  export declare const TestItemSchema: import("@sinclair/typebox").TObject<{
8
8
  name: import("@sinclair/typebox").TString;
9
9
  value: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
10
- eventDate: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TTransform<import("@sinclair/typebox").TString, Date>, import("@sinclair/typebox").TUndefined]>>;
10
+ eventDate: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TTransform<import("@sinclair/typebox").TString, Date>>;
11
11
  }>;
12
12
  export declare const TestItemSpec: import("@loomcore/common/models").IModelSpec<import("@sinclair/typebox").TSchema>;
@@ -17,7 +17,6 @@ export class CreateTestUsersTableMigration {
17
17
  "firstName" VARCHAR(255),
18
18
  "lastName" VARCHAR(255),
19
19
  "displayName" VARCHAR(255),
20
- "roles" TEXT[],
21
20
  "_lastLoggedIn" TIMESTAMP,
22
21
  "_lastPasswordChange" TIMESTAMP,
23
22
  "_created" TIMESTAMP NOT NULL,
@@ -6,6 +6,7 @@ import { setupDatabaseForMultitenant } from '../databases/postgres/migrations/se
6
6
  import { setupDatabaseForAuth } from '../databases/postgres/migrations/setup-for-auth.migration.js';
7
7
  import { runTestMigrations } from './postgres-test-migrations/run-test-migrations.js';
8
8
  import { PostgresDatabase } from '../databases/postgres/postgres.database.js';
9
+ import { getTestMetaOrg, setTestMetaOrgId } from './test-objects.js';
9
10
  import { getSystemUserContext } from '@loomcore/common/models';
10
11
  export class TestPostgresDatabase {
11
12
  database = null;
@@ -29,10 +30,13 @@ export class TestPostgresDatabase {
29
30
  const testDatabase = new PostgresDatabase(postgresClient);
30
31
  this.database = testDatabase;
31
32
  this.postgresClient = postgresClient;
32
- let success = (await setupDatabaseForMultitenant(postgresClient, 'Test Org', 'test-org')).success;
33
- if (!success) {
33
+ const metaOrg = getTestMetaOrg();
34
+ const multitenantResult = await setupDatabaseForMultitenant(postgresClient, metaOrg.name, metaOrg.code);
35
+ let success = multitenantResult.success;
36
+ if (!success || !multitenantResult.metaOrgId) {
34
37
  throw new Error('Failed to setup for multitenant');
35
38
  }
39
+ setTestMetaOrgId(multitenantResult.metaOrgId);
36
40
  await initSystemUserContext(this.database);
37
41
  success = (await setupDatabaseForAuth(postgresClient, adminUsername || 'admin', adminPassword || 'password')).success;
38
42
  if (!success) {
@@ -1,41 +1,9 @@
1
- import { IOrganization, IUserContext } from "@loomcore/common/models";
2
- export declare const testMetaOrg: IOrganization;
3
- export declare function getTestMetaOrgUser(): {
4
- email: string;
5
- firstName?: string;
6
- lastName?: string;
7
- displayName?: string;
8
- password: string;
9
- roles?: string[];
10
- _lastLoggedIn?: Date;
11
- _lastPasswordChange?: Date;
12
- _created: Date;
13
- _createdBy: string;
14
- _updated: Date;
15
- _updatedBy: string;
16
- _deleted?: Date;
17
- _deletedBy?: string;
18
- _id: string;
19
- _orgId?: string;
20
- };
21
- export declare const testMetaOrgUserContext: IUserContext;
22
- export declare const testOrg: IOrganization;
23
- export declare function getTestOrgUser(): {
24
- email: string;
25
- firstName?: string;
26
- lastName?: string;
27
- displayName?: string;
28
- password: string;
29
- roles?: string[];
30
- _lastLoggedIn?: Date;
31
- _lastPasswordChange?: Date;
32
- _created: Date;
33
- _createdBy: string;
34
- _updated: Date;
35
- _updatedBy: string;
36
- _deleted?: Date;
37
- _deletedBy?: string;
38
- _id: string;
39
- _orgId?: string;
40
- };
41
- export declare const testOrgUserContext: IUserContext;
1
+ import { IOrganization, IUser, IUserContext } from "@loomcore/common/models";
2
+ export declare let TEST_META_ORG_ID: string;
3
+ export declare function setTestMetaOrgId(metaOrgId: string): void;
4
+ export declare function getTestMetaOrg(): IOrganization;
5
+ export declare function getTestMetaOrgUser(): IUser;
6
+ export declare function getTestMetaOrgUserContext(): IUserContext;
7
+ export declare function getTestOrg(): IOrganization;
8
+ export declare function getTestOrgUser(): IUser;
9
+ export declare function getTestOrgUserContext(): IUserContext;
@@ -1,67 +1,100 @@
1
- export const testMetaOrg = {
2
- _id: '69261691f936c45f85da24d0',
3
- name: 'Test Meta Organization',
4
- code: 'test-meta-org',
5
- status: 1,
6
- isMetaOrg: true,
7
- _created: new Date(),
8
- _createdBy: 'system',
9
- _updated: new Date(),
10
- _updatedBy: 'system',
11
- };
12
- const testMetaOrgUser = {
13
- _id: '69261672f48fb7bf76e54dfb',
14
- email: 'test@example.com',
15
- password: 'testpassword',
16
- firstName: 'Test',
17
- lastName: 'User',
18
- displayName: 'Test User',
19
- roles: ['user'],
20
- _orgId: testMetaOrg._id,
21
- _created: new Date(),
22
- _createdBy: 'system',
23
- _lastLoggedIn: new Date(),
24
- _lastPasswordChange: new Date(),
25
- _updated: new Date(),
26
- _updatedBy: 'system',
27
- };
1
+ export let TEST_META_ORG_ID = '69261691f936c45f85da24d0';
2
+ export function setTestMetaOrgId(metaOrgId) {
3
+ TEST_META_ORG_ID = metaOrgId;
4
+ }
5
+ export function getTestMetaOrg() {
6
+ return {
7
+ _id: TEST_META_ORG_ID,
8
+ name: 'Test Meta Organization',
9
+ code: 'test-meta-org',
10
+ status: 1,
11
+ isMetaOrg: true,
12
+ _created: new Date(),
13
+ _createdBy: 'system',
14
+ _updated: new Date(),
15
+ _updatedBy: 'system',
16
+ };
17
+ }
18
+ ;
28
19
  export function getTestMetaOrgUser() {
29
- return { ...testMetaOrgUser };
20
+ return {
21
+ _id: '69261672f48fb7bf76e54dfb',
22
+ _orgId: getTestMetaOrg()._id,
23
+ email: 'test@example.com',
24
+ password: 'testpassword',
25
+ firstName: 'Test',
26
+ lastName: 'User',
27
+ displayName: 'Test User',
28
+ authorizations: [{
29
+ _id: '6939c54e57a1c6576a40c590',
30
+ _orgId: getTestMetaOrg()._id,
31
+ feature: 'metaorgUser',
32
+ config: {},
33
+ _created: new Date(),
34
+ _createdBy: 'system',
35
+ _updated: new Date(),
36
+ _updatedBy: 'system',
37
+ }],
38
+ _created: new Date(),
39
+ _createdBy: 'system',
40
+ _lastLoggedIn: new Date(),
41
+ _updated: new Date(),
42
+ _updatedBy: 'system',
43
+ };
44
+ }
45
+ ;
46
+ export function getTestMetaOrgUserContext() {
47
+ return {
48
+ user: getTestMetaOrgUser(),
49
+ _orgId: getTestMetaOrg()._id,
50
+ };
30
51
  }
31
- export const testMetaOrgUserContext = {
32
- user: getTestMetaOrgUser(),
33
- _orgId: testMetaOrg._id,
34
- };
35
- export const testOrg = {
36
- _id: '6926167d06c0073a778a124f',
37
- name: 'Test Organization',
38
- code: 'test-org',
39
- status: 1,
40
- isMetaOrg: false,
41
- _created: new Date(),
42
- _createdBy: 'system',
43
- _updated: new Date(),
44
- _updatedBy: 'system',
45
- };
46
- const testOrgUser = {
47
- _id: '6926167d06c0073a778a1250',
48
- email: 'test-org-user@example.com',
49
- password: 'testpassword',
50
- firstName: 'Test',
51
- lastName: 'User',
52
- displayName: 'Test User',
53
- roles: ['user'],
54
- _orgId: testOrg._id,
55
- _created: new Date(),
56
- _createdBy: 'system',
57
- _lastLoggedIn: new Date(),
58
- _updated: new Date(),
59
- _updatedBy: 'system',
60
- };
52
+ ;
53
+ export function getTestOrg() {
54
+ return {
55
+ _id: '6926167d06c0073a778a124f',
56
+ name: 'Test Organization',
57
+ code: 'test-org',
58
+ status: 1,
59
+ isMetaOrg: false,
60
+ _created: new Date(),
61
+ _createdBy: 'system',
62
+ _updated: new Date(),
63
+ _updatedBy: 'system',
64
+ };
65
+ }
66
+ ;
61
67
  export function getTestOrgUser() {
62
- return { ...testOrgUser };
68
+ return {
69
+ _id: '6926167d06c0073a778a1250',
70
+ _orgId: getTestOrg()._id,
71
+ email: 'test-org-user@example.com',
72
+ password: 'testpassword',
73
+ firstName: 'Test',
74
+ lastName: 'User',
75
+ displayName: 'Test User',
76
+ authorizations: [{
77
+ _id: '6939c54e57a1c6576a40c591',
78
+ _orgId: getTestOrg()._id,
79
+ feature: 'testOrgUser',
80
+ config: {},
81
+ _created: new Date(),
82
+ _createdBy: 'system',
83
+ _updated: new Date(),
84
+ _updatedBy: 'system',
85
+ }],
86
+ _created: new Date(),
87
+ _createdBy: 'system',
88
+ _lastLoggedIn: new Date(),
89
+ _updated: new Date(),
90
+ _updatedBy: 'system',
91
+ };
92
+ }
93
+ ;
94
+ export function getTestOrgUserContext() {
95
+ return {
96
+ user: getTestOrgUser(),
97
+ _orgId: getTestOrg()._id,
98
+ };
63
99
  }
64
- export const testOrgUserContext = {
65
- user: getTestOrgUser(),
66
- _orgId: testOrg._id,
67
- };
100
+ ;
@@ -1,4 +1,4 @@
1
- import { LoginResponseSpec, TokenResponseSpec, UserSpec, PublicUserSchema, UserContextSpec, passwordValidator, } from '@loomcore/common/models';
1
+ import { LoginResponseSpec, TokenResponseSpec, UserSpec, PublicUserSchema, passwordValidator, PublicUserContextSpec, } from '@loomcore/common/models';
2
2
  import { entityUtils } from '@loomcore/common/utils';
3
3
  import { BadRequestError, UnauthenticatedError } from '../errors/index.js';
4
4
  import { isAuthenticated } from '../middleware/index.js';
@@ -38,13 +38,12 @@ export class AuthController {
38
38
  apiUtils.apiResponse(res, 201, { data: user || undefined }, UserSpec, PublicUserSchema);
39
39
  }
40
40
  async requestTokenUsingRefreshToken(req, res, next) {
41
- const userContext = req.userContext;
42
41
  const refreshToken = req.query.refreshToken;
43
- if (!userContext || !refreshToken || typeof refreshToken !== 'string') {
44
- throw new BadRequestError('Missing required fields: userContext and refreshToken are required.');
42
+ if (!refreshToken || typeof refreshToken !== 'string') {
43
+ throw new BadRequestError('Missing required fields: refreshToken is required.');
45
44
  }
46
45
  const deviceId = this.authService.getDeviceIdFromCookie(req);
47
- const tokens = await this.authService.requestTokenUsingRefreshToken(userContext, refreshToken, deviceId);
46
+ const tokens = await this.authService.requestTokenUsingRefreshToken(refreshToken, deviceId);
48
47
  if (tokens) {
49
48
  apiUtils.apiResponse(res, 200, { data: tokens }, TokenResponseSpec);
50
49
  }
@@ -54,7 +53,7 @@ export class AuthController {
54
53
  }
55
54
  async getUserContext(req, res, next) {
56
55
  const userContext = req.userContext;
57
- apiUtils.apiResponse(res, 200, { data: userContext }, UserContextSpec);
56
+ apiUtils.apiResponse(res, 200, { data: userContext }, PublicUserContextSpec);
58
57
  }
59
58
  afterAuth(req, res, loginResponse) {
60
59
  console.log('in afterAuth');
@@ -1,4 +1,5 @@
1
1
  import { randomUUID } from 'crypto';
2
+ import { doesTableExist } from "../utils/does-table-exist.util.js";
2
3
  export class CreateMigrationTableMigration {
3
4
  client;
4
5
  constructor(client) {
@@ -8,42 +9,39 @@ export class CreateMigrationTableMigration {
8
9
  async execute() {
9
10
  const _id = randomUUID().toString();
10
11
  try {
11
- await this.client.query(`
12
- CREATE TABLE "migrations" (
13
- "_id" VARCHAR(255) PRIMARY KEY,
14
- "index" INTEGER NOT NULL,
15
- "hasRun" BOOLEAN NOT NULL,
16
- "reverted" BOOLEAN NOT NULL
17
- )
18
- `);
19
- }
20
- catch (error) {
21
- if (error.code !== '42P07' && !error.data?.error?.includes('already exists')) {
22
- return { success: false, error: new Error(`Error creating migrations table: ${error.message}`) };
12
+ await this.client.query('BEGIN');
13
+ const tableExists = await doesTableExist(this.client, 'migrations');
14
+ if (!tableExists) {
15
+ await this.client.query(`
16
+ CREATE TABLE "migrations" (
17
+ "_id" VARCHAR(255) PRIMARY KEY,
18
+ "index" INTEGER NOT NULL UNIQUE,
19
+ "hasRun" BOOLEAN NOT NULL,
20
+ "reverted" BOOLEAN NOT NULL
21
+ )
22
+ `);
23
23
  }
24
- }
25
- try {
26
24
  const result = await this.client.query(`
27
25
  INSERT INTO "migrations" ("_id", "index", "hasRun", "reverted")
28
26
  VALUES ('${_id}', ${this.index}, TRUE, FALSE);
29
27
  `);
30
28
  if (result.rowCount === 0) {
29
+ await this.client.query('ROLLBACK');
31
30
  return { success: false, error: new Error(`Error inserting migration ${this.index} to migrations table: No row returned`) };
32
31
  }
32
+ await this.client.query('COMMIT');
33
+ return { success: true, error: null };
33
34
  }
34
35
  catch (error) {
35
- return { success: false, error: new Error(`Error inserting migration ${this.index} to migrations table: ${error.message}`) };
36
+ await this.client.query('ROLLBACK');
37
+ return { success: false, error: new Error(`Error executing migration ${this.index}: ${error.message}`) };
36
38
  }
37
- return { success: true, error: null };
38
39
  }
39
40
  async revert() {
40
41
  try {
41
- const result = await this.client.query(`
42
+ await this.client.query(`
42
43
  DROP TABLE "migrations";
43
44
  `);
44
- if (result.rowCount === 0) {
45
- return { success: false, error: new Error(`Error dropping migrations table: No row returned`) };
46
- }
47
45
  }
48
46
  catch (error) {
49
47
  return { success: false, error: new Error(`Error reverting migration ${this.index} from migrations table: ${error.message}`) };
@@ -2,9 +2,7 @@ import { Client } from "pg";
2
2
  import { IMigration } from "./migration.interface.js";
3
3
  export declare class CreateOrganizationsTableMigration implements IMigration {
4
4
  private readonly client;
5
- private readonly orgName;
6
- private readonly orgCode;
7
- constructor(client: Client, orgName: string, orgCode: string);
5
+ constructor(client: Client);
8
6
  index: number;
9
7
  execute(): Promise<{
10
8
  success: boolean;
@@ -1,83 +1,76 @@
1
1
  import { randomUUID } from "crypto";
2
+ import { doesTableExist } from "../utils/does-table-exist.util.js";
2
3
  export class CreateOrganizationsTableMigration {
3
4
  client;
4
- orgName;
5
- orgCode;
6
- constructor(client, orgName, orgCode) {
5
+ constructor(client) {
7
6
  this.client = client;
8
- this.orgName = orgName;
9
- this.orgCode = orgCode;
10
7
  }
11
8
  index = 2;
12
9
  async execute() {
13
10
  const _id = randomUUID().toString();
14
11
  try {
15
- await this.client.query(`
16
- CREATE TABLE "organizations" (
17
- "_id" VARCHAR(255) PRIMARY KEY,
18
- "name" VARCHAR(255) NOT NULL,
19
- "code" VARCHAR(255) NOT NULL,
20
- "description" TEXT,
21
- "status" INTEGER NOT NULL,
22
- "isMetaOrg" BOOLEAN NOT NULL,
23
- "authToken" TEXT,
24
- "_created" TIMESTAMP NOT NULL,
25
- "_createdBy" VARCHAR(255) NOT NULL,
26
- "_updated" TIMESTAMP NOT NULL,
27
- "_updatedBy" VARCHAR(255) NOT NULL,
28
- "_deleted" TIMESTAMP,
29
- "_deletedBy" VARCHAR(255)
30
- )
31
- `);
32
- }
33
- catch (error) {
34
- if (error.code === '42P07' || error.data?.error?.includes('already exists')) {
35
- console.log(`Organization table already exists`);
36
- }
37
- else {
38
- return { success: false, error: new Error(`Error creating organization table: ${error.message}`) };
12
+ await this.client.query('BEGIN');
13
+ const tableExists = await doesTableExist(this.client, 'organizations');
14
+ if (!tableExists) {
15
+ await this.client.query(`
16
+ CREATE TABLE "organizations" (
17
+ "_id" VARCHAR(255) PRIMARY KEY,
18
+ "name" VARCHAR(255) NOT NULL UNIQUE,
19
+ "code" VARCHAR(255) NOT NULL UNIQUE,
20
+ "description" TEXT,
21
+ "status" INTEGER NOT NULL,
22
+ "isMetaOrg" BOOLEAN NOT NULL,
23
+ "authToken" TEXT,
24
+ "_created" TIMESTAMP NOT NULL,
25
+ "_createdBy" VARCHAR(255) NOT NULL,
26
+ "_updated" TIMESTAMP NOT NULL,
27
+ "_updatedBy" VARCHAR(255) NOT NULL,
28
+ "_deleted" TIMESTAMP,
29
+ "_deletedBy" VARCHAR(255)
30
+ )
31
+ `);
39
32
  }
40
- }
41
- try {
42
33
  const result = await this.client.query(`
43
34
  INSERT INTO "migrations" ("_id", "index", "hasRun", "reverted")
44
35
  VALUES ('${_id}', ${this.index}, TRUE, FALSE);
45
36
  `);
46
37
  if (result.rowCount === 0) {
38
+ await this.client.query('ROLLBACK');
47
39
  return { success: false, error: new Error(`Error inserting migration ${this.index} to migrations table: No row returned`) };
48
40
  }
41
+ await this.client.query('COMMIT');
42
+ return { success: true, error: null };
49
43
  }
50
44
  catch (error) {
51
- return { success: false, error: new Error(`Error inserting migration ${this.index} to migrations table: ${error.message}`) };
45
+ await this.client.query('ROLLBACK');
46
+ return { success: false, error: new Error(`Error executing migration ${this.index}: ${error.message}`) };
52
47
  }
53
- return { success: true, error: null };
54
48
  }
55
49
  async revert() {
56
50
  try {
57
- const result = await this.client.query(`
58
- DROP TABLE "organizations";
59
- `);
60
- if (result.rowCount === 0) {
61
- return { success: false, error: new Error(`Error dropping organizations table: No row returned`) };
51
+ await this.client.query('BEGIN');
52
+ const tableExists = await doesTableExist(this.client, 'organizations');
53
+ if (tableExists) {
54
+ await this.client.query(`
55
+ DROP TABLE "organizations";
56
+ `);
62
57
  }
63
- }
64
- catch (error) {
65
- return { success: false, error: new Error(`Error dropping organizations table: ${error.message}`) };
66
- }
67
- try {
68
- const result = await this.client.query(`
69
- Update "migrations" SET "reverted" = TRUE WHERE "index" = '${this.index}';
58
+ const updateResult = await this.client.query(`
59
+ UPDATE "migrations" SET "reverted" = TRUE WHERE "index" = '${this.index}';
70
60
  `);
71
- if (result.rowCount === 0) {
61
+ if (updateResult.rowCount === 0) {
62
+ await this.client.query('ROLLBACK');
72
63
  return {
73
64
  success: false, error: new Error(`Error updating migration record for index ${this.index}: Migration record not found.
74
65
  Migration index: ${this.index}`)
75
66
  };
76
67
  }
68
+ await this.client.query('COMMIT');
69
+ return { success: true, error: null };
77
70
  }
78
71
  catch (error) {
79
- return { success: false, error: new Error(`Error updating migration record for index ${this.index}: ${error.message}`) };
72
+ await this.client.query('ROLLBACK');
73
+ return { success: false, error: new Error(`Error reverting migration ${this.index}: ${error.message}`) };
80
74
  }
81
- return { success: true, error: null };
82
75
  }
83
76
  }