diff --git a/apps/auth-cron/src/job.ts b/apps/auth-cron/src/job.ts index 7834ee95..274db6f3 100644 --- a/apps/auth-cron/src/job.ts +++ b/apps/auth-cron/src/job.ts @@ -1,6 +1,6 @@ import path from 'node:path'; import type { BundleDatabase } from '@map-colonies/auth-bundler'; -import { createBundle, getVersionCommand } from '@map-colonies/auth-bundler'; +import { computeRevision, createBundle, getVersionCommand } from '@map-colonies/auth-bundler'; import type { Environments } from '@map-colonies/auth-core'; import { getS3Client } from './s3'; import { compareVersionsToBundle } from './util'; @@ -30,15 +30,16 @@ export function getJob(bundleDatabase: BundleDatabase, environment: Environments logger.debug({ msg: 'creating new bundle as ', bundleEnv: environment }); + const revision = computeRevision(latestVersions); const bundleContent = await bundleDatabase.getBundleFromVersions(latestVersions); - await createBundle(bundleContent, workdir, 'bundle.tar.gz'); + await createBundle(bundleContent, workdir, 'bundle.tar.gz', undefined, revision); const hash = await getS3Client(environment).uploadFile(path.join(workdir, 'bundle.tar.gz')); if (shouldSaveBundleToDb) { logger.debug({ msg: 'saving bundle metadata to the database', bundleEnv: environment }); - await bundleDatabase.saveBundle(latestVersions, hash); + await bundleDatabase.saveBundle(latestVersions, hash, revision); } logger.info({ msg: 'created new bundle successfully', bundleEnv: environment }); diff --git a/apps/auth-cron/tests/job.spec.mts b/apps/auth-cron/tests/job.spec.mts index 5ee74fb0..8c3c739b 100644 --- a/apps/auth-cron/tests/job.spec.mts +++ b/apps/auth-cron/tests/job.spec.mts @@ -91,6 +91,7 @@ describe('job.ts', function () { createdAt: new Date(), hash: 'avi', metadata: null, + revision: 'revision', }); bundleDbMock.getLatestVersions.mockResolvedValue({ assets: [{ name: 'avi', version: 1 }], @@ -117,6 +118,7 @@ describe('job.ts', function () { opaVersion: '0.52.0', createdAt: new Date(), metadata: null, + revision: 'revision', }); bundleDbMock.getLatestVersions.mockResolvedValue({ assets: [{ name: 'avi', version: 1 }], @@ -146,6 +148,7 @@ describe('job.ts', function () { opaVersion: '0.52.0', createdAt: new Date(), metadata: null, + revision: 'revision', }); bundleDbMock.getLatestVersions.mockResolvedValue({ assets: [{ name: 'avi', version: 1 }], @@ -174,6 +177,7 @@ describe('job.ts', function () { opaVersion: '0.51.0', createdAt: new Date(), metadata: null, + revision: 'revision', }); bundleDbMock.getLatestVersions.mockResolvedValue({ assets: [{ name: 'avi', version: 1 }], diff --git a/packages/auth-bundler/package.json b/packages/auth-bundler/package.json index 8fdff5d0..c376cc84 100644 --- a/packages/auth-bundler/package.json +++ b/packages/auth-bundler/package.json @@ -43,7 +43,8 @@ "handlebars": "4.7.7", "pg": "catalog:", "drizzle-orm": "catalog:", - "@map-colonies/drizzle-utils": "catalog:" + "@map-colonies/drizzle-utils": "catalog:", + "fast-json-stable-stringify": "^2.1.0" }, "devDependencies": { "@map-colonies/config": "catalog:", diff --git a/packages/auth-bundler/src/db.ts b/packages/auth-bundler/src/db.ts index c2160f82..eaa26cd6 100644 --- a/packages/auth-bundler/src/db.ts +++ b/packages/auth-bundler/src/db.ts @@ -92,7 +92,7 @@ export class BundleDatabase { * @param hash The md5 hash of the created bundle tarball * @returns The ID of the created bundle */ - public async saveBundle(versions: BundleContentVersions, hash: string): Promise { + public async saveBundle(versions: BundleContentVersions, hash: string, revision: string): Promise { logger?.debug('saving bundle to db'); const bundle: NewBundle = { environment: versions.environment, @@ -101,6 +101,7 @@ export class BundleDatabase { keyVersion: versions.keyVersion, hash, opaVersion: await getVersionCommand(), + revision, }; const res = await this.drizzle.insert(bundleTable).values(bundle).returning(); diff --git a/packages/auth-bundler/src/index.ts b/packages/auth-bundler/src/index.ts index b3fe4d6a..97f14d06 100644 --- a/packages/auth-bundler/src/index.ts +++ b/packages/auth-bundler/src/index.ts @@ -19,6 +19,7 @@ export { setLogger } from './logger'; export { BundleDatabase } from './db'; export * from './types'; export { getVersionCommand } from './opa'; +export { computeRevision } from './util'; /** * This function creates an opa bundle tarball from the given content @@ -33,7 +34,13 @@ export { getVersionCommand } from './opa'; * @throws {@link OpaBundleCreationError} If the OPA bundle creation process failed. */ -export async function createBundle(content: BundleContent, workDir: string, bundlePath: string, tests?: TestOptions): Promise { +export async function createBundle( + content: BundleContent, + workDir: string, + bundlePath: string, + tests?: TestOptions, + revision?: string +): Promise { logger?.info({ msg: 'creating bundle', workDir, bundlePath }); if (!existsSync(workDir)) { logger?.debug('workdir does not exists'); @@ -66,7 +73,7 @@ export async function createBundle(content: BundleContent, workDir: string, bund } } - const [creationCompleted, creationErr] = await createBundleCommand(workDir, bundlePath); + const [creationCompleted, creationErr] = await createBundleCommand(workDir, bundlePath, revision); if (!creationCompleted) { logger?.debug('failed creating bundle'); diff --git a/packages/auth-bundler/src/opa.ts b/packages/auth-bundler/src/opa.ts index 1c292ee3..0fb76246 100644 --- a/packages/auth-bundler/src/opa.ts +++ b/packages/auth-bundler/src/opa.ts @@ -67,13 +67,17 @@ export async function testCoverageCommand(path: string): Promise { * Runs the build command to create a bundle tarball from the given directory * @param filesPath The path of the files from which to create the bundle * @param bundlePath The path in which to save the created bundle. note: the path is relative to filesPath. + * @param revision The revision string to associate with the bundle * @returns true if the command ran successfully, false and the returned error otherwise * @see {@link https://www.openpolicyagent.org/docs/latest/cli/#opa-build} * @ignore */ -export async function createBundleCommand(filesPath: string, bundlePath: string): Promise<[true, undefined] | [false, string]> { - const res = await execa('opa', ['build', '-o', bundlePath, '-b', '.'], { cwd: filesPath, reject: false }); - +export async function createBundleCommand(filesPath: string, bundlePath: string, revision?: string): Promise<[true, undefined] | [false, string]> { + const args = ['build', '-o', bundlePath, '-b', '.']; + if (revision !== undefined) { + args.push('--revision', revision); + } + const res = await execa('opa', args, { cwd: filesPath, reject: false }); if (!res.failed) { return [true, undefined]; } diff --git a/packages/auth-bundler/src/util.ts b/packages/auth-bundler/src/util.ts index 1b897203..c9f91847 100644 --- a/packages/auth-bundler/src/util.ts +++ b/packages/auth-bundler/src/util.ts @@ -1,3 +1,9 @@ +import { createHash } from 'node:crypto'; +import stringify from 'fast-json-stable-stringify'; +import type { BundleContentVersions } from './types'; + +const HASH_LENGTH = 12; + export function extractNameAndVersion(entities: T[]): [string[], number[]] { const names: string[] = []; const versions: number[] = []; @@ -7,3 +13,16 @@ export function extractNameAndVersion a.name.localeCompare(b.name)); + const sortedConnections = [...versions.connections].sort((a, b) => a.name.localeCompare(b.name)); + const canonical = stringify({ + environment: versions.environment, + assets: sortedAssets, + connections: sortedConnections, + keyVersion: versions.keyVersion, + }); + const shortHash = createHash('sha256').update(canonical).digest('hex').substring(0, HASH_LENGTH); + return `${versions.environment}-${shortHash}`; +} diff --git a/packages/auth-bundler/tests/db.spec.mts b/packages/auth-bundler/tests/db.spec.mts index cafb6a18..6d87ec04 100644 --- a/packages/auth-bundler/tests/db.spec.mts +++ b/packages/auth-bundler/tests/db.spec.mts @@ -57,7 +57,7 @@ describe('db.ts', function () { const db = new BundleDatabase(pool); - const res = await db.saveBundle({ assets: [], connections: [], environment: Environment.PROD, keyVersion: 3 }, 'xdxd'); + const res = await db.saveBundle({ assets: [], connections: [], environment: Environment.PROD, keyVersion: 3 }, 'xdxd', 'lol'); expect(res).toBeGreaterThan(0); @@ -134,8 +134,8 @@ describe('db.ts', function () { it('should return the latest bundle ordered by id for the given environment', async function () { await drizzle.insert(bundleTable).values([ - { environment: Environment.STAGE, hash: 'hash1', opaVersion: '0.52.0', assets: [], connections: [] }, - { environment: Environment.STAGE, hash: 'hash2', opaVersion: '0.52.0', assets: [], connections: [] }, + { environment: Environment.STAGE, hash: 'hash1', opaVersion: '0.52.0', assets: [], connections: [], revision: 'revision1' }, + { environment: Environment.STAGE, hash: 'hash2', opaVersion: '0.52.0', assets: [], connections: [], revision: 'revision2' }, ]); const db = new BundleDatabase(pool); diff --git a/packages/auth-bundler/tests/index.spec.mts b/packages/auth-bundler/tests/index.spec.mts index 37b4d84a..66c47caf 100644 --- a/packages/auth-bundler/tests/index.spec.mts +++ b/packages/auth-bundler/tests/index.spec.mts @@ -79,5 +79,23 @@ describe('index.ts', function () { await expect(promise).rejects.toThrow(OpaBundleCreationError); }); + + it('should pass the revision to createBundleCommand when provided', async function () { + const createBundleCommandSpy = vi.spyOn(opa, 'createBundleCommand').mockResolvedValue([true, undefined]); + vi.spyOn(opa, 'validateBinaryExistCommand').mockResolvedValue(true); + + await createBundle(bundleContent, baseFolder, 'bundle.tar.gz', { enable: false }, 'np-abc123def456'); + + expect(createBundleCommandSpy).toHaveBeenCalledWith(baseFolder, 'bundle.tar.gz', 'np-abc123def456'); + }); + + it('should pass undefined revision to createBundleCommand when not provided', async function () { + const createBundleCommandSpy = vi.spyOn(opa, 'createBundleCommand').mockResolvedValue([true, undefined]); + vi.spyOn(opa, 'validateBinaryExistCommand').mockResolvedValue(true); + + await createBundle(bundleContent, baseFolder, 'bundle.tar.gz', { enable: false }); + + expect(createBundleCommandSpy).toHaveBeenCalledWith(baseFolder, 'bundle.tar.gz', undefined); + }); }); }); diff --git a/packages/auth-bundler/tests/opa.spec.mts b/packages/auth-bundler/tests/opa.spec.mts index f5300ba3..b5c5d782 100644 --- a/packages/auth-bundler/tests/opa.spec.mts +++ b/packages/auth-bundler/tests/opa.spec.mts @@ -138,6 +138,21 @@ describe('opa.ts', function () { expect(err).toBe('oh no'); }); + + it('should include the --revision flag when revision is provided', async function () { + const execaSpy = vi.spyOn(execa, 'execa').mockResolvedValue({ failed: false } as ExecaChildProcess); + await createBundleCommand(baseFolder, 'bundle.tar.gz', 'np-abc123def456'); + + expect(execaSpy).toHaveBeenCalledWith('opa', expect.arrayContaining(['--revision', 'np-abc123def456']), expect.anything()); + }); + + it('should not include the --revision flag when revision is not provided', async function () { + const execaSpy = vi.spyOn(execa, 'execa').mockResolvedValue({ failed: false } as ExecaChildProcess); + await createBundleCommand(baseFolder, 'bundle.tar.gz'); + + const calledArgs = execaSpy.mock.calls[0]?.[1] as string[]; + expect(calledArgs).not.toContain('--revision'); + }); }); describe('#getVersionCommand', function () { diff --git a/packages/auth-bundler/tests/util.spec.ts b/packages/auth-bundler/tests/util.spec.ts index 97dc1885..c5ce4005 100644 --- a/packages/auth-bundler/tests/util.spec.ts +++ b/packages/auth-bundler/tests/util.spec.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from 'vitest'; -import { extractNameAndVersion } from '@src/util'; +import { computeRevision, extractNameAndVersion } from '@src/util'; +import { Environment } from '../../auth-core/dist/entities'; describe('util.ts', function () { describe('#extractNameAndVersion', function () { @@ -17,4 +18,47 @@ describe('util.ts', function () { ]); }); }); + + describe('#computeRevision', function () { + const versions = { + environment: Environment.NP, + assets: [ + { name: 'b', version: 2 }, + { name: 'a', version: 1 }, + ], + connections: [{ name: 'c', version: 1 }], + keyVersion: 1, + }; + + it('should return a string prefixed with the environment followed by a 12-char hex hash', function () { + expect(computeRevision(versions)).toMatch(/^np-[a-f0-9]{12}$/); + }); + + it('should be reproducible for the same versions', function () { + expect(computeRevision(versions)).toBe(computeRevision(versions)); + }); + + it('should produce different revisions for different content versions', function () { + expect(computeRevision(versions)).not.toBe(computeRevision({ ...versions, keyVersion: 2 })); + }); + + it('should produce the same revision regardless of asset order', function () { + const reordered = { ...versions, assets: [...versions.assets].reverse() }; + + expect(computeRevision(versions)).toBe(computeRevision(reordered)); + }); + + it('should produce the same revision regardless of connection order', function () { + const moreConnections = { + ...versions, + connections: [ + { name: 'z', version: 1 }, + { name: 'a', version: 1 }, + ], + }; + const reordered = { ...moreConnections, connections: [...moreConnections.connections].reverse() }; + + expect(computeRevision(moreConnections)).toBe(computeRevision(reordered)); + }); + }); }); diff --git a/packages/auth-core/drizzle.config.mts b/packages/auth-core/drizzle.config.mts index 50a7192d..35591f54 100644 --- a/packages/auth-core/drizzle.config.mts +++ b/packages/auth-core/drizzle.config.mts @@ -2,7 +2,7 @@ import { ConnectionConfig } from 'pg'; import { defineConfig } from 'drizzle-kit'; import { getConfig, initConfig } from './src/config.js'; -import { createConnectionOptions } from './src/db/utils/createConnection.js'; +import { createConnectionOptions } from '@map-colonies/drizzle-utils'; import { ConnectionOptions } from 'node:tls'; await initConfig(); diff --git a/packages/auth-core/src/entities/bundle.ts b/packages/auth-core/src/entities/bundle.ts index 77476ed6..c916990f 100644 --- a/packages/auth-core/src/entities/bundle.ts +++ b/packages/auth-core/src/entities/bundle.ts @@ -11,6 +11,7 @@ export const bundleTable = authManagerSchema.table('bundle', { createdAt: createdAtColumn, keyVersion: integer(), opaVersion: text().notNull(), + revision: text().notNull(), }); export type Bundle = typeof bundleTable.$inferSelect; diff --git a/packages/auth-core/src/migrations/20260611074358_eager_krista_starr/migration.sql b/packages/auth-core/src/migrations/20260611074358_eager_krista_starr/migration.sql new file mode 100644 index 00000000..8b560cbb --- /dev/null +++ b/packages/auth-core/src/migrations/20260611074358_eager_krista_starr/migration.sql @@ -0,0 +1,5 @@ +ALTER TABLE "auth_manager"."bundle" ADD COLUMN "revision" text; + +UPDATE "auth_manager"."bundle" SET "revision" = environment::text || '-' || id::text; + +ALTER TABLE "auth_manager"."bundle" ALTER COLUMN "revision" SET NOT NULL; diff --git a/packages/auth-core/src/migrations/20260611074358_eager_krista_starr/snapshot.json b/packages/auth-core/src/migrations/20260611074358_eager_krista_starr/snapshot.json new file mode 100644 index 00000000..b0050318 --- /dev/null +++ b/packages/auth-core/src/migrations/20260611074358_eager_krista_starr/snapshot.json @@ -0,0 +1,664 @@ +{ + "version": "8", + "dialect": "postgres", + "id": "0eff498c-954d-4c4b-abf5-3ece226f1e4c", + "prevIds": ["bf848b15-704f-44e1-9541-6173dc6afa43"], + "ddl": [ + { + "name": "auth_manager", + "entityType": "schemas" + }, + { + "values": ["TEST", "TEST_DATA", "POLICY", "DATA"], + "name": "asset_type_enum", + "entityType": "enums", + "schema": "auth_manager" + }, + { + "values": ["np", "stage", "prod"], + "name": "environment_enum", + "entityType": "enums", + "schema": "auth_manager" + }, + { + "isRlsEnabled": false, + "name": "asset", + "entityType": "tables", + "schema": "auth_manager" + }, + { + "isRlsEnabled": false, + "name": "bundle", + "entityType": "tables", + "schema": "auth_manager" + }, + { + "isRlsEnabled": false, + "name": "client", + "entityType": "tables", + "schema": "auth_manager" + }, + { + "isRlsEnabled": false, + "name": "connection", + "entityType": "tables", + "schema": "auth_manager" + }, + { + "isRlsEnabled": false, + "name": "domain", + "entityType": "tables", + "schema": "auth_manager" + }, + { + "isRlsEnabled": false, + "name": "key", + "entityType": "tables", + "schema": "auth_manager" + }, + { + "type": "varchar", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "auth_manager", + "table": "asset" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "version", + "entityType": "columns", + "schema": "auth_manager", + "table": "asset" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "auth_manager", + "table": "asset" + }, + { + "type": "bytea", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "value", + "entityType": "columns", + "schema": "auth_manager", + "table": "asset" + }, + { + "type": "varchar", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "uri", + "entityType": "columns", + "schema": "auth_manager", + "table": "asset" + }, + { + "type": "asset_type_enum", + "typeSchema": "auth_manager", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "type", + "entityType": "columns", + "schema": "auth_manager", + "table": "asset" + }, + { + "type": "environment_enum", + "typeSchema": "auth_manager", + "notNull": true, + "dimensions": 1, + "default": null, + "generated": null, + "identity": null, + "name": "environment", + "entityType": "columns", + "schema": "auth_manager", + "table": "asset" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "is_template", + "entityType": "columns", + "schema": "auth_manager", + "table": "asset" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": { + "type": "always", + "name": "bundle_id_seq", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": 1, + "cycle": false + }, + "name": "id", + "entityType": "columns", + "schema": "auth_manager", + "table": "bundle" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "hash", + "entityType": "columns", + "schema": "auth_manager", + "table": "bundle" + }, + { + "type": "environment_enum", + "typeSchema": "auth_manager", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "environment", + "entityType": "columns", + "schema": "auth_manager", + "table": "bundle" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "metadata", + "entityType": "columns", + "schema": "auth_manager", + "table": "bundle" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "assets", + "entityType": "columns", + "schema": "auth_manager", + "table": "bundle" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "connections", + "entityType": "columns", + "schema": "auth_manager", + "table": "bundle" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "auth_manager", + "table": "bundle" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "key_version", + "entityType": "columns", + "schema": "auth_manager", + "table": "bundle" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "opa_version", + "entityType": "columns", + "schema": "auth_manager", + "table": "bundle" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "revision", + "entityType": "columns", + "schema": "auth_manager", + "table": "bundle" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "auth_manager", + "table": "client" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "heb_name", + "entityType": "columns", + "schema": "auth_manager", + "table": "client" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "description", + "entityType": "columns", + "schema": "auth_manager", + "table": "client" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "branch", + "entityType": "columns", + "schema": "auth_manager", + "table": "client" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "auth_manager", + "table": "client" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "update_at", + "entityType": "columns", + "schema": "auth_manager", + "table": "client" + }, + { + "type": "json", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "tech_point_of_contact", + "entityType": "columns", + "schema": "auth_manager", + "table": "client" + }, + { + "type": "json", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "product_point_of_contact", + "entityType": "columns", + "schema": "auth_manager", + "table": "client" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 1, + "default": null, + "generated": null, + "identity": null, + "name": "tags", + "entityType": "columns", + "schema": "auth_manager", + "table": "client" + }, + { + "type": "varchar", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "auth_manager", + "table": "connection" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "version", + "entityType": "columns", + "schema": "auth_manager", + "table": "connection" + }, + { + "type": "environment_enum", + "typeSchema": "auth_manager", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "environment", + "entityType": "columns", + "schema": "auth_manager", + "table": "connection" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "enabled", + "entityType": "columns", + "schema": "auth_manager", + "table": "connection" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "token", + "entityType": "columns", + "schema": "auth_manager", + "table": "connection" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "allow_no_browser", + "entityType": "columns", + "schema": "auth_manager", + "table": "connection" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "allow_no_origin", + "entityType": "columns", + "schema": "auth_manager", + "table": "connection" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 1, + "default": null, + "generated": null, + "identity": null, + "name": "domains", + "entityType": "columns", + "schema": "auth_manager", + "table": "connection" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 1, + "default": null, + "generated": null, + "identity": null, + "name": "origins", + "entityType": "columns", + "schema": "auth_manager", + "table": "connection" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "auth_manager", + "table": "connection" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "auth_manager", + "table": "domain" + }, + { + "type": "environment_enum", + "typeSchema": "auth_manager", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "environment", + "entityType": "columns", + "schema": "auth_manager", + "table": "key" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "version", + "entityType": "columns", + "schema": "auth_manager", + "table": "key" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "private_key", + "entityType": "columns", + "schema": "auth_manager", + "table": "key" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "public_key", + "entityType": "columns", + "schema": "auth_manager", + "table": "key" + }, + { + "columns": ["name", "version"], + "nameExplicit": true, + "name": "PK_c3670311f777dc6ab9965408f97", + "entityType": "pks", + "schema": "auth_manager", + "table": "asset" + }, + { + "columns": ["name", "version", "environment"], + "nameExplicit": true, + "name": "PK_4c3be048a366c9ce9277bac4c38", + "entityType": "pks", + "schema": "auth_manager", + "table": "connection" + }, + { + "columns": ["environment", "version"], + "nameExplicit": true, + "name": "PK_ddf3d991c46b66651794ee56d58", + "entityType": "pks", + "schema": "auth_manager", + "table": "key" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "bundle_pkey", + "schema": "auth_manager", + "table": "bundle", + "entityType": "pks" + }, + { + "columns": ["name"], + "nameExplicit": false, + "name": "client_pkey", + "schema": "auth_manager", + "table": "client", + "entityType": "pks" + }, + { + "columns": ["name"], + "nameExplicit": false, + "name": "domain_pkey", + "schema": "auth_manager", + "table": "domain", + "entityType": "pks" + } + ], + "renames": [] +} diff --git a/packages/auth-openapi/index.d.ts b/packages/auth-openapi/index.d.ts index cbd411b1..6608d127 100644 --- a/packages/auth-openapi/index.d.ts +++ b/packages/auth-openapi/index.d.ts @@ -440,6 +440,7 @@ export type components = { /** Format: int32 */ id?: number; hash?: string; + revision?: string; metadata?: { [key: string]: unknown; }; diff --git a/packages/auth-openapi/openapi3.yaml b/packages/auth-openapi/openapi3.yaml index 9e3822c7..d3022deb 100644 --- a/packages/auth-openapi/openapi3.yaml +++ b/packages/auth-openapi/openapi3.yaml @@ -1135,6 +1135,8 @@ components: format: int32 hash: type: string + revision: + type: string metadata: type: object additionalProperties: true diff --git a/packages/test-utils/src/fakers.ts b/packages/test-utils/src/fakers.ts index d7c15308..8c8ab12a 100644 --- a/packages/test-utils/src/fakers.ts +++ b/packages/test-utils/src/fakers.ts @@ -58,6 +58,7 @@ export function getFakeBundle(includeCreated?: boolean): Bundle | NewBundle { hash: faker.string.alpha(EIGHT), createdAt: includeCreated === true ? faker.date.past() : undefined, environment: Environment.NP, + revision: `${Environment.NP}-${faker.string.hexadecimal({ length: 12, casing: 'lower', prefix: '' })}`, keyVersion: 1, assets: [{ name: 'aaaa', version: 1 }], connections: [{ name: 'bbb', version: 2 }], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fc59a7b3..c8c7713b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -934,6 +934,9 @@ importers: execa: specifier: ^7.1.1 version: 7.2.0 + fast-json-stable-stringify: + specifier: ^2.1.0 + version: 2.1.0 handlebars: specifier: 4.7.7 version: 4.7.7