@contember/engine-system-api 1.2.0-alpha.9 → 1.2.0-beta.2

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 (49) hide show
  1. package/dist/src/SystemContainer.js +1 -1
  2. package/dist/src/SystemContainer.js.map +1 -1
  3. package/dist/src/migrations/2020-05-06-150000-composed-primary.d.ts +1 -1
  4. package/dist/src/migrations/2020-05-06-150000-composed-primary.d.ts.map +1 -1
  5. package/dist/src/migrations/2020-06-01-103000-event-trigger-perf.d.ts +1 -1
  6. package/dist/src/migrations/2020-06-01-103000-event-trigger-perf.d.ts.map +1 -1
  7. package/dist/src/migrations/2021-05-07-155800-event-log-rework.d.ts +1 -1
  8. package/dist/src/migrations/2021-05-07-155800-event-log-rework.d.ts.map +1 -1
  9. package/dist/src/model/ProjectInitializer.d.ts +1 -1
  10. package/dist/src/model/ProjectInitializer.d.ts.map +1 -1
  11. package/dist/src/model/ProjectInitializer.js +31 -30
  12. package/dist/src/model/ProjectInitializer.js.map +1 -1
  13. package/dist/src/model/helpers/modelHelpers.js +1 -1
  14. package/dist/src/model/helpers/modelHelpers.js.map +1 -1
  15. package/dist/src/model/migrations/ProjectMigrator.d.ts.map +1 -1
  16. package/dist/src/model/migrations/ProjectMigrator.js +10 -4
  17. package/dist/src/model/migrations/ProjectMigrator.js.map +1 -1
  18. package/dist/src/model/queries/events/LatestTransactionIdByStageQuery.d.ts +2 -2
  19. package/dist/src/model/queries/events/LatestTransactionIdByStageQuery.d.ts.map +1 -1
  20. package/dist/src/model/queries/events/LatestTransactionIdByStageQuery.js +9 -14
  21. package/dist/src/model/queries/events/LatestTransactionIdByStageQuery.js.map +1 -1
  22. package/dist/src/resolvers/SystemResolverContext.d.ts +3 -4
  23. package/dist/src/resolvers/SystemResolverContext.d.ts.map +1 -1
  24. package/dist/src/resolvers/SystemResolverContext.js +2 -4
  25. package/dist/src/resolvers/SystemResolverContext.js.map +1 -1
  26. package/dist/src/schema/system.graphql.d.ts.map +1 -1
  27. package/dist/src/schema/system.graphql.js +6 -0
  28. package/dist/src/schema/system.graphql.js.map +1 -1
  29. package/dist/src/schema/types.d.ts +7 -0
  30. package/dist/src/schema/types.d.ts.map +1 -1
  31. package/dist/src/schema/types.js.map +1 -1
  32. package/dist/src/tsconfig.tsbuildinfo +1 -1
  33. package/dist/tests/cases/integration/systemApi/projectInitializer.test.js +3 -3
  34. package/dist/tests/cases/integration/systemApi/projectInitializer.test.js.map +1 -1
  35. package/dist/tests/tsconfig.tsbuildinfo +1 -1
  36. package/package.json +14 -13
  37. package/src/SystemContainer.ts +2 -2
  38. package/src/migrations/2020-05-06-150000-composed-primary.ts +1 -1
  39. package/src/migrations/2020-06-01-103000-event-trigger-perf.ts +1 -1
  40. package/src/migrations/2021-05-07-155800-event-log-rework.ts +1 -1
  41. package/src/model/ProjectInitializer.ts +40 -40
  42. package/src/model/helpers/modelHelpers.ts +1 -1
  43. package/src/model/migrations/ProjectMigrator.ts +13 -4
  44. package/src/model/queries/events/LatestTransactionIdByStageQuery.ts +10 -24
  45. package/src/resolvers/SystemResolverContext.ts +1 -2
  46. package/src/schema/system.graphql.ts +6 -0
  47. package/src/schema/types.ts +8 -0
  48. package/src/tsconfig.json +7 -4
  49. package/tests/cases/integration/systemApi/projectInitializer.test.ts +3 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contember/engine-system-api",
