@backstage/backend-defaults 0.11.2-next.0 → 0.12.1-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 +31 -0
- package/config.d.ts +195 -0
- package/dist/CreateBackend.cjs.js.map +1 -1
- package/dist/PackageDiscoveryService.cjs.js +13 -3
- package/dist/PackageDiscoveryService.cjs.js.map +1 -1
- package/dist/alpha/entrypoints/actions/DefaultActionsService.cjs.js.map +1 -1
- package/dist/alpha/entrypoints/actions/actionsServiceFactory.cjs.js.map +1 -1
- package/dist/alpha/entrypoints/actionsRegistry/DefaultActionsRegistryService.cjs.js.map +1 -1
- package/dist/alpha/entrypoints/actionsRegistry/actionsRegistryServiceFactory.cjs.js.map +1 -1
- package/dist/cache.d.ts +20 -1
- package/dist/database.d.ts +1 -1
- package/dist/discovery.d.ts +1 -1
- package/dist/discoveryFeatureLoader.cjs.js.map +1 -1
- package/dist/entrypoints/auditor/DefaultAuditorService.cjs.js.map +1 -1
- package/dist/entrypoints/auditor/WinstonRootAuditorService.cjs.js.map +1 -1
- package/dist/entrypoints/auditor/auditorServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/auditor/types.cjs.js.map +1 -1
- package/dist/entrypoints/auditor/utils.cjs.js.map +1 -1
- package/dist/entrypoints/auth/DefaultAuthService.cjs.js.map +1 -1
- package/dist/entrypoints/auth/JwksClient.cjs.js.map +1 -1
- package/dist/entrypoints/auth/authServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/auth/external/ExternalTokenHandler.cjs.js.map +1 -1
- package/dist/entrypoints/auth/external/helpers.cjs.js.map +1 -1
- package/dist/entrypoints/auth/external/jwks.cjs.js.map +1 -1
- package/dist/entrypoints/auth/external/legacy.cjs.js.map +1 -1
- package/dist/entrypoints/auth/external/static.cjs.js.map +1 -1
- package/dist/entrypoints/auth/helpers.cjs.js.map +1 -1
- package/dist/entrypoints/auth/plugin/PluginTokenHandler.cjs.js.map +1 -1
- package/dist/entrypoints/auth/plugin/keys/DatabaseKeyStore.cjs.js.map +1 -1
- package/dist/entrypoints/auth/plugin/keys/DatabasePluginKeySource.cjs.js.map +1 -1
- package/dist/entrypoints/auth/plugin/keys/StaticConfigPluginKeySource.cjs.js.map +1 -1
- package/dist/entrypoints/auth/plugin/keys/createPluginKeySource.cjs.js.map +1 -1
- package/dist/entrypoints/auth/user/UserTokenHandler.cjs.js.map +1 -1
- package/dist/entrypoints/cache/CacheClient.cjs.js.map +1 -1
- package/dist/entrypoints/cache/CacheManager.cjs.js +112 -7
- package/dist/entrypoints/cache/CacheManager.cjs.js.map +1 -1
- package/dist/entrypoints/cache/cacheServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/cache/providers/infinispan/InfinispanKeyvStore.cjs.js +141 -0
- package/dist/entrypoints/cache/providers/infinispan/InfinispanKeyvStore.cjs.js.map +1 -0
- package/dist/entrypoints/cache/providers/infinispan/InfinispanOptionsMapper.cjs.js +129 -0
- package/dist/entrypoints/cache/providers/infinispan/InfinispanOptionsMapper.cjs.js.map +1 -0
- package/dist/entrypoints/cache/types.cjs.js.map +1 -1
- package/dist/entrypoints/database/DatabaseManager.cjs.js.map +1 -1
- package/dist/entrypoints/database/connectors/defaultNameOverride.cjs.js.map +1 -1
- package/dist/entrypoints/database/connectors/defaultSchemaOverride.cjs.js.map +1 -1
- package/dist/entrypoints/database/connectors/mergeDatabaseConfig.cjs.js.map +1 -1
- package/dist/entrypoints/database/connectors/mysql.cjs.js.map +1 -1
- package/dist/entrypoints/database/connectors/postgres.cjs.js.map +1 -1
- package/dist/entrypoints/database/connectors/sqlite3.cjs.js.map +1 -1
- package/dist/entrypoints/database/databaseServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/discovery/HostDiscovery.cjs.js.map +1 -1
- package/dist/entrypoints/discovery/SrvResolvers.cjs.js.map +1 -1
- package/dist/entrypoints/discovery/discoveryServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/httpAuth/httpAuthServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/httpRouter/http/createAuthIntegrationRouter.cjs.js.map +1 -1
- package/dist/entrypoints/httpRouter/http/createCookieAuthRefreshMiddleware.cjs.js.map +1 -1
- package/dist/entrypoints/httpRouter/http/createCredentialsBarrier.cjs.js.map +1 -1
- package/dist/entrypoints/httpRouter/http/createLifecycleMiddleware.cjs.js.map +1 -1
- package/dist/entrypoints/httpRouter/http/createRateLimitMiddleware.cjs.js.map +1 -1
- package/dist/entrypoints/httpRouter/httpRouterServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/lifecycle/lifecycleServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/logger/loggerServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/permissions/permissionsServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/permissionsRegistry/permissionsRegistryServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/rootConfig/createConfigSecretEnumerator.cjs.js.map +1 -1
- package/dist/entrypoints/rootConfig/rootConfigServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/rootHealth/rootHealthServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/rootHttpRouter/DefaultRootHttpRouter.cjs.js.map +1 -1
- package/dist/entrypoints/rootHttpRouter/createHealthRouter.cjs.js.map +1 -1
- package/dist/entrypoints/rootHttpRouter/http/MiddlewareFactory.cjs.js.map +1 -1
- package/dist/entrypoints/rootHttpRouter/http/applyInternalErrorFilter.cjs.js.map +1 -1
- package/dist/entrypoints/rootHttpRouter/http/config.cjs.js.map +1 -1
- package/dist/entrypoints/rootHttpRouter/http/createHttpServer.cjs.js.map +1 -1
- package/dist/entrypoints/rootHttpRouter/http/getGeneratedCertificate.cjs.js.map +1 -1
- package/dist/entrypoints/rootHttpRouter/http/readCorsOptions.cjs.js.map +1 -1
- package/dist/entrypoints/rootHttpRouter/http/readHelmetOptions.cjs.js.map +1 -1
- package/dist/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/rootLifecycle/rootLifecycleServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/rootLogger/WinstonLogger.cjs.js.map +1 -1
- package/dist/entrypoints/rootLogger/rootLoggerServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/scheduler/database/migrateBackendTasks.cjs.js.map +1 -1
- package/dist/entrypoints/scheduler/database/tables.cjs.js.map +1 -1
- package/dist/entrypoints/scheduler/lib/DefaultSchedulerService.cjs.js.map +1 -1
- package/dist/entrypoints/scheduler/lib/LocalTaskWorker.cjs.js.map +1 -1
- package/dist/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.cjs.js.map +1 -1
- package/dist/entrypoints/scheduler/lib/PluginTaskSchedulerJanitor.cjs.js.map +1 -1
- package/dist/entrypoints/scheduler/lib/TaskWorker.cjs.js.map +1 -1
- package/dist/entrypoints/scheduler/lib/types.cjs.js.map +1 -1
- package/dist/entrypoints/scheduler/lib/util.cjs.js.map +1 -1
- package/dist/entrypoints/scheduler/schedulerServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/AwsCodeCommitUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/AwsS3UrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/AzureBlobStorageUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/AzureUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/BitbucketCloudUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/BitbucketServerUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/BitbucketUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/FetchUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/GerritUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/GiteaUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/GithubUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/GitlabUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/GoogleGcsUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/HarnessUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/ReadUrlResponseFactory.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/UrlReaderPredicateMux.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/UrlReaders.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/tree/ReadTreeResponseFactory.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/tree/ReadableArrayResponse.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/tree/TarArchiveResponse.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/tree/ZipArchiveResponse.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/tree/util.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/util.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/urlReaderServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/userInfo/DefaultUserInfoService.cjs.js.map +1 -1
- package/dist/entrypoints/userInfo/userInfoServiceFactory.cjs.js.map +1 -1
- package/dist/httpAuth.d.ts +1 -1
- package/dist/lib/RateLimitStoreFactory.cjs.js.map +1 -1
- package/dist/lib/escapeRegExp.cjs.js.map +1 -1
- package/dist/lib/rateLimitMiddleware.cjs.js.map +1 -1
- package/dist/package.json.cjs.js +2 -269
- package/dist/package.json.cjs.js.map +1 -1
- package/dist/urlReader.d.ts +1 -1
- package/package.json +12 -11
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var events = require('events');
|
|
4
|
+
|
|
5
|
+
class InfinispanKeyvStore extends events.EventEmitter {
|
|
6
|
+
clientPromise;
|
|
7
|
+
logger;
|
|
8
|
+
defaultTtl;
|
|
9
|
+
resolvedClient = null;
|
|
10
|
+
namespace;
|
|
11
|
+
// Keyv expects this
|
|
12
|
+
constructor(options) {
|
|
13
|
+
super();
|
|
14
|
+
this.clientPromise = options.clientPromise;
|
|
15
|
+
this.logger = options.logger.child({ class: InfinispanKeyvStore.name });
|
|
16
|
+
this.defaultTtl = options.defaultTtl;
|
|
17
|
+
this.clientPromise.then((client) => {
|
|
18
|
+
this.resolvedClient = client;
|
|
19
|
+
if (typeof client.on === "function") {
|
|
20
|
+
client.on("error", (error) => {
|
|
21
|
+
this.logger.error("Native Infinispan client reported an error.", {
|
|
22
|
+
error: error.message
|
|
23
|
+
});
|
|
24
|
+
this.emit("error", error);
|
|
25
|
+
});
|
|
26
|
+
} else {
|
|
27
|
+
this.logger.warn(
|
|
28
|
+
'Native Infinispan client does not appear to support .on("error") event listening.'
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}).catch((err) => {
|
|
32
|
+
this.logger.error(
|
|
33
|
+
"Failed to resolve Infinispan client promise in constructor.",
|
|
34
|
+
{ error: err.message }
|
|
35
|
+
);
|
|
36
|
+
this.emit("error", err);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
async getClient() {
|
|
40
|
+
if (this.resolvedClient) {
|
|
41
|
+
return this.resolvedClient;
|
|
42
|
+
}
|
|
43
|
+
this.resolvedClient = await this.clientPromise;
|
|
44
|
+
return this.resolvedClient;
|
|
45
|
+
}
|
|
46
|
+
async get(key) {
|
|
47
|
+
this.logger.debug(`Getting key: ${key}`);
|
|
48
|
+
try {
|
|
49
|
+
const client = await this.getClient();
|
|
50
|
+
const value = await client.get(key);
|
|
51
|
+
if (value === null || value === void 0) {
|
|
52
|
+
this.logger.debug(`Key not found or value is null/undefined: ${key}`);
|
|
53
|
+
return void 0;
|
|
54
|
+
}
|
|
55
|
+
this.logger.debug(`Successfully retrieved key: ${key}`);
|
|
56
|
+
return value;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
this.logger.error(`Error getting key '${key}' from Infinispan.`, {
|
|
59
|
+
error: error.message
|
|
60
|
+
});
|
|
61
|
+
this.emit(
|
|
62
|
+
"error",
|
|
63
|
+
error instanceof Error ? error : new Error(String(error))
|
|
64
|
+
);
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async set(key, value, ttl) {
|
|
69
|
+
this.logger.debug(`Setting key: ${key}`, { ttl });
|
|
70
|
+
this.logger.debug(`Setting key: ${key}`, { ttlInput: ttl });
|
|
71
|
+
const currentTtl = ttl ?? this.defaultTtl;
|
|
72
|
+
this.logger.debug(`Calculated currentTtl for key ${key}: ${currentTtl}ms`);
|
|
73
|
+
const storeOptions = {};
|
|
74
|
+
if (typeof currentTtl === "number" && currentTtl > 0) {
|
|
75
|
+
storeOptions.lifespan = `${currentTtl}ms`;
|
|
76
|
+
} else if (typeof currentTtl === "string") {
|
|
77
|
+
storeOptions.lifespan = currentTtl;
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const client = await this.getClient();
|
|
81
|
+
await client.put(key, value, storeOptions);
|
|
82
|
+
this.logger.debug(`Successfully set key: ${key}`);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
this.logger.error(`Error setting key '${key}' in Infinispan.`, {
|
|
85
|
+
error: error.message
|
|
86
|
+
});
|
|
87
|
+
this.emit(
|
|
88
|
+
"error",
|
|
89
|
+
error instanceof Error ? error : new Error(String(error))
|
|
90
|
+
);
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async delete(key) {
|
|
95
|
+
this.logger.debug(`Deleting key: ${key}`);
|
|
96
|
+
try {
|
|
97
|
+
const client = await this.getClient();
|
|
98
|
+
const deleted = await client.remove(key);
|
|
99
|
+
this.logger.debug(`Key deletion status for '${key}': ${deleted}`);
|
|
100
|
+
return deleted;
|
|
101
|
+
} catch (error) {
|
|
102
|
+
this.logger.error(`Error deleting key '${key}' from Infinispan.`, {
|
|
103
|
+
error: error.message
|
|
104
|
+
});
|
|
105
|
+
this.emit(
|
|
106
|
+
"error",
|
|
107
|
+
error instanceof Error ? error : new Error(String(error))
|
|
108
|
+
);
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async clear() {
|
|
113
|
+
this.logger.info("Clearing all entries from Infinispan cache.");
|
|
114
|
+
try {
|
|
115
|
+
const client = await this.getClient();
|
|
116
|
+
await client.clear();
|
|
117
|
+
this.logger.info("Infinispan cache cleared successfully.");
|
|
118
|
+
} catch (error) {
|
|
119
|
+
this.logger.error("Error clearing Infinispan cache.", {
|
|
120
|
+
error: error.message
|
|
121
|
+
});
|
|
122
|
+
this.emit(
|
|
123
|
+
"error",
|
|
124
|
+
error instanceof Error ? error : new Error(String(error))
|
|
125
|
+
);
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// This disconnect is for the Keyv store instance, but the actual client is shared.
|
|
130
|
+
// The CacheManager should handle the shared client's disconnection.
|
|
131
|
+
// However, if Keyv calls this, we shouldn't error.
|
|
132
|
+
async disconnect() {
|
|
133
|
+
this.logger.info(
|
|
134
|
+
"InfinispanKeyvStore disconnect called. Shared client managed by CacheManager."
|
|
135
|
+
);
|
|
136
|
+
return Promise.resolve();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
exports.InfinispanKeyvStore = InfinispanKeyvStore;
|
|
141
|
+
//# sourceMappingURL=InfinispanKeyvStore.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InfinispanKeyvStore.cjs.js","sources":["../../../../../src/entrypoints/cache/providers/infinispan/InfinispanKeyvStore.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 { EventEmitter } from 'events';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { InfinispanPutOptions } from '../../types';\n\n/**\n * Interface defining the required methods for an Infinispan client.\n * @public\n */\nexport interface InfinispanClientCacheInterface {\n get(key: string): Promise<string | null | undefined>;\n put(key: string, value: string, options?: InfinispanPutOptions): Promise<any>;\n remove(key: string): Promise<boolean>;\n clear(): Promise<void>;\n disconnect(): Promise<void>;\n on?(event: 'error' | string, listener: (...args: any[]) => void): this;\n connect?(): Promise<any>;\n query?(query: string): Promise<any[] | null>;\n containsKey?(key: string): Promise<boolean>;\n}\n\n/**\n * Options for creating an InfinispanKeyvStore instance.\n */\nexport interface InfinispanKeyvStoreOptions {\n clientPromise: Promise<InfinispanClientCacheInterface>;\n logger: LoggerService;\n defaultTtl?: number; // TTL in milliseconds\n}\n\n/**\n * A Keyv store implementation that uses Infinispan as the backend.\n * This store implements the Keyv store interface and provides caching functionality\n * using Infinispan's distributed cache capabilities.\n */\nexport class InfinispanKeyvStore extends EventEmitter {\n private readonly clientPromise: Promise<InfinispanClientCacheInterface>;\n private readonly logger: LoggerService;\n private readonly defaultTtl?: number;\n private resolvedClient: InfinispanClientCacheInterface | null = null;\n\n public readonly namespace?: string; // Keyv expects this\n\n constructor(options: InfinispanKeyvStoreOptions) {\n super();\n this.clientPromise = options.clientPromise;\n this.logger = options.logger.child({ class: InfinispanKeyvStore.name });\n this.defaultTtl = options.defaultTtl;\n\n // Eagerly try to resolve the client to attach error listeners early\n // and to have it ready for disconnect if resolved.\n this.clientPromise\n .then(client => {\n this.resolvedClient = client;\n if (typeof client.on === 'function') {\n client.on('error', (error: Error) => {\n this.logger.error('Native Infinispan client reported an error.', {\n error: error.message,\n });\n this.emit('error', error);\n });\n } else {\n this.logger.warn(\n 'Native Infinispan client does not appear to support .on(\"error\") event listening.',\n );\n }\n })\n .catch(err => {\n this.logger.error(\n 'Failed to resolve Infinispan client promise in constructor.',\n { error: err.message },\n );\n // Errors from operations will also be emitted when clientPromise is awaited and rejects.\n this.emit('error', err);\n });\n }\n\n private async getClient(): Promise<InfinispanClientCacheInterface> {\n if (this.resolvedClient) {\n return this.resolvedClient;\n }\n // If not yet resolved (e.g. called very quickly or promise rejected and retrying implicitly)\n // Await the promise. This will throw if the promise is rejected.\n this.resolvedClient = await this.clientPromise;\n return this.resolvedClient;\n }\n\n async get(key: string): Promise<string | undefined> {\n this.logger.debug(`Getting key: ${key}`);\n try {\n const client = await this.getClient();\n const value = await client.get(key);\n if (value === null || value === undefined) {\n this.logger.debug(`Key not found or value is null/undefined: ${key}`);\n return undefined;\n }\n this.logger.debug(`Successfully retrieved key: ${key}`);\n return value;\n } catch (error: any) {\n this.logger.error(`Error getting key '${key}' from Infinispan.`, {\n error: error.message,\n });\n this.emit(\n 'error',\n error instanceof Error ? error : new Error(String(error)),\n );\n throw error;\n }\n }\n\n async set(key: string, value: string, ttl?: number): Promise<void> {\n this.logger.debug(`Setting key: ${key}`, { ttl });\n this.logger.debug(`Setting key: ${key}`, { ttlInput: ttl });\n const currentTtl = ttl ?? this.defaultTtl;\n this.logger.debug(`Calculated currentTtl for key ${key}: ${currentTtl}ms`); // Log do TTL calculado\n const storeOptions: InfinispanPutOptions = {};\n\n if (typeof currentTtl === 'number' && currentTtl > 0) {\n storeOptions.lifespan = `${currentTtl}ms`; // Ensure time unit is passed as string\n // Ensure this matches client expectations. If client expects string '10s', convert here.\n // For now, assuming number in ms is fine or string like '10000ms'.\n // The PutOptions defines lifespan as string | number | null.\n } else if (typeof currentTtl === 'string') {\n storeOptions.lifespan = currentTtl;\n }\n\n try {\n const client = await this.getClient();\n await client.put(key, value, storeOptions);\n this.logger.debug(`Successfully set key: ${key}`);\n } catch (error: any) {\n this.logger.error(`Error setting key '${key}' in Infinispan.`, {\n error: error.message,\n });\n this.emit(\n 'error',\n error instanceof Error ? error : new Error(String(error)),\n );\n throw error;\n }\n }\n\n async delete(key: string): Promise<boolean> {\n this.logger.debug(`Deleting key: ${key}`);\n try {\n const client = await this.getClient();\n const deleted = await client.remove(key);\n this.logger.debug(`Key deletion status for '${key}': ${deleted}`);\n return deleted;\n } catch (error: any) {\n this.logger.error(`Error deleting key '${key}' from Infinispan.`, {\n error: error.message,\n });\n this.emit(\n 'error',\n error instanceof Error ? error : new Error(String(error)),\n );\n throw error;\n }\n }\n\n async clear(): Promise<void> {\n this.logger.info('Clearing all entries from Infinispan cache.');\n try {\n const client = await this.getClient();\n await client.clear();\n this.logger.info('Infinispan cache cleared successfully.');\n } catch (error: any) {\n this.logger.error('Error clearing Infinispan cache.', {\n error: error.message,\n });\n this.emit(\n 'error',\n error instanceof Error ? error : new Error(String(error)),\n );\n throw error;\n }\n }\n\n // This disconnect is for the Keyv store instance, but the actual client is shared.\n // The CacheManager should handle the shared client's disconnection.\n // However, if Keyv calls this, we shouldn't error.\n async disconnect(): Promise<void> {\n this.logger.info(\n 'InfinispanKeyvStore disconnect called. Shared client managed by CacheManager.',\n );\n // No-op for this store instance as the actual client is managed externally by CacheManager.\n // The CacheManager's stop() method will disconnect the shared native client.\n return Promise.resolve();\n }\n}\n"],"names":["EventEmitter"],"mappings":"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"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
class InfinispanOptionsMapper {
|
|
4
|
+
/**
|
|
5
|
+
* Parses Infinispan options from the provided configuration path.
|
|
6
|
+
*
|
|
7
|
+
* @param storeConfigPath - The configuration path for the Infinispan store.
|
|
8
|
+
* @param config - The root configuration service to retrieve the Infinispan configuration.
|
|
9
|
+
* @param logger - An optional logger service for logging errors and warnings.
|
|
10
|
+
* @returns Parsed Infinispan cache store options.
|
|
11
|
+
*/
|
|
12
|
+
static parseInfinispanOptions(storeConfigPath, config, logger) {
|
|
13
|
+
const infinispanConfig = config.getConfig(storeConfigPath);
|
|
14
|
+
const parsedOptions = {
|
|
15
|
+
type: "infinispan"
|
|
16
|
+
};
|
|
17
|
+
if (infinispanConfig.has("servers")) {
|
|
18
|
+
const serversConfig = infinispanConfig.get("servers");
|
|
19
|
+
if (Array.isArray(serversConfig)) {
|
|
20
|
+
parsedOptions.servers = infinispanConfig.getConfigArray("servers").map(
|
|
21
|
+
(serverConf) => ({
|
|
22
|
+
host: serverConf.getString("host"),
|
|
23
|
+
port: serverConf.getOptionalNumber("port") ?? 11222
|
|
24
|
+
})
|
|
25
|
+
);
|
|
26
|
+
} else if (typeof serversConfig === "object" && serversConfig !== null) {
|
|
27
|
+
const serverConf = infinispanConfig.getConfig("servers");
|
|
28
|
+
parsedOptions.servers = {
|
|
29
|
+
host: serverConf.getOptionalString("host") ?? "127.0.0.1",
|
|
30
|
+
port: serverConf.getOptionalString("port") ?? 11222
|
|
31
|
+
};
|
|
32
|
+
} else {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Infinispan 'servers' configuration at ${storeConfigPath} is invalid, must be an object or an array.`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
logger?.warn(
|
|
39
|
+
`Infinispan configuration at ${storeConfigPath} is missing the 'servers' definition, will use client defaults.`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
const behaviorOptions = {};
|
|
43
|
+
behaviorOptions.clusters = infinispanConfig.getOptionalConfigArray("clusters")?.map((clusterConf) => {
|
|
44
|
+
const name = clusterConf.getOptionalString("name");
|
|
45
|
+
return {
|
|
46
|
+
...name && { name },
|
|
47
|
+
servers: clusterConf.getConfigArray("servers").map((serverConf) => ({
|
|
48
|
+
host: serverConf.getString("host"),
|
|
49
|
+
port: serverConf.getOptionalNumber("port") ?? 11222
|
|
50
|
+
}))
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
const clientVersion = infinispanConfig.getOptionalString("version") ?? "2.9";
|
|
54
|
+
if (clientVersion === "2.9" || clientVersion === "2.5" || clientVersion === "2.2") {
|
|
55
|
+
behaviorOptions.version = clientVersion;
|
|
56
|
+
} else if (clientVersion !== null && clientVersion !== void 0) {
|
|
57
|
+
logger?.warn(
|
|
58
|
+
`Invalid Infinispan client version "${clientVersion}" in config at ${storeConfigPath}.version. Must be "2.9", "2.5", or "2.2". It will be ignored, and the client may use a default or fail.`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
behaviorOptions.cacheName = infinispanConfig.getOptionalString("cacheName") ?? "cache";
|
|
62
|
+
const mediaType = infinispanConfig.getOptionalString("mediaType");
|
|
63
|
+
if (mediaType === "text/plain" || mediaType === "application/json") {
|
|
64
|
+
behaviorOptions.mediaType = mediaType;
|
|
65
|
+
} else if (mediaType !== null && mediaType !== void 0) {
|
|
66
|
+
logger?.warn(
|
|
67
|
+
`Invalid Infinispan mediaType "${mediaType}" in config at ${storeConfigPath}.mediaType. Must be "text/plain" | "application/json". It will be ignored, and the client may use a default or fail.`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
behaviorOptions.maxRetries = infinispanConfig.getOptionalNumber("maxRetries");
|
|
71
|
+
behaviorOptions.topologyUpdates = infinispanConfig.getOptionalBoolean("topologyUpdates") ?? true;
|
|
72
|
+
const authConfig = infinispanConfig.getOptionalConfig("authentication");
|
|
73
|
+
const auth = {};
|
|
74
|
+
auth.enabled = authConfig?.getOptionalBoolean("enabled");
|
|
75
|
+
auth.saslMechanism = authConfig?.getOptionalString("saslMechanism");
|
|
76
|
+
auth.userName = authConfig?.getOptionalString("userName");
|
|
77
|
+
auth.password = authConfig?.getOptionalString("password");
|
|
78
|
+
auth.token = authConfig?.getOptionalString("token");
|
|
79
|
+
auth.authzid = authConfig?.getOptionalString("authzid");
|
|
80
|
+
behaviorOptions.authentication = auth;
|
|
81
|
+
const sslConfig = infinispanConfig.getOptionalConfig("ssl");
|
|
82
|
+
const ssl = {};
|
|
83
|
+
ssl.enabled = sslConfig?.getOptionalBoolean("enabled");
|
|
84
|
+
ssl.secureProtocol = sslConfig?.getOptionalString("secureProtocol");
|
|
85
|
+
ssl.trustCerts = sslConfig?.getOptionalStringArray("trustCerts");
|
|
86
|
+
ssl.sniHostName = sslConfig?.getOptionalString("sniHostname");
|
|
87
|
+
const clientAuth = infinispanConfig.getOptionalConfig("clientAuth");
|
|
88
|
+
const clientAuthOpts = {};
|
|
89
|
+
clientAuthOpts.key = clientAuth?.getOptionalString("key");
|
|
90
|
+
clientAuthOpts.passphrase = clientAuth?.getOptionalString("passphrase");
|
|
91
|
+
clientAuthOpts.cert = clientAuth?.getOptionalString("cert");
|
|
92
|
+
ssl.clientAuth = clientAuthOpts;
|
|
93
|
+
const cryptoStore = infinispanConfig.getOptionalConfig("cryptoStore");
|
|
94
|
+
const cryptoStoreOpts = {};
|
|
95
|
+
cryptoStoreOpts.key = cryptoStore?.getOptionalString("path");
|
|
96
|
+
cryptoStoreOpts.passphrase = cryptoStore?.getOptionalString("passphrase");
|
|
97
|
+
ssl.cryptoStore = cryptoStoreOpts;
|
|
98
|
+
behaviorOptions.ssl = ssl;
|
|
99
|
+
const dataFormat = infinispanConfig.getOptionalConfig("dataFormat");
|
|
100
|
+
const dataFormatOpts = {};
|
|
101
|
+
const keyType = dataFormat?.getOptionalString("keyType") ?? "text/plain";
|
|
102
|
+
const valueType = dataFormat?.getOptionalString("valueType") ?? "text/plain";
|
|
103
|
+
if (keyType === "text/plain" || keyType === "application/json") {
|
|
104
|
+
dataFormatOpts.keyType = keyType;
|
|
105
|
+
} else if (keyType !== null && keyType !== void 0) {
|
|
106
|
+
logger?.warn(
|
|
107
|
+
`Invalid Infinispan dataFormat.keyType "${keyType}" in config at ${storeConfigPath}.dataFormat.keyType. Must be "text/plain" | "application/json". Not mapped, client will use default 'text/plain'.`
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
if (valueType === "text/plain" || valueType === "application/json") {
|
|
111
|
+
dataFormatOpts.valueType = valueType;
|
|
112
|
+
} else if (valueType !== null && valueType !== void 0) {
|
|
113
|
+
logger?.warn(
|
|
114
|
+
`Invalid Infinispan dataFormat.valueType "${valueType}" in config at ${storeConfigPath}.dataFormat.valueType. Must be "text/plain" | "application/json". Not mapped, client will use default 'text/plain'.`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
behaviorOptions.dataFormat = dataFormatOpts;
|
|
118
|
+
parsedOptions.options = behaviorOptions;
|
|
119
|
+
logger?.debug(
|
|
120
|
+
`Parsed Infinispan options from config at ${storeConfigPath}: ${JSON.stringify(
|
|
121
|
+
parsedOptions
|
|
122
|
+
)}`
|
|
123
|
+
);
|
|
124
|
+
return parsedOptions;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
exports.InfinispanOptionsMapper = InfinispanOptionsMapper;
|
|
129
|
+
//# sourceMappingURL=InfinispanOptionsMapper.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InfinispanOptionsMapper.cjs.js","sources":["../../../../../src/entrypoints/cache/providers/infinispan/InfinispanOptionsMapper.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 LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport {\n DataFormatOptions,\n InfinispanAuthOptions,\n InfinispanCacheStoreOptions,\n InfinispanClientAuthOptions,\n InfinispanClientBehaviorOptions,\n InfinispanClusterConfig,\n InfinispanServerConfig,\n InfinispanSslOptions,\n} from '../../types';\n\nexport class InfinispanOptionsMapper {\n /**\n * Parses Infinispan options from the provided configuration path.\n *\n * @param storeConfigPath - The configuration path for the Infinispan store.\n * @param config - The root configuration service to retrieve the Infinispan configuration.\n * @param logger - An optional logger service for logging errors and warnings.\n * @returns Parsed Infinispan cache store options.\n */\n public static parseInfinispanOptions(\n storeConfigPath: string,\n config: RootConfigService,\n logger?: LoggerService,\n ): InfinispanCacheStoreOptions {\n const infinispanConfig = config.getConfig(storeConfigPath);\n const parsedOptions: Partial<InfinispanCacheStoreOptions> = {\n type: 'infinispan',\n };\n\n // Parse Servers & Clusters Configurations\n if (infinispanConfig.has('servers')) {\n const serversConfig = infinispanConfig.get('servers');\n if (Array.isArray(serversConfig)) {\n parsedOptions.servers = infinispanConfig.getConfigArray('servers').map(\n serverConf =>\n ({\n host: serverConf.getString('host'),\n port: serverConf.getOptionalNumber('port') ?? 11222,\n } as InfinispanServerConfig),\n );\n } else if (typeof serversConfig === 'object' && serversConfig !== null) {\n const serverConf = infinispanConfig.getConfig('servers');\n parsedOptions.servers = {\n host: serverConf.getOptionalString('host') ?? '127.0.0.1',\n port: serverConf.getOptionalString('port') ?? 11222,\n } as InfinispanServerConfig;\n } else {\n throw new Error(\n `Infinispan 'servers' configuration at ${storeConfigPath} is invalid, must be an object or an array.`,\n );\n }\n } else {\n logger?.warn(\n `Infinispan configuration at ${storeConfigPath} is missing the 'servers' definition, will use client defaults.`,\n );\n }\n\n // The parsed options block to send to the Infinispan client\n // This will be used to configure the Infinispan client behavior.\n const behaviorOptions: Partial<InfinispanClientBehaviorOptions> = {};\n\n behaviorOptions.clusters = infinispanConfig\n .getOptionalConfigArray('clusters')\n ?.map(clusterConf => {\n const name = clusterConf.getOptionalString('name');\n return {\n ...(name && { name }),\n servers: clusterConf.getConfigArray('servers').map(serverConf => ({\n host: serverConf.getString('host'),\n port: serverConf.getOptionalNumber('port') ?? 11222,\n })),\n } as InfinispanClusterConfig;\n });\n\n // Parse Default Options Start...\n const clientVersion =\n infinispanConfig.getOptionalString('version') ?? '2.9';\n if (\n clientVersion === '2.9' ||\n clientVersion === '2.5' ||\n clientVersion === '2.2'\n ) {\n behaviorOptions.version = clientVersion;\n } else if (clientVersion !== null && clientVersion !== undefined) {\n logger?.warn(\n `Invalid Infinispan client version \"${clientVersion}\" in config at ${storeConfigPath}.version. Must be \"2.9\", \"2.5\", or \"2.2\". It will be ignored, and the client may use a default or fail.`,\n );\n }\n\n behaviorOptions.cacheName =\n infinispanConfig.getOptionalString('cacheName') ?? 'cache';\n\n const mediaType = infinispanConfig.getOptionalString('mediaType');\n if (mediaType === 'text/plain' || mediaType === 'application/json') {\n behaviorOptions.mediaType = mediaType;\n } else if (mediaType !== null && mediaType !== undefined) {\n logger?.warn(\n `Invalid Infinispan mediaType \"${mediaType}\" in config at ${storeConfigPath}.mediaType. Must be \"text/plain\" | \"application/json\". It will be ignored, and the client may use a default or fail.`,\n );\n }\n\n behaviorOptions.maxRetries =\n infinispanConfig.getOptionalNumber('maxRetries');\n\n behaviorOptions.topologyUpdates =\n infinispanConfig.getOptionalBoolean('topologyUpdates') ?? true;\n\n // Default Options End...\n\n // Parse Authentication and SSL Options\n const authConfig = infinispanConfig.getOptionalConfig('authentication');\n const auth: Partial<InfinispanAuthOptions> = {};\n\n auth.enabled = authConfig?.getOptionalBoolean('enabled');\n auth.saslMechanism = authConfig?.getOptionalString('saslMechanism');\n auth.userName = authConfig?.getOptionalString('userName');\n auth.password = authConfig?.getOptionalString('password');\n auth.token = authConfig?.getOptionalString('token');\n auth.authzid = authConfig?.getOptionalString('authzid');\n\n behaviorOptions.authentication = auth as InfinispanAuthOptions;\n\n const sslConfig = infinispanConfig.getOptionalConfig('ssl');\n const ssl: Partial<InfinispanSslOptions> = {};\n\n ssl.enabled = sslConfig?.getOptionalBoolean('enabled');\n ssl.secureProtocol = sslConfig?.getOptionalString('secureProtocol');\n ssl.trustCerts = sslConfig?.getOptionalStringArray('trustCerts');\n ssl.sniHostName = sslConfig?.getOptionalString('sniHostname');\n\n const clientAuth = infinispanConfig.getOptionalConfig('clientAuth');\n const clientAuthOpts: Partial<InfinispanClientAuthOptions> = {};\n\n clientAuthOpts.key = clientAuth?.getOptionalString('key');\n clientAuthOpts.passphrase = clientAuth?.getOptionalString('passphrase');\n clientAuthOpts.cert = clientAuth?.getOptionalString('cert');\n\n ssl.clientAuth = clientAuthOpts as InfinispanClientAuthOptions;\n\n const cryptoStore = infinispanConfig.getOptionalConfig('cryptoStore');\n const cryptoStoreOpts: Partial<InfinispanClientAuthOptions> = {};\n\n cryptoStoreOpts.key = cryptoStore?.getOptionalString('path');\n cryptoStoreOpts.passphrase = cryptoStore?.getOptionalString('passphrase');\n\n ssl.cryptoStore = cryptoStoreOpts as InfinispanClientAuthOptions;\n\n behaviorOptions.ssl = ssl as InfinispanSslOptions;\n\n const dataFormat = infinispanConfig.getOptionalConfig('dataFormat');\n const dataFormatOpts: Partial<DataFormatOptions> = {};\n\n const keyType = dataFormat?.getOptionalString('keyType') ?? 'text/plain';\n const valueType =\n dataFormat?.getOptionalString('valueType') ?? 'text/plain';\n\n if (keyType === 'text/plain' || keyType === 'application/json') {\n dataFormatOpts.keyType = keyType;\n } else if (keyType !== null && keyType !== undefined) {\n logger?.warn(\n `Invalid Infinispan dataFormat.keyType \"${keyType}\" in config at ${storeConfigPath}.dataFormat.keyType. Must be \"text/plain\" | \"application/json\". Not mapped, client will use default 'text/plain'.`,\n );\n }\n\n if (valueType === 'text/plain' || valueType === 'application/json') {\n dataFormatOpts.valueType = valueType;\n } else if (valueType !== null && valueType !== undefined) {\n logger?.warn(\n `Invalid Infinispan dataFormat.valueType \"${valueType}\" in config at ${storeConfigPath}.dataFormat.valueType. Must be \"text/plain\" | \"application/json\". Not mapped, client will use default 'text/plain'.`,\n );\n }\n behaviorOptions.dataFormat = dataFormatOpts as DataFormatOptions;\n parsedOptions.options = behaviorOptions as InfinispanClientBehaviorOptions;\n\n logger?.debug(\n `Parsed Infinispan options from config at ${storeConfigPath}: ${JSON.stringify(\n parsedOptions,\n )}`,\n );\n return parsedOptions as InfinispanCacheStoreOptions;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.cjs.js","sources":["../../../src/entrypoints/cache/types.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\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { HumanDuration, durationToMilliseconds } from '@backstage/types';\nimport { RedisClusterOptions, KeyvRedisOptions } from '@keyv/redis';\n\n/**\n * Options for Redis cache store.\n *\n * @public\n */\nexport type RedisCacheStoreOptions = {\n client?: KeyvRedisOptions;\n cluster?: RedisClusterOptions;\n};\n\n/**\n * Union type of all cache store options.\n *\n * @public\n */\nexport type CacheStoreOptions
|
|
1
|
+
{"version":3,"file":"types.cjs.js","sources":["../../../src/entrypoints/cache/types.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\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { HumanDuration, durationToMilliseconds } from '@backstage/types';\nimport { RedisClusterOptions, KeyvRedisOptions } from '@keyv/redis';\n\n/**\n * Options for Redis cache store.\n *\n * @public\n */\nexport type RedisCacheStoreOptions = {\n type: 'redis' | 'valkey';\n client?: KeyvRedisOptions;\n cluster?: RedisClusterOptions;\n};\n\n/**\n * Union type of all cache store options.\n *\n * @public\n */\nexport type CacheStoreOptions =\n | RedisCacheStoreOptions\n | InfinispanCacheStoreOptions;\n\n/**\n * Options given when constructing a {@link CacheManager}.\n *\n * @public\n */\nexport type CacheManagerOptions = {\n /**\n * An optional logger for use by the PluginCacheManager.\n */\n logger?: LoggerService;\n\n /**\n * An optional handler for connection errors emitted from the underlying data\n * store.\n */\n onError?: (err: Error) => void;\n};\n\nexport function ttlToMilliseconds(ttl: number | HumanDuration): number {\n return typeof ttl === 'number' ? ttl : durationToMilliseconds(ttl);\n}\n\n/**\n * Configuration for a single Infinispan server.\n */\nexport type InfinispanServerConfig = {\n host: string;\n port: number;\n};\n\n/**\n * Options for putting values into Infinispan cache.\n */\nexport type InfinispanPutOptions = {\n lifespan?: string;\n maxIdle?: string;\n previous?: boolean;\n flags?: string[];\n};\n/**\n * SSL/TLS options for the Infinispan client.\n */\nexport type InfinispanSslOptions = {\n enabled: boolean;\n secureProtocol?: string;\n trustCerts?: string[]; // Array of trusted CA certificates\n clientAuth?: InfinispanClientAuthOptions;\n cryptoStore?: InfinispanCryptoStoreOptions;\n sniHostName?: string;\n};\n\n/**\n * Authentication options for the Infinispan client.\n * This is used for client-side authentication with the Infinispan server.\n */\nexport type InfinispanClientAuthOptions = {\n key?: string;\n passphrase?: string;\n cert?: string;\n};\n\n/**\n * Options for the Infinispan client crypto store.\n * This is used for storing keys and certificates securely.\n */\nexport type InfinispanCryptoStoreOptions = {\n path?: string;\n passphrase?: string;\n};\n\n/**\n * Authentication options for the Infinispan client.\n */\nexport type InfinispanAuthOptions = {\n enabled: boolean;\n saslMechanism?: string;\n userName?: string;\n password?: string;\n token?: string;\n authzid?: string;\n};\n\n/**\n * Options for the Infinispan cache store, designed to be configured\n * in app-config.yaml under `backend.cache.infinispan`.\n */\nexport type InfinispanCacheStoreOptions = {\n type: 'infinispan';\n servers: InfinispanServerConfig | InfinispanServerConfig[];\n options?: InfinispanClientBehaviorOptions;\n};\n\nexport type InfinispanClusterConfig = {\n name?: string;\n servers: InfinispanServerConfig[];\n};\n\nexport type DataFormatOptions = {\n keyType: 'text/plain' | 'application/json';\n valueType: 'text/plain' | 'application/json';\n};\n\n/**\n * Detailed client behavior options for the Infinispan client.\n * @public\n */\nexport type InfinispanClientBehaviorOptions = {\n version?: '2.9' | '2.5' | '2.2';\n cacheName?: string;\n maxRetries?: number;\n connectionTimeout?: number;\n socketTimeout?: number;\n authentication?: InfinispanAuthOptions;\n ssl?: InfinispanSslOptions;\n mediaType?: 'text/plain' | 'application/json';\n topologyUpdates?: boolean;\n clusters?: InfinispanClusterConfig[];\n dataFormat: DataFormatOptions;\n};\n"],"names":["durationToMilliseconds"],"mappings":";;;;AA0DO,SAAS,kBAAkB,GAAA,EAAqC;AACrE,EAAA,OAAO,OAAO,GAAA,KAAQ,QAAA,GAAW,GAAA,GAAMA,6BAAuB,GAAG,CAAA;AACnE;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatabaseManager.cjs.js","sources":["../../../src/entrypoints/database/DatabaseManager.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 {\n DatabaseService,\n LifecycleService,\n LoggerService,\n RootConfigService,\n RootLifecycleService,\n RootLoggerService,\n} from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { stringifyError } from '@backstage/errors';\nimport { Knex } from 'knex';\nimport { MysqlConnector } from './connectors/mysql';\nimport { PgConnector } from './connectors/postgres';\nimport { Sqlite3Connector } from './connectors/sqlite3';\nimport { Connector } from './types';\n\n/**\n * Provides a config lookup path for a plugin's config block.\n */\nfunction pluginPath(pluginId: string): string {\n return `plugin.${pluginId}`;\n}\n\n/**\n * Creation options for {@link DatabaseManager}.\n *\n * @public\n */\nexport type DatabaseManagerOptions = {\n migrations?: DatabaseService['migrations'];\n rootLogger?: RootLoggerService;\n rootLifecycle?: RootLifecycleService;\n};\n\n/**\n * Testable implementation class for {@link DatabaseManager} below.\n */\nexport class DatabaseManagerImpl {\n constructor(\n private readonly config: Config,\n private readonly connectors: Record<string, Connector>,\n private readonly options?: DatabaseManagerOptions,\n private readonly databaseCache: Map<string, Promise<Knex>> = new Map(),\n private readonly keepaliveIntervals: Map<\n string,\n NodeJS.Timeout\n > = new Map(),\n ) {\n // If a rootLifecycle service was provided, register a shutdown hook to\n // clean up any database connections.\n if (options?.rootLifecycle !== undefined) {\n options.rootLifecycle.addShutdownHook(async () => {\n await this.shutdown({ logger: options.rootLogger });\n });\n }\n }\n\n /**\n * Generates a DatabaseService for consumption by plugins.\n *\n * @param pluginId - The plugin that the database manager should be created for. Plugin names\n * should be unique as they are used to look up database config overrides under\n * `backend.database.plugin`.\n */\n forPlugin(\n pluginId: string,\n deps: {\n logger: LoggerService;\n lifecycle: LifecycleService;\n },\n ): DatabaseService {\n const client = this.getClientType(pluginId).client;\n const connector = this.connectors[client];\n if (!connector) {\n throw new Error(\n `Unsupported database client type '${client}' specified for plugin '${pluginId}'`,\n );\n }\n const getClient = () => this.getDatabase(pluginId, connector, deps);\n\n const skip =\n this.options?.migrations?.skip ??\n this.config.getOptionalBoolean(`plugin.${pluginId}.skipMigrations`) ??\n this.config.getOptionalBoolean('skipMigrations') ??\n false;\n\n return { getClient, migrations: { skip } };\n }\n\n /**\n * Destroys all known connections.\n */\n private async shutdown(deps?: { logger?: LoggerService }): Promise<void> {\n const pluginIds = Array.from(this.databaseCache.keys());\n await Promise.allSettled(\n pluginIds.map(async pluginId => {\n // We no longer need to keep connections alive.\n clearInterval(this.keepaliveIntervals.get(pluginId));\n\n const connection = await this.databaseCache.get(pluginId);\n if (connection) {\n if (connection.client.config.includes('sqlite3')) {\n return; // sqlite3 does not support destroy, it hangs\n }\n await connection.destroy().catch((error: unknown) => {\n deps?.logger?.error(\n `Problem closing database connection for ${pluginId}: ${stringifyError(\n error,\n )}`,\n );\n });\n }\n }),\n );\n }\n\n /**\n * Provides the client type which should be used for a given plugin.\n *\n * The client type is determined by plugin specific config if present.\n * Otherwise the base client is used as the fallback.\n *\n * @param pluginId - Plugin to get the client type for\n * @returns Object with client type returned as `client` and boolean\n * representing whether or not the client was overridden as\n * `overridden`\n */\n private getClientType(pluginId: string): {\n client: string;\n overridden: boolean;\n } {\n const pluginClient = this.config.getOptionalString(\n `${pluginPath(pluginId)}.client`,\n );\n\n const baseClient = this.config.getString('client');\n const client = pluginClient ?? baseClient;\n return {\n client,\n overridden: client !== baseClient,\n };\n }\n\n /**\n * Provides a scoped Knex client for a plugin as per application config.\n *\n * @param pluginId - Plugin to get a Knex client for\n * @returns Promise which resolves to a scoped Knex database client for a\n * plugin\n */\n private async getDatabase(\n pluginId: string,\n connector: Connector,\n deps: {\n logger: LoggerService;\n lifecycle: LifecycleService;\n },\n ): Promise<Knex> {\n if (this.databaseCache.has(pluginId)) {\n return this.databaseCache.get(pluginId)!;\n }\n\n const clientPromise = connector.getClient(pluginId, deps);\n this.databaseCache.set(pluginId, clientPromise);\n\n if (process.env.NODE_ENV !== 'test') {\n clientPromise.then(client =>\n this.startKeepaliveLoop(pluginId, client, deps.logger),\n );\n }\n\n return clientPromise;\n }\n\n private startKeepaliveLoop(\n pluginId: string,\n client: Knex,\n logger: LoggerService,\n ): void {\n let lastKeepaliveFailed = false;\n\n this.keepaliveIntervals.set(\n pluginId,\n setInterval(() => {\n // During testing it can happen that the environment is torn down and\n // this client is `undefined`, but this interval is still run.\n client?.raw('select 1').then(\n () => {\n lastKeepaliveFailed = false;\n },\n (error: unknown) => {\n if (!lastKeepaliveFailed) {\n lastKeepaliveFailed = true;\n logger.warn(\n `Database keepalive failed for plugin ${pluginId}, ${stringifyError(\n error,\n )}`,\n );\n }\n },\n );\n }, 60 * 1000),\n );\n }\n}\n\n// NOTE: This class looks odd but is kept around for API compatibility reasons\n/**\n * Manages database connections for Backstage backend plugins.\n *\n * @public\n * @remarks\n *\n * The database manager allows the user to set connection and client settings on\n * a per pluginId basis by defining a database config block under\n * `plugin.<pluginId>` in addition to top level defaults. Optionally, a user may\n * set `prefix` which is used to prefix generated database names if config is\n * not provided.\n */\nexport class DatabaseManager {\n /**\n * Creates a {@link DatabaseManager} from `backend.database` config.\n *\n * @param config - The loaded application configuration.\n * @param options - An optional configuration object.\n */\n static fromConfig(\n config: RootConfigService,\n options?: DatabaseManagerOptions,\n ): DatabaseManager {\n const databaseConfig = config.getConfig('backend.database');\n const prefix =\n databaseConfig.getOptionalString('prefix') || 'backstage_plugin_';\n return new DatabaseManager(\n new DatabaseManagerImpl(\n databaseConfig,\n {\n pg: new PgConnector(databaseConfig, prefix),\n sqlite3: new Sqlite3Connector(databaseConfig),\n 'better-sqlite3': new Sqlite3Connector(databaseConfig),\n mysql: new MysqlConnector(databaseConfig, prefix),\n mysql2: new MysqlConnector(databaseConfig, prefix),\n },\n options,\n ),\n );\n }\n\n private constructor(private readonly impl: DatabaseManagerImpl) {}\n\n /**\n * Generates a DatabaseService for consumption by plugins.\n *\n * @param pluginId - The plugin that the database manager should be created for. Plugin names\n * should be unique as they are used to look up database config overrides under\n * `backend.database.plugin`.\n */\n forPlugin(\n pluginId: string,\n deps: {\n logger: LoggerService;\n lifecycle: LifecycleService;\n },\n ): DatabaseService {\n return this.impl.forPlugin(pluginId, deps);\n }\n}\n"],"names":["stringifyError","PgConnector","Sqlite3Connector","MysqlConnector"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DatabaseManager.cjs.js","sources":["../../../src/entrypoints/database/DatabaseManager.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 {\n DatabaseService,\n LifecycleService,\n LoggerService,\n RootConfigService,\n RootLifecycleService,\n RootLoggerService,\n} from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { stringifyError } from '@backstage/errors';\nimport { Knex } from 'knex';\nimport { MysqlConnector } from './connectors/mysql';\nimport { PgConnector } from './connectors/postgres';\nimport { Sqlite3Connector } from './connectors/sqlite3';\nimport { Connector } from './types';\n\n/**\n * Provides a config lookup path for a plugin's config block.\n */\nfunction pluginPath(pluginId: string): string {\n return `plugin.${pluginId}`;\n}\n\n/**\n * Creation options for {@link DatabaseManager}.\n *\n * @public\n */\nexport type DatabaseManagerOptions = {\n migrations?: DatabaseService['migrations'];\n rootLogger?: RootLoggerService;\n rootLifecycle?: RootLifecycleService;\n};\n\n/**\n * Testable implementation class for {@link DatabaseManager} below.\n */\nexport class DatabaseManagerImpl {\n constructor(\n private readonly config: Config,\n private readonly connectors: Record<string, Connector>,\n private readonly options?: DatabaseManagerOptions,\n private readonly databaseCache: Map<string, Promise<Knex>> = new Map(),\n private readonly keepaliveIntervals: Map<\n string,\n NodeJS.Timeout\n > = new Map(),\n ) {\n // If a rootLifecycle service was provided, register a shutdown hook to\n // clean up any database connections.\n if (options?.rootLifecycle !== undefined) {\n options.rootLifecycle.addShutdownHook(async () => {\n await this.shutdown({ logger: options.rootLogger });\n });\n }\n }\n\n /**\n * Generates a DatabaseService for consumption by plugins.\n *\n * @param pluginId - The plugin that the database manager should be created for. Plugin names\n * should be unique as they are used to look up database config overrides under\n * `backend.database.plugin`.\n */\n forPlugin(\n pluginId: string,\n deps: {\n logger: LoggerService;\n lifecycle: LifecycleService;\n },\n ): DatabaseService {\n const client = this.getClientType(pluginId).client;\n const connector = this.connectors[client];\n if (!connector) {\n throw new Error(\n `Unsupported database client type '${client}' specified for plugin '${pluginId}'`,\n );\n }\n const getClient = () => this.getDatabase(pluginId, connector, deps);\n\n const skip =\n this.options?.migrations?.skip ??\n this.config.getOptionalBoolean(`plugin.${pluginId}.skipMigrations`) ??\n this.config.getOptionalBoolean('skipMigrations') ??\n false;\n\n return { getClient, migrations: { skip } };\n }\n\n /**\n * Destroys all known connections.\n */\n private async shutdown(deps?: { logger?: LoggerService }): Promise<void> {\n const pluginIds = Array.from(this.databaseCache.keys());\n await Promise.allSettled(\n pluginIds.map(async pluginId => {\n // We no longer need to keep connections alive.\n clearInterval(this.keepaliveIntervals.get(pluginId));\n\n const connection = await this.databaseCache.get(pluginId);\n if (connection) {\n if (connection.client.config.includes('sqlite3')) {\n return; // sqlite3 does not support destroy, it hangs\n }\n await connection.destroy().catch((error: unknown) => {\n deps?.logger?.error(\n `Problem closing database connection for ${pluginId}: ${stringifyError(\n error,\n )}`,\n );\n });\n }\n }),\n );\n }\n\n /**\n * Provides the client type which should be used for a given plugin.\n *\n * The client type is determined by plugin specific config if present.\n * Otherwise the base client is used as the fallback.\n *\n * @param pluginId - Plugin to get the client type for\n * @returns Object with client type returned as `client` and boolean\n * representing whether or not the client was overridden as\n * `overridden`\n */\n private getClientType(pluginId: string): {\n client: string;\n overridden: boolean;\n } {\n const pluginClient = this.config.getOptionalString(\n `${pluginPath(pluginId)}.client`,\n );\n\n const baseClient = this.config.getString('client');\n const client = pluginClient ?? baseClient;\n return {\n client,\n overridden: client !== baseClient,\n };\n }\n\n /**\n * Provides a scoped Knex client for a plugin as per application config.\n *\n * @param pluginId - Plugin to get a Knex client for\n * @returns Promise which resolves to a scoped Knex database client for a\n * plugin\n */\n private async getDatabase(\n pluginId: string,\n connector: Connector,\n deps: {\n logger: LoggerService;\n lifecycle: LifecycleService;\n },\n ): Promise<Knex> {\n if (this.databaseCache.has(pluginId)) {\n return this.databaseCache.get(pluginId)!;\n }\n\n const clientPromise = connector.getClient(pluginId, deps);\n this.databaseCache.set(pluginId, clientPromise);\n\n if (process.env.NODE_ENV !== 'test') {\n clientPromise.then(client =>\n this.startKeepaliveLoop(pluginId, client, deps.logger),\n );\n }\n\n return clientPromise;\n }\n\n private startKeepaliveLoop(\n pluginId: string,\n client: Knex,\n logger: LoggerService,\n ): void {\n let lastKeepaliveFailed = false;\n\n this.keepaliveIntervals.set(\n pluginId,\n setInterval(() => {\n // During testing it can happen that the environment is torn down and\n // this client is `undefined`, but this interval is still run.\n client?.raw('select 1').then(\n () => {\n lastKeepaliveFailed = false;\n },\n (error: unknown) => {\n if (!lastKeepaliveFailed) {\n lastKeepaliveFailed = true;\n logger.warn(\n `Database keepalive failed for plugin ${pluginId}, ${stringifyError(\n error,\n )}`,\n );\n }\n },\n );\n }, 60 * 1000),\n );\n }\n}\n\n// NOTE: This class looks odd but is kept around for API compatibility reasons\n/**\n * Manages database connections for Backstage backend plugins.\n *\n * @public\n * @remarks\n *\n * The database manager allows the user to set connection and client settings on\n * a per pluginId basis by defining a database config block under\n * `plugin.<pluginId>` in addition to top level defaults. Optionally, a user may\n * set `prefix` which is used to prefix generated database names if config is\n * not provided.\n */\nexport class DatabaseManager {\n /**\n * Creates a {@link DatabaseManager} from `backend.database` config.\n *\n * @param config - The loaded application configuration.\n * @param options - An optional configuration object.\n */\n static fromConfig(\n config: RootConfigService,\n options?: DatabaseManagerOptions,\n ): DatabaseManager {\n const databaseConfig = config.getConfig('backend.database');\n const prefix =\n databaseConfig.getOptionalString('prefix') || 'backstage_plugin_';\n return new DatabaseManager(\n new DatabaseManagerImpl(\n databaseConfig,\n {\n pg: new PgConnector(databaseConfig, prefix),\n sqlite3: new Sqlite3Connector(databaseConfig),\n 'better-sqlite3': new Sqlite3Connector(databaseConfig),\n mysql: new MysqlConnector(databaseConfig, prefix),\n mysql2: new MysqlConnector(databaseConfig, prefix),\n },\n options,\n ),\n );\n }\n\n private constructor(private readonly impl: DatabaseManagerImpl) {}\n\n /**\n * Generates a DatabaseService for consumption by plugins.\n *\n * @param pluginId - The plugin that the database manager should be created for. Plugin names\n * should be unique as they are used to look up database config overrides under\n * `backend.database.plugin`.\n */\n forPlugin(\n pluginId: string,\n deps: {\n logger: LoggerService;\n lifecycle: LifecycleService;\n },\n ): DatabaseService {\n return this.impl.forPlugin(pluginId, deps);\n }\n}\n"],"names":["stringifyError","PgConnector","Sqlite3Connector","MysqlConnector"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaultNameOverride.cjs.js","sources":["../../../../src/entrypoints/database/connectors/defaultNameOverride.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';\n\n/**\n * Provides a partial knex config with database name override.\n *\n * Default override for knex database drivers which accept ConnectionConfig\n * with `connection.database` as the database name field.\n *\n * @param name - database name to get config override for\n */\nexport default function defaultNameOverride(\n name: string,\n): Partial<Knex.Config> {\n return {\n connection: {\n database: name,\n },\n };\n}\n"],"names":[],"mappings":";;;;AA0BA,SAAwB,oBACtB,
|
|
1
|
+
{"version":3,"file":"defaultNameOverride.cjs.js","sources":["../../../../src/entrypoints/database/connectors/defaultNameOverride.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';\n\n/**\n * Provides a partial knex config with database name override.\n *\n * Default override for knex database drivers which accept ConnectionConfig\n * with `connection.database` as the database name field.\n *\n * @param name - database name to get config override for\n */\nexport default function defaultNameOverride(\n name: string,\n): Partial<Knex.Config> {\n return {\n connection: {\n database: name,\n },\n };\n}\n"],"names":[],"mappings":";;;;AA0BA,SAAwB,oBACtB,IAAA,EACsB;AACtB,EAAA,OAAO;AAAA,IACL,UAAA,EAAY;AAAA,MACV,QAAA,EAAU;AAAA;AACZ,GACF;AACF;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaultSchemaOverride.cjs.js","sources":["../../../../src/entrypoints/database/connectors/defaultSchemaOverride.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';\n\n/**\n * Provides a partial knex config with schema name override.\n *\n * @param name - schema name to get config override for\n */\nexport default function defaultSchemaOverride(\n name: string,\n): Partial<Knex.Config> {\n return {\n searchPath: [name],\n };\n}\n"],"names":[],"mappings":";;;;AAuBA,SAAwB,sBACtB,
|
|
1
|
+
{"version":3,"file":"defaultSchemaOverride.cjs.js","sources":["../../../../src/entrypoints/database/connectors/defaultSchemaOverride.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';\n\n/**\n * Provides a partial knex config with schema name override.\n *\n * @param name - schema name to get config override for\n */\nexport default function defaultSchemaOverride(\n name: string,\n): Partial<Knex.Config> {\n return {\n searchPath: [name],\n };\n}\n"],"names":[],"mappings":";;;;AAuBA,SAAwB,sBACtB,IAAA,EACsB;AACtB,EAAA,OAAO;AAAA,IACL,UAAA,EAAY,CAAC,IAAI;AAAA,GACnB;AACF;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mergeDatabaseConfig.cjs.js","sources":["../../../../src/entrypoints/database/connectors/mergeDatabaseConfig.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\nimport { merge } from 'lodash';\n\n/**\n * Merges database objects together\n *\n * @public\n * @param config - The base config. The input is not modified\n * @param overrides - Any additional overrides\n */\nexport function mergeDatabaseConfig(config: any, ...overrides: any[]) {\n return merge({}, config, ...overrides);\n}\n"],"names":["merge"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"mergeDatabaseConfig.cjs.js","sources":["../../../../src/entrypoints/database/connectors/mergeDatabaseConfig.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\nimport { merge } from 'lodash';\n\n/**\n * Merges database objects together\n *\n * @public\n * @param config - The base config. The input is not modified\n * @param overrides - Any additional overrides\n */\nexport function mergeDatabaseConfig(config: any, ...overrides: any[]) {\n return merge({}, config, ...overrides);\n}\n"],"names":["merge"],"mappings":";;;;AAyBO,SAAS,mBAAA,CAAoB,WAAgB,SAAA,EAAkB;AACpE,EAAA,OAAOA,YAAA,CAAM,EAAC,EAAG,MAAA,EAAQ,GAAG,SAAS,CAAA;AACvC;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mysql.cjs.js","sources":["../../../../src/entrypoints/database/connectors/mysql.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\nimport { LifecycleService, LoggerService } from '@backstage/backend-plugin-api';\nimport { Config, ConfigReader } from '@backstage/config';\nimport { InputError } from '@backstage/errors';\nimport { JsonObject } from '@backstage/types';\nimport knexFactory, { Knex } from 'knex';\nimport { merge, omit } from 'lodash';\nimport limiterFactory from 'p-limit';\nimport yn from 'yn';\nimport { Connector } from '../types';\nimport defaultNameOverride from './defaultNameOverride';\nimport { mergeDatabaseConfig } from './mergeDatabaseConfig';\n\n// Limits the number of concurrent DDL operations to 1\nconst ddlLimiter = limiterFactory(1);\n\n/**\n * Creates a knex mysql database connection\n *\n * @param dbConfig - The database config\n * @param overrides - Additional options to merge with the config\n */\nexport function createMysqlDatabaseClient(\n dbConfig: Config,\n overrides?: Knex.Config,\n) {\n const knexConfig = buildMysqlDatabaseConfig(dbConfig, overrides);\n const database = knexFactory(knexConfig);\n return database;\n}\n\n/**\n * Builds a knex mysql database connection\n *\n * @param dbConfig - The database config\n * @param overrides - Additional options to merge with the config\n */\nexport function buildMysqlDatabaseConfig(\n dbConfig: Config,\n overrides?: Knex.Config,\n) {\n return mergeDatabaseConfig(\n dbConfig.get(),\n {\n connection: getMysqlConnectionConfig(dbConfig, !!overrides),\n useNullAsDefault: true,\n },\n overrides,\n );\n}\n\n/**\n * Gets the mysql connection config\n *\n * @param dbConfig - The database config\n * @param parseConnectionString - Flag to explicitly control connection string parsing\n */\nexport function getMysqlConnectionConfig(\n dbConfig: Config,\n parseConnectionString?: boolean,\n): Knex.MySqlConnectionConfig | string {\n const connection = dbConfig.get('connection') as any;\n const isConnectionString =\n typeof connection === 'string' || connection instanceof String;\n const autoParse = typeof parseConnectionString !== 'boolean';\n\n const shouldParseConnectionString = autoParse\n ? isConnectionString\n : parseConnectionString && isConnectionString;\n\n return shouldParseConnectionString\n ? parseMysqlConnectionString(connection as string)\n : connection;\n}\n\n/**\n * Parses a mysql connection string.\n *\n * e.g. mysql://examplename:somepassword@examplehost:3306/dbname\n * @param connectionString - The mysql connection string\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 InputError(\n `Error while parsing MySQL connection string, ${e}`,\n e,\n );\n }\n}\n\n/**\n * Creates the missing mysql database if it does not exist\n *\n * @param dbConfig - The database config\n * @param databases - The names of the databases to create\n */\nexport async function ensureMysqlDatabaseExists(\n dbConfig: Config,\n ...databases: Array<string>\n) {\n const admin = createMysqlDatabaseClient(dbConfig, {\n connection: {\n database: null as unknown as string,\n },\n pool: {\n min: 0,\n acquireTimeoutMillis: 10000,\n },\n });\n\n try {\n const ensureDatabase = async (database: string) => {\n await admin.raw(`CREATE DATABASE IF NOT EXISTS ??`, [database]);\n };\n await Promise.all(\n databases.map(async database => {\n // For initial setup we use a smaller timeout but several retries. Given that this\n // is a separate connection pool we should never really run into issues with connection\n // acquisition timeouts, but we do anyway. This might be a bug in knex or some other dependency.\n let lastErr: Error | undefined = undefined;\n for (let i = 0; i < 3; i++) {\n try {\n return await ddlLimiter(() => ensureDatabase(database));\n } catch (err) {\n lastErr = err;\n }\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n throw lastErr;\n }),\n );\n } finally {\n await admin.destroy();\n }\n}\n\n/**\n * Drops the given mysql databases.\n *\n * @param dbConfig - The database config\n * @param databases - The names of the databases to create\n */\nexport async function dropMysqlDatabase(\n dbConfig: Config,\n ...databases: Array<string>\n) {\n const admin = createMysqlDatabaseClient(dbConfig, {\n connection: {\n database: null as unknown as string,\n },\n pool: {\n min: 0,\n acquireTimeoutMillis: 10000,\n },\n });\n\n try {\n const dropDatabase = async (database: string) => {\n await admin.raw(`DROP DATABASE ??`, [database]);\n };\n await Promise.all(\n databases.map(async database => {\n return await ddlLimiter(() => dropDatabase(database));\n }),\n );\n } finally {\n await admin.destroy();\n }\n}\n\n/**\n * Provides a config lookup path for a plugin's config block.\n */\nfunction pluginPath(pluginId: string): string {\n return `plugin.${pluginId}`;\n}\n\nfunction normalizeConnection(\n connection: Knex.StaticConnectionConfig | JsonObject | string | undefined,\n): Partial<Knex.StaticConnectionConfig> {\n if (typeof connection === 'undefined' || connection === null) {\n return {};\n }\n\n return typeof connection === 'string' || connection instanceof String\n ? parseMysqlConnectionString(connection as string)\n : connection;\n}\n\nexport class MysqlConnector implements Connector {\n constructor(\n private readonly config: Config,\n private readonly prefix: string,\n ) {}\n\n async getClient(\n pluginId: string,\n _deps: {\n logger: LoggerService;\n lifecycle: LifecycleService;\n },\n ): Promise<Knex> {\n const pluginConfig = new ConfigReader(\n this.getConfigForPlugin(pluginId) as JsonObject,\n );\n\n const databaseName = this.getDatabaseName(pluginId);\n if (databaseName && this.getEnsureExistsConfig(pluginId)) {\n try {\n await ensureMysqlDatabaseExists(pluginConfig, databaseName);\n } catch (error) {\n throw new Error(\n `Failed to connect to the database to make sure that '${databaseName}' exists, ${error}`,\n );\n }\n }\n\n const pluginDivisionMode = this.getPluginDivisionModeConfig();\n if (pluginDivisionMode !== 'database') {\n throw new Error(\n `The MySQL driver does not support plugin division mode '${pluginDivisionMode}'`,\n );\n }\n\n const databaseClientOverrides = mergeDatabaseConfig(\n {},\n this.getDatabaseOverrides(pluginId),\n );\n\n const client = createMysqlDatabaseClient(\n pluginConfig,\n databaseClientOverrides,\n );\n\n return client;\n }\n\n /**\n * Provides the canonical database name for a given plugin.\n *\n * This method provides the effective database name which is determined using\n * global and plugin specific database config. If no explicit database name,\n * this method will provide a generated name which is the pluginId prefixed\n * with 'backstage_plugin_'.\n *\n * @param pluginId - Lookup the database name for given plugin\n * @returns String representing the plugin's database name\n */\n private getDatabaseName(pluginId: string): string | undefined {\n const connection = this.getConnectionConfig(pluginId);\n const databaseName = (connection as Knex.ConnectionConfig)?.database;\n return databaseName ?? `${this.prefix}${pluginId}`;\n }\n\n /**\n * Provides the client type which should be used for a given plugin.\n *\n * The client type is determined by plugin specific config if present.\n * Otherwise the base client is used as the fallback.\n *\n * @param pluginId - Plugin to get the client type for\n * @returns Object with client type returned as `client` and boolean\n * representing whether or not the client was overridden as\n * `overridden`\n */\n private getClientType(pluginId: string): {\n client: string;\n overridden: boolean;\n } {\n const pluginClient = this.config.getOptionalString(\n `${pluginPath(pluginId)}.client`,\n );\n\n const baseClient = this.config.getString('client');\n const client = pluginClient ?? baseClient;\n return {\n client,\n overridden: client !== baseClient,\n };\n }\n\n private getRoleConfig(pluginId: string): string | undefined {\n return (\n this.config.getOptionalString(`${pluginPath(pluginId)}.role`) ??\n this.config.getOptionalString('role')\n );\n }\n\n /**\n * Provides the knexConfig which should be used for a given plugin.\n *\n * @param pluginId - Plugin to get the knexConfig for\n * @returns The merged knexConfig value or undefined if it isn't specified\n */\n private getAdditionalKnexConfig(pluginId: string): JsonObject | undefined {\n const pluginConfig = this.config\n .getOptionalConfig(`${pluginPath(pluginId)}.knexConfig`)\n ?.get<JsonObject>();\n\n const baseConfig = this.config\n .getOptionalConfig('knexConfig')\n ?.get<JsonObject>();\n\n return merge(baseConfig, pluginConfig);\n }\n\n private getEnsureExistsConfig(pluginId: string): boolean {\n const baseConfig = this.config.getOptionalBoolean('ensureExists') ?? true;\n return (\n this.config.getOptionalBoolean(`${pluginPath(pluginId)}.ensureExists`) ??\n baseConfig\n );\n }\n\n private getPluginDivisionModeConfig(): string {\n return this.config.getOptionalString('pluginDivisionMode') ?? 'database';\n }\n\n /**\n * Provides a Knex connection plugin config by combining base and plugin\n * config.\n *\n * This method provides a baseConfig for a plugin database connector. If the\n * client type has not been overridden, the global connection config will be\n * included with plugin specific config as the base. Values from the plugin\n * connection take precedence over the base. Base database name is omitted\n * unless `pluginDivisionMode` is set to `schema`.\n */\n private getConnectionConfig(pluginId: string): Knex.StaticConnectionConfig {\n const { overridden } = this.getClientType(pluginId);\n\n let baseConnection = normalizeConnection(this.config.get('connection'));\n\n // Databases cannot be shared unless the `pluginDivisionMode` is set to `schema`. The\n // `database` property from the base connection is omitted unless `pluginDivisionMode`\n // is set to `schema`.\n if (this.getPluginDivisionModeConfig() !== 'schema') {\n baseConnection = omit(baseConnection, 'database');\n }\n\n // get and normalize optional plugin specific database connection\n const connection = normalizeConnection(\n this.config.getOptional(`${pluginPath(pluginId)}.connection`),\n );\n\n return {\n // include base connection if client type has not been overridden\n ...(overridden ? {} : baseConnection),\n ...connection,\n } as Knex.StaticConnectionConfig;\n }\n\n /**\n * Provides a Knex database config for a given plugin.\n *\n * This method provides a Knex configuration object along with the plugin's\n * client type.\n *\n * @param pluginId - The plugin that the database config should correspond with\n */\n private getConfigForPlugin(pluginId: string): Knex.Config {\n const { client } = this.getClientType(pluginId);\n const role = this.getRoleConfig(pluginId);\n\n return {\n ...this.getAdditionalKnexConfig(pluginId),\n client,\n connection: this.getConnectionConfig(pluginId),\n ...(role && { role }),\n };\n }\n\n /**\n * Provides a partial `Knex.Config`• database name override for a given plugin.\n *\n * @param pluginId - Target plugin to get database name override\n * @returns Partial `Knex.Config` with database name override\n */\n private getDatabaseOverrides(pluginId: string): Knex.Config {\n const databaseName = this.getDatabaseName(pluginId);\n return databaseName ? defaultNameOverride(databaseName) : {};\n }\n}\n"],"names":["limiterFactory","knexFactory","mergeDatabaseConfig","yn","InputError","ConfigReader","merge","omit","defaultNameOverride"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mysql.cjs.js","sources":["../../../../src/entrypoints/database/connectors/mysql.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\nimport { LifecycleService, LoggerService } from '@backstage/backend-plugin-api';\nimport { Config, ConfigReader } from '@backstage/config';\nimport { InputError } from '@backstage/errors';\nimport { JsonObject } from '@backstage/types';\nimport knexFactory, { Knex } from 'knex';\nimport { merge, omit } from 'lodash';\nimport limiterFactory from 'p-limit';\nimport yn from 'yn';\nimport { Connector } from '../types';\nimport defaultNameOverride from './defaultNameOverride';\nimport { mergeDatabaseConfig } from './mergeDatabaseConfig';\n\n// Limits the number of concurrent DDL operations to 1\nconst ddlLimiter = limiterFactory(1);\n\n/**\n * Creates a knex mysql database connection\n *\n * @param dbConfig - The database config\n * @param overrides - Additional options to merge with the config\n */\nexport function createMysqlDatabaseClient(\n dbConfig: Config,\n overrides?: Knex.Config,\n) {\n const knexConfig = buildMysqlDatabaseConfig(dbConfig, overrides);\n const database = knexFactory(knexConfig);\n return database;\n}\n\n/**\n * Builds a knex mysql database connection\n *\n * @param dbConfig - The database config\n * @param overrides - Additional options to merge with the config\n */\nexport function buildMysqlDatabaseConfig(\n dbConfig: Config,\n overrides?: Knex.Config,\n) {\n return mergeDatabaseConfig(\n dbConfig.get(),\n {\n connection: getMysqlConnectionConfig(dbConfig, !!overrides),\n useNullAsDefault: true,\n },\n overrides,\n );\n}\n\n/**\n * Gets the mysql connection config\n *\n * @param dbConfig - The database config\n * @param parseConnectionString - Flag to explicitly control connection string parsing\n */\nexport function getMysqlConnectionConfig(\n dbConfig: Config,\n parseConnectionString?: boolean,\n): Knex.MySqlConnectionConfig | string {\n const connection = dbConfig.get('connection') as any;\n const isConnectionString =\n typeof connection === 'string' || connection instanceof String;\n const autoParse = typeof parseConnectionString !== 'boolean';\n\n const shouldParseConnectionString = autoParse\n ? isConnectionString\n : parseConnectionString && isConnectionString;\n\n return shouldParseConnectionString\n ? parseMysqlConnectionString(connection as string)\n : connection;\n}\n\n/**\n * Parses a mysql connection string.\n *\n * e.g. mysql://examplename:somepassword@examplehost:3306/dbname\n * @param connectionString - The mysql connection string\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 InputError(\n `Error while parsing MySQL connection string, ${e}`,\n e,\n );\n }\n}\n\n/**\n * Creates the missing mysql database if it does not exist\n *\n * @param dbConfig - The database config\n * @param databases - The names of the databases to create\n */\nexport async function ensureMysqlDatabaseExists(\n dbConfig: Config,\n ...databases: Array<string>\n) {\n const admin = createMysqlDatabaseClient(dbConfig, {\n connection: {\n database: null as unknown as string,\n },\n pool: {\n min: 0,\n acquireTimeoutMillis: 10000,\n },\n });\n\n try {\n const ensureDatabase = async (database: string) => {\n await admin.raw(`CREATE DATABASE IF NOT EXISTS ??`, [database]);\n };\n await Promise.all(\n databases.map(async database => {\n // For initial setup we use a smaller timeout but several retries. Given that this\n // is a separate connection pool we should never really run into issues with connection\n // acquisition timeouts, but we do anyway. This might be a bug in knex or some other dependency.\n let lastErr: Error | undefined = undefined;\n for (let i = 0; i < 3; i++) {\n try {\n return await ddlLimiter(() => ensureDatabase(database));\n } catch (err) {\n lastErr = err;\n }\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n throw lastErr;\n }),\n );\n } finally {\n await admin.destroy();\n }\n}\n\n/**\n * Drops the given mysql databases.\n *\n * @param dbConfig - The database config\n * @param databases - The names of the databases to create\n */\nexport async function dropMysqlDatabase(\n dbConfig: Config,\n ...databases: Array<string>\n) {\n const admin = createMysqlDatabaseClient(dbConfig, {\n connection: {\n database: null as unknown as string,\n },\n pool: {\n min: 0,\n acquireTimeoutMillis: 10000,\n },\n });\n\n try {\n const dropDatabase = async (database: string) => {\n await admin.raw(`DROP DATABASE ??`, [database]);\n };\n await Promise.all(\n databases.map(async database => {\n return await ddlLimiter(() => dropDatabase(database));\n }),\n );\n } finally {\n await admin.destroy();\n }\n}\n\n/**\n * Provides a config lookup path for a plugin's config block.\n */\nfunction pluginPath(pluginId: string): string {\n return `plugin.${pluginId}`;\n}\n\nfunction normalizeConnection(\n connection: Knex.StaticConnectionConfig | JsonObject | string | undefined,\n): Partial<Knex.StaticConnectionConfig> {\n if (typeof connection === 'undefined' || connection === null) {\n return {};\n }\n\n return typeof connection === 'string' || connection instanceof String\n ? parseMysqlConnectionString(connection as string)\n : connection;\n}\n\nexport class MysqlConnector implements Connector {\n constructor(\n private readonly config: Config,\n private readonly prefix: string,\n ) {}\n\n async getClient(\n pluginId: string,\n _deps: {\n logger: LoggerService;\n lifecycle: LifecycleService;\n },\n ): Promise<Knex> {\n const pluginConfig = new ConfigReader(\n this.getConfigForPlugin(pluginId) as JsonObject,\n );\n\n const databaseName = this.getDatabaseName(pluginId);\n if (databaseName && this.getEnsureExistsConfig(pluginId)) {\n try {\n await ensureMysqlDatabaseExists(pluginConfig, databaseName);\n } catch (error) {\n throw new Error(\n `Failed to connect to the database to make sure that '${databaseName}' exists, ${error}`,\n );\n }\n }\n\n const pluginDivisionMode = this.getPluginDivisionModeConfig();\n if (pluginDivisionMode !== 'database') {\n throw new Error(\n `The MySQL driver does not support plugin division mode '${pluginDivisionMode}'`,\n );\n }\n\n const databaseClientOverrides = mergeDatabaseConfig(\n {},\n this.getDatabaseOverrides(pluginId),\n );\n\n const client = createMysqlDatabaseClient(\n pluginConfig,\n databaseClientOverrides,\n );\n\n return client;\n }\n\n /**\n * Provides the canonical database name for a given plugin.\n *\n * This method provides the effective database name which is determined using\n * global and plugin specific database config. If no explicit database name,\n * this method will provide a generated name which is the pluginId prefixed\n * with 'backstage_plugin_'.\n *\n * @param pluginId - Lookup the database name for given plugin\n * @returns String representing the plugin's database name\n */\n private getDatabaseName(pluginId: string): string | undefined {\n const connection = this.getConnectionConfig(pluginId);\n const databaseName = (connection as Knex.ConnectionConfig)?.database;\n return databaseName ?? `${this.prefix}${pluginId}`;\n }\n\n /**\n * Provides the client type which should be used for a given plugin.\n *\n * The client type is determined by plugin specific config if present.\n * Otherwise the base client is used as the fallback.\n *\n * @param pluginId - Plugin to get the client type for\n * @returns Object with client type returned as `client` and boolean\n * representing whether or not the client was overridden as\n * `overridden`\n */\n private getClientType(pluginId: string): {\n client: string;\n overridden: boolean;\n } {\n const pluginClient = this.config.getOptionalString(\n `${pluginPath(pluginId)}.client`,\n );\n\n const baseClient = this.config.getString('client');\n const client = pluginClient ?? baseClient;\n return {\n client,\n overridden: client !== baseClient,\n };\n }\n\n private getRoleConfig(pluginId: string): string | undefined {\n return (\n this.config.getOptionalString(`${pluginPath(pluginId)}.role`) ??\n this.config.getOptionalString('role')\n );\n }\n\n /**\n * Provides the knexConfig which should be used for a given plugin.\n *\n * @param pluginId - Plugin to get the knexConfig for\n * @returns The merged knexConfig value or undefined if it isn't specified\n */\n private getAdditionalKnexConfig(pluginId: string): JsonObject | undefined {\n const pluginConfig = this.config\n .getOptionalConfig(`${pluginPath(pluginId)}.knexConfig`)\n ?.get<JsonObject>();\n\n const baseConfig = this.config\n .getOptionalConfig('knexConfig')\n ?.get<JsonObject>();\n\n return merge(baseConfig, pluginConfig);\n }\n\n private getEnsureExistsConfig(pluginId: string): boolean {\n const baseConfig = this.config.getOptionalBoolean('ensureExists') ?? true;\n return (\n this.config.getOptionalBoolean(`${pluginPath(pluginId)}.ensureExists`) ??\n baseConfig\n );\n }\n\n private getPluginDivisionModeConfig(): string {\n return this.config.getOptionalString('pluginDivisionMode') ?? 'database';\n }\n\n /**\n * Provides a Knex connection plugin config by combining base and plugin\n * config.\n *\n * This method provides a baseConfig for a plugin database connector. If the\n * client type has not been overridden, the global connection config will be\n * included with plugin specific config as the base. Values from the plugin\n * connection take precedence over the base. Base database name is omitted\n * unless `pluginDivisionMode` is set to `schema`.\n */\n private getConnectionConfig(pluginId: string): Knex.StaticConnectionConfig {\n const { overridden } = this.getClientType(pluginId);\n\n let baseConnection = normalizeConnection(this.config.get('connection'));\n\n // Databases cannot be shared unless the `pluginDivisionMode` is set to `schema`. The\n // `database` property from the base connection is omitted unless `pluginDivisionMode`\n // is set to `schema`.\n if (this.getPluginDivisionModeConfig() !== 'schema') {\n baseConnection = omit(baseConnection, 'database');\n }\n\n // get and normalize optional plugin specific database connection\n const connection = normalizeConnection(\n this.config.getOptional(`${pluginPath(pluginId)}.connection`),\n );\n\n return {\n // include base connection if client type has not been overridden\n ...(overridden ? {} : baseConnection),\n ...connection,\n } as Knex.StaticConnectionConfig;\n }\n\n /**\n * Provides a Knex database config for a given plugin.\n *\n * This method provides a Knex configuration object along with the plugin's\n * client type.\n *\n * @param pluginId - The plugin that the database config should correspond with\n */\n private getConfigForPlugin(pluginId: string): Knex.Config {\n const { client } = this.getClientType(pluginId);\n const role = this.getRoleConfig(pluginId);\n\n return {\n ...this.getAdditionalKnexConfig(pluginId),\n client,\n connection: this.getConnectionConfig(pluginId),\n ...(role && { role }),\n };\n }\n\n /**\n * Provides a partial `Knex.Config`• database name override for a given plugin.\n *\n * @param pluginId - Target plugin to get database name override\n * @returns Partial `Knex.Config` with database name override\n */\n private getDatabaseOverrides(pluginId: string): Knex.Config {\n const databaseName = this.getDatabaseName(pluginId);\n return databaseName ? defaultNameOverride(databaseName) : {};\n }\n}\n"],"names":["limiterFactory","knexFactory","mergeDatabaseConfig","yn","InputError","ConfigReader","merge","omit","defaultNameOverride"],"mappings":"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"}
|