@kapeta/local-cluster-service 0.17.0 → 0.18.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.
Files changed (57) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/cjs/index.js +4 -2
  3. package/dist/cjs/src/assetManager.d.ts +2 -2
  4. package/dist/cjs/src/assetManager.js +16 -16
  5. package/dist/cjs/src/assets/routes.js +2 -2
  6. package/dist/cjs/src/authManager.d.ts +12 -0
  7. package/dist/cjs/src/authManager.js +60 -0
  8. package/dist/cjs/src/codeGeneratorManager.d.ts +1 -1
  9. package/dist/cjs/src/codeGeneratorManager.js +3 -3
  10. package/dist/cjs/src/configManager.js +2 -2
  11. package/dist/cjs/src/definitionsManager.d.ts +7 -6
  12. package/dist/cjs/src/definitionsManager.js +102 -18
  13. package/dist/cjs/src/instanceManager.d.ts +1 -1
  14. package/dist/cjs/src/instanceManager.js +4 -4
  15. package/dist/cjs/src/instances/routes.js +2 -2
  16. package/dist/cjs/src/operatorManager.d.ts +1 -1
  17. package/dist/cjs/src/operatorManager.js +7 -9
  18. package/dist/cjs/src/providerManager.d.ts +2 -1
  19. package/dist/cjs/src/providerManager.js +23 -15
  20. package/dist/cjs/src/repositoryManager.d.ts +2 -2
  21. package/dist/cjs/src/repositoryManager.js +8 -9
  22. package/dist/cjs/src/utils/BlockInstanceRunner.js +6 -8
  23. package/dist/esm/index.js +4 -2
  24. package/dist/esm/src/assetManager.d.ts +2 -2
  25. package/dist/esm/src/assetManager.js +16 -16
  26. package/dist/esm/src/assets/routes.js +2 -2
  27. package/dist/esm/src/authManager.d.ts +12 -0
  28. package/dist/esm/src/authManager.js +60 -0
  29. package/dist/esm/src/codeGeneratorManager.d.ts +1 -1
  30. package/dist/esm/src/codeGeneratorManager.js +3 -3
  31. package/dist/esm/src/configManager.js +2 -2
  32. package/dist/esm/src/definitionsManager.d.ts +7 -6
  33. package/dist/esm/src/definitionsManager.js +102 -18
  34. package/dist/esm/src/instanceManager.d.ts +1 -1
  35. package/dist/esm/src/instanceManager.js +4 -4
  36. package/dist/esm/src/instances/routes.js +2 -2
  37. package/dist/esm/src/operatorManager.d.ts +1 -1
  38. package/dist/esm/src/operatorManager.js +7 -9
  39. package/dist/esm/src/providerManager.d.ts +2 -1
  40. package/dist/esm/src/providerManager.js +23 -15
  41. package/dist/esm/src/repositoryManager.d.ts +2 -2
  42. package/dist/esm/src/repositoryManager.js +8 -9
  43. package/dist/esm/src/utils/BlockInstanceRunner.js +6 -8
  44. package/index.ts +4 -2
  45. package/package.json +1 -1
  46. package/src/assetManager.ts +18 -16
  47. package/src/assets/routes.ts +2 -2
  48. package/src/authManager.ts +62 -0
  49. package/src/codeGeneratorManager.ts +3 -3
  50. package/src/configManager.ts +2 -2
  51. package/src/definitionsManager.ts +132 -17
  52. package/src/instanceManager.ts +4 -4
  53. package/src/instances/routes.ts +2 -2
  54. package/src/operatorManager.ts +7 -12
  55. package/src/providerManager.ts +27 -19
  56. package/src/repositoryManager.ts +8 -11
  57. package/src/utils/BlockInstanceRunner.ts +6 -8
@@ -1,5 +1,6 @@
1
+ import { DefinitionInfo } from '@kapeta/local-cluster-config';
1
2
  declare class ProviderManager {
2
- getWebProviders(): import("@kapeta/local-cluster-config").DefinitionInfo[];
3
+ getWebProviders(): Promise<DefinitionInfo[]>;
3
4
  getProviderWebJS(handle: string, name: string, version: string, sourceMap?: boolean): Promise<unknown>;
4
5
  }
5
6
  export declare const providerManager: ProviderManager;
