@backstage/backend-test-utils 1.7.1-next.0 → 1.9.0-next.0
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.
- package/CHANGELOG.md +45 -0
- package/dist/alpha/services/ActionsRegistryServiceMock.cjs.js.map +1 -1
- package/dist/alpha/services/ActionsServiceMock.cjs.js.map +1 -1
- package/dist/alpha/services/MockActionsRegistry.cjs.js.map +1 -1
- package/dist/alpha/services/simpleMock.cjs.js.map +1 -1
- package/dist/backend-app-api/src/lib/DependencyGraph.cjs.js.map +1 -1
- package/dist/backend-app-api/src/wiring/ServiceRegistry.cjs.js.map +1 -1
- package/dist/cache/TestCaches.cjs.js.map +1 -1
- package/dist/cache/memcache.cjs.js.map +1 -1
- package/dist/cache/redis.cjs.js.map +1 -1
- package/dist/cache/types.cjs.js.map +1 -1
- package/dist/cache/valkey.cjs.js.map +1 -1
- package/dist/config-loader/src/sources/ObservableConfigProxy.cjs.js +123 -0
- package/dist/config-loader/src/sources/ObservableConfigProxy.cjs.js.map +1 -0
- package/dist/database/TestDatabases.cjs.js.map +1 -1
- package/dist/database/mysql.cjs.js.map +1 -1
- package/dist/database/postgres.cjs.js.map +1 -1
- package/dist/database/sqlite.cjs.js.map +1 -1
- package/dist/database/types.cjs.js.map +1 -1
- package/dist/filesystem/MockDirectory.cjs.js.map +1 -1
- package/dist/index.d.ts +20 -5
- package/dist/msw/registerMswTestHooks.cjs.js.map +1 -1
- package/dist/services/MockAuthService.cjs.js.map +1 -1
- package/dist/services/MockEventsService.cjs.js.map +1 -1
- package/dist/services/MockHttpAuthService.cjs.js.map +1 -1
- package/dist/services/MockPermissionsService.cjs.js.map +1 -1
- package/dist/services/MockRootLoggerService.cjs.js.map +1 -1
- package/dist/services/MockSchedulerService.cjs.js +123 -0
- package/dist/services/MockSchedulerService.cjs.js.map +1 -0
- package/dist/services/MockUserInfoService.cjs.js.map +1 -1
- package/dist/services/mockCredentials.cjs.js.map +1 -1
- package/dist/services/mockServices.cjs.js +15 -4
- package/dist/services/mockServices.cjs.js.map +1 -1
- package/dist/services/simpleMock.cjs.js.map +1 -1
- package/dist/util/errorHandler.cjs.js +1 -1
- package/dist/util/errorHandler.cjs.js.map +1 -1
- package/dist/util/getDockerImageForName.cjs.js.map +1 -1
- package/dist/util/isDockerDisabledForTests.cjs.js.map +1 -1
- package/dist/wiring/ServiceFactoryTester.cjs.js.map +1 -1
- package/dist/wiring/TestBackend.cjs.js.map +1 -1
- package/package.json +9 -7
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var config = require('@backstage/config');
|
|
4
|
+
var isEqual = require('lodash/isEqual');
|
|
5
|
+
|
|
6
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
7
|
+
|
|
8
|
+
var isEqual__default = /*#__PURE__*/_interopDefaultCompat(isEqual);
|
|
9
|
+
|
|
10
|
+
class ObservableConfigProxy {
|
|
11
|
+
constructor(parent, parentKey, abortController) {
|
|
12
|
+
this.parent = parent;
|
|
13
|
+
this.parentKey = parentKey;
|
|
14
|
+
this.abortController = abortController;
|
|
15
|
+
if (parent && !parentKey) {
|
|
16
|
+
throw new Error("parentKey is required if parent is set");
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
config = new config.ConfigReader({});
|
|
20
|
+
subscribers = [];
|
|
21
|
+
static create(abortController) {
|
|
22
|
+
return new ObservableConfigProxy(void 0, void 0, abortController);
|
|
23
|
+
}
|
|
24
|
+
setConfig(config) {
|
|
25
|
+
if (this.parent) {
|
|
26
|
+
throw new Error("immutable");
|
|
27
|
+
}
|
|
28
|
+
const changed = !isEqual__default.default(this.config.get(), config.get());
|
|
29
|
+
this.config = config;
|
|
30
|
+
if (changed) {
|
|
31
|
+
for (const subscriber of this.subscribers) {
|
|
32
|
+
try {
|
|
33
|
+
subscriber();
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.error(`Config subscriber threw error, ${error}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
close() {
|
|
41
|
+
if (!this.abortController) {
|
|
42
|
+
throw new Error("Only the root config can be closed");
|
|
43
|
+
}
|
|
44
|
+
this.abortController.abort();
|
|
45
|
+
}
|
|
46
|
+
subscribe(onChange) {
|
|
47
|
+
if (this.parent) {
|
|
48
|
+
return this.parent.subscribe(onChange);
|
|
49
|
+
}
|
|
50
|
+
this.subscribers.push(onChange);
|
|
51
|
+
return {
|
|
52
|
+
unsubscribe: () => {
|
|
53
|
+
const index = this.subscribers.indexOf(onChange);
|
|
54
|
+
if (index >= 0) {
|
|
55
|
+
this.subscribers.splice(index, 1);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
select(required) {
|
|
61
|
+
if (this.parent && this.parentKey) {
|
|
62
|
+
if (required) {
|
|
63
|
+
return this.parent.select(true).getConfig(this.parentKey);
|
|
64
|
+
}
|
|
65
|
+
return this.parent.select(false)?.getOptionalConfig(this.parentKey);
|
|
66
|
+
}
|
|
67
|
+
return this.config;
|
|
68
|
+
}
|
|
69
|
+
has(key) {
|
|
70
|
+
return this.select(false)?.has(key) ?? false;
|
|
71
|
+
}
|
|
72
|
+
keys() {
|
|
73
|
+
return this.select(false)?.keys() ?? [];
|
|
74
|
+
}
|
|
75
|
+
get(key) {
|
|
76
|
+
return this.select(true).get(key);
|
|
77
|
+
}
|
|
78
|
+
getOptional(key) {
|
|
79
|
+
return this.select(false)?.getOptional(key);
|
|
80
|
+
}
|
|
81
|
+
getConfig(key) {
|
|
82
|
+
return new ObservableConfigProxy(this, key);
|
|
83
|
+
}
|
|
84
|
+
getOptionalConfig(key) {
|
|
85
|
+
if (this.select(false)?.has(key)) {
|
|
86
|
+
return new ObservableConfigProxy(this, key);
|
|
87
|
+
}
|
|
88
|
+
return void 0;
|
|
89
|
+
}
|
|
90
|
+
getConfigArray(key) {
|
|
91
|
+
return this.select(true).getConfigArray(key);
|
|
92
|
+
}
|
|
93
|
+
getOptionalConfigArray(key) {
|
|
94
|
+
return this.select(false)?.getOptionalConfigArray(key);
|
|
95
|
+
}
|
|
96
|
+
getNumber(key) {
|
|
97
|
+
return this.select(true).getNumber(key);
|
|
98
|
+
}
|
|
99
|
+
getOptionalNumber(key) {
|
|
100
|
+
return this.select(false)?.getOptionalNumber(key);
|
|
101
|
+
}
|
|
102
|
+
getBoolean(key) {
|
|
103
|
+
return this.select(true).getBoolean(key);
|
|
104
|
+
}
|
|
105
|
+
getOptionalBoolean(key) {
|
|
106
|
+
return this.select(false)?.getOptionalBoolean(key);
|
|
107
|
+
}
|
|
108
|
+
getString(key) {
|
|
109
|
+
return this.select(true).getString(key);
|
|
110
|
+
}
|
|
111
|
+
getOptionalString(key) {
|
|
112
|
+
return this.select(false)?.getOptionalString(key);
|
|
113
|
+
}
|
|
114
|
+
getStringArray(key) {
|
|
115
|
+
return this.select(true).getStringArray(key);
|
|
116
|
+
}
|
|
117
|
+
getOptionalStringArray(key) {
|
|
118
|
+
return this.select(false)?.getOptionalStringArray(key);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
exports.ObservableConfigProxy = ObservableConfigProxy;
|
|
123
|
+
//# sourceMappingURL=ObservableConfigProxy.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ObservableConfigProxy.cjs.js","sources":["../../../../../config-loader/src/sources/ObservableConfigProxy.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config, ConfigReader } from '@backstage/config';\nimport { JsonValue } from '@backstage/types';\nimport isEqual from 'lodash/isEqual';\n\nexport class ObservableConfigProxy implements Config {\n private config: Config = new ConfigReader({});\n\n private readonly subscribers: (() => void)[] = [];\n\n static create(abortController: AbortController): ObservableConfigProxy {\n return new ObservableConfigProxy(undefined, undefined, abortController);\n }\n\n private constructor(\n private readonly parent?: ObservableConfigProxy,\n private readonly parentKey?: string,\n private readonly abortController?: AbortController,\n ) {\n if (parent && !parentKey) {\n throw new Error('parentKey is required if parent is set');\n }\n }\n\n setConfig(config: Config) {\n if (this.parent) {\n throw new Error('immutable');\n }\n\n // We only notify subscribers if the data contents of the config actually\n // changed. If they didn't, there's no point in callers trying to re-read\n // them. However we still want to replace the local config object, since its\n // runtime implementation could be entirely different.\n const changed = !isEqual(this.config.get(), config.get());\n\n this.config = config;\n\n if (changed) {\n for (const subscriber of this.subscribers) {\n try {\n subscriber();\n } catch (error) {\n console.error(`Config subscriber threw error, ${error}`);\n }\n }\n }\n }\n\n close() {\n if (!this.abortController) {\n throw new Error('Only the root config can be closed');\n }\n this.abortController.abort();\n }\n\n subscribe(onChange: () => void): { unsubscribe: () => void } {\n if (this.parent) {\n return this.parent.subscribe(onChange);\n }\n\n this.subscribers.push(onChange);\n return {\n unsubscribe: () => {\n const index = this.subscribers.indexOf(onChange);\n if (index >= 0) {\n this.subscribers.splice(index, 1);\n }\n },\n };\n }\n\n private select(required: true): Config;\n private select(required: false): Config | undefined;\n private select(required: boolean): Config | undefined {\n if (this.parent && this.parentKey) {\n if (required) {\n return this.parent.select(true).getConfig(this.parentKey);\n }\n return this.parent.select(false)?.getOptionalConfig(this.parentKey);\n }\n\n return this.config;\n }\n\n has(key: string): boolean {\n return this.select(false)?.has(key) ?? false;\n }\n keys(): string[] {\n return this.select(false)?.keys() ?? [];\n }\n get<T = JsonValue>(key?: string): T {\n return this.select(true).get(key);\n }\n getOptional<T = JsonValue>(key?: string): T | undefined {\n return this.select(false)?.getOptional(key);\n }\n getConfig(key: string): Config {\n return new ObservableConfigProxy(this, key);\n }\n getOptionalConfig(key: string): Config | undefined {\n if (this.select(false)?.has(key)) {\n return new ObservableConfigProxy(this, key);\n }\n return undefined;\n }\n getConfigArray(key: string): Config[] {\n return this.select(true).getConfigArray(key);\n }\n getOptionalConfigArray(key: string): Config[] | undefined {\n return this.select(false)?.getOptionalConfigArray(key);\n }\n getNumber(key: string): number {\n return this.select(true).getNumber(key);\n }\n getOptionalNumber(key: string): number | undefined {\n return this.select(false)?.getOptionalNumber(key);\n }\n getBoolean(key: string): boolean {\n return this.select(true).getBoolean(key);\n }\n getOptionalBoolean(key: string): boolean | undefined {\n return this.select(false)?.getOptionalBoolean(key);\n }\n getString(key: string): string {\n return this.select(true).getString(key);\n }\n getOptionalString(key: string): string | undefined {\n return this.select(false)?.getOptionalString(key);\n }\n getStringArray(key: string): string[] {\n return this.select(true).getStringArray(key);\n }\n getOptionalStringArray(key: string): string[] | undefined {\n return this.select(false)?.getOptionalStringArray(key);\n }\n}\n"],"names":["ConfigReader","isEqual"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TestDatabases.cjs.js","sources":["../../src/database/TestDatabases.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Knex } from 'knex';\nimport { isDockerDisabledForTests } from '../util/isDockerDisabledForTests';\nimport { MysqlEngine } from './mysql';\nimport { PostgresEngine } from './postgres';\nimport { SqliteEngine } from './sqlite';\nimport {\n Engine,\n TestDatabaseId,\n TestDatabaseProperties,\n allDatabases,\n} from './types';\n\n/**\n * Encapsulates the creation of ephemeral test database instances for use\n * inside unit or integration tests.\n *\n * @public\n */\nexport class TestDatabases {\n private readonly engineFactoryByDriver: Record<\n string,\n (properties: TestDatabaseProperties) => Promise<Engine>\n > = {\n pg: PostgresEngine.create,\n mysql: MysqlEngine.create,\n mysql2: MysqlEngine.create,\n 'better-sqlite3': SqliteEngine.create,\n sqlite3: SqliteEngine.create,\n };\n private readonly engineByTestDatabaseId: Map<string, Engine>;\n private readonly supportedIds: TestDatabaseId[];\n private static defaultIds?: TestDatabaseId[];\n\n /**\n * Creates an empty `TestDatabases` instance, and sets up Jest to clean up\n * all of its acquired resources after all tests finish.\n *\n * You typically want to create just a single instance like this at the top\n * of your test file or `describe` block, and then call `init` many times on\n * that instance inside the individual tests. Spinning up a \"physical\"\n * database instance takes a considerable amount of time, slowing down tests.\n * But initializing a new logical database inside that instance using `init`\n * is very fast.\n */\n static create(options?: {\n ids?: TestDatabaseId[];\n disableDocker?: boolean;\n }): TestDatabases {\n const ids = options?.ids;\n const disableDocker = options?.disableDocker ?? isDockerDisabledForTests();\n\n let testDatabaseIds: TestDatabaseId[];\n if (ids) {\n testDatabaseIds = ids;\n } else if (TestDatabases.defaultIds) {\n testDatabaseIds = TestDatabases.defaultIds;\n } else {\n testDatabaseIds = Object.keys(allDatabases) as TestDatabaseId[];\n }\n\n const supportedIds = testDatabaseIds.filter(id => {\n const properties = allDatabases[id];\n if (!properties) {\n return false;\n }\n // If the caller has set up the env with an explicit connection string,\n // we'll assume that this database will work\n if (\n properties.connectionStringEnvironmentVariableName &&\n process.env[properties.connectionStringEnvironmentVariableName]\n ) {\n return true;\n }\n // If the database doesn't require docker at all, there's nothing to worry\n // about\n if (!properties.dockerImageName) {\n return true;\n }\n // If the database requires docker, but docker is disabled, we will fail.\n if (disableDocker) {\n return false;\n }\n return true;\n });\n\n const databases = new TestDatabases(supportedIds);\n\n if (supportedIds.length > 0) {\n afterAll(async () => {\n await databases.shutdown();\n });\n }\n\n return databases;\n }\n\n static setDefaults(options: { ids?: TestDatabaseId[] }) {\n TestDatabases.defaultIds = options.ids;\n }\n\n private constructor(supportedIds: TestDatabaseId[]) {\n this.engineByTestDatabaseId = new Map();\n this.supportedIds = supportedIds;\n }\n\n supports(id: TestDatabaseId): boolean {\n return this.supportedIds.includes(id);\n }\n\n eachSupportedId(): [TestDatabaseId][] {\n return this.supportedIds.map(id => [id]);\n }\n\n /**\n * Returns a fresh, unique, empty logical database on an instance of the\n * given database ID platform.\n *\n * @param id - The ID of the database platform to use, e.g. 'POSTGRES_13'\n * @returns A `Knex` connection object\n */\n async init(id: TestDatabaseId): Promise<Knex> {\n const properties = allDatabases[id];\n if (!properties) {\n const candidates = Object.keys(allDatabases).join(', ');\n throw new Error(\n `Unknown test database ${id}, possible values are ${candidates}`,\n );\n }\n if (!this.supportedIds.includes(id)) {\n const candidates = this.supportedIds.join(', ');\n throw new Error(\n `Unsupported test database ${id} for this environment, possible values are ${candidates}`,\n );\n }\n\n let engine = this.engineByTestDatabaseId.get(id);\n if (!engine) {\n const factory = this.engineFactoryByDriver[properties.driver];\n if (!factory) {\n throw new Error(`Unknown database driver ${properties.driver}`);\n }\n engine = await factory(properties);\n this.engineByTestDatabaseId.set(id, engine);\n }\n\n return await engine.createDatabaseInstance();\n }\n\n private async shutdown() {\n const engines = [...this.engineByTestDatabaseId.values()];\n this.engineByTestDatabaseId.clear();\n\n for (const engine of engines) {\n try {\n await engine.shutdown();\n } catch (error) {\n console.warn(`TestDatabases: Failed to shutdown engine`, {\n engine,\n error,\n });\n }\n }\n }\n}\n"],"names":["PostgresEngine","MysqlEngine","SqliteEngine","isDockerDisabledForTests","allDatabases"],"mappings":";;;;;;;;AAkCO,MAAM,
|
|
1
|
+
{"version":3,"file":"TestDatabases.cjs.js","sources":["../../src/database/TestDatabases.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Knex } from 'knex';\nimport { isDockerDisabledForTests } from '../util/isDockerDisabledForTests';\nimport { MysqlEngine } from './mysql';\nimport { PostgresEngine } from './postgres';\nimport { SqliteEngine } from './sqlite';\nimport {\n Engine,\n TestDatabaseId,\n TestDatabaseProperties,\n allDatabases,\n} from './types';\n\n/**\n * Encapsulates the creation of ephemeral test database instances for use\n * inside unit or integration tests.\n *\n * @public\n */\nexport class TestDatabases {\n private readonly engineFactoryByDriver: Record<\n string,\n (properties: TestDatabaseProperties) => Promise<Engine>\n > = {\n pg: PostgresEngine.create,\n mysql: MysqlEngine.create,\n mysql2: MysqlEngine.create,\n 'better-sqlite3': SqliteEngine.create,\n sqlite3: SqliteEngine.create,\n };\n private readonly engineByTestDatabaseId: Map<string, Engine>;\n private readonly supportedIds: TestDatabaseId[];\n private static defaultIds?: TestDatabaseId[];\n\n /**\n * Creates an empty `TestDatabases` instance, and sets up Jest to clean up\n * all of its acquired resources after all tests finish.\n *\n * You typically want to create just a single instance like this at the top\n * of your test file or `describe` block, and then call `init` many times on\n * that instance inside the individual tests. Spinning up a \"physical\"\n * database instance takes a considerable amount of time, slowing down tests.\n * But initializing a new logical database inside that instance using `init`\n * is very fast.\n */\n static create(options?: {\n ids?: TestDatabaseId[];\n disableDocker?: boolean;\n }): TestDatabases {\n const ids = options?.ids;\n const disableDocker = options?.disableDocker ?? isDockerDisabledForTests();\n\n let testDatabaseIds: TestDatabaseId[];\n if (ids) {\n testDatabaseIds = ids;\n } else if (TestDatabases.defaultIds) {\n testDatabaseIds = TestDatabases.defaultIds;\n } else {\n testDatabaseIds = Object.keys(allDatabases) as TestDatabaseId[];\n }\n\n const supportedIds = testDatabaseIds.filter(id => {\n const properties = allDatabases[id];\n if (!properties) {\n return false;\n }\n // If the caller has set up the env with an explicit connection string,\n // we'll assume that this database will work\n if (\n properties.connectionStringEnvironmentVariableName &&\n process.env[properties.connectionStringEnvironmentVariableName]\n ) {\n return true;\n }\n // If the database doesn't require docker at all, there's nothing to worry\n // about\n if (!properties.dockerImageName) {\n return true;\n }\n // If the database requires docker, but docker is disabled, we will fail.\n if (disableDocker) {\n return false;\n }\n return true;\n });\n\n const databases = new TestDatabases(supportedIds);\n\n if (supportedIds.length > 0) {\n afterAll(async () => {\n await databases.shutdown();\n });\n }\n\n return databases;\n }\n\n static setDefaults(options: { ids?: TestDatabaseId[] }) {\n TestDatabases.defaultIds = options.ids;\n }\n\n private constructor(supportedIds: TestDatabaseId[]) {\n this.engineByTestDatabaseId = new Map();\n this.supportedIds = supportedIds;\n }\n\n supports(id: TestDatabaseId): boolean {\n return this.supportedIds.includes(id);\n }\n\n eachSupportedId(): [TestDatabaseId][] {\n return this.supportedIds.map(id => [id]);\n }\n\n /**\n * Returns a fresh, unique, empty logical database on an instance of the\n * given database ID platform.\n *\n * @param id - The ID of the database platform to use, e.g. 'POSTGRES_13'\n * @returns A `Knex` connection object\n */\n async init(id: TestDatabaseId): Promise<Knex> {\n const properties = allDatabases[id];\n if (!properties) {\n const candidates = Object.keys(allDatabases).join(', ');\n throw new Error(\n `Unknown test database ${id}, possible values are ${candidates}`,\n );\n }\n if (!this.supportedIds.includes(id)) {\n const candidates = this.supportedIds.join(', ');\n throw new Error(\n `Unsupported test database ${id} for this environment, possible values are ${candidates}`,\n );\n }\n\n let engine = this.engineByTestDatabaseId.get(id);\n if (!engine) {\n const factory = this.engineFactoryByDriver[properties.driver];\n if (!factory) {\n throw new Error(`Unknown database driver ${properties.driver}`);\n }\n engine = await factory(properties);\n this.engineByTestDatabaseId.set(id, engine);\n }\n\n return await engine.createDatabaseInstance();\n }\n\n private async shutdown() {\n const engines = [...this.engineByTestDatabaseId.values()];\n this.engineByTestDatabaseId.clear();\n\n for (const engine of engines) {\n try {\n await engine.shutdown();\n } catch (error) {\n console.warn(`TestDatabases: Failed to shutdown engine`, {\n engine,\n error,\n });\n }\n }\n }\n}\n"],"names":["PostgresEngine","MysqlEngine","SqliteEngine","isDockerDisabledForTests","allDatabases"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mysql.cjs.js","sources":["../../src/database/mysql.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { stringifyError } from '@backstage/errors';\nimport { randomBytes } from 'crypto';\nimport knexFactory, { Knex } from 'knex';\nimport { v4 as uuid } from 'uuid';\nimport yn from 'yn';\nimport { Engine, LARGER_POOL_CONFIG, TestDatabaseProperties } from './types';\n\nasync function waitForMysqlReady(\n connection: Knex.MySqlConnectionConfig,\n): Promise<void> {\n const startTime = Date.now();\n\n let lastError: Error | undefined;\n let attempts = 0;\n for (;;) {\n attempts += 1;\n\n let knex: Knex | undefined;\n try {\n knex = knexFactory({\n client: 'mysql2',\n connection: {\n // make a copy because the driver mutates this\n ...connection,\n },\n });\n const result = await knex.select(knex.raw('version() AS version'));\n if (Array.isArray(result) && result[0]?.version) {\n return;\n }\n } catch (e) {\n lastError = e;\n } finally {\n await knex?.destroy();\n }\n\n if (Date.now() - startTime > 30_000) {\n throw new Error(\n `Timed out waiting for the database to be ready for connections, ${attempts} attempts, ${\n lastError\n ? `last error was ${stringifyError(lastError)}`\n : '(no errors thrown)'\n }`,\n );\n }\n\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n}\n\nexport async function startMysqlContainer(image: string): Promise<{\n connection: Knex.MySqlConnectionConfig;\n stopContainer: () => Promise<void>;\n}> {\n const user = 'root';\n const password = uuid();\n\n // Lazy-load to avoid side-effect of importing testcontainers\n const { GenericContainer } =\n require('testcontainers') as typeof import('testcontainers');\n\n const container = await new GenericContainer(image)\n .withExposedPorts(3306)\n .withEnvironment({ MYSQL_ROOT_PASSWORD: password })\n .withTmpFs({ '/var/lib/mysql': 'rw' })\n .start();\n\n const host = container.getHost();\n const port = container.getMappedPort(3306);\n const connection = { host, port, user, password };\n const stopContainer = async () => {\n await container.stop({ timeout: 10_000 });\n };\n\n await waitForMysqlReady(connection);\n\n return { connection, stopContainer };\n}\n\nexport function parseMysqlConnectionString(\n connectionString: string,\n): Knex.MySqlConnectionConfig {\n try {\n const {\n protocol,\n username,\n password,\n port,\n hostname,\n pathname,\n searchParams,\n } = new URL(connectionString);\n\n if (protocol !== 'mysql:') {\n throw new Error(`Unknown protocol ${protocol}`);\n } else if (!username || !password) {\n throw new Error(`Missing username/password`);\n } else if (!pathname.match(/^\\/[^/]+$/)) {\n throw new Error(`Expected single path segment`);\n }\n\n const result: Knex.MySqlConnectionConfig = {\n user: username,\n password,\n host: hostname,\n port: Number(port || 3306),\n database: decodeURIComponent(pathname.substring(1)),\n };\n\n const ssl = searchParams.get('ssl');\n if (ssl) {\n result.ssl = ssl;\n }\n\n const debug = searchParams.get('debug');\n if (debug) {\n result.debug = yn(debug);\n }\n\n return result;\n } catch (e) {\n throw new Error(`Error while parsing MySQL connection string, ${e}`, e);\n }\n}\n\nexport class MysqlEngine implements Engine {\n static async create(\n properties: TestDatabaseProperties,\n ): Promise<MysqlEngine> {\n const { connectionStringEnvironmentVariableName, dockerImageName } =\n properties;\n\n if (connectionStringEnvironmentVariableName) {\n const connectionString =\n process.env[connectionStringEnvironmentVariableName];\n if (connectionString) {\n const connection = parseMysqlConnectionString(connectionString);\n return new MysqlEngine(\n properties,\n connection as Knex.MySqlConnectionConfig,\n );\n }\n }\n\n if (dockerImageName) {\n const { connection, stopContainer } = await startMysqlContainer(\n dockerImageName,\n );\n return new MysqlEngine(properties, connection, stopContainer);\n }\n\n throw new Error(`Test databasee for ${properties.name} not configured`);\n }\n\n readonly #properties: TestDatabaseProperties;\n readonly #connection: Knex.MySqlConnectionConfig;\n readonly #knexInstances: Knex[];\n readonly #databaseNames: string[];\n readonly #stopContainer?: () => Promise<void>;\n\n constructor(\n properties: TestDatabaseProperties,\n connection: Knex.MySqlConnectionConfig,\n stopContainer?: () => Promise<void>,\n ) {\n this.#properties = properties;\n this.#connection = connection;\n this.#knexInstances = [];\n this.#databaseNames = [];\n this.#stopContainer = stopContainer;\n }\n\n async createDatabaseInstance(): Promise<Knex> {\n const adminConnection = this.#connectAdmin();\n try {\n const databaseName = `db${randomBytes(16).toString('hex')}`;\n\n await adminConnection.raw('CREATE DATABASE ??', [databaseName]);\n this.#databaseNames.push(databaseName);\n\n const knexInstance = knexFactory({\n client: this.#properties.driver,\n connection: {\n ...this.#connection,\n database: databaseName,\n },\n ...LARGER_POOL_CONFIG,\n });\n this.#knexInstances.push(knexInstance);\n\n return knexInstance;\n } finally {\n await adminConnection.destroy();\n }\n }\n\n async shutdown(): Promise<void> {\n for (const instance of this.#knexInstances) {\n await instance.destroy();\n }\n\n const adminConnection = this.#connectAdmin();\n try {\n for (const databaseName of this.#databaseNames) {\n await adminConnection.raw('DROP DATABASE ??', [databaseName]);\n }\n } finally {\n await adminConnection.destroy();\n }\n\n await this.#stopContainer?.();\n }\n\n #connectAdmin(): Knex {\n const connection = {\n ...this.#connection,\n database: null as unknown as string,\n };\n return knexFactory({\n client: this.#properties.driver,\n connection,\n pool: {\n min: 0,\n max: 1,\n acquireTimeoutMillis: 20_000,\n createTimeoutMillis: 20_000,\n createRetryIntervalMillis: 1_000,\n },\n });\n }\n}\n"],"names":["knexFactory","stringifyError","uuid","yn","randomBytes","LARGER_POOL_CONFIG"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mysql.cjs.js","sources":["../../src/database/mysql.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { stringifyError } from '@backstage/errors';\nimport { randomBytes } from 'crypto';\nimport knexFactory, { Knex } from 'knex';\nimport { v4 as uuid } from 'uuid';\nimport yn from 'yn';\nimport { Engine, LARGER_POOL_CONFIG, TestDatabaseProperties } from './types';\n\nasync function waitForMysqlReady(\n connection: Knex.MySqlConnectionConfig,\n): Promise<void> {\n const startTime = Date.now();\n\n let lastError: Error | undefined;\n let attempts = 0;\n for (;;) {\n attempts += 1;\n\n let knex: Knex | undefined;\n try {\n knex = knexFactory({\n client: 'mysql2',\n connection: {\n // make a copy because the driver mutates this\n ...connection,\n },\n });\n const result = await knex.select(knex.raw('version() AS version'));\n if (Array.isArray(result) && result[0]?.version) {\n return;\n }\n } catch (e) {\n lastError = e;\n } finally {\n await knex?.destroy();\n }\n\n if (Date.now() - startTime > 30_000) {\n throw new Error(\n `Timed out waiting for the database to be ready for connections, ${attempts} attempts, ${\n lastError\n ? `last error was ${stringifyError(lastError)}`\n : '(no errors thrown)'\n }`,\n );\n }\n\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n}\n\nexport async function startMysqlContainer(image: string): Promise<{\n connection: Knex.MySqlConnectionConfig;\n stopContainer: () => Promise<void>;\n}> {\n const user = 'root';\n const password = uuid();\n\n // Lazy-load to avoid side-effect of importing testcontainers\n const { GenericContainer } =\n require('testcontainers') as typeof import('testcontainers');\n\n const container = await new GenericContainer(image)\n .withExposedPorts(3306)\n .withEnvironment({ MYSQL_ROOT_PASSWORD: password })\n .withTmpFs({ '/var/lib/mysql': 'rw' })\n .start();\n\n const host = container.getHost();\n const port = container.getMappedPort(3306);\n const connection = { host, port, user, password };\n const stopContainer = async () => {\n await container.stop({ timeout: 10_000 });\n };\n\n await waitForMysqlReady(connection);\n\n return { connection, stopContainer };\n}\n\nexport function parseMysqlConnectionString(\n connectionString: string,\n): Knex.MySqlConnectionConfig {\n try {\n const {\n protocol,\n username,\n password,\n port,\n hostname,\n pathname,\n searchParams,\n } = new URL(connectionString);\n\n if (protocol !== 'mysql:') {\n throw new Error(`Unknown protocol ${protocol}`);\n } else if (!username || !password) {\n throw new Error(`Missing username/password`);\n } else if (!pathname.match(/^\\/[^/]+$/)) {\n throw new Error(`Expected single path segment`);\n }\n\n const result: Knex.MySqlConnectionConfig = {\n user: username,\n password,\n host: hostname,\n port: Number(port || 3306),\n database: decodeURIComponent(pathname.substring(1)),\n };\n\n const ssl = searchParams.get('ssl');\n if (ssl) {\n result.ssl = ssl;\n }\n\n const debug = searchParams.get('debug');\n if (debug) {\n result.debug = yn(debug);\n }\n\n return result;\n } catch (e) {\n throw new Error(`Error while parsing MySQL connection string, ${e}`, e);\n }\n}\n\nexport class MysqlEngine implements Engine {\n static async create(\n properties: TestDatabaseProperties,\n ): Promise<MysqlEngine> {\n const { connectionStringEnvironmentVariableName, dockerImageName } =\n properties;\n\n if (connectionStringEnvironmentVariableName) {\n const connectionString =\n process.env[connectionStringEnvironmentVariableName];\n if (connectionString) {\n const connection = parseMysqlConnectionString(connectionString);\n return new MysqlEngine(\n properties,\n connection as Knex.MySqlConnectionConfig,\n );\n }\n }\n\n if (dockerImageName) {\n const { connection, stopContainer } = await startMysqlContainer(\n dockerImageName,\n );\n return new MysqlEngine(properties, connection, stopContainer);\n }\n\n throw new Error(`Test databasee for ${properties.name} not configured`);\n }\n\n readonly #properties: TestDatabaseProperties;\n readonly #connection: Knex.MySqlConnectionConfig;\n readonly #knexInstances: Knex[];\n readonly #databaseNames: string[];\n readonly #stopContainer?: () => Promise<void>;\n\n constructor(\n properties: TestDatabaseProperties,\n connection: Knex.MySqlConnectionConfig,\n stopContainer?: () => Promise<void>,\n ) {\n this.#properties = properties;\n this.#connection = connection;\n this.#knexInstances = [];\n this.#databaseNames = [];\n this.#stopContainer = stopContainer;\n }\n\n async createDatabaseInstance(): Promise<Knex> {\n const adminConnection = this.#connectAdmin();\n try {\n const databaseName = `db${randomBytes(16).toString('hex')}`;\n\n await adminConnection.raw('CREATE DATABASE ??', [databaseName]);\n this.#databaseNames.push(databaseName);\n\n const knexInstance = knexFactory({\n client: this.#properties.driver,\n connection: {\n ...this.#connection,\n database: databaseName,\n },\n ...LARGER_POOL_CONFIG,\n });\n this.#knexInstances.push(knexInstance);\n\n return knexInstance;\n } finally {\n await adminConnection.destroy();\n }\n }\n\n async shutdown(): Promise<void> {\n for (const instance of this.#knexInstances) {\n await instance.destroy();\n }\n\n const adminConnection = this.#connectAdmin();\n try {\n for (const databaseName of this.#databaseNames) {\n await adminConnection.raw('DROP DATABASE ??', [databaseName]);\n }\n } finally {\n await adminConnection.destroy();\n }\n\n await this.#stopContainer?.();\n }\n\n #connectAdmin(): Knex {\n const connection = {\n ...this.#connection,\n database: null as unknown as string,\n };\n return knexFactory({\n client: this.#properties.driver,\n connection,\n pool: {\n min: 0,\n max: 1,\n acquireTimeoutMillis: 20_000,\n createTimeoutMillis: 20_000,\n createRetryIntervalMillis: 1_000,\n },\n });\n }\n}\n"],"names":["knexFactory","stringifyError","uuid","yn","randomBytes","LARGER_POOL_CONFIG"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres.cjs.js","sources":["../../src/database/postgres.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { stringifyError } from '@backstage/errors';\nimport { randomBytes } from 'crypto';\nimport knexFactory, { Knex } from 'knex';\nimport { parse as parsePgConnectionString } from 'pg-connection-string';\nimport { v4 as uuid } from 'uuid';\nimport { Engine, LARGER_POOL_CONFIG, TestDatabaseProperties } from './types';\n\nasync function waitForPostgresReady(\n connection: Knex.PgConnectionConfig,\n): Promise<void> {\n const startTime = Date.now();\n\n let lastError: Error | undefined;\n let attempts = 0;\n for (;;) {\n attempts += 1;\n\n let knex: Knex | undefined;\n try {\n knex = knexFactory({\n client: 'pg',\n connection: {\n // make a copy because the driver mutates this\n ...connection,\n },\n });\n const result = await knex.select(knex.raw('version()'));\n if (Array.isArray(result) && result[0]?.version) {\n return;\n }\n } catch (e) {\n lastError = e;\n } finally {\n await knex?.destroy();\n }\n\n if (Date.now() - startTime > 30_000) {\n throw new Error(\n `Timed out waiting for the database to be ready for connections, ${attempts} attempts, ${\n lastError\n ? `last error was ${stringifyError(lastError)}`\n : '(no errors thrown)'\n }`,\n );\n }\n\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n}\n\nexport async function startPostgresContainer(image: string): Promise<{\n connection: Knex.PgConnectionConfig;\n stopContainer: () => Promise<void>;\n}> {\n const user = 'postgres';\n const password = uuid();\n\n // Lazy-load to avoid side-effect of importing testcontainers\n const { GenericContainer } =\n require('testcontainers') as typeof import('testcontainers');\n\n const container = await new GenericContainer(image)\n .withExposedPorts(5432)\n .withEnvironment({ POSTGRES_PASSWORD: password })\n .withTmpFs({ '/var/lib/postgresql/data': 'rw' })\n .start();\n\n const host = container.getHost();\n const port = container.getMappedPort(5432);\n const connection = { host, port, user, password };\n const stopContainer = async () => {\n await container.stop({ timeout: 10_000 });\n };\n\n await waitForPostgresReady(connection);\n\n return { connection, stopContainer };\n}\n\nexport class PostgresEngine implements Engine {\n static async create(\n properties: TestDatabaseProperties,\n ): Promise<PostgresEngine> {\n const { connectionStringEnvironmentVariableName, dockerImageName } =\n properties;\n\n if (connectionStringEnvironmentVariableName) {\n const connectionString =\n process.env[connectionStringEnvironmentVariableName];\n if (connectionString) {\n const connection = parsePgConnectionString(connectionString);\n return new PostgresEngine(\n properties,\n connection as Knex.PgConnectionConfig,\n );\n }\n }\n\n if (dockerImageName) {\n const { connection, stopContainer } = await startPostgresContainer(\n dockerImageName,\n );\n return new PostgresEngine(properties, connection, stopContainer);\n }\n\n throw new Error(`Test databasee for ${properties.name} not configured`);\n }\n\n readonly #properties: TestDatabaseProperties;\n readonly #connection: Knex.PgConnectionConfig;\n readonly #knexInstances: Knex[];\n readonly #databaseNames: string[];\n readonly #stopContainer?: () => Promise<void>;\n\n constructor(\n properties: TestDatabaseProperties,\n connection: Knex.PgConnectionConfig,\n stopContainer?: () => Promise<void>,\n ) {\n this.#properties = properties;\n this.#connection = connection;\n this.#knexInstances = [];\n this.#databaseNames = [];\n this.#stopContainer = stopContainer;\n }\n\n async createDatabaseInstance(): Promise<Knex> {\n const adminConnection = this.#connectAdmin();\n try {\n const databaseName = `db${randomBytes(16).toString('hex')}`;\n\n await adminConnection.raw('CREATE DATABASE ??', [databaseName]);\n this.#databaseNames.push(databaseName);\n\n const knexInstance = knexFactory({\n client: this.#properties.driver,\n connection: {\n ...this.#connection,\n database: databaseName,\n },\n ...LARGER_POOL_CONFIG,\n });\n this.#knexInstances.push(knexInstance);\n\n return knexInstance;\n } finally {\n await adminConnection.destroy();\n }\n }\n\n async shutdown(): Promise<void> {\n for (const instance of this.#knexInstances) {\n await instance.destroy();\n }\n\n const adminConnection = this.#connectAdmin();\n try {\n for (const databaseName of this.#databaseNames) {\n await adminConnection.raw('DROP DATABASE ??', [databaseName]);\n }\n } finally {\n await adminConnection.destroy();\n }\n\n await this.#stopContainer?.();\n }\n\n #connectAdmin(): Knex {\n return knexFactory({\n client: this.#properties.driver,\n connection: {\n ...this.#connection,\n database: 'postgres',\n },\n pool: {\n acquireTimeoutMillis: 10000,\n },\n });\n }\n}\n"],"names":["knexFactory","stringifyError","uuid","parsePgConnectionString","randomBytes","LARGER_POOL_CONFIG"],"mappings":";;;;;;;;;;;;;AAuBA,eAAe,qBACb,
|
|
1
|
+
{"version":3,"file":"postgres.cjs.js","sources":["../../src/database/postgres.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { stringifyError } from '@backstage/errors';\nimport { randomBytes } from 'crypto';\nimport knexFactory, { Knex } from 'knex';\nimport { parse as parsePgConnectionString } from 'pg-connection-string';\nimport { v4 as uuid } from 'uuid';\nimport { Engine, LARGER_POOL_CONFIG, TestDatabaseProperties } from './types';\n\nasync function waitForPostgresReady(\n connection: Knex.PgConnectionConfig,\n): Promise<void> {\n const startTime = Date.now();\n\n let lastError: Error | undefined;\n let attempts = 0;\n for (;;) {\n attempts += 1;\n\n let knex: Knex | undefined;\n try {\n knex = knexFactory({\n client: 'pg',\n connection: {\n // make a copy because the driver mutates this\n ...connection,\n },\n });\n const result = await knex.select(knex.raw('version()'));\n if (Array.isArray(result) && result[0]?.version) {\n return;\n }\n } catch (e) {\n lastError = e;\n } finally {\n await knex?.destroy();\n }\n\n if (Date.now() - startTime > 30_000) {\n throw new Error(\n `Timed out waiting for the database to be ready for connections, ${attempts} attempts, ${\n lastError\n ? `last error was ${stringifyError(lastError)}`\n : '(no errors thrown)'\n }`,\n );\n }\n\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n}\n\nexport async function startPostgresContainer(image: string): Promise<{\n connection: Knex.PgConnectionConfig;\n stopContainer: () => Promise<void>;\n}> {\n const user = 'postgres';\n const password = uuid();\n\n // Lazy-load to avoid side-effect of importing testcontainers\n const { GenericContainer } =\n require('testcontainers') as typeof import('testcontainers');\n\n const container = await new GenericContainer(image)\n .withExposedPorts(5432)\n .withEnvironment({ POSTGRES_PASSWORD: password })\n .withTmpFs({ '/var/lib/postgresql/data': 'rw' })\n .start();\n\n const host = container.getHost();\n const port = container.getMappedPort(5432);\n const connection = { host, port, user, password };\n const stopContainer = async () => {\n await container.stop({ timeout: 10_000 });\n };\n\n await waitForPostgresReady(connection);\n\n return { connection, stopContainer };\n}\n\nexport class PostgresEngine implements Engine {\n static async create(\n properties: TestDatabaseProperties,\n ): Promise<PostgresEngine> {\n const { connectionStringEnvironmentVariableName, dockerImageName } =\n properties;\n\n if (connectionStringEnvironmentVariableName) {\n const connectionString =\n process.env[connectionStringEnvironmentVariableName];\n if (connectionString) {\n const connection = parsePgConnectionString(connectionString);\n return new PostgresEngine(\n properties,\n connection as Knex.PgConnectionConfig,\n );\n }\n }\n\n if (dockerImageName) {\n const { connection, stopContainer } = await startPostgresContainer(\n dockerImageName,\n );\n return new PostgresEngine(properties, connection, stopContainer);\n }\n\n throw new Error(`Test databasee for ${properties.name} not configured`);\n }\n\n readonly #properties: TestDatabaseProperties;\n readonly #connection: Knex.PgConnectionConfig;\n readonly #knexInstances: Knex[];\n readonly #databaseNames: string[];\n readonly #stopContainer?: () => Promise<void>;\n\n constructor(\n properties: TestDatabaseProperties,\n connection: Knex.PgConnectionConfig,\n stopContainer?: () => Promise<void>,\n ) {\n this.#properties = properties;\n this.#connection = connection;\n this.#knexInstances = [];\n this.#databaseNames = [];\n this.#stopContainer = stopContainer;\n }\n\n async createDatabaseInstance(): Promise<Knex> {\n const adminConnection = this.#connectAdmin();\n try {\n const databaseName = `db${randomBytes(16).toString('hex')}`;\n\n await adminConnection.raw('CREATE DATABASE ??', [databaseName]);\n this.#databaseNames.push(databaseName);\n\n const knexInstance = knexFactory({\n client: this.#properties.driver,\n connection: {\n ...this.#connection,\n database: databaseName,\n },\n ...LARGER_POOL_CONFIG,\n });\n this.#knexInstances.push(knexInstance);\n\n return knexInstance;\n } finally {\n await adminConnection.destroy();\n }\n }\n\n async shutdown(): Promise<void> {\n for (const instance of this.#knexInstances) {\n await instance.destroy();\n }\n\n const adminConnection = this.#connectAdmin();\n try {\n for (const databaseName of this.#databaseNames) {\n await adminConnection.raw('DROP DATABASE ??', [databaseName]);\n }\n } finally {\n await adminConnection.destroy();\n }\n\n await this.#stopContainer?.();\n }\n\n #connectAdmin(): Knex {\n return knexFactory({\n client: this.#properties.driver,\n connection: {\n ...this.#connection,\n database: 'postgres',\n },\n pool: {\n acquireTimeoutMillis: 10000,\n },\n });\n }\n}\n"],"names":["knexFactory","stringifyError","uuid","parsePgConnectionString","randomBytes","LARGER_POOL_CONFIG"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.cjs.js","sources":["../../src/database/sqlite.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport knexFactory, { Knex } from 'knex';\nimport { Engine, TestDatabaseProperties } from './types';\n\nexport class SqliteEngine implements Engine {\n static async create(\n properties: TestDatabaseProperties,\n ): Promise<SqliteEngine> {\n return new SqliteEngine(properties);\n }\n\n readonly #properties: TestDatabaseProperties;\n readonly #instances: Knex[];\n\n constructor(properties: TestDatabaseProperties) {\n this.#properties = properties;\n this.#instances = [];\n }\n\n async createDatabaseInstance(): Promise<Knex> {\n const instance = knexFactory({\n client: this.#properties.driver,\n connection: ':memory:',\n useNullAsDefault: true,\n });\n\n instance.client.pool.on('createSuccess', (_eventId: any, resource: any) => {\n resource.run('PRAGMA foreign_keys = ON', () => {});\n });\n\n this.#instances.push(instance);\n return instance;\n }\n\n async shutdown(): Promise<void> {\n for (const instance of this.#instances) {\n await instance.destroy();\n }\n }\n}\n"],"names":["knexFactory"],"mappings":";;;;;;;;AAmBO,MAAM,
|
|
1
|
+
{"version":3,"file":"sqlite.cjs.js","sources":["../../src/database/sqlite.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport knexFactory, { Knex } from 'knex';\nimport { Engine, TestDatabaseProperties } from './types';\n\nexport class SqliteEngine implements Engine {\n static async create(\n properties: TestDatabaseProperties,\n ): Promise<SqliteEngine> {\n return new SqliteEngine(properties);\n }\n\n readonly #properties: TestDatabaseProperties;\n readonly #instances: Knex[];\n\n constructor(properties: TestDatabaseProperties) {\n this.#properties = properties;\n this.#instances = [];\n }\n\n async createDatabaseInstance(): Promise<Knex> {\n const instance = knexFactory({\n client: this.#properties.driver,\n connection: ':memory:',\n useNullAsDefault: true,\n });\n\n instance.client.pool.on('createSuccess', (_eventId: any, resource: any) => {\n resource.run('PRAGMA foreign_keys = ON', () => {});\n });\n\n this.#instances.push(instance);\n return instance;\n }\n\n async shutdown(): Promise<void> {\n for (const instance of this.#instances) {\n await instance.destroy();\n }\n }\n}\n"],"names":["knexFactory"],"mappings":";;;;;;;;AAmBO,MAAM,YAAA,CAA+B;AAAA,EAC1C,aAAa,OACX,UAAA,EACuB;AACvB,IAAA,OAAO,IAAI,aAAa,UAAU,CAAA;AAAA,EACpC;AAAA,EAES,WAAA;AAAA,EACA,UAAA;AAAA,EAET,YAAY,UAAA,EAAoC;AAC9C,IAAA,IAAA,CAAK,WAAA,GAAc,UAAA;AACnB,IAAA,IAAA,CAAK,aAAa,EAAC;AAAA,EACrB;AAAA,EAEA,MAAM,sBAAA,GAAwC;AAC5C,IAAA,MAAM,WAAWA,4BAAA,CAAY;AAAA,MAC3B,MAAA,EAAQ,KAAK,WAAA,CAAY,MAAA;AAAA,MACzB,UAAA,EAAY,UAAA;AAAA,MACZ,gBAAA,EAAkB;AAAA,KACnB,CAAA;AAED,IAAA,QAAA,CAAS,OAAO,IAAA,CAAK,EAAA,CAAG,eAAA,EAAiB,CAAC,UAAe,QAAA,KAAkB;AACzE,MAAA,QAAA,CAAS,GAAA,CAAI,4BAA4B,MAAM;AAAA,MAAC,CAAC,CAAA;AAAA,IACnD,CAAC,CAAA;AAED,IAAA,IAAA,CAAK,UAAA,CAAW,KAAK,QAAQ,CAAA;AAC7B,IAAA,OAAO,QAAA;AAAA,EACT;AAAA,EAEA,MAAM,QAAA,GAA0B;AAC9B,IAAA,KAAA,MAAW,QAAA,IAAY,KAAK,UAAA,EAAY;AACtC,MAAA,MAAM,SAAS,OAAA,EAAQ;AAAA,IACzB;AAAA,EACF;AACF;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.cjs.js","sources":["../../src/database/types.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Knex } from 'knex';\nimport { getDockerImageForName } from '../util/getDockerImageForName';\n\nexport interface Engine {\n createDatabaseInstance(): Promise<Knex>;\n shutdown(): Promise<void>;\n}\n\n/**\n * The possible databases to test against.\n *\n * @public\n */\nexport type TestDatabaseId =\n | 'POSTGRES_17'\n | 'POSTGRES_16'\n | 'POSTGRES_15'\n | 'POSTGRES_14'\n | 'POSTGRES_13'\n | 'POSTGRES_12'\n | 'POSTGRES_11'\n | 'POSTGRES_9'\n | 'MYSQL_8'\n | 'SQLITE_3';\n\nexport type TestDatabaseProperties = {\n name: string;\n driver: string;\n dockerImageName?: string;\n connectionStringEnvironmentVariableName?: string;\n};\n\nexport const allDatabases: Record<TestDatabaseId, TestDatabaseProperties> =\n Object.freeze({\n POSTGRES_17: {\n name: 'Postgres 17.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:17'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES17_CONNECTION_STRING',\n },\n POSTGRES_16: {\n name: 'Postgres 16.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:16'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES16_CONNECTION_STRING',\n },\n POSTGRES_15: {\n name: 'Postgres 15.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:15'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES15_CONNECTION_STRING',\n },\n POSTGRES_14: {\n name: 'Postgres 14.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:14'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES14_CONNECTION_STRING',\n },\n POSTGRES_13: {\n name: 'Postgres 13.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:13'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES13_CONNECTION_STRING',\n },\n POSTGRES_12: {\n name: 'Postgres 12.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:12'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES12_CONNECTION_STRING',\n },\n POSTGRES_11: {\n name: 'Postgres 11.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:11'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES11_CONNECTION_STRING',\n },\n POSTGRES_9: {\n name: 'Postgres 9.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:9'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES9_CONNECTION_STRING',\n },\n MYSQL_8: {\n name: 'MySQL 8.x',\n driver: 'mysql2',\n dockerImageName: getDockerImageForName('mysql:8'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_MYSQL8_CONNECTION_STRING',\n },\n SQLITE_3: {\n name: 'SQLite 3.x',\n driver: 'better-sqlite3',\n },\n });\n\nexport const LARGER_POOL_CONFIG = {\n pool: {\n min: 0,\n max: 50,\n },\n};\n"],"names":["getDockerImageForName"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"types.cjs.js","sources":["../../src/database/types.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Knex } from 'knex';\nimport { getDockerImageForName } from '../util/getDockerImageForName';\n\nexport interface Engine {\n createDatabaseInstance(): Promise<Knex>;\n shutdown(): Promise<void>;\n}\n\n/**\n * The possible databases to test against.\n *\n * @public\n */\nexport type TestDatabaseId =\n | 'POSTGRES_17'\n | 'POSTGRES_16'\n | 'POSTGRES_15'\n | 'POSTGRES_14'\n | 'POSTGRES_13'\n | 'POSTGRES_12'\n | 'POSTGRES_11'\n | 'POSTGRES_9'\n | 'MYSQL_8'\n | 'SQLITE_3';\n\nexport type TestDatabaseProperties = {\n name: string;\n driver: string;\n dockerImageName?: string;\n connectionStringEnvironmentVariableName?: string;\n};\n\nexport const allDatabases: Record<TestDatabaseId, TestDatabaseProperties> =\n Object.freeze({\n POSTGRES_17: {\n name: 'Postgres 17.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:17'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES17_CONNECTION_STRING',\n },\n POSTGRES_16: {\n name: 'Postgres 16.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:16'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES16_CONNECTION_STRING',\n },\n POSTGRES_15: {\n name: 'Postgres 15.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:15'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES15_CONNECTION_STRING',\n },\n POSTGRES_14: {\n name: 'Postgres 14.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:14'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES14_CONNECTION_STRING',\n },\n POSTGRES_13: {\n name: 'Postgres 13.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:13'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES13_CONNECTION_STRING',\n },\n POSTGRES_12: {\n name: 'Postgres 12.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:12'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES12_CONNECTION_STRING',\n },\n POSTGRES_11: {\n name: 'Postgres 11.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:11'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES11_CONNECTION_STRING',\n },\n POSTGRES_9: {\n name: 'Postgres 9.x',\n driver: 'pg',\n dockerImageName: getDockerImageForName('postgres:9'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_POSTGRES9_CONNECTION_STRING',\n },\n MYSQL_8: {\n name: 'MySQL 8.x',\n driver: 'mysql2',\n dockerImageName: getDockerImageForName('mysql:8'),\n connectionStringEnvironmentVariableName:\n 'BACKSTAGE_TEST_DATABASE_MYSQL8_CONNECTION_STRING',\n },\n SQLITE_3: {\n name: 'SQLite 3.x',\n driver: 'better-sqlite3',\n },\n });\n\nexport const LARGER_POOL_CONFIG = {\n pool: {\n min: 0,\n max: 50,\n },\n};\n"],"names":["getDockerImageForName"],"mappings":";;;;AAgDO,MAAM,YAAA,GACX,OAAO,MAAA,CAAO;AAAA,EACZ,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,aAAa,CAAA;AAAA,IACpD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,UAAA,EAAY;AAAA,IACV,IAAA,EAAM,cAAA;AAAA,IACN,MAAA,EAAQ,IAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,YAAY,CAAA;AAAA,IACnD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,OAAA,EAAS;AAAA,IACP,IAAA,EAAM,WAAA;AAAA,IACN,MAAA,EAAQ,QAAA;AAAA,IACR,eAAA,EAAiBA,4CAAsB,SAAS,CAAA;AAAA,IAChD,uCAAA,EACE;AAAA,GACJ;AAAA,EACA,QAAA,EAAU;AAAA,IACR,IAAA,EAAM,YAAA;AAAA,IACN,MAAA,EAAQ;AAAA;AAEZ,CAAC;AAEI,MAAM,kBAAA,GAAqB;AAAA,EAChC,IAAA,EAAM;AAAA,IACJ,GAAA,EAAK,CAAA;AAAA,IACL,GAAA,EAAK;AAAA;AAET;;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MockDirectory.cjs.js","sources":["../../src/filesystem/MockDirectory.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport os from 'os';\nimport { isChildPath } from '@backstage/backend-plugin-api';\nimport fs from 'fs-extra';\nimport textextensions from 'textextensions';\nimport {\n dirname,\n extname,\n join as joinPath,\n resolve as resolvePath,\n relative as relativePath,\n win32,\n posix,\n} from 'path';\n\nconst tmpdirMarker = Symbol('os-tmpdir-mock');\n\n/**\n * A context that allows for more advanced file system operations when writing mock directory content.\n *\n * @public\n */\nexport interface MockDirectoryContentCallbackContext {\n /** Absolute path to the location of this piece of content on the filesystem */\n path: string;\n\n /** Creates a symbolic link at the current location */\n symlink(target: string): void;\n}\n\n/**\n * A callback that allows for more advanced file system operations when writing mock directory content.\n *\n * @public\n */\nexport type MockDirectoryContentCallback = (\n ctx: MockDirectoryContentCallbackContext,\n) => void;\n\n/**\n * The content of a mock directory represented by a nested object structure.\n *\n * @remarks\n *\n * When used as input, the keys may contain forward slashes to indicate nested directories.\n * Then returned as output, each directory will always be represented as a separate object.\n *\n * @example\n * ```ts\n * {\n * 'test.txt': 'content',\n * 'sub-dir': {\n * 'file.txt': 'content',\n * 'nested-dir/file.txt': 'content',\n * },\n * 'empty-dir': {},\n * 'binary-file': Buffer.from([0, 1, 2]),\n * }\n * ```\n *\n * @public\n */\nexport type MockDirectoryContent = {\n [name in string]:\n | MockDirectoryContent\n | string\n | Buffer\n | MockDirectoryContentCallback;\n};\n\n/**\n * Options for {@link MockDirectory.content}.\n *\n * @public\n */\nexport interface MockDirectoryContentOptions {\n /**\n * The path to read content from. Defaults to the root of the mock directory.\n *\n * An absolute path can also be provided, as long as it is a child path of the mock directory.\n */\n path?: string;\n\n /**\n * Whether or not to return files as text rather than buffers.\n *\n * Defaults to checking the file extension against a list of known text extensions.\n */\n shouldReadAsText?: boolean | ((path: string, buffer: Buffer) => boolean);\n}\n\n/**\n * A utility for creating a mock directory that is automatically cleaned up.\n *\n * @public\n */\nexport interface MockDirectory {\n /**\n * The path to the root of the mock directory\n */\n readonly path: string;\n\n /**\n * Resolves a path relative to the root of the mock directory.\n */\n resolve(...paths: string[]): string;\n\n /**\n * Sets the content of the mock directory. This will remove any existing content.\n *\n * @example\n * ```ts\n * mockDir.setContent({\n * 'test.txt': 'content',\n * 'sub-dir': {\n * 'file.txt': 'content',\n * 'nested-dir/file.txt': 'content',\n * },\n * 'empty-dir': {},\n * 'binary-file': Buffer.from([0, 1, 2]),\n * });\n * ```\n */\n setContent(root: MockDirectoryContent): void;\n\n /**\n * Adds content of the mock directory. This will overwrite existing files.\n *\n * @example\n * ```ts\n * mockDir.addContent({\n * 'test.txt': 'content',\n * 'sub-dir': {\n * 'file.txt': 'content',\n * 'nested-dir/file.txt': 'content',\n * },\n * 'empty-dir': {},\n * 'binary-file': Buffer.from([0, 1, 2]),\n * });\n * ```\n */\n addContent(root: MockDirectoryContent): void;\n\n /**\n * Reads the content of the mock directory.\n *\n * @remarks\n *\n * Text files will be returned as strings, while binary files will be returned as buffers.\n * By default the file extension is used to determine whether a file should be read as text.\n *\n * @example\n * ```ts\n * expect(mockDir.content()).toEqual({\n * 'test.txt': 'content',\n * 'sub-dir': {\n * 'file.txt': 'content',\n * 'nested-dir': {\n * 'file.txt': 'content',\n * },\n * },\n * 'empty-dir': {},\n * 'binary-file': Buffer.from([0, 1, 2]),\n * });\n * ```\n */\n content(\n options?: MockDirectoryContentOptions,\n ): MockDirectoryContent | undefined;\n\n /**\n * Clears the content of the mock directory, ensuring that the directory itself exists.\n */\n clear(): void;\n\n /**\n * Removes the mock directory and all its contents.\n */\n remove(): void;\n}\n\n/** @internal */\ntype MockEntry =\n | {\n type: 'file';\n path: string;\n content: Buffer;\n }\n | {\n type: 'dir';\n path: string;\n }\n | {\n type: 'callback';\n path: string;\n callback: MockDirectoryContentCallback;\n };\n\n/** @internal */\nclass MockDirectoryImpl {\n readonly #root: string;\n\n constructor(root: string) {\n this.#root = root;\n }\n\n get path(): string {\n return this.#root;\n }\n\n resolve(...paths: string[]): string {\n return resolvePath(this.#root, ...paths);\n }\n\n setContent(root: MockDirectoryContent): void {\n this.remove();\n\n return this.addContent(root);\n }\n\n addContent(root: MockDirectoryContent): void {\n const entries = this.#transformInput(root);\n\n for (const entry of entries) {\n const fullPath = resolvePath(this.#root, entry.path);\n if (!isChildPath(this.#root, fullPath)) {\n throw new Error(\n `Provided path must resolve to a child path of the mock directory, got '${fullPath}'`,\n );\n }\n\n if (entry.type === 'dir') {\n fs.ensureDirSync(fullPath);\n } else if (entry.type === 'file') {\n fs.ensureDirSync(dirname(fullPath));\n fs.writeFileSync(fullPath, entry.content);\n } else if (entry.type === 'callback') {\n fs.ensureDirSync(dirname(fullPath));\n entry.callback({\n path: fullPath,\n symlink(target: string) {\n fs.symlinkSync(target, fullPath);\n },\n });\n }\n }\n }\n\n content(\n options?: MockDirectoryContentOptions,\n ): MockDirectoryContent | undefined {\n const shouldReadAsText =\n (typeof options?.shouldReadAsText === 'boolean'\n ? () => options?.shouldReadAsText\n : options?.shouldReadAsText) ??\n ((path: string) => textextensions.includes(extname(path).slice(1)));\n\n const root = resolvePath(this.#root, options?.path ?? '');\n if (!isChildPath(this.#root, root)) {\n throw new Error(\n `Provided path must resolve to a child path of the mock directory, got '${root}'`,\n );\n }\n\n function read(path: string): MockDirectoryContent | undefined {\n if (!fs.pathExistsSync(path)) {\n return undefined;\n }\n\n const entries = fs.readdirSync(path, { withFileTypes: true });\n return Object.fromEntries(\n entries.map(entry => {\n const fullPath = resolvePath(path, entry.name);\n\n if (entry.isDirectory()) {\n return [entry.name, read(fullPath)];\n }\n const content = fs.readFileSync(fullPath);\n const relativePosixPath = relativePath(root, fullPath)\n .split(win32.sep)\n .join(posix.sep);\n\n if (shouldReadAsText(relativePosixPath, content)) {\n return [entry.name, content.toString('utf8')];\n }\n return [entry.name, content];\n }),\n );\n }\n\n return read(root);\n }\n\n clear = (): void => {\n this.setContent({});\n };\n\n remove = (): void => {\n fs.rmSync(this.#root, { recursive: true, force: true, maxRetries: 10 });\n };\n\n #transformInput(input: MockDirectoryContent[string]): MockEntry[] {\n const entries: MockEntry[] = [];\n\n function traverse(node: MockDirectoryContent[string], path: string) {\n if (typeof node === 'string') {\n entries.push({\n type: 'file',\n path,\n content: Buffer.from(node, 'utf8'),\n });\n } else if (node instanceof Buffer) {\n entries.push({ type: 'file', path, content: node });\n } else if (typeof node === 'function') {\n entries.push({ type: 'callback', path, callback: node });\n } else {\n entries.push({ type: 'dir', path });\n for (const [name, child] of Object.entries(node)) {\n traverse(child, path ? `${path}/${name}` : name);\n }\n }\n }\n\n traverse(input, '');\n\n return entries;\n }\n}\n\n/**\n * Options for {@link createMockDirectory}.\n *\n * @public\n */\nexport interface CreateMockDirectoryOptions {\n /**\n * In addition to creating a temporary directory, also mock `os.tmpdir()` to\n * return the mock directory path until the end of the test suite.\n *\n * When this option is provided the `createMockDirectory` call must happen in\n * a scope where calling `afterAll` from Jest is allowed\n *\n * @returns\n */\n mockOsTmpDir?: boolean;\n\n /**\n * Initializes the directory with the given content, see {@link MockDirectory.setContent}.\n */\n content?: MockDirectoryContent;\n}\n\nconst cleanupCallbacks = new Array<() => void>();\n\nlet registered = false;\nfunction registerTestHooks() {\n if (typeof afterAll !== 'function') {\n return;\n }\n if (registered) {\n return;\n }\n registered = true;\n\n afterAll(async () => {\n for (const callback of cleanupCallbacks) {\n try {\n callback();\n } catch (error) {\n console.error(\n `Failed to clean up mock directory after tests, ${error}`,\n );\n }\n }\n cleanupCallbacks.length = 0;\n });\n}\n\nregisterTestHooks();\n\n/**\n * Creates a new temporary mock directory that will be removed after the tests have completed.\n *\n * @public\n * @remarks\n *\n * This method is intended to be called outside of any test, either at top-level or\n * within a `describe` block. It will call `afterAll` to make sure that the mock directory\n * is removed after the tests have run.\n *\n * @example\n * ```ts\n * describe('MySubject', () => {\n * const mockDir = createMockDirectory();\n *\n * beforeEach(mockDir.clear);\n *\n * it('should work', () => {\n * // ... use mockDir\n * })\n * })\n * ```\n */\nexport function createMockDirectory(\n options?: CreateMockDirectoryOptions,\n): MockDirectory {\n const tmpDir = process.env.RUNNER_TEMP || os.tmpdir(); // GitHub Actions\n const root = fs.mkdtempSync(joinPath(tmpDir, 'backstage-tmp-test-dir-'));\n\n const mocker = new MockDirectoryImpl(root);\n\n const origTmpdir = options?.mockOsTmpDir ? os.tmpdir : undefined;\n if (origTmpdir) {\n if (Object.hasOwn(origTmpdir, tmpdirMarker)) {\n throw new Error(\n 'Cannot mock os.tmpdir() when it has already been mocked',\n );\n }\n const mock = Object.assign(() => mocker.path, { [tmpdirMarker]: true });\n os.tmpdir = mock;\n }\n\n // In CI we expect there to be no need to clean up temporary directories\n const needsCleanup = !process.env.CI;\n if (needsCleanup) {\n process.on('beforeExit', mocker.remove);\n }\n\n if (needsCleanup) {\n cleanupCallbacks.push(() => mocker.remove());\n }\n\n if (origTmpdir) {\n afterAll(() => {\n os.tmpdir = origTmpdir;\n });\n }\n\n if (options?.content) {\n mocker.setContent(options.content);\n }\n\n return mocker;\n}\n"],"names":["resolvePath","isChildPath","fs","dirname","path","textextensions","extname","relativePath","win32","posix","os","joinPath"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MockDirectory.cjs.js","sources":["../../src/filesystem/MockDirectory.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport os from 'os';\nimport { isChildPath } from '@backstage/backend-plugin-api';\nimport fs from 'fs-extra';\nimport textextensions from 'textextensions';\nimport {\n dirname,\n extname,\n join as joinPath,\n resolve as resolvePath,\n relative as relativePath,\n win32,\n posix,\n} from 'path';\n\nconst tmpdirMarker = Symbol('os-tmpdir-mock');\n\n/**\n * A context that allows for more advanced file system operations when writing mock directory content.\n *\n * @public\n */\nexport interface MockDirectoryContentCallbackContext {\n /** Absolute path to the location of this piece of content on the filesystem */\n path: string;\n\n /** Creates a symbolic link at the current location */\n symlink(target: string): void;\n}\n\n/**\n * A callback that allows for more advanced file system operations when writing mock directory content.\n *\n * @public\n */\nexport type MockDirectoryContentCallback = (\n ctx: MockDirectoryContentCallbackContext,\n) => void;\n\n/**\n * The content of a mock directory represented by a nested object structure.\n *\n * @remarks\n *\n * When used as input, the keys may contain forward slashes to indicate nested directories.\n * Then returned as output, each directory will always be represented as a separate object.\n *\n * @example\n * ```ts\n * {\n * 'test.txt': 'content',\n * 'sub-dir': {\n * 'file.txt': 'content',\n * 'nested-dir/file.txt': 'content',\n * },\n * 'empty-dir': {},\n * 'binary-file': Buffer.from([0, 1, 2]),\n * }\n * ```\n *\n * @public\n */\nexport type MockDirectoryContent = {\n [name in string]:\n | MockDirectoryContent\n | string\n | Buffer\n | MockDirectoryContentCallback;\n};\n\n/**\n * Options for {@link MockDirectory.content}.\n *\n * @public\n */\nexport interface MockDirectoryContentOptions {\n /**\n * The path to read content from. Defaults to the root of the mock directory.\n *\n * An absolute path can also be provided, as long as it is a child path of the mock directory.\n */\n path?: string;\n\n /**\n * Whether or not to return files as text rather than buffers.\n *\n * Defaults to checking the file extension against a list of known text extensions.\n */\n shouldReadAsText?: boolean | ((path: string, buffer: Buffer) => boolean);\n}\n\n/**\n * A utility for creating a mock directory that is automatically cleaned up.\n *\n * @public\n */\nexport interface MockDirectory {\n /**\n * The path to the root of the mock directory\n */\n readonly path: string;\n\n /**\n * Resolves a path relative to the root of the mock directory.\n */\n resolve(...paths: string[]): string;\n\n /**\n * Sets the content of the mock directory. This will remove any existing content.\n *\n * @example\n * ```ts\n * mockDir.setContent({\n * 'test.txt': 'content',\n * 'sub-dir': {\n * 'file.txt': 'content',\n * 'nested-dir/file.txt': 'content',\n * },\n * 'empty-dir': {},\n * 'binary-file': Buffer.from([0, 1, 2]),\n * });\n * ```\n */\n setContent(root: MockDirectoryContent): void;\n\n /**\n * Adds content of the mock directory. This will overwrite existing files.\n *\n * @example\n * ```ts\n * mockDir.addContent({\n * 'test.txt': 'content',\n * 'sub-dir': {\n * 'file.txt': 'content',\n * 'nested-dir/file.txt': 'content',\n * },\n * 'empty-dir': {},\n * 'binary-file': Buffer.from([0, 1, 2]),\n * });\n * ```\n */\n addContent(root: MockDirectoryContent): void;\n\n /**\n * Reads the content of the mock directory.\n *\n * @remarks\n *\n * Text files will be returned as strings, while binary files will be returned as buffers.\n * By default the file extension is used to determine whether a file should be read as text.\n *\n * @example\n * ```ts\n * expect(mockDir.content()).toEqual({\n * 'test.txt': 'content',\n * 'sub-dir': {\n * 'file.txt': 'content',\n * 'nested-dir': {\n * 'file.txt': 'content',\n * },\n * },\n * 'empty-dir': {},\n * 'binary-file': Buffer.from([0, 1, 2]),\n * });\n * ```\n */\n content(\n options?: MockDirectoryContentOptions,\n ): MockDirectoryContent | undefined;\n\n /**\n * Clears the content of the mock directory, ensuring that the directory itself exists.\n */\n clear(): void;\n\n /**\n * Removes the mock directory and all its contents.\n */\n remove(): void;\n}\n\n/** @internal */\ntype MockEntry =\n | {\n type: 'file';\n path: string;\n content: Buffer;\n }\n | {\n type: 'dir';\n path: string;\n }\n | {\n type: 'callback';\n path: string;\n callback: MockDirectoryContentCallback;\n };\n\n/** @internal */\nclass MockDirectoryImpl {\n readonly #root: string;\n\n constructor(root: string) {\n this.#root = root;\n }\n\n get path(): string {\n return this.#root;\n }\n\n resolve(...paths: string[]): string {\n return resolvePath(this.#root, ...paths);\n }\n\n setContent(root: MockDirectoryContent): void {\n this.remove();\n\n return this.addContent(root);\n }\n\n addContent(root: MockDirectoryContent): void {\n const entries = this.#transformInput(root);\n\n for (const entry of entries) {\n const fullPath = resolvePath(this.#root, entry.path);\n if (!isChildPath(this.#root, fullPath)) {\n throw new Error(\n `Provided path must resolve to a child path of the mock directory, got '${fullPath}'`,\n );\n }\n\n if (entry.type === 'dir') {\n fs.ensureDirSync(fullPath);\n } else if (entry.type === 'file') {\n fs.ensureDirSync(dirname(fullPath));\n fs.writeFileSync(fullPath, entry.content);\n } else if (entry.type === 'callback') {\n fs.ensureDirSync(dirname(fullPath));\n entry.callback({\n path: fullPath,\n symlink(target: string) {\n fs.symlinkSync(target, fullPath);\n },\n });\n }\n }\n }\n\n content(\n options?: MockDirectoryContentOptions,\n ): MockDirectoryContent | undefined {\n const shouldReadAsText =\n (typeof options?.shouldReadAsText === 'boolean'\n ? () => options?.shouldReadAsText\n : options?.shouldReadAsText) ??\n ((path: string) => textextensions.includes(extname(path).slice(1)));\n\n const root = resolvePath(this.#root, options?.path ?? '');\n if (!isChildPath(this.#root, root)) {\n throw new Error(\n `Provided path must resolve to a child path of the mock directory, got '${root}'`,\n );\n }\n\n function read(path: string): MockDirectoryContent | undefined {\n if (!fs.pathExistsSync(path)) {\n return undefined;\n }\n\n const entries = fs.readdirSync(path, { withFileTypes: true });\n return Object.fromEntries(\n entries.map(entry => {\n const fullPath = resolvePath(path, entry.name);\n\n if (entry.isDirectory()) {\n return [entry.name, read(fullPath)];\n }\n const content = fs.readFileSync(fullPath);\n const relativePosixPath = relativePath(root, fullPath)\n .split(win32.sep)\n .join(posix.sep);\n\n if (shouldReadAsText(relativePosixPath, content)) {\n return [entry.name, content.toString('utf8')];\n }\n return [entry.name, content];\n }),\n );\n }\n\n return read(root);\n }\n\n clear = (): void => {\n this.setContent({});\n };\n\n remove = (): void => {\n fs.rmSync(this.#root, { recursive: true, force: true, maxRetries: 10 });\n };\n\n #transformInput(input: MockDirectoryContent[string]): MockEntry[] {\n const entries: MockEntry[] = [];\n\n function traverse(node: MockDirectoryContent[string], path: string) {\n if (typeof node === 'string') {\n entries.push({\n type: 'file',\n path,\n content: Buffer.from(node, 'utf8'),\n });\n } else if (node instanceof Buffer) {\n entries.push({ type: 'file', path, content: node });\n } else if (typeof node === 'function') {\n entries.push({ type: 'callback', path, callback: node });\n } else {\n entries.push({ type: 'dir', path });\n for (const [name, child] of Object.entries(node)) {\n traverse(child, path ? `${path}/${name}` : name);\n }\n }\n }\n\n traverse(input, '');\n\n return entries;\n }\n}\n\n/**\n * Options for {@link createMockDirectory}.\n *\n * @public\n */\nexport interface CreateMockDirectoryOptions {\n /**\n * In addition to creating a temporary directory, also mock `os.tmpdir()` to\n * return the mock directory path until the end of the test suite.\n *\n * When this option is provided the `createMockDirectory` call must happen in\n * a scope where calling `afterAll` from Jest is allowed\n *\n * @returns\n */\n mockOsTmpDir?: boolean;\n\n /**\n * Initializes the directory with the given content, see {@link MockDirectory.setContent}.\n */\n content?: MockDirectoryContent;\n}\n\nconst cleanupCallbacks = new Array<() => void>();\n\nlet registered = false;\nfunction registerTestHooks() {\n if (typeof afterAll !== 'function') {\n return;\n }\n if (registered) {\n return;\n }\n registered = true;\n\n afterAll(async () => {\n for (const callback of cleanupCallbacks) {\n try {\n callback();\n } catch (error) {\n console.error(\n `Failed to clean up mock directory after tests, ${error}`,\n );\n }\n }\n cleanupCallbacks.length = 0;\n });\n}\n\nregisterTestHooks();\n\n/**\n * Creates a new temporary mock directory that will be removed after the tests have completed.\n *\n * @public\n * @remarks\n *\n * This method is intended to be called outside of any test, either at top-level or\n * within a `describe` block. It will call `afterAll` to make sure that the mock directory\n * is removed after the tests have run.\n *\n * @example\n * ```ts\n * describe('MySubject', () => {\n * const mockDir = createMockDirectory();\n *\n * beforeEach(mockDir.clear);\n *\n * it('should work', () => {\n * // ... use mockDir\n * })\n * })\n * ```\n */\nexport function createMockDirectory(\n options?: CreateMockDirectoryOptions,\n): MockDirectory {\n const tmpDir = process.env.RUNNER_TEMP || os.tmpdir(); // GitHub Actions\n const root = fs.mkdtempSync(joinPath(tmpDir, 'backstage-tmp-test-dir-'));\n\n const mocker = new MockDirectoryImpl(root);\n\n const origTmpdir = options?.mockOsTmpDir ? os.tmpdir : undefined;\n if (origTmpdir) {\n if (Object.hasOwn(origTmpdir, tmpdirMarker)) {\n throw new Error(\n 'Cannot mock os.tmpdir() when it has already been mocked',\n );\n }\n const mock = Object.assign(() => mocker.path, { [tmpdirMarker]: true });\n os.tmpdir = mock;\n }\n\n // In CI we expect there to be no need to clean up temporary directories\n const needsCleanup = !process.env.CI;\n if (needsCleanup) {\n process.on('beforeExit', mocker.remove);\n }\n\n if (needsCleanup) {\n cleanupCallbacks.push(() => mocker.remove());\n }\n\n if (origTmpdir) {\n afterAll(() => {\n os.tmpdir = origTmpdir;\n });\n }\n\n if (options?.content) {\n mocker.setContent(options.content);\n }\n\n return mocker;\n}\n"],"names":["resolvePath","isChildPath","fs","dirname","path","textextensions","extname","relativePath","win32","posix","os","joinPath"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Keyv from 'keyv';
|
|
2
2
|
import { Knex } from 'knex';
|
|
3
3
|
import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
|
|
4
|
-
import { ServiceFactory, RootConfigService, LoggerService, AuthService, DiscoveryService, BackstageCredentials, HttpAuthService, BackstageUserInfo, UserInfoService, DatabaseService, PermissionsService, BackstageNonePrincipal, BackstageUserPrincipal, BackstagePrincipalAccessRestrictions, BackstageServicePrincipal, ServiceRef, ExtensionPoint, BackendFeature } from '@backstage/backend-plugin-api';
|
|
4
|
+
import { ServiceFactory, RootConfigService, LoggerService, AuthService, DiscoveryService, BackstageCredentials, HttpAuthService, BackstageUserInfo, UserInfoService, DatabaseService, PermissionsService, SchedulerService, BackstageNonePrincipal, BackstageUserPrincipal, BackstagePrincipalAccessRestrictions, BackstageServicePrincipal, ServiceRef, ExtensionPoint, BackendFeature } from '@backstage/backend-plugin-api';
|
|
5
5
|
import { EventsService } from '@backstage/plugin-events-node';
|
|
6
6
|
import { AuthorizeResult } from '@backstage/plugin-permission-common';
|
|
7
7
|
import { JsonObject } from '@backstage/types';
|
|
@@ -366,7 +366,11 @@ type ServiceMock<TService> = {
|
|
|
366
366
|
* ```
|
|
367
367
|
*/
|
|
368
368
|
declare namespace mockServices {
|
|
369
|
-
function rootConfig(options?: rootConfig.Options): RootConfigService
|
|
369
|
+
function rootConfig(options?: rootConfig.Options): RootConfigService & {
|
|
370
|
+
update(options: {
|
|
371
|
+
data: JsonObject;
|
|
372
|
+
}): void;
|
|
373
|
+
};
|
|
370
374
|
namespace rootConfig {
|
|
371
375
|
type Options = {
|
|
372
376
|
data?: JsonObject;
|
|
@@ -537,9 +541,14 @@ declare namespace mockServices {
|
|
|
537
541
|
const factory: () => ServiceFactory<_backstage_backend_plugin_api.RootLifecycleService, "root", "singleton">;
|
|
538
542
|
const mock: (partialImpl?: Partial<_backstage_backend_plugin_api.RootLifecycleService> | undefined) => ServiceMock<_backstage_backend_plugin_api.RootLifecycleService>;
|
|
539
543
|
}
|
|
544
|
+
function scheduler(): SchedulerService;
|
|
540
545
|
namespace scheduler {
|
|
541
|
-
const factory: (
|
|
542
|
-
|
|
546
|
+
const factory: (options?: {
|
|
547
|
+
skipTaskRunOnStartup?: boolean;
|
|
548
|
+
includeManualTasksOnStartup?: boolean;
|
|
549
|
+
includeInitialDelayedTasksOnStartup?: boolean;
|
|
550
|
+
}) => ServiceFactory<SchedulerService, "plugin", "singleton">;
|
|
551
|
+
const mock: (partialImpl?: Partial<SchedulerService> | undefined) => ServiceMock<SchedulerService>;
|
|
543
552
|
}
|
|
544
553
|
namespace urlReader {
|
|
545
554
|
const factory: () => ServiceFactory<_backstage_backend_plugin_api.UrlReaderService, "plugin", "singleton">;
|
|
@@ -762,7 +771,13 @@ declare function startTestBackend<TExtensionPoints extends any[]>(options: TestB
|
|
|
762
771
|
/**
|
|
763
772
|
* A mock for error handler middleware that can be used in router tests.
|
|
764
773
|
* @public
|
|
774
|
+
*
|
|
775
|
+
* @example
|
|
776
|
+
* ```ts
|
|
777
|
+
* const app = express();
|
|
778
|
+
* app.use(mockErrorHandler());
|
|
779
|
+
* ```
|
|
765
780
|
*/
|
|
766
|
-
declare function mockErrorHandler(): express.ErrorRequestHandler<express_serve_static_core.ParamsDictionary, any, any, qs.ParsedQs, Record<string, any>>;
|
|
781
|
+
declare function mockErrorHandler(_options?: {}, ..._args: never[]): express.ErrorRequestHandler<express_serve_static_core.ParamsDictionary, any, any, qs.ParsedQs, Record<string, any>>;
|
|
767
782
|
|
|
768
783
|
export { type CreateMockDirectoryOptions, type MockDirectory, type MockDirectoryContent, type MockDirectoryContentCallback, type MockDirectoryContentCallbackContext, type MockDirectoryContentOptions, ServiceFactoryTester, type ServiceFactoryTesterOptions, type ServiceMock, type TestBackend, type TestBackendOptions, type TestCacheId, TestCaches, type TestDatabaseId, TestDatabases, createMockDirectory, mockCredentials, mockErrorHandler, mockServices, registerMswTestHooks, startTestBackend };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registerMswTestHooks.cjs.js","sources":["../../src/msw/registerMswTestHooks.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Sets up handlers for request mocking\n * @public\n * @param worker - service worker\n */\nexport function registerMswTestHooks(worker: {\n listen: (t: any) => void;\n close: () => void;\n resetHandlers: () => void;\n}) {\n beforeAll(() => worker.listen({ onUnhandledRequest: 'error' }));\n afterAll(() => worker.close());\n afterEach(() => worker.resetHandlers());\n}\n"],"names":[],"mappings":";;AAqBO,SAAS,qBAAqB,
|
|
1
|
+
{"version":3,"file":"registerMswTestHooks.cjs.js","sources":["../../src/msw/registerMswTestHooks.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Sets up handlers for request mocking\n * @public\n * @param worker - service worker\n */\nexport function registerMswTestHooks(worker: {\n listen: (t: any) => void;\n close: () => void;\n resetHandlers: () => void;\n}) {\n beforeAll(() => worker.listen({ onUnhandledRequest: 'error' }));\n afterAll(() => worker.close());\n afterEach(() => worker.resetHandlers());\n}\n"],"names":[],"mappings":";;AAqBO,SAAS,qBAAqB,MAAA,EAIlC;AACD,EAAA,SAAA,CAAU,MAAM,MAAA,CAAO,MAAA,CAAO,EAAE,kBAAA,EAAoB,OAAA,EAAS,CAAC,CAAA;AAC9D,EAAA,QAAA,CAAS,MAAM,MAAA,CAAO,KAAA,EAAO,CAAA;AAC7B,EAAA,SAAA,CAAU,MAAM,MAAA,CAAO,aAAA,EAAe,CAAA;AACxC;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MockAuthService.cjs.js","sources":["../../src/services/MockAuthService.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n BackstageCredentials,\n BackstageServicePrincipal,\n BackstagePrincipalTypes,\n BackstageUserPrincipal,\n BackstageNonePrincipal,\n AuthService,\n} from '@backstage/backend-plugin-api';\nimport { AuthenticationError } from '@backstage/errors';\nimport {\n mockCredentials,\n MOCK_USER_TOKEN,\n MOCK_USER_TOKEN_PREFIX,\n MOCK_INVALID_USER_TOKEN,\n MOCK_USER_LIMITED_TOKEN_PREFIX,\n MOCK_INVALID_USER_LIMITED_TOKEN,\n MOCK_SERVICE_TOKEN,\n MOCK_SERVICE_TOKEN_PREFIX,\n MOCK_INVALID_SERVICE_TOKEN,\n UserTokenPayload,\n ServiceTokenPayload,\n} from './mockCredentials';\nimport { JsonObject } from '@backstage/types';\n\n/** @internal */\nexport class MockAuthService implements AuthService {\n readonly pluginId: string;\n readonly disableDefaultAuthPolicy: boolean;\n\n constructor(options: {\n pluginId: string;\n disableDefaultAuthPolicy: boolean;\n }) {\n this.pluginId = options.pluginId;\n this.disableDefaultAuthPolicy = options.disableDefaultAuthPolicy;\n }\n\n async authenticate(\n token: string,\n options?: { allowLimitedAccess?: boolean },\n ): Promise<BackstageCredentials> {\n switch (token) {\n case MOCK_USER_TOKEN:\n return mockCredentials.user();\n case MOCK_SERVICE_TOKEN:\n return mockCredentials.service();\n case MOCK_INVALID_USER_TOKEN:\n throw new AuthenticationError('User token is invalid');\n case MOCK_INVALID_USER_LIMITED_TOKEN:\n throw new AuthenticationError('Limited user token is invalid');\n case MOCK_INVALID_SERVICE_TOKEN:\n throw new AuthenticationError('Service token is invalid');\n case '':\n throw new AuthenticationError('Token is empty');\n default:\n break;\n }\n\n if (token.startsWith(MOCK_USER_TOKEN_PREFIX)) {\n const { sub: userEntityRef, actor }: UserTokenPayload = JSON.parse(\n token.slice(MOCK_USER_TOKEN_PREFIX.length),\n );\n\n return mockCredentials.user(userEntityRef, { actor });\n }\n\n if (token.startsWith(MOCK_USER_LIMITED_TOKEN_PREFIX)) {\n if (!options?.allowLimitedAccess) {\n throw new AuthenticationError('Limited user token is not allowed');\n }\n\n const { sub: userEntityRef }: UserTokenPayload = JSON.parse(\n token.slice(MOCK_USER_LIMITED_TOKEN_PREFIX.length),\n );\n\n return mockCredentials.user(userEntityRef);\n }\n\n if (token.startsWith(MOCK_SERVICE_TOKEN_PREFIX)) {\n const { sub, target, obo }: ServiceTokenPayload = JSON.parse(\n token.slice(MOCK_SERVICE_TOKEN_PREFIX.length),\n );\n\n if (target && target !== this.pluginId) {\n throw new AuthenticationError(\n `Invalid mock token target plugin ID, got '${target}' but expected '${this.pluginId}'`,\n );\n }\n if (obo) {\n return mockCredentials.user(obo);\n }\n\n return mockCredentials.service(sub);\n }\n\n throw new AuthenticationError(`Unknown mock token '${token}'`);\n }\n\n async getNoneCredentials() {\n return mockCredentials.none();\n }\n\n async getOwnServiceCredentials(): Promise<\n BackstageCredentials<BackstageServicePrincipal>\n > {\n return mockCredentials.service(`plugin:${this.pluginId}`);\n }\n\n isPrincipal<TType extends keyof BackstagePrincipalTypes>(\n credentials: BackstageCredentials,\n type: TType,\n ): credentials is BackstageCredentials<BackstagePrincipalTypes[TType]> {\n const principal = credentials.principal as\n | BackstageUserPrincipal\n | BackstageServicePrincipal\n | BackstageNonePrincipal;\n\n if (type === 'unknown') {\n return true;\n }\n\n if (principal.type !== type) {\n return false;\n }\n\n return true;\n }\n\n async getPluginRequestToken(options: {\n onBehalfOf: BackstageCredentials;\n targetPluginId: string;\n }): Promise<{ token: string }> {\n const principal = options.onBehalfOf.principal as\n | BackstageUserPrincipal\n | BackstageServicePrincipal\n | BackstageNonePrincipal;\n\n if (principal.type === 'none' && this.disableDefaultAuthPolicy) {\n return { token: '' };\n }\n\n if (principal.type !== 'user' && principal.type !== 'service') {\n throw new AuthenticationError(\n `Refused to issue service token for credential type '${principal.type}'`,\n );\n }\n\n return {\n token: mockCredentials.service.token({\n onBehalfOf: options.onBehalfOf,\n targetPluginId: options.targetPluginId,\n }),\n };\n }\n\n async getLimitedUserToken(\n credentials: BackstageCredentials<BackstageUserPrincipal>,\n ): Promise<{ token: string; expiresAt: Date }> {\n if (credentials.principal.type !== 'user') {\n throw new AuthenticationError(\n `Refused to issue limited user token for credential type '${credentials.principal.type}'`,\n );\n }\n\n return {\n token: mockCredentials.limitedUser.token(\n credentials.principal.userEntityRef,\n ),\n expiresAt: new Date(Date.now() + 3600_000),\n };\n }\n\n listPublicServiceKeys(): Promise<{ keys: JsonObject[] }> {\n throw new Error('Not implemented');\n }\n}\n"],"names":["MOCK_USER_TOKEN","mockCredentials","MOCK_SERVICE_TOKEN","MOCK_INVALID_USER_TOKEN","AuthenticationError","MOCK_INVALID_USER_LIMITED_TOKEN","MOCK_INVALID_SERVICE_TOKEN","MOCK_USER_TOKEN_PREFIX","MOCK_USER_LIMITED_TOKEN_PREFIX","MOCK_SERVICE_TOKEN_PREFIX"],"mappings":";;;;;AAyCO,MAAM,
|
|
1
|
+
{"version":3,"file":"MockAuthService.cjs.js","sources":["../../src/services/MockAuthService.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n BackstageCredentials,\n BackstageServicePrincipal,\n BackstagePrincipalTypes,\n BackstageUserPrincipal,\n BackstageNonePrincipal,\n AuthService,\n} from '@backstage/backend-plugin-api';\nimport { AuthenticationError } from '@backstage/errors';\nimport {\n mockCredentials,\n MOCK_USER_TOKEN,\n MOCK_USER_TOKEN_PREFIX,\n MOCK_INVALID_USER_TOKEN,\n MOCK_USER_LIMITED_TOKEN_PREFIX,\n MOCK_INVALID_USER_LIMITED_TOKEN,\n MOCK_SERVICE_TOKEN,\n MOCK_SERVICE_TOKEN_PREFIX,\n MOCK_INVALID_SERVICE_TOKEN,\n UserTokenPayload,\n ServiceTokenPayload,\n} from './mockCredentials';\nimport { JsonObject } from '@backstage/types';\n\n/** @internal */\nexport class MockAuthService implements AuthService {\n readonly pluginId: string;\n readonly disableDefaultAuthPolicy: boolean;\n\n constructor(options: {\n pluginId: string;\n disableDefaultAuthPolicy: boolean;\n }) {\n this.pluginId = options.pluginId;\n this.disableDefaultAuthPolicy = options.disableDefaultAuthPolicy;\n }\n\n async authenticate(\n token: string,\n options?: { allowLimitedAccess?: boolean },\n ): Promise<BackstageCredentials> {\n switch (token) {\n case MOCK_USER_TOKEN:\n return mockCredentials.user();\n case MOCK_SERVICE_TOKEN:\n return mockCredentials.service();\n case MOCK_INVALID_USER_TOKEN:\n throw new AuthenticationError('User token is invalid');\n case MOCK_INVALID_USER_LIMITED_TOKEN:\n throw new AuthenticationError('Limited user token is invalid');\n case MOCK_INVALID_SERVICE_TOKEN:\n throw new AuthenticationError('Service token is invalid');\n case '':\n throw new AuthenticationError('Token is empty');\n default:\n break;\n }\n\n if (token.startsWith(MOCK_USER_TOKEN_PREFIX)) {\n const { sub: userEntityRef, actor }: UserTokenPayload = JSON.parse(\n token.slice(MOCK_USER_TOKEN_PREFIX.length),\n );\n\n return mockCredentials.user(userEntityRef, { actor });\n }\n\n if (token.startsWith(MOCK_USER_LIMITED_TOKEN_PREFIX)) {\n if (!options?.allowLimitedAccess) {\n throw new AuthenticationError('Limited user token is not allowed');\n }\n\n const { sub: userEntityRef }: UserTokenPayload = JSON.parse(\n token.slice(MOCK_USER_LIMITED_TOKEN_PREFIX.length),\n );\n\n return mockCredentials.user(userEntityRef);\n }\n\n if (token.startsWith(MOCK_SERVICE_TOKEN_PREFIX)) {\n const { sub, target, obo }: ServiceTokenPayload = JSON.parse(\n token.slice(MOCK_SERVICE_TOKEN_PREFIX.length),\n );\n\n if (target && target !== this.pluginId) {\n throw new AuthenticationError(\n `Invalid mock token target plugin ID, got '${target}' but expected '${this.pluginId}'`,\n );\n }\n if (obo) {\n return mockCredentials.user(obo);\n }\n\n return mockCredentials.service(sub);\n }\n\n throw new AuthenticationError(`Unknown mock token '${token}'`);\n }\n\n async getNoneCredentials() {\n return mockCredentials.none();\n }\n\n async getOwnServiceCredentials(): Promise<\n BackstageCredentials<BackstageServicePrincipal>\n > {\n return mockCredentials.service(`plugin:${this.pluginId}`);\n }\n\n isPrincipal<TType extends keyof BackstagePrincipalTypes>(\n credentials: BackstageCredentials,\n type: TType,\n ): credentials is BackstageCredentials<BackstagePrincipalTypes[TType]> {\n const principal = credentials.principal as\n | BackstageUserPrincipal\n | BackstageServicePrincipal\n | BackstageNonePrincipal;\n\n if (type === 'unknown') {\n return true;\n }\n\n if (principal.type !== type) {\n return false;\n }\n\n return true;\n }\n\n async getPluginRequestToken(options: {\n onBehalfOf: BackstageCredentials;\n targetPluginId: string;\n }): Promise<{ token: string }> {\n const principal = options.onBehalfOf.principal as\n | BackstageUserPrincipal\n | BackstageServicePrincipal\n | BackstageNonePrincipal;\n\n if (principal.type === 'none' && this.disableDefaultAuthPolicy) {\n return { token: '' };\n }\n\n if (principal.type !== 'user' && principal.type !== 'service') {\n throw new AuthenticationError(\n `Refused to issue service token for credential type '${principal.type}'`,\n );\n }\n\n return {\n token: mockCredentials.service.token({\n onBehalfOf: options.onBehalfOf,\n targetPluginId: options.targetPluginId,\n }),\n };\n }\n\n async getLimitedUserToken(\n credentials: BackstageCredentials<BackstageUserPrincipal>,\n ): Promise<{ token: string; expiresAt: Date }> {\n if (credentials.principal.type !== 'user') {\n throw new AuthenticationError(\n `Refused to issue limited user token for credential type '${credentials.principal.type}'`,\n );\n }\n\n return {\n token: mockCredentials.limitedUser.token(\n credentials.principal.userEntityRef,\n ),\n expiresAt: new Date(Date.now() + 3600_000),\n };\n }\n\n listPublicServiceKeys(): Promise<{ keys: JsonObject[] }> {\n throw new Error('Not implemented');\n }\n}\n"],"names":["MOCK_USER_TOKEN","mockCredentials","MOCK_SERVICE_TOKEN","MOCK_INVALID_USER_TOKEN","AuthenticationError","MOCK_INVALID_USER_LIMITED_TOKEN","MOCK_INVALID_SERVICE_TOKEN","MOCK_USER_TOKEN_PREFIX","MOCK_USER_LIMITED_TOKEN_PREFIX","MOCK_SERVICE_TOKEN_PREFIX"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MockEventsService.cjs.js","sources":["../../src/services/MockEventsService.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n EventParams,\n EventsService,\n EventsServiceSubscribeOptions,\n} from '@backstage/plugin-events-node';\n\nexport class MockEventsService implements EventsService {\n #subscribers: EventsServiceSubscribeOptions[];\n\n constructor() {\n this.#subscribers = [];\n }\n\n async publish(params: EventParams): Promise<void> {\n for (const subscriber of this.#subscribers) {\n if (subscriber.topics.includes(params.topic)) {\n await subscriber.onEvent(params);\n }\n }\n }\n\n async subscribe(options: EventsServiceSubscribeOptions): Promise<void> {\n this.#subscribers.push(options);\n }\n}\n"],"names":[],"mappings":";;AAsBO,MAAM,
|
|
1
|
+
{"version":3,"file":"MockEventsService.cjs.js","sources":["../../src/services/MockEventsService.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n EventParams,\n EventsService,\n EventsServiceSubscribeOptions,\n} from '@backstage/plugin-events-node';\n\nexport class MockEventsService implements EventsService {\n #subscribers: EventsServiceSubscribeOptions[];\n\n constructor() {\n this.#subscribers = [];\n }\n\n async publish(params: EventParams): Promise<void> {\n for (const subscriber of this.#subscribers) {\n if (subscriber.topics.includes(params.topic)) {\n await subscriber.onEvent(params);\n }\n }\n }\n\n async subscribe(options: EventsServiceSubscribeOptions): Promise<void> {\n this.#subscribers.push(options);\n }\n}\n"],"names":[],"mappings":";;AAsBO,MAAM,iBAAA,CAA2C;AAAA,EACtD,YAAA;AAAA,EAEA,WAAA,GAAc;AACZ,IAAA,IAAA,CAAK,eAAe,EAAC;AAAA,EACvB;AAAA,EAEA,MAAM,QAAQ,MAAA,EAAoC;AAChD,IAAA,KAAA,MAAW,UAAA,IAAc,KAAK,YAAA,EAAc;AAC1C,MAAA,IAAI,UAAA,CAAW,MAAA,CAAO,QAAA,CAAS,MAAA,CAAO,KAAK,CAAA,EAAG;AAC5C,QAAA,MAAM,UAAA,CAAW,QAAQ,MAAM,CAAA;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,UAAU,OAAA,EAAuD;AACrE,IAAA,IAAA,CAAK,YAAA,CAAa,KAAK,OAAO,CAAA;AAAA,EAChC;AACF;;;;"}
|