3
- "version": "1.2.0-alpha.9",
3
+ "version": "1.2.0-beta.2",
4
4
  "license": "Apache-2.0",
5
5
  "main": "dist/src/index.js",
6
6
  "typings": "dist/src/index.d.ts",
@@ -9,22 +9,23 @@
9
9
  "test": "vitest"
10
10
  },
11
11
  "dependencies": {
12
- "@contember/authorization": "^1.2.0-alpha.9",
13
- "@contember/database": "^1.2.0-alpha.9",
14
- "@contember/database-migrations": "^1.2.0-alpha.9",
15
- "@contember/dic": "^1.2.0-alpha.9",
16
- "@contember/engine-common": "^1.2.0-alpha.9",
17
- "@contember/graphql-utils": "^1.2.0-alpha.9",
18
- "@contember/queryable": "^1.2.0-alpha.9",
19
- "@contember/schema": "^1.2.0-alpha.9",
20
- "@contember/schema-migrations": "^1.2.0-alpha.9",
21
- "@contember/schema-utils": "^1.2.0-alpha.9",
12
+ "@contember/authorization": "^1.2.0-beta.2",
13
+ "@contember/database": "^1.2.0-beta.2",
14
+ "@contember/database-migrations": "^1.2.0-beta.2",
15
+ "@contember/dic": "^1.2.0-beta.2",
16
+ "@contember/engine-common": "^1.2.0-beta.2",
17
+ "@contember/graphql-utils": "^1.2.0-beta.2",
18
+ "@contember/logger": "^1.2.0-beta.2",
19
+ "@contember/queryable": "^1.2.0-beta.2",
20
+ "@contember/schema": "^1.2.0-beta.2",
21
+ "@contember/schema-migrations": "^1.2.0-beta.2",
22
+ "@contember/schema-utils": "^1.2.0-beta.2",
22
23
  "@graphql-tools/utils": "^8.6.13",
23
24
  "graphql-tag": "^2.12.5"
24
25
  },
25
26
  "devDependencies": {
26
- "@contember/engine-api-tester": "^1.2.0-alpha.9",
27
- "@contember/schema-definition": "^1.2.0-alpha.9",
27
+ "@contember/engine-api-tester": "^1.2.0-beta.2",
28
+ "@contember/schema-definition": "^1.2.0-beta.2",
28
29
  "@graphql-codegen/cli": "^2.6.2",
29
30
  "@graphql-codegen/typescript": "^2.5.1",
30
31
  "@graphql-codegen/typescript-operations": "^2.4.2",
@@ -118,7 +118,7 @@ export class SystemContainerFactory {
118
118
  new EventOldValuesResolver())
119
119
  .addService('systemResolversFactory', ({ stagesQueryResolver, executedMigrationsQueryResolver, migrateMutationResolver, truncateMutationResolver, migrationAlterMutationResolver, eventsQueryResolver, eventOldValuesResolver }) =>
120
120
  new ResolverFactory(stagesQueryResolver, executedMigrationsQueryResolver, migrateMutationResolver, truncateMutationResolver, migrationAlterMutationResolver, eventsQueryResolver, eventOldValuesResolver))
121
- .addService('resolverContextFactory', ({ authorizator, schemaVersionBuilder }) =>
122
- new SystemResolverContextFactory(authorizator, schemaVersionBuilder))
121
+ .addService('resolverContextFactory', ({ authorizator }) =>
122
+ new SystemResolverContextFactory(authorizator))
123
123
  }
124
124
  }
@@ -1,7 +1,7 @@
1
1
  import { escapeValue, MigrationBuilder, Name } from '@contember/database-migrations'
2
2
  import { SystemMigrationArgs } from './types'