@@ -11,8 +11,9 @@ const cacheManager_1 = require("./cacheManager");
11
11
  const request_1 = __importDefault(require("request"));
12
12
  const PROVIDER_FILE_BASE = 'https://providers.kapeta.com/files';
13
13
  class ProviderManager {
14
- getWebProviders() {
15
- return definitionsManager_1.definitionsManager.getProviderDefinitions().filter((providerDefinition) => providerDefinition.hasWeb);
14
+ async getWebProviders() {
15
+ const providers = await definitionsManager_1.definitionsManager.getProviderDefinitions();
16
+ return providers.filter((providerDefinition) => providerDefinition.hasWeb);
16
17
  }
17
18
  async getProviderWebJS(handle, name, version, sourceMap = false) {
18
19
  const fullName = `${handle}/${name}`;
@@ -22,7 +23,8 @@ class ProviderManager {
22
23
  if (file && (await fs_extra_1.default.pathExists(file))) {
23
24
  return fs_extra_1.default.readFile(file, 'utf8');
24
25
  }
25
- const installedProvider = this.getWebProviders().find((providerDefinition) => {
26
+ const providers = await this.getWebProviders();
27
+ const installedProvider = providers.find((providerDefinition) => {
26
28
  return providerDefinition.definition.metadata.name === fullName && providerDefinition.version === version;
27
29
  });
28
30
  if (installedProvider) {
@@ -57,16 +59,22 @@ class ProviderManager {
57
59
  });
58
60
  }
59
61
  }
60
- const providerDefinitions = definitionsManager_1.definitionsManager.getProviderDefinitions();
61
- if (providerDefinitions.length > 0) {
62
- console.log('## Loaded the following providers ##');
63
- providerDefinitions.forEach((providerDefinition) => {
64
- console.log(' - %s[%s:%s]', providerDefinition.definition.kind, providerDefinition.definition.metadata.name, providerDefinition.version);
65
- console.log(' from %s', providerDefinition.path);
66
- });
67
- console.log('##');
68
- }
69
- else {
70
- console.log('## No providers found ##');
71
- }
62
+ definitionsManager_1.definitionsManager
63
+ .getProviderDefinitions()
64
+ .then((providerDefinitions) => {
65
+ if (providerDefinitions.length > 0) {
66
+ console.log('## Loaded the following providers ##');
67
+ providerDefinitions.forEach((providerDefinition) => {
68
+ console.log(' - %s[%s:%s]', providerDefinition.definition.kind, providerDefinition.definition.metadata.name, providerDefinition.version);
69
+ console.log(' from %s', providerDefinition.path);
70
+ });
71
+ console.log('##');
72
+ }
73
+ else {
74
+ console.log('## No providers found ##');
75
+ }
76
+ })
77
+ .catch((e) => {
78
+ console.error('Failed to load providers', e);
79
+ });
72
80
  exports.providerManager = new ProviderManager();
@@ -12,8 +12,8 @@ declare class RepositoryManager {
12
12
  */
13
13
  setSourceOfChangeFor(file: string, source: SourceOfChange): Promise<void>;
14
14
  clearSourceOfChangeFor(file: string): Promise<void>;
15
- ensureDefaultProviders(): void;
16
- private _install;
15
+ ensureDefaultProviders(): Promise<void>;
16
+ private scheduleInstallation;
17
17
  ensureAsset(handle: string, name: string, version: string, wait?: boolean): Promise<undefined | Task[]>;
18
18
  }
19
19
  export declare const repositoryManager: RepositoryManager;
@@ -53,18 +53,18 @@ class RepositoryManager {
53
53
  clearSourceOfChangeFor(file) {
54
54
  return this.watcher.clearSourceOfChangeFor(file);
55
55
  }
56
- ensureDefaultProviders() {
56
+ async ensureDefaultProviders() {
57
57
  socketManager_1.socketManager.emitGlobal(EVENT_DEFAULT_PROVIDERS_START, { providers: DEFAULT_PROVIDERS });
58
- const tasks = this._install(DEFAULT_PROVIDERS);
58
+ const tasks = await this.scheduleInstallation(DEFAULT_PROVIDERS);
59
59
  Promise.allSettled(tasks.map((t) => t.wait())).then(() => {
60
60
  socketManager_1.socketManager.emitGlobal(EVENT_DEFAULT_PROVIDERS_END, {});
61
61
  });
62
62
  }
63
- _install(refs) {
63
+ async scheduleInstallation(refs) {
64
64
  //We make sure to only install one asset at a time - otherwise unexpected things might happen
65
65
  const createInstaller = (ref) => {
66
66
  return async () => {
67
- if (definitionsManager_1.definitionsManager.exists(ref)) {
67
+ if (await definitionsManager_1.definitionsManager.exists(ref)) {
68
68
  return;
69
69
  }
70
70
  //console.log(`Installing asset: ${ref}`);
@@ -89,7 +89,7 @@ class RepositoryManager {
89
89
  continue;
90
90
  }
91
91
  ref = (0, utils_1.normalizeKapetaUri)(ref);
92
- if (definitionsManager_1.definitionsManager.exists(ref)) {
92
+ if (await definitionsManager_1.definitionsManager.exists(ref)) {
93
93
  continue;
94
94
  }
95
95
  const task = taskManager_1.taskManager.add(`asset:install:${ref}`, createInstaller(ref), {
@@ -107,8 +107,7 @@ class RepositoryManager {
107
107
  //TODO: Get dependencies for local asset
108
108
  return;
109
109
  }
110
- const definitions = definitionsManager_1.definitionsManager.getDefinitions();
111
- const installedAsset = definitions.find((d) => d.definition.metadata.name === fullName && d.version === version);
110
+ const installedAsset = await definitionsManager_1.definitionsManager.getDefinition(`${fullName}:${version}`);
112
111
  let assetVersion;
113
112
  try {
114
113
  assetVersion = await this._registryService.getVersion(fullName, version);
@@ -125,13 +124,13 @@ class RepositoryManager {
125
124
  }
126
125
  let tasks = undefined;
127
126
  if (!installedAsset) {
128
- tasks = this._install([ref]);
127
+ tasks = await this.scheduleInstallation([ref]);
129
128
  }
130
129
  else {
131
130
  //Ensure dependencies are installed
132
131
  const refs = assetVersion.dependencies.map((dep) => dep.name);
133
132
  if (refs.length > 0) {
134
- tasks = this._install(refs);
133
+ tasks = await this.scheduleInstallation(refs);
135
134
  }
136
135
  }
137
136
  if (tasks && wait) {
@@ -27,8 +27,9 @@ const DOCKER_ENV_VARS = [
27
27
  `KAPETA_LOCAL_CLUSTER_HOST=host.docker.internal`,
28
28
  `KAPETA_ENVIRONMENT_TYPE=docker`,
29
29
  ];
30
- function getProvider(uri) {
31
- return definitionsManager_1.definitionsManager.getProviderDefinitions().find((provider) => {
30
+ async function getProvider(uri) {
31
+ const providers = await definitionsManager_1.definitionsManager.getProviderDefinitions();
32
+ return providers.find((provider) => {
32
33
  const ref = `${provider.definition.metadata.name}:${provider.version}`;
33
34
  return (0, nodejs_utils_1.parseKapetaUri)(ref).id === uri.id;
34
35
  });
@@ -76,15 +77,12 @@ class BlockInstanceRunner {
76
77
  if (!blockUri.version) {
77
78
  blockUri.version = 'local';
78
79
  }
79
- const assetVersion = definitionsManager_1.definitionsManager.getDefinitions().find((definitions) => {
80
- const ref = `${definitions.definition.metadata.name}:${definitions.version}`;
81
- return (0, nodejs_utils_1.parseKapetaUri)(ref).id === blockUri.id;
82
- });
80
+ const assetVersion = await definitionsManager_1.definitionsManager.getDefinition(blockUri.id);
83
81
  if (!assetVersion) {
84
82
  throw new Error(`Block definition not found: ${blockUri.id}`);
85
83
  }
86
84
  const kindUri = (0, nodejs_utils_1.parseKapetaUri)(assetVersion.definition.kind);
87
- const providerVersion = getProvider(kindUri);
85
+ const providerVersion = await getProvider(kindUri);
88
86
  if (!providerVersion) {
89
87
  throw new Error(`Kind not found: ${kindUri.id}`);
90
88
  }
@@ -120,7 +118,7 @@ class BlockInstanceRunner {
120
118
  throw new Error('Missing target kind in block definition');
121
119
  }
122
120
  const kindUri = (0, nodejs_utils_1.parseKapetaUri)(assetVersion.definition.spec?.target?.kind);
123
- const targetVersion = getProvider(kindUri);
121
+ const targetVersion = await getProvider(kindUri);
124
122
  if (!targetVersion) {
125
123
  throw new Error(`Target not found: ${kindUri.id}`);
126
124
  }
package/index.ts CHANGED
@@ -23,6 +23,7 @@ import request from 'request';
23
23
  import { repositoryManager } from './src/repositoryManager';
24
24
  import { ensureCLI } from './src/utils/commandLineUtils';
25
25
  import { defaultProviderInstaller } from './src/utils/DefaultProviderInstaller';
26
+ import { authManager } from './src/authManager';
26
27
 
27
28
  export type LocalClusterService = HTTP.Server & { host?: string; port?: number };
28
29
 
@@ -147,6 +148,7 @@ export default {
147
148
  }
148
149
 
149
150
  await clusterService.init();
151
+ await authManager.listenForChanges();
150
152
 
151
153
  currentServer = createServer();
152
154
 
@@ -177,7 +179,7 @@ export default {
177
179
 
178
180
  const bindHost = getBindHost(host);
179
181
 
180
- currentServer.listen(port, bindHost, () => {
182
+ currentServer.listen(port, bindHost, async () => {
181
183
  try {
182
184
  ensureCLI().catch((e: any) => console.error('Failed to install CLI.', e));
183
185
  } catch (e: any) {
@@ -186,7 +188,7 @@ export default {
186
188
 
187
189
  try {
188
190
  // Start installation process for all default providers
189
- repositoryManager.ensureDefaultProviders();
191
+ await repositoryManager.ensureDefaultProviders();
190
192
  } catch (e: any) {
191
193
  console.error('Failed to install default providers.', e);
192
194
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kapeta/local-cluster-service",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "Manages configuration, ports and service discovery for locally running Kapeta systems",
5
5
  "type": "commonjs",
6
6
  "exports": {
@@ -64,9 +64,9 @@ class AssetManager {
64
64
  * @param {string[]} [assetKinds]
65
65
  * @returns {{path: *, ref: string, data: *, editable: boolean, kind: *, exists: boolean}[]}
66
66
  */
67
- getAssets(assetKinds?: string[]): EnrichedAsset[] {
67
+ async getAssets(assetKinds?: string[]): Promise<EnrichedAsset[]> {
68
68
  if (!assetKinds) {
69
- const blockTypeProviders = definitionsManager.getDefinitions([
69
+ const blockTypeProviders = await definitionsManager.getDefinitions([
70
70
  'core/block-type',
71
71
  'core/block-type-operator',
72
72
  ]);
@@ -76,12 +76,12 @@ class AssetManager {
76
76
  assetKinds.push('core/plan');
77
77
  }
78
78
 
79
- const assets = definitionsManager.getDefinitions(assetKinds);
79
+ const assets = await definitionsManager.getDefinitions(assetKinds);
80
80
 
81
81
  return assets.map(enrichAsset);
82
82
  }
83
83
 
84
- getPlans(): EnrichedAsset[] {
84
+ async getPlans(): Promise<EnrichedAsset[]> {
85
85
  return this.getAssets(['core/plan']);
86
86
  }
87
87
 
@@ -110,18 +110,18 @@ class AssetManager {
110
110
  await repositoryManager.ensureAsset(uri.handle, uri.name, uri.version, true);
111
111
  }
112
112
 
113
- let asset = definitionsManager
114
- .getDefinitions()
115
- .map(enrichAsset)
116
- .find((a) => parseKapetaUri(a.ref).equals(uri));
117
- if (autoFetch && !asset) {
113
+ const definitionInfo = await definitionsManager.getDefinition(ref);
114
+ if (autoFetch && !definitionInfo) {
118
115
  throw new Error('Asset not found: ' + ref);
119
116
  }
120
- if (asset) {
117
+
118
+ if (definitionInfo) {
119
+ const asset = enrichAsset(definitionInfo);
121
120
  cacheManager.set(cacheKey, asset, CACHE_TTL);
121
+ return asset;
122
122
  }
123
123
 
124
- return asset;
124
+ return undefined;
125
125
  }
126
126
 
127
127
  async createAsset(
@@ -151,7 +151,7 @@ class AssetManager {
151
151
 
152
152
  const ref = `kapeta://${yaml.metadata.name}:local`;
153
153
 
154
- this.maybeGenerateCode(ref, path, yaml);
154
+ await this.maybeGenerateCode(ref, path, yaml);
155
155
 
156
156
  return asset;
157
157
  }
@@ -178,7 +178,7 @@ class AssetManager {
178
178
  cacheManager.remove(toKey(ref));
179
179
  definitionsManager.clearCache();
180
180
 
181
- this.maybeGenerateCode(asset.ref, asset.ymlPath, yaml);
181
+ await this.maybeGenerateCode(asset.ref, asset.ymlPath, yaml);
182
182
  }
183
183
 
184
184
  async importFile(filePath: string) {
@@ -206,7 +206,9 @@ class AssetManager {
206
206
 
207
207
  definitionsManager.clearCache();
208
208
 
209
- return this.getAssets().filter((a) => refs.some((ref) => compareRefs(ref, a.ref)));
209
+ const assets = await this.getAssets();
210
+
211
+ return assets.filter((a) => refs.some((ref) => compareRefs(ref, a.ref)));
210
212
  }
211
213
 
212
214
  async unregisterAsset(ref: string) {
@@ -236,9 +238,9 @@ class AssetManager {
236
238
  return await repositoryManager.ensureAsset(uri.handle, uri.name, uri.version, false);
237
239
  }
238
240
 
239
- private maybeGenerateCode(ref: string, ymlPath: string, block: BlockDefinition) {
241
+ private async maybeGenerateCode(ref: string, ymlPath: string, block: BlockDefinition) {
240
242
  ref = normalizeKapetaUri(ref);
241
- if (codeGeneratorManager.canGenerateCode(block)) {
243
+ if (await codeGeneratorManager.canGenerateCode(block)) {
242
244
  const assetTitle = block.metadata.title ? block.metadata.title : parseKapetaUri(block.metadata.name).name;
243
245
  taskManager.add(
244
246
  `codegen:${ref}`,
@@ -31,8 +31,8 @@ router.use('/', stringBody);
31
31
  /**
32
32
  * Get all local assets available
33
33
  */
34
- router.get('/', (req: Request, res: Response) => {
35
- res.send(assetManager.getAssets([]));
34
+ router.get('/', async (req: Request, res: Response) => {
35
+ res.send(await assetManager.getAssets([]));
36
36
  });
37
37
 
38
38
  /**
@@ -0,0 +1,62 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import Path from 'node:path';
3
+ import chokidar, { FSWatcher } from 'chokidar';
4
+ import ClusterConfiguration from '@kapeta/local-cluster-config';
5
+ import { WatchEventName } from './types';
6
+ import { definitionsManager } from './definitionsManager';
7
+ import { KapetaAPI } from '@kapeta/nodejs-api-client';
8
+ import { socketManager } from './socketManager';
9
+
10
+ class AuthManager extends EventEmitter {
11
+ private watcher?: FSWatcher;
12
+
13
+ private hadToken: boolean;
14
+
15
+ constructor() {
16
+ super();
17
+ this.hadToken = this.hasToken();
18
+ }
19
+
20
+ public listenForChanges() {
21
+ const parentDir = Path.dirname(ClusterConfiguration.getKapetaBasedir());
22
+ //We watch the parent dir to catch changes to the base dir itself
23
+ this.watcher = chokidar.watch(parentDir, {
24
+ followSymlinks: false,
25
+ ignorePermissionErrors: true,
26
+ disableGlobbing: true,
27
+ persistent: true,
28
+ ignoreInitial: true,
29
+ depth: 1,
30
+ ignored: (path) => {
31
+ return !path.startsWith(ClusterConfiguration.getKapetaBasedir());
32
+ },
33
+ });
34
+ this.watcher.add(ClusterConfiguration.getKapetaBasedir());
35
+ this.watcher.on('all', this.handleFileChange.bind(this));
36
+ this.watcher.on('error', (error) => {
37
+ console.log('Error watching repository', error);
38
+ });
39
+ this.watcher.on('ready', () => {
40
+ console.log('Watching for auth changes: %s', ClusterConfiguration.getKapetaBasedir());
41
+ });
42
+ }
43
+
44
+ private hasToken() {
45
+ const api = new KapetaAPI();
46
+ return api.hasToken();
47
+ }
48
+
49
+ private async handleFileChange(eventName: WatchEventName, path: string) {
50
+ const hasTokenNow = this.hasToken();
51
+ if (this.hadToken !== hasTokenNow) {
52
+ socketManager.emitGlobal('auth-change', {});
53
+ if (hasTokenNow) {
54
+ // Clear the cache in case we need to rewrite the sample plan
55
+ definitionsManager.clearCache();
56
+ }
57
+ this.hadToken = hasTokenNow;
58
+ }
59
+ }
60
+ }
61
+
62
+ export const authManager = new AuthManager();
@@ -10,7 +10,7 @@ const BLOCK_TYPE_KIND = 'core/block-type';
10
10
  class CodeGeneratorManager {
11
11
  async reload() {
12
12
  Targets.reset();
13
- const languageTargets = definitionsManager.getDefinitions(TARGET_KIND);
13
+ const languageTargets = await definitionsManager.getDefinitions(TARGET_KIND);
14
14
  for (const languageTarget of languageTargets) {
15
15
  const key = `${languageTarget.definition.metadata.name}:${languageTarget.version}`;
16
16
  try {
@@ -26,13 +26,13 @@ class CodeGeneratorManager {
26
26
  }
27
27
  }
28
28
 
29
- canGenerateCode(yamlContent: BlockDefinition): boolean {
29
+ async canGenerateCode(yamlContent: BlockDefinition): Promise<boolean> {
30
30
  if (!yamlContent.spec.target?.kind) {
31
31
  //Not all block types have targets
32
32
  return false;
33
33
  }
34
34
 
35
- const blockTypes = definitionsManager.getDefinitions(BLOCK_TYPE_KIND);
35
+ const blockTypes = await definitionsManager.getDefinitions(BLOCK_TYPE_KIND);
36
36
  const blockTypeKinds = blockTypes.map(
37
37
  (blockType) => blockType.definition.metadata.name.toLowerCase() + ':' + blockType.version
38
38
  );
@@ -80,7 +80,7 @@ class ConfigManager {
80
80
  if (systemId) {
81
81
  systemId = normalizeKapetaUri(systemId);
82
82
  }
83
- const planAssets = assetManager.getPlans();
83
+ const planAssets = await assetManager.getPlans();
84
84
 
85
85
  const blockUri = parseKapetaUri(blockRef);
86
86
 
@@ -132,7 +132,7 @@ class ConfigManager {
132
132
  async verifyIdentity(blockRef: string, systemId: string, instanceId: string) {
133
133
  blockRef = normalizeKapetaUri(blockRef);
134
134
  systemId = normalizeKapetaUri(systemId);
135
- const planAssets = assetManager.getPlans();
135
+ const planAssets = await assetManager.getPlans();
136
136
  const systemUri = systemId ? parseKapetaUri(systemId) : null;
137
137
  const blockUri = parseKapetaUri(blockRef);
138
138
  let found = false;
@@ -1,39 +1,154 @@
1
1
  import ClusterConfiguration, { DefinitionInfo } from '@kapeta/local-cluster-config';
2
2
  import { parseKapetaUri } from '@kapeta/nodejs-utils';
3
3
  import { cacheManager, doCached } from './cacheManager';
4
+ import { KapetaAPI } from '@kapeta/nodejs-api-client';
5
+ import { Plan } from '@kapeta/schemas';
6
+ import FS from 'fs-extra';
7
+ import { normalizeKapetaUri } from './utils/utils';
8
+ import YAML from 'yaml';
9
+ import { Actions } from '@kapeta/nodejs-registry-utils';
10
+ import { ProgressListener } from './progressListener';
11
+ import Path from 'path';
12
+
13
+ export const SAMPLE_PLAN_NAME = 'kapeta/sample-nodejs-plan';
14
+
15
+ function applyHandleChange(definition: DefinitionInfo, targetHandle: string) {
16
+ const originalUri = parseKapetaUri(definition.definition.metadata.name);
17
+ definition.definition.metadata.name = `${targetHandle}/${originalUri.name}`;
18
+ return definition;
19
+ }
20
+
21
+ function normalizeFilters(kindFilter?: string | string[]) {
22
+ let resolvedFilters: string[] = [];
23
+
24
+ if (kindFilter) {
25
+ if (Array.isArray(kindFilter)) {
26
+ resolvedFilters = [...kindFilter];
27
+ } else {
28
+ resolvedFilters = [kindFilter];
29
+ }
30
+ }
31
+
32
+ return resolvedFilters.map((k) => k.toLowerCase());
33
+ }
4
34
 
5
35
  class DefinitionsManager {
6
- private getHash(kindFilter?: string | string[]) {
7
- if (kindFilter) {
8
- if (Array.isArray(kindFilter)) {
9
- return kindFilter.join(',');
36
+ private async resolveDefinitionsAndSamples() {
37
+ const definitions = ClusterConfiguration.getDefinitions();
38
+ const samplePlan = definitions.find(
39
+ (d) => d.version === 'local' && d.definition.metadata.name === SAMPLE_PLAN_NAME
40
+ );
41
+
42
+ if (!samplePlan) {
43
+ return definitions;
44
+ }
45
+
46
+ // We will only rewrite the sample plan once since we change the handle to be the users handle
47
+ const api = new KapetaAPI();
48
+ if (!api.hasToken()) {
49
+ // Not logged in yet, so we can't rewrite the sample plan
50
+ return definitions;
51
+ }
52
+ const profile = await api.getCurrentIdentity();
53
+ if (!profile) {
54
+ // Not logged in yet, so we can't rewrite the sample plan
55
+ return definitions;
56
+ }
57
+
58
+ console.log('Rewriting sample plan to use handle %s', profile.handle);
59
+
60
+ applyHandleChange(samplePlan, profile.handle);
61
+
62
+ const planDef = samplePlan.definition as Plan;
63
+
64
+ const blockRefs = new Set<string>();
65
+
66
+ planDef.spec.blocks.forEach((b) => {
67
+ const blockUri = parseKapetaUri(b.block.ref);
68
+ if (blockUri.version === 'local') {
69
+ blockRefs.add(blockUri.id);
70
+ b.block.ref = normalizeKapetaUri(`${profile.handle}/${blockUri.name}:local`);
10
71
  }
11
- return kindFilter;
72
+ });
73
+
74
+ // Rewrite all blocks that are referenced by the sample plan
75
+ const rewrittenBlocks = Array.from(blockRefs)
76
+ .map((ref) =>
77
+ definitions.find(
78
+ (d) => normalizeKapetaUri(d.definition.metadata.name + ':' + d.version) === normalizeKapetaUri(ref)
79
+ )
80
+ )
81
+ .filter((d) => d !== undefined)
82
+ .map((d) => applyHandleChange(d!, profile.handle));
83
+
84
+ // Persist the rewritten assets
85
+ const progressListener = new ProgressListener();
86
+ const rewrittenAssets = [samplePlan, ...rewrittenBlocks];
87
+ const originalRefs = [`${SAMPLE_PLAN_NAME}:local`, ...Array.from(blockRefs)];
88
+
89
+ // Store the original paths on the assets - we'll need them later
90
+ for (const asset of rewrittenAssets) {
91
+ asset.path = await FS.readlink(asset.path);
92
+ asset.ymlPath = Path.join(asset.path, Path.basename(asset.ymlPath));
93
+ }
94
+
95
+ // Uninstall the original assets
96
+ // This removes the symlinks
97
+ console.log('Uninstalling original assets', originalRefs);
98
+ try {
99
+ await Actions.uninstall(progressListener, originalRefs);
100
+ } catch (err) {
101
+ console.warn('Failed to uninstall original assets', err);
12
102
  }
13
- return 'none';
103
+
104
+ for (const asset of rewrittenAssets) {
105
+ console.log('Updating %s ', asset.ymlPath);
106
+ await FS.writeFile(asset.ymlPath, YAML.stringify(asset.definition));
107
+
108
+ console.log('Linking %s ', asset.path);
109
+ await Actions.link(progressListener, asset.path);
110
+ }
111
+
112
+ console.log('Rewrite done for sample plan');
113
+
114
+ // Return the rewritten definitions
115
+ return ClusterConfiguration.getDefinitions();
14
116
  }
15
117
 
16
- private getFullKey(kindFilter?: string | string[]) {
17
- return `definitionsManager:${this.getHash(kindFilter)}`;
118
+ private applyFilters(definitions: DefinitionInfo[], kindFilter: string[]): DefinitionInfo[] {
119
+ if (kindFilter.length === 0) {
120
+ return definitions;
121
+ }
122
+
123
+ return definitions.filter((d) => {
124
+ return kindFilter.includes(d.definition.kind.toLowerCase());
125
+ });
18
126
  }
19
127
 
20
- public getDefinitions(kindFilter?: string | string[]): DefinitionInfo[] {
21
- const key = this.getFullKey(kindFilter);
128
+ public async getDefinitions(kindFilter?: string | string[]): Promise<DefinitionInfo[]> {
129
+ kindFilter = normalizeFilters(kindFilter);
130
+
131
+ const definitions = await doCached<Promise<DefinitionInfo[]>>('definitionsManager:all', () =>
132
+ this.resolveDefinitionsAndSamples()
133
+ );
22
134
 
23
- return doCached<DefinitionInfo[]>(key, () => ClusterConfiguration.getDefinitions(kindFilter));
135
+ return this.applyFilters(definitions, kindFilter);
24
136
  }
25
137
 
26
- public exists(ref: string) {
27
- return !!this.getDefinition(ref);
138
+ public async exists(ref: string) {
139
+ return !!(await this.getDefinition(ref));
28
140
  }
29
141
 
30
- public getProviderDefinitions(): DefinitionInfo[] {
31
- return doCached<DefinitionInfo[]>('providers', () => ClusterConfiguration.getProviderDefinitions());
142
+ public async getProviderDefinitions(): Promise<DefinitionInfo[]> {
143
+ return doCached<DefinitionInfo[]>('definitionsManager:providers', () =>
144
+ ClusterConfiguration.getProviderDefinitions()
145
+ );
32
146
  }
33
147
 
34
- public getDefinition(ref: string) {
148
+ public async getDefinition(ref: string) {
35
149
  const uri = parseKapetaUri(ref);
36
- return this.getDefinitions().find((d) => {
150
+ const definitions = await this.getDefinitions();
151
+ return definitions.find((d) => {
37
152
  if (!uri.version) {
38
153
  return d.definition.metadata.name === uri.fullName;
39
154
  }
@@ -54,14 +54,14 @@ export class InstanceManager {
54
54
  return [...this._instances];
55
55
  }
56
56
 
57
- public getInstancesForPlan(systemId: string) {
57
+ public async getInstancesForPlan(systemId: string) {
58
58
  if (!this._instances) {
59
59
  return [];
60
60
  }
61
61
 
62
62
  systemId = normalizeKapetaUri(systemId);
63
63
 
64
- const planInfo = definitionsManager.getDefinition(systemId);
64
+ const planInfo = await definitionsManager.getDefinition(systemId);
65
65
 
66
66
  if (!planInfo) {
67
67
  return [];
@@ -458,9 +458,9 @@ export class InstanceManager {
458
458
 
459
459
  const blockSpec = blockAsset.data.spec as BlockDefinitionSpec;
460
460
  if (blockSpec.consumers) {
461
- const promises = blockSpec.consumers.map((consumer) => {
461
+ const promises = blockSpec.consumers.map(async (consumer) => {
462
462
  const consumerUri = parseKapetaUri(consumer.kind);
463
- const asset = definitionsManager.getDefinition(consumer.kind);
463
+ const asset = await definitionsManager.getDefinition(consumer.kind);
464
464
  if (!asset) {
465
465
  // Definition not found
466
466
  return Promise.resolve();
@@ -21,8 +21,8 @@ router.get('/', (req: Request, res: Response) => {
21
21
  /**
22
22
  * Get all instances
23
23
  */
24
- router.get('/:systemId/instances', (req: Request, res: Response) => {
25
- res.send(instanceManager.getInstancesForPlan(req.params.systemId));
24
+ router.get('/:systemId/instances', async (req: Request, res: Response) => {
25
+ res.send(await instanceManager.getInstancesForPlan(req.params.systemId));
26
26
  });
27
27
 
28
28
  /**