3
3
  import { getJunctionTables } from '../model'
4
- import { MigrationArgs } from '@contember/database-migrations/dist/src/Migration'
4
+ import { MigrationArgs } from '@contember/database-migrations'
5
5
 
6
6
  const createEventTrigger = (builder: MigrationBuilder, tableName: Name, primaryColumns: string[]) => {
7
7
  builder.createTrigger(tableName, 'log_event', {
@@ -1,7 +1,7 @@
1
1
  import { MigrationBuilder, Name } from '@contember/database-migrations'
2
2
  import { SystemMigrationArgs } from './types'
3
3
  import { getJunctionTables } from '../model'
4
- import { MigrationArgs } from '@contember/database-migrations/dist/src/Migration'
4
+ import { MigrationArgs } from '@contember/database-migrations'
5
5
 
6
6
  const createEventStatementTrigger = (builder: MigrationBuilder, tableName: Name) => {
7
7
  builder.createTrigger(tableName, 'log_event_statement', {
@@ -1,7 +1,7 @@
1
1
  import { MigrationBuilder, Name } from '@contember/database-migrations'
2
2
  import { SystemMigrationArgs } from './types'
3
3
  import { getJunctionTables } from '../model'
4
- import { MigrationArgs } from '@contember/database-migrations/dist/src/Migration'
4
+ import { MigrationArgs } from '@contember/database-migrations'
5
5
 
6
6
  const createTrxEvent = (builder: MigrationBuilder, tableName: Name) => {
7
7
  builder.createTrigger(tableName, 'log_event_trx', {
@@ -8,9 +8,10 @@ import {
8
8
  Connection,
9
9
  createDatabaseIfNotExists,
10
10
  DatabaseConfig,
11
+ EventManager,
11
12
  retryTransaction,
12
13
  } from '@contember/database'
13
- import { Logger } from '@contember/engine-common'
14
+ import { FingerCrossedLoggerHandler, Logger } from '@contember/logger'
14
15
 
15
16
  export class ProjectInitializer {
16
17
  constructor(
@@ -26,54 +27,53 @@ export class ProjectInitializer {
26
27
  logger: Logger,
27
28
  ) {
28
29
  const dbContext = databaseContextFactory.create()
29
- if (project.db) {
30
- // todo: use dbContext
31
- logger.group(`Executing system schema migration`)
32
- await createDatabaseIfNotExists(project.db, logger.write.bind(logger))
33
- const singleConnection = Connection.createSingle(project.db)
34
- await singleConnection.scope(async connection => {
35
- const systemSchema = dbContext.client.schema
30
+ return await logger.scope(async initLogger => {
31
+ if (project.db) {
32
+ // todo: use dbContext
33
+ initLogger.debug('Executing system schema migrations')
34
+ await createDatabaseIfNotExists(project.db, message => typeof message === 'string' ? initLogger.warn(message) : initLogger.error(message))
35
+ const singleConnection = Connection.createSingle(project.db, err => initLogger.error(err, { message: 'Database client error' }))
36
36
 
37
- const schemaResolver = (connection: Connection.ConnectionLike) => {
38
- const dbContextMigrations = databaseContextFactory
39
- .withClient(new Client(connection, systemSchema, { module: 'system' }))
40
- .create()
41
- return this.schemaVersionBuilder.buildSchema(dbContextMigrations)
42
- }
43
- await this.systemDbMigrationsRunnerFactory(connection, systemSchema).migrate(
44
- logger.write.bind(logger),
45
- {
46
- schemaResolver,
47
- project,
48
- },
49
- )
50
- })
51
- await singleConnection.end()
52
- logger.groupEnd()
53
- }
54
- await retryTransaction(() =>
55
- dbContext.transaction(async trx => {
56
- await this.initStages(trx, project, logger)
57
- }),
58
- )
59
- if (dbContext.client.connection instanceof Connection) {
60
- await dbContext.client.connection.clearPool()
61
- }
37
+ await singleConnection.scope(async connection => {
38
+ const systemSchema = dbContext.client.schema
39
+
40
+ const schemaResolver = (connection: Connection.ConnectionLike) => {
41
+ const dbContextMigrations = databaseContextFactory
42
+ .withClient(new Client(connection, systemSchema, { module: 'system' }))
43
+ .create()
44
+ return this.schemaVersionBuilder.buildSchema(dbContextMigrations)
45
+ }
46
+ await this.systemDbMigrationsRunnerFactory(connection, systemSchema).migrate(
47
+ message => initLogger.warn(message),
48
+ {
49
+ schemaResolver,
50
+ project,
51
+ },
52
+ )
53
+ })
54
+ await singleConnection.end()
55
+ }
56
+ await retryTransaction(() =>
57
+ dbContext.transaction(async trx => {
58
+ await this.initStages(trx, project, initLogger)
59
+ }),
60
+ message => initLogger.warn(message),
61
+ )
62
+ if (dbContext.client.connection instanceof Connection) {
63
+ await dbContext.client.connection.clearPool()
64
+ }
65
+ }, {}, { handler: FingerCrossedLoggerHandler.factory() })
62
66
  }
63
67
 
64
68
  private async initStages(db: DatabaseContext<Connection.TransactionLike>, project: ProjectConfig, logger: Logger) {
65
-
66
- logger.group(`Creating stages`)
67
-
69
+ logger.debug(`Creating stages`)
68
70
  for (const stage of project.stages) {
69
71
  const created = await this.stageCreator.createStage(db, stage)
70
72
  if (created) {
71
- logger.write(`Created stage ${stage.slug} `)
73
+ logger.warn(`Created stage ${stage.slug}`, { stage: stage.slug })
72
74
  } else {
73
- logger.breadcrumb(`Updated stage ${stage.slug}`)
75
+ logger.debug(`Updated stage ${stage.slug}`, { stage: stage.slug })
74
76
  }
75
77
  }
76
-
77
- logger.groupEnd()
78
78
  }
79
79
  }
@@ -5,7 +5,7 @@ export const getJunctionTables = (model: Model.Schema): Model.JoiningTable[] =>
5
5
  const tables: Model.JoiningTable[] = []
6
6
  Object.values(model.entities).forEach(entity => {
7
7
  acceptEveryFieldVisitor(model, entity, {
8
- visitManyHasManyOwning: ({}, relation) => {
8
+ visitManyHasManyOwning: ({ relation }) => {
9
9
  tables.push(relation.joiningTable)
10
10
  },
11
11
  visitColumn: () => {},
@@ -8,6 +8,7 @@ import { ExecutedMigrationsResolver } from './ExecutedMigrationsResolver'
8
8
  import { MigrateErrorCode } from '../../schema'
9
9
  import { SchemaVersionBuilder } from './SchemaVersionBuilder'
10
10
  import { SchemaValidator } from '@contember/schema-utils'
11
+ import { logger } from '@contember/logger'
11
12
 
12
13
  export class ProjectMigrator {
13
14
  constructor(
@@ -81,16 +82,25 @@ export class ProjectMigrator {
81
82
  }
82
83
  const latestModification = described[described.length - 1]
83
84
  schema = latestModification.schema
84
- const errors = SchemaValidator.validate(schema)
85
+ const errors = SchemaValidator.validate(schema, migration.skippedErrors)
85
86
  if (errors.length > 0) {
86
87
  throw new InvalidSchemaError(
87
88
  migration.version,
88
89
  'Migration generates invalid schema: \n' +
89
- errors.map(it => it.path.join('.') + ': ' + it.message).join('\n'),
90
+ errors.map(it => `${it.path.join('.')}: [${it.code}] ${it.message}`).join('\n'),
90
91
  )
91
92
  }
92
93
  toExecute.push(migration)
93
94
  }
95
+ // intentionally not using skippedErrors here
96
+ const errors = SchemaValidator.validate(schema)
97
+ if (errors.length > 0) {
98
+ throw new InvalidSchemaError(
99
+ toExecute[toExecute.length - 1].version,
100
+ 'Migration generates invalid schema: \n' +
101
+ errors.map(it => it.path.join('.') + ': ' + it.message).join('\n'),
102
+ )
103
+ }
94
104
  return toExecute
95
105
  }
96
106
 
@@ -120,8 +130,7 @@ export class ProjectMigrator {
120
130
  await db.query(sql)
121
131
  } catch (e) {
122
132
  if (e instanceof QueryError) {
123
- // eslint-disable-next-line no-console
124
- console.error(e)
133
+ logger.error(e, { message: 'Migration failed' })
125
134
  throw new MigrationFailedError(migrationVersion, e.message)
126
135
  }
127
136
  throw e
@@ -1,34 +1,20 @@
1
- import { DatabaseQuery, DatabaseQueryable } from '@contember/database'
1
+ import { DatabaseQuery, DatabaseQueryable, SelectBuilder } from '@contember/database'
2
2
  import { ImplementationException } from '../../../utils'
3
3
 
4
4
  export class LatestTransactionIdByStageQuery extends DatabaseQuery<string> {
5
- constructor(private readonly stageSlug: string) {
5
+ constructor(private readonly stageId: string) {
6
6
  super()
7
7
  }
8
8
 
9
9
  async fetch(queryable: DatabaseQueryable): Promise<string> {
10
- const stageId = (
11
- await queryable.db.query<{ id: string }>(
12
- `
13
- SELECT id
14
- FROM system.stage
15
- WHERE slug = ?
16
- `,
17
- [this.stageSlug],
18
- )
19
- ).rows[0].id
20
- const rows = (
21
- await queryable.db.query<{ transaction_id: string }>(
22
- `
23
- SELECT transaction_id
24
- FROM system.stage_transaction
25
- WHERE stage_id = ?
26
- ORDER BY applied_at DESC
27
- LIMIT 1
28
- `,
29
- [stageId],
30
- )
31
- ).rows
10
+ const rows = await SelectBuilder.create<{ transaction_id: string }>()
11
+ .from('stage_transaction')
12
+ .select('transaction_id')
13
+ .where({ stage_id: this.stageId })
14
+ .orderBy('applied_at', 'desc')
15
+ .limit(1)
16
+ .getResult(queryable.db)
17
+
32
18
  if (rows.length !== 1) {
33
19
  throw new ImplementationException()
34
20
  }
@@ -10,15 +10,14 @@ import { ItemLoader } from '../utils/batchQuery'
10
10
  export class SystemResolverContextFactory {
11
11
  constructor(
12
12
  private readonly authorizator: Authorizator<Identity>,
13
- private readonly schemaVersionBuilder: SchemaVersionBuilder,
14
13
  ) {}
15
14
 
16
15
  public async create(
16
+ schema: Schema,
17
17
  systemDbContext: DatabaseContext,
18
18
  project: ProjectConfig,
19
19
  identity: Identity,
20
20
  ): Promise<SystemResolverContext> {
21
- const schema = await this.schemaVersionBuilder.buildSchema(systemDbContext)
22
21
  const stagePermissionsFactory = new StagePermissionsFactory(schema)
23
22
  const loaders = new Map<LoaderFactory<any, any>, ItemLoader<any, any>>()
24
23
  return {
@@ -139,6 +139,12 @@ const schema: DocumentNode = gql`
139
139
  name: String!
140
140
  formatVersion: Int!
141
141
  modifications: [Json!]!
142
+ skippedErrors: [MigrationSkippedError!]
143
+ }
144
+
145
+ input MigrationSkippedError {
146
+ code: String!
147
+ path: String
142
148
  }
143
149
 
144
150
  enum MigrateErrorCode {
@@ -145,6 +145,7 @@ export type Migration = {
145
145
  readonly formatVersion: Scalars['Int']
146
146
  readonly modifications: ReadonlyArray<Scalars['Json']>
147
147
  readonly name: Scalars['String']
148
+ readonly skippedErrors?: InputMaybe<ReadonlyArray<MigrationSkippedError>>
148
149
  readonly version: Scalars['String']
149
150
  }
150
151
 
@@ -188,6 +189,11 @@ export type MigrationModifyResponse = {
188
189
  readonly ok: Scalars['Boolean']
189
190
  }
190
191
 
192
+ export type MigrationSkippedError = {
193
+ readonly code: Scalars['String']
194
+ readonly path?: InputMaybe<Scalars['String']>
195
+ }
196
+
191
197
  export type Mutation = {
192
198
  readonly __typename?: 'Mutation'
193
199
  readonly forceMigrate: MigrateResponse
@@ -358,6 +364,7 @@ export type ResolversTypes = {
358
364
  MigrationModifyError: ResolverTypeWrapper<MigrationModifyError>
359
365
  MigrationModifyErrorCode: MigrationModifyErrorCode
360
366
  MigrationModifyResponse: ResolverTypeWrapper<MigrationModifyResponse>
367
+ MigrationSkippedError: MigrationSkippedError
361
368
  Mutation: ResolverTypeWrapper<{}>
362
369
  PrimaryKey: ResolverTypeWrapper<Scalars['PrimaryKey']>
363
370
  Query: ResolverTypeWrapper<{}>
@@ -390,6 +397,7 @@ export type ResolversParentTypes = {
390
397
  MigrationModification: MigrationModification
391
398
  MigrationModifyError: MigrationModifyError
392
399
  MigrationModifyResponse: MigrationModifyResponse
400
+ MigrationSkippedError: MigrationSkippedError
393
401
  Mutation: {}
394
402
  PrimaryKey: Scalars['PrimaryKey']
395
403
  Query: {}
package/src/tsconfig.json CHANGED
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "references": [
8
8
  {
9
- "path": "../../queryable/src"
9
+ "path": "../../authorization/src"
10
10
  },
11
11
  {
12
12
  "path": "../../database/src"
@@ -14,15 +14,18 @@
14
14
  {
15
15
  "path": "../../database-migrations/src"
16
16
  },
17
- {
18
- "path": "../../authorization/src"
19
- },
20
17
  {
21
18
  "path": "../../engine-common/src"
22
19
  },
23
20
  {
24
21
  "path": "../../graphql-utils/src"
25
22
  },
23
+ {
24
+ "path": "../../logger/src"
25
+ },
26
+ {
27
+ "path": "../../queryable/src"
28
+ },
26
29
  {
27
30
  "path": "../../schema/src"
28
31
  },
@@ -1,10 +1,10 @@
1
1
  import { ApiTester } from '@contember/engine-api-tester'
2
2
  import { ProjectConfig, StageConfig } from '../../../../src'
3
3
  import { assert, test } from 'vitest'
4
+ import { createLogger, JsonStreamLoggerHandler } from '@contember/logger'
4
5
 
5
- import { Logger } from '@contember/engine-common'
6
6
 
7
- const nullLogger = new Logger(() => {})
7
+ const logger = createLogger(new JsonStreamLoggerHandler(process.stderr))
8
8
 
9
9
  test('create stage', async () => {
10
10
  const prodStage: StageConfig = {
@@ -22,7 +22,7 @@ test('create stage', async () => {
22
22
  },
23
23
  })
24
24
 
25
- await tester.systemContainer.projectInitializer.initialize(tester.databaseContextFactory, project, nullLogger)
25
+ await tester.systemContainer.projectInitializer.initialize(tester.databaseContextFactory, project, logger)
26
26
  const createdStagesA = await tester.stages.refreshCreatedStages()
27
27
  assert.ok(createdStagesA.has('prod'))
28
28