@kapeta/local-cluster-service 0.15.3 → 0.16.1
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 +14 -0
- package/dist/cjs/src/RepositoryWatcher.d.ts +23 -0
- package/dist/cjs/src/RepositoryWatcher.js +269 -0
- package/dist/cjs/src/assetManager.d.ts +4 -6
- package/dist/cjs/src/assetManager.js +34 -44
- package/dist/cjs/src/assets/routes.js +2 -2
- package/dist/cjs/src/cacheManager.d.ts +16 -0
- package/dist/cjs/src/cacheManager.js +38 -0
- package/dist/cjs/src/definitionsManager.d.ts +2 -4
- package/dist/cjs/src/definitionsManager.js +7 -21
- package/dist/cjs/src/instanceManager.js +4 -1
- package/dist/cjs/src/repositoryManager.d.ts +9 -4
- package/dist/cjs/src/repositoryManager.js +18 -91
- package/dist/cjs/src/types.d.ts +2 -0
- package/dist/cjs/src/utils/utils.js +1 -1
- package/dist/esm/src/RepositoryWatcher.d.ts +23 -0
- package/dist/esm/src/RepositoryWatcher.js +262 -0
- package/dist/esm/src/assetManager.d.ts +4 -6
- package/dist/esm/src/assetManager.js +35 -45
- package/dist/esm/src/assets/routes.js +2 -2
- package/dist/esm/src/cacheManager.d.ts +16 -0
- package/dist/esm/src/cacheManager.js +31 -0
- package/dist/esm/src/definitionsManager.d.ts +2 -4
- package/dist/esm/src/definitionsManager.js +7 -21
- package/dist/esm/src/instanceManager.js +4 -1
- package/dist/esm/src/repositoryManager.d.ts +9 -4
- package/dist/esm/src/repositoryManager.js +18 -91
- package/dist/esm/src/types.d.ts +2 -0
- package/dist/esm/src/utils/utils.js +1 -1
- package/package.json +2 -2
- package/src/RepositoryWatcher.ts +302 -0
- package/src/assetManager.ts +51 -55
- package/src/assets/routes.ts +2 -2
- package/src/cacheManager.ts +45 -0
- package/src/definitionsManager.ts +9 -33
- package/src/identities/routes.ts +0 -1
- package/src/instanceManager.ts +4 -1
- package/src/repositoryManager.ts +20 -95
- package/src/types.ts +2 -2
- package/src/utils/utils.ts +1 -1
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## [0.16.1](https://github.com/kapetacom/local-cluster-service/compare/v0.16.0...v0.16.1) (2023-08-11)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* Instead of ignoring we set source-of-change ([ecd23c4](https://github.com/kapetacom/local-cluster-service/commit/ecd23c4c3316a189038a1f2f2f2c8794f6153261))
|
7
|
+
|
8
|
+
# [0.16.0](https://github.com/kapetacom/local-cluster-service/compare/v0.15.3...v0.16.0) (2023-08-11)
|
9
|
+
|
10
|
+
|
11
|
+
### Features
|
12
|
+
|
13
|
+
* Use chokidar lib for watching for changes on disk ([#60](https://github.com/kapetacom/local-cluster-service/issues/60)) ([f2af855](https://github.com/kapetacom/local-cluster-service/commit/f2af85554fc2a23133ce27a4f8989cabdea097d7))
|
14
|
+
|
1
15
|
## [0.15.3](https://github.com/kapetacom/local-cluster-service/compare/v0.15.2...v0.15.3) (2023-08-10)
|
2
16
|
|
3
17
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
import { SourceOfChange } from './types';
|
2
|
+
export declare class RepositoryWatcher {
|
3
|
+
private watcher?;
|
4
|
+
private disabled;
|
5
|
+
private readonly baseDir;
|
6
|
+
private allDefinitions;
|
7
|
+
private symbolicLinks;
|
8
|
+
private sourceOfChange;
|
9
|
+
constructor();
|
10
|
+
setDisabled(disabled: boolean): void;
|
11
|
+
watch(): void;
|
12
|
+
setSourceOfChangeFor(file: string, source: SourceOfChange): Promise<void>;
|
13
|
+
clearSourceOfChangeFor(file: string): Promise<void>;
|
14
|
+
unwatch(): Promise<void>;
|
15
|
+
private getAssetIdentity;
|
16
|
+
private handleFileChange;
|
17
|
+
private checkForChange;
|
18
|
+
private exists;
|
19
|
+
private removeSymlinkTarget;
|
20
|
+
private updateSymlinkTarget;
|
21
|
+
private addSymlinkTarget;
|
22
|
+
private ignoreFile;
|
23
|
+
}
|
@@ -0,0 +1,269 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.RepositoryWatcher = void 0;
|
7
|
+
const chokidar_1 = __importDefault(require("chokidar"));
|
8
|
+
const local_cluster_config_1 = __importDefault(require("@kapeta/local-cluster-config"));
|
9
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
10
|
+
const node_path_1 = __importDefault(require("node:path"));
|
11
|
+
const yaml_1 = __importDefault(require("yaml"));
|
12
|
+
const nodejs_utils_1 = require("@kapeta/nodejs-utils");
|
13
|
+
const lodash_1 = __importDefault(require("lodash"));
|
14
|
+
const socketManager_1 = require("./socketManager");
|
15
|
+
const cacheManager_1 = require("./cacheManager");
|
16
|
+
const KAPETA_YML_RX = /^kapeta.ya?ml$/;
|
17
|
+
class RepositoryWatcher {
|
18
|
+
watcher;
|
19
|
+
disabled = false;
|
20
|
+
baseDir;
|
21
|
+
allDefinitions = [];
|
22
|
+
symbolicLinks = {};
|
23
|
+
sourceOfChange = new Map();
|
24
|
+
constructor() {
|
25
|
+
this.baseDir = local_cluster_config_1.default.getRepositoryBasedir();
|
26
|
+
}
|
27
|
+
setDisabled(disabled) {
|
28
|
+
this.disabled = disabled;
|
29
|
+
}
|
30
|
+
watch() {
|
31
|
+
if (!fs_extra_1.default.existsSync(this.baseDir)) {
|
32
|
+
fs_extra_1.default.mkdirpSync(this.baseDir);
|
33
|
+
}
|
34
|
+
this.allDefinitions = local_cluster_config_1.default.getDefinitions();
|
35
|
+
try {
|
36
|
+
this.watcher = chokidar_1.default.watch(this.baseDir, {
|
37
|
+
followSymlinks: false,
|
38
|
+
ignorePermissionErrors: true,
|
39
|
+
disableGlobbing: true,
|
40
|
+
persistent: true,
|
41
|
+
depth: 2,
|
42
|
+
ignored: (path) => this.ignoreFile(path),
|
43
|
+
});
|
44
|
+
this.watcher.on('all', this.handleFileChange.bind(this));
|
45
|
+
this.watcher.on('error', (error) => {
|
46
|
+
console.log('Error watching repository', error);
|
47
|
+
});
|
48
|
+
this.watcher.on('ready', () => {
|
49
|
+
console.log('Watching local repository for provider changes: %s', this.baseDir);
|
50
|
+
});
|
51
|
+
}
|
52
|
+
catch (e) {
|
53
|
+
// Fallback to run without watch mode due to potential platform issues.
|
54
|
+
// https://nodejs.org/docs/latest/api/fs.html#caveats
|
55
|
+
console.log('Unable to watch for changes. Changes to assets will not update automatically.', e);
|
56
|
+
return;
|
57
|
+
}
|
58
|
+
}
|
59
|
+
async setSourceOfChangeFor(file, source) {
|
60
|
+
this.sourceOfChange.set(file, source);
|
61
|
+
const realPath = await fs_extra_1.default.realpath(file);
|
62
|
+
if (realPath !== file) {
|
63
|
+
this.sourceOfChange.set(realPath, source);
|
64
|
+
}
|
65
|
+
}
|
66
|
+
async clearSourceOfChangeFor(file) {
|
67
|
+
this.sourceOfChange.delete(file);
|
68
|
+
const realPath = await fs_extra_1.default.realpath(file);
|
69
|
+
if (realPath !== file) {
|
70
|
+
this.sourceOfChange.delete(realPath);
|
71
|
+
}
|
72
|
+
}
|
73
|
+
async unwatch() {
|
74
|
+
if (!this.watcher) {
|
75
|
+
return;
|
76
|
+
}
|
77
|
+
this.symbolicLinks = {};
|
78
|
+
await this.watcher.close();
|
79
|
+
this.watcher = undefined;
|
80
|
+
}
|
81
|
+
async getAssetIdentity(path) {
|
82
|
+
const baseName = node_path_1.default.basename(path);
|
83
|
+
let handle, name, version;
|
84
|
+
if (path.startsWith(this.baseDir)) {
|
85
|
+
const relativePath = node_path_1.default.relative(this.baseDir, path);
|
86
|
+
// Inside the repo we can use the path to determine the handle, name and version
|
87
|
+
[handle, name, version] = relativePath.split(/\//g);
|
88
|
+
if (!handle || !name || !version) {
|
89
|
+
// Do nothing with this
|
90
|
+
return;
|
91
|
+
}
|
92
|
+
return {
|
93
|
+
handle,
|
94
|
+
name,
|
95
|
+
version,
|
96
|
+
};
|
97
|
+
}
|
98
|
+
if (!KAPETA_YML_RX.test(baseName)) {
|
99
|
+
// Do nothing with this
|
100
|
+
return;
|
101
|
+
}
|
102
|
+
// Outside the repo we need to use the file content to determine the handle, name
|
103
|
+
// Version is always 'local'
|
104
|
+
version = 'local';
|
105
|
+
try {
|
106
|
+
const definition = yaml_1.default.parse((await fs_extra_1.default.readFile(path)).toString());
|
107
|
+
const uri = (0, nodejs_utils_1.parseKapetaUri)(definition.metadata.name);
|
108
|
+
handle = uri.handle;
|
109
|
+
name = uri.name;
|
110
|
+
return {
|
111
|
+
handle,
|
112
|
+
name,
|
113
|
+
version,
|
114
|
+
};
|
115
|
+
}
|
116
|
+
catch (e) {
|
117
|
+
// Ignore issues in the YML file
|
118
|
+
return;
|
119
|
+
}
|
120
|
+
}
|
121
|
+
async handleFileChange(eventName, path) {
|
122
|
+
if (!path) {
|
123
|
+
return;
|
124
|
+
}
|
125
|
+
//console.log('File changed', eventName, path);
|
126
|
+
const assetIdentity = await this.getAssetIdentity(path);
|
127
|
+
if (!assetIdentity) {
|
128
|
+
return;
|
129
|
+
}
|
130
|
+
if (this.disabled) {
|
131
|
+
return;
|
132
|
+
}
|
133
|
+
// If this is false it's because we're watching a symlink target
|
134
|
+
const withinRepo = path.startsWith(this.baseDir);
|
135
|
+
if (withinRepo && assetIdentity.version === 'local' && path.endsWith('/local')) {
|
136
|
+
// This is likely a symlink target
|
137
|
+
if (eventName === 'add') {
|
138
|
+
//console.log('Checking if we should add symlink target', handle, name, version, path);
|
139
|
+
await this.addSymlinkTarget(path);
|
140
|
+
}
|
141
|
+
if (eventName === 'unlink') {
|
142
|
+
await this.removeSymlinkTarget(path);
|
143
|
+
}
|
144
|
+
if (eventName === 'change') {
|
145
|
+
await this.updateSymlinkTarget(path);
|
146
|
+
}
|
147
|
+
}
|
148
|
+
const sourceOfChange = this.sourceOfChange.get(path) ?? 'filesystem';
|
149
|
+
await this.checkForChange(assetIdentity, sourceOfChange);
|
150
|
+
// We consume the sourceOfChange when the file is changed
|
151
|
+
this.sourceOfChange.delete(path);
|
152
|
+
}
|
153
|
+
async checkForChange(assetIdentity, sourceOfChange) {
|
154
|
+
const ymlPath = node_path_1.default.join(this.baseDir, assetIdentity.handle, assetIdentity.name, assetIdentity.version, 'kapeta.yml');
|
155
|
+
const newDefinitions = local_cluster_config_1.default.getDefinitions();
|
156
|
+
const newDefinition = newDefinitions.find((d) => d.ymlPath === ymlPath);
|
157
|
+
let currentDefinition = this.allDefinitions.find((d) => d.ymlPath === ymlPath);
|
158
|
+
const ymlExists = await this.exists(ymlPath);
|
159
|
+
let type;
|
160
|
+
if (ymlExists) {
|
161
|
+
if (currentDefinition) {
|
162
|
+
if (newDefinition && lodash_1.default.isEqual(currentDefinition, newDefinition)) {
|
163
|
+
//Definition was not changed
|
164
|
+
return;
|
165
|
+
}
|
166
|
+
type = 'updated';
|
167
|
+
}
|
168
|
+
else if (newDefinition) {
|
169
|
+
type = 'added';
|
170
|
+
currentDefinition = newDefinition;
|
171
|
+
}
|
172
|
+
else {
|
173
|
+
//Other definition was added / updated - ignore
|
174
|
+
return;
|
175
|
+
}
|
176
|
+
}
|
177
|
+
else {
|
178
|
+
if (currentDefinition) {
|
179
|
+
const ref = (0, nodejs_utils_1.parseKapetaUri)(`${currentDefinition.definition.metadata.name}:${currentDefinition.version}`).id;
|
180
|
+
//Something was removed
|
181
|
+
type = 'removed';
|
182
|
+
}
|
183
|
+
else {
|
184
|
+
//Other definition was removed - ignore
|
185
|
+
return;
|
186
|
+
}
|
187
|
+
}
|
188
|
+
const payload = {
|
189
|
+
type,
|
190
|
+
definition: newDefinition?.definition ?? currentDefinition?.definition,
|
191
|
+
asset: assetIdentity,
|
192
|
+
sourceOfChange,
|
193
|
+
};
|
194
|
+
this.allDefinitions = newDefinitions;
|
195
|
+
//console.log('Asset changed', payload);
|
196
|
+
socketManager_1.socketManager.emitGlobal('asset-change', payload);
|
197
|
+
cacheManager_1.cacheManager.flush();
|
198
|
+
}
|
199
|
+
async exists(path) {
|
200
|
+
try {
|
201
|
+
await fs_extra_1.default.access(path);
|
202
|
+
return true;
|
203
|
+
}
|
204
|
+
catch (e) {
|
205
|
+
return false;
|
206
|
+
}
|
207
|
+
}
|
208
|
+
async removeSymlinkTarget(path) {
|
209
|
+
if (this.symbolicLinks[path]) {
|
210
|
+
//console.log('Unwatching symlink target %s => %s', path, this.symbolicLinks[path]);
|
211
|
+
this.watcher?.unwatch(this.symbolicLinks[path]);
|
212
|
+
delete this.symbolicLinks[path];
|
213
|
+
}
|
214
|
+
}
|
215
|
+
async updateSymlinkTarget(path) {
|
216
|
+
if (this.symbolicLinks[path]) {
|
217
|
+
//console.log('Updating symlink target %s => %s', path, this.symbolicLinks[path]);
|
218
|
+
this.watcher?.unwatch(this.symbolicLinks[path]);
|
219
|
+
delete this.symbolicLinks[path];
|
220
|
+
await this.addSymlinkTarget(path);
|
221
|
+
}
|
222
|
+
}
|
223
|
+
async addSymlinkTarget(path) {
|
224
|
+
try {
|
225
|
+
// Make sure we're not watching the symlink target
|
226
|
+
await this.removeSymlinkTarget(path);
|
227
|
+
const stat = await fs_extra_1.default.lstat(path);
|
228
|
+
if (stat.isSymbolicLink()) {
|
229
|
+
const realPath = `${await fs_extra_1.default.realpath(path)}/kapeta.yml`;
|
230
|
+
if (await this.exists(realPath)) {
|
231
|
+
//console.log('Watching symlink target %s => %s', path, realPath);
|
232
|
+
this.watcher?.add(realPath);
|
233
|
+
this.symbolicLinks[path] = realPath;
|
234
|
+
}
|
235
|
+
}
|
236
|
+
}
|
237
|
+
catch (e) {
|
238
|
+
// Ignore
|
239
|
+
console.warn('Failed to check local symlink target', e);
|
240
|
+
}
|
241
|
+
}
|
242
|
+
ignoreFile(path) {
|
243
|
+
if (!path.startsWith(this.baseDir)) {
|
244
|
+
return false;
|
245
|
+
}
|
246
|
+
if (path.includes('/node_modules/')) {
|
247
|
+
return true;
|
248
|
+
}
|
249
|
+
const filename = node_path_1.default.basename(path);
|
250
|
+
if (filename.startsWith('.')) {
|
251
|
+
return true;
|
252
|
+
}
|
253
|
+
const relativePath = node_path_1.default.relative(this.baseDir, path).split(node_path_1.default.sep);
|
254
|
+
try {
|
255
|
+
if (fs_extra_1.default.statSync(path).isDirectory()) {
|
256
|
+
if (relativePath.length > 3) {
|
257
|
+
return true;
|
258
|
+
}
|
259
|
+
return false;
|
260
|
+
}
|
261
|
+
}
|
262
|
+
catch (e) {
|
263
|
+
// Didn't exist - dont ignore
|
264
|
+
return false;
|
265
|
+
}
|
266
|
+
return !/^kapeta\.ya?ml$/.test(filename);
|
267
|
+
}
|
268
|
+
}
|
269
|
+
exports.RepositoryWatcher = RepositoryWatcher;
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import { Definition } from '@kapeta/local-cluster-config';
|
2
2
|
import { BlockDefinition } from '@kapeta/schemas';
|
3
|
+
import { SourceOfChange } from './types';
|
3
4
|
export interface EnrichedAsset {
|
4
5
|
ref: string;
|
5
6
|
editable: boolean;
|
@@ -11,9 +12,6 @@ export interface EnrichedAsset {
|
|
11
12
|
ymlPath: string;
|
12
13
|
}
|
13
14
|
declare class AssetManager {
|
14
|
-
private cache;
|
15
|
-
constructor();
|
16
|
-
clearCache(): void;
|
17
15
|
/**
|
18
16
|
*
|
19
17
|
* @param {string[]} [assetKinds]
|
@@ -23,12 +21,12 @@ declare class AssetManager {
|
|
23
21
|
getPlans(): EnrichedAsset[];
|
24
22
|
getPlan(ref: string, noCache?: boolean): Promise<Definition>;
|
25
23
|
getAsset(ref: string, noCache?: boolean, autoFetch?: boolean): Promise<EnrichedAsset | undefined>;
|
26
|
-
createAsset(path: string, yaml: BlockDefinition): Promise<EnrichedAsset[]>;
|
27
|
-
updateAsset(ref: string, yaml: BlockDefinition): Promise<void>;
|
28
|
-
private maybeGenerateCode;
|
24
|
+
createAsset(path: string, yaml: BlockDefinition, sourceOfChange?: SourceOfChange): Promise<EnrichedAsset[]>;
|
25
|
+
updateAsset(ref: string, yaml: BlockDefinition, sourceOfChange?: SourceOfChange): Promise<void>;
|
29
26
|
importFile(filePath: string): Promise<EnrichedAsset[]>;
|
30
27
|
unregisterAsset(ref: string): Promise<void>;
|
31
28
|
installAsset(ref: string): Promise<import("./taskManager").Task<void>[] | undefined>;
|
29
|
+
private maybeGenerateCode;
|
32
30
|
}
|
33
31
|
export declare const assetManager: AssetManager;
|
34
32
|
export {};
|
@@ -5,10 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
6
|
exports.assetManager = void 0;
|
7
7
|
const node_path_1 = __importDefault(require("node:path"));
|
8
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
9
8
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
10
9
|
const yaml_1 = __importDefault(require("yaml"));
|
11
|
-
const node_cache_1 = __importDefault(require("node-cache"));
|
12
10
|
const codeGeneratorManager_1 = require("./codeGeneratorManager");
|
13
11
|
const progressListener_1 = require("./progressListener");
|
14
12
|
const nodejs_utils_1 = require("@kapeta/nodejs-utils");
|
@@ -17,6 +15,8 @@ const nodejs_registry_utils_1 = require("@kapeta/nodejs-registry-utils");
|
|
17
15
|
const definitionsManager_1 = require("./definitionsManager");
|
18
16
|
const utils_1 = require("./utils/utils");
|
19
17
|
const taskManager_1 = require("./taskManager");
|
18
|
+
const cacheManager_1 = require("./cacheManager");
|
19
|
+
const CACHE_TTL = 60 * 60 * 1000; // 1 hour
|
20
20
|
function enrichAsset(asset) {
|
21
21
|
return {
|
22
22
|
ref: `kapeta://${asset.definition.metadata.name}:${asset.version}`,
|
@@ -42,15 +42,6 @@ function parseRef(ref) {
|
|
42
42
|
return [out[0].toLowerCase(), out[1].toLowerCase()];
|
43
43
|
}
|
44
44
|
class AssetManager {
|
45
|
-
cache;
|
46
|
-
constructor() {
|
47
|
-
this.cache = new node_cache_1.default({
|
48
|
-
stdTTL: 60 * 60, // 1 hour
|
49
|
-
});
|
50
|
-
}
|
51
|
-
clearCache() {
|
52
|
-
this.cache.flushAll();
|
53
|
-
}
|
54
45
|
/**
|
55
46
|
*
|
56
47
|
* @param {string[]} [assetKinds]
|
@@ -83,8 +74,8 @@ class AssetManager {
|
|
83
74
|
async getAsset(ref, noCache = false, autoFetch = true) {
|
84
75
|
ref = (0, utils_1.normalizeKapetaUri)(ref);
|
85
76
|
const cacheKey = `getAsset:${ref}`;
|
86
|
-
if (!noCache &&
|
87
|
-
return
|
77
|
+
if (!noCache && cacheManager_1.cacheManager.has(cacheKey)) {
|
78
|
+
return cacheManager_1.cacheManager.get(cacheKey);
|
88
79
|
}
|
89
80
|
const uri = (0, nodejs_utils_1.parseKapetaUri)(ref);
|
90
81
|
if (autoFetch) {
|
@@ -98,29 +89,27 @@ class AssetManager {
|
|
98
89
|
throw new Error('Asset not found: ' + ref);
|
99
90
|
}
|
100
91
|
if (asset) {
|
101
|
-
|
92
|
+
cacheManager_1.cacheManager.set(cacheKey, asset, CACHE_TTL);
|
102
93
|
}
|
103
94
|
return asset;
|
104
95
|
}
|
105
|
-
async createAsset(path, yaml) {
|
106
|
-
if (
|
96
|
+
async createAsset(path, yaml, sourceOfChange = 'filesystem') {
|
97
|
+
if (await fs_extra_1.default.pathExists(path)) {
|
107
98
|
throw new Error('File already exists: ' + path);
|
108
99
|
}
|
109
100
|
const dirName = node_path_1.default.dirname(path);
|
110
|
-
if (!
|
111
|
-
fs_extra_1.default.
|
101
|
+
if (!(await fs_extra_1.default.pathExists(dirName))) {
|
102
|
+
await fs_extra_1.default.mkdirp(dirName);
|
112
103
|
}
|
113
|
-
|
114
|
-
|
104
|
+
await repositoryManager_1.repositoryManager.setSourceOfChangeFor(path, sourceOfChange);
|
105
|
+
await fs_extra_1.default.writeFile(path, yaml_1.default.stringify(yaml));
|
115
106
|
const asset = await this.importFile(path);
|
116
|
-
|
117
|
-
this.cache.flushAll();
|
118
|
-
definitionsManager_1.definitionsManager.clearCache();
|
107
|
+
cacheManager_1.cacheManager.flush();
|
119
108
|
const ref = `kapeta://${yaml.metadata.name}:local`;
|
120
109
|
this.maybeGenerateCode(ref, path, yaml);
|
121
110
|
return asset;
|
122
111
|
}
|
123
|
-
async updateAsset(ref, yaml) {
|
112
|
+
async updateAsset(ref, yaml, sourceOfChange = 'filesystem') {
|
124
113
|
const asset = await this.getAsset(ref, true, false);
|
125
114
|
if (!asset) {
|
126
115
|
throw new Error('Attempted to update unknown asset: ' + ref);
|
@@ -131,37 +120,25 @@ class AssetManager {
|
|
131
120
|
if (!asset.ymlPath) {
|
132
121
|
throw new Error('Attempted to update corrupted asset: ' + ref);
|
133
122
|
}
|
123
|
+
await repositoryManager_1.repositoryManager.setSourceOfChangeFor(asset.ymlPath, sourceOfChange);
|
124
|
+
await fs_extra_1.default.writeFile(asset.ymlPath, yaml_1.default.stringify(yaml));
|
134
125
|
console.log('Wrote to ' + asset.ymlPath);
|
135
|
-
|
136
|
-
this.cache.flushAll();
|
137
|
-
definitionsManager_1.definitionsManager.clearCache();
|
126
|
+
cacheManager_1.cacheManager.flush();
|
138
127
|
this.maybeGenerateCode(asset.ref, asset.ymlPath, yaml);
|
139
128
|
}
|
140
|
-
maybeGenerateCode(ref, ymlPath, block) {
|
141
|
-
ref = (0, utils_1.normalizeKapetaUri)(ref);
|
142
|
-
if (codeGeneratorManager_1.codeGeneratorManager.canGenerateCode(block)) {
|
143
|
-
const assetTitle = block.metadata.title ? block.metadata.title : (0, nodejs_utils_1.parseKapetaUri)(block.metadata.name).name;
|
144
|
-
taskManager_1.taskManager.add(`codegen:${ref}`, async () => {
|
145
|
-
await codeGeneratorManager_1.codeGeneratorManager.generate(ymlPath, block);
|
146
|
-
}, {
|
147
|
-
name: `Generating code for ${assetTitle}`,
|
148
|
-
});
|
149
|
-
return true;
|
150
|
-
}
|
151
|
-
return false;
|
152
|
-
}
|
153
129
|
async importFile(filePath) {
|
154
130
|
if (filePath.startsWith('file://')) {
|
155
131
|
filePath = filePath.substring('file://'.length);
|
156
132
|
}
|
157
|
-
if (!
|
133
|
+
if (!(await fs_extra_1.default.pathExists(filePath))) {
|
158
134
|
throw new Error('File not found: ' + filePath);
|
159
135
|
}
|
160
|
-
const
|
136
|
+
const content = await fs_extra_1.default.readFile(filePath);
|
137
|
+
const assetInfos = yaml_1.default.parseAllDocuments(content.toString()).map((doc) => doc.toJSON());
|
161
138
|
await nodejs_registry_utils_1.Actions.link(new progressListener_1.ProgressListener(), node_path_1.default.dirname(filePath));
|
162
139
|
const version = 'local';
|
163
140
|
const refs = assetInfos.map((assetInfo) => `kapeta://${assetInfo.metadata.name}:${version}`);
|
164
|
-
|
141
|
+
cacheManager_1.cacheManager.flush();
|
165
142
|
return this.getAssets().filter((a) => refs.some((ref) => compareRefs(ref, a.ref)));
|
166
143
|
}
|
167
144
|
async unregisterAsset(ref) {
|
@@ -169,7 +146,7 @@ class AssetManager {
|
|
169
146
|
if (!asset) {
|
170
147
|
throw new Error('Asset does not exists: ' + ref);
|
171
148
|
}
|
172
|
-
|
149
|
+
cacheManager_1.cacheManager.flush();
|
173
150
|
await nodejs_registry_utils_1.Actions.uninstall(new progressListener_1.ProgressListener(), [asset.ref]);
|
174
151
|
}
|
175
152
|
async installAsset(ref) {
|
@@ -181,5 +158,18 @@ class AssetManager {
|
|
181
158
|
console.log('Installing %s', ref);
|
182
159
|
return await repositoryManager_1.repositoryManager.ensureAsset(uri.handle, uri.name, uri.version, false);
|
183
160
|
}
|
161
|
+
maybeGenerateCode(ref, ymlPath, block) {
|
162
|
+
ref = (0, utils_1.normalizeKapetaUri)(ref);
|
163
|
+
if (codeGeneratorManager_1.codeGeneratorManager.canGenerateCode(block)) {
|
164
|
+
const assetTitle = block.metadata.title ? block.metadata.title : (0, nodejs_utils_1.parseKapetaUri)(block.metadata.name).name;
|
165
|
+
taskManager_1.taskManager.add(`codegen:${ref}`, async () => {
|
166
|
+
await codeGeneratorManager_1.codeGeneratorManager.generate(ymlPath, block);
|
167
|
+
}, {
|
168
|
+
name: `Generating code for ${assetTitle}`,
|
169
|
+
});
|
170
|
+
return true;
|
171
|
+
}
|
172
|
+
return false;
|
173
|
+
}
|
184
174
|
}
|
185
175
|
exports.assetManager = new AssetManager();
|
@@ -63,7 +63,7 @@ router.post('/create', async (req, res) => {
|
|
63
63
|
}
|
64
64
|
const content = parseBody(req);
|
65
65
|
try {
|
66
|
-
const assets = await assetManager_1.assetManager.createAsset(req.query.path, content);
|
66
|
+
const assets = await assetManager_1.assetManager.createAsset(req.query.path, content, 'user');
|
67
67
|
res.status(200).send(assets);
|
68
68
|
}
|
69
69
|
catch (err) {
|
@@ -81,7 +81,7 @@ router.put('/update', async (req, res) => {
|
|
81
81
|
}
|
82
82
|
const content = parseBody(req);
|
83
83
|
try {
|
84
|
-
await assetManager_1.assetManager.updateAsset(req.query.ref, content);
|
84
|
+
await assetManager_1.assetManager.updateAsset(req.query.ref, content, 'user');
|
85
85
|
res.sendStatus(204);
|
86
86
|
}
|
87
87
|
catch (err) {
|
@@ -0,0 +1,16 @@
|
|
1
|
+
export interface CacheEntry<T = any> {
|
2
|
+
expires: number;
|
3
|
+
data: T;
|
4
|
+
}
|
5
|
+
declare class CacheManager {
|
6
|
+
private cache;
|
7
|
+
flush(): void;
|
8
|
+
doCached<T>(key: string, getter: () => T, ttl?: number): T;
|
9
|
+
get<T>(key: string): T | undefined;
|
10
|
+
set<T>(key: string, data: T, ttl?: number): void;
|
11
|
+
has(key: string): boolean;
|
12
|
+
remove(key: string): number;
|
13
|
+
}
|
14
|
+
export declare const cacheManager: CacheManager;
|
15
|
+
export declare const doCached: <T>(key: string, getter: () => T, ttl?: number) => T;
|
16
|
+
export {};
|
@@ -0,0 +1,38 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.doCached = exports.cacheManager = void 0;
|
7
|
+
const node_cache_1 = __importDefault(require("node-cache"));
|
8
|
+
const DEFAULT_CACHE_TTL = 60 * 1000; // 1 min
|
9
|
+
class CacheManager {
|
10
|
+
cache = new node_cache_1.default();
|
11
|
+
flush() {
|
12
|
+
this.cache.flushAll();
|
13
|
+
}
|
14
|
+
doCached(key, getter, ttl = DEFAULT_CACHE_TTL) {
|
15
|
+
const data = this.cache.get(key);
|
16
|
+
if (data !== undefined) {
|
17
|
+
return data;
|
18
|
+
}
|
19
|
+
const result = getter();
|
20
|
+
this.cache.set(key, result, ttl);
|
21
|
+
return result;
|
22
|
+
}
|
23
|
+
get(key) {
|
24
|
+
return this.cache.get(key);
|
25
|
+
}
|
26
|
+
set(key, data, ttl = DEFAULT_CACHE_TTL) {
|
27
|
+
this.cache.set(key, data, ttl);
|
28
|
+
}
|
29
|
+
has(key) {
|
30
|
+
return this.cache.has(key);
|
31
|
+
}
|
32
|
+
remove(key) {
|
33
|
+
return this.cache.del(key);
|
34
|
+
}
|
35
|
+
}
|
36
|
+
exports.cacheManager = new CacheManager();
|
37
|
+
const doCached = (key, getter, ttl = DEFAULT_CACHE_TTL) => exports.cacheManager.doCached(key, getter, ttl);
|
38
|
+
exports.doCached = doCached;
|
@@ -1,9 +1,7 @@
|
|
1
1
|
import { DefinitionInfo } from '@kapeta/local-cluster-config';
|
2
2
|
declare class DefinitionsManager {
|
3
|
-
private
|
4
|
-
private
|
5
|
-
clearCache(): void;
|
6
|
-
private doCached;
|
3
|
+
private getHash;
|
4
|
+
private getFullKey;
|
7
5
|
getDefinitions(kindFilter?: string | string[]): DefinitionInfo[];
|
8
6
|
exists(ref: string): boolean;
|
9
7
|
getProviderDefinitions(): DefinitionInfo[];
|
@@ -6,10 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.definitionsManager = void 0;
|
7
7
|
const local_cluster_config_1 = __importDefault(require("@kapeta/local-cluster-config"));
|
8
8
|
const nodejs_utils_1 = require("@kapeta/nodejs-utils");
|
9
|
-
const
|
9
|
+
const cacheManager_1 = require("./cacheManager");
|
10
10
|
class DefinitionsManager {
|
11
|
-
|
12
|
-
getKey(kindFilter) {
|
11
|
+
getHash(kindFilter) {
|
13
12
|
if (kindFilter) {
|
14
13
|
if (Array.isArray(kindFilter)) {
|
15
14
|
return kindFilter.join(',');
|
@@ -18,31 +17,18 @@ class DefinitionsManager {
|
|
18
17
|
}
|
19
18
|
return 'none';
|
20
19
|
}
|
21
|
-
|
22
|
-
this.
|
23
|
-
}
|
24
|
-
doCached(key, getter) {
|
25
|
-
if (this.cache[key]) {
|
26
|
-
if (this.cache[key].expires > Date.now()) {
|
27
|
-
return this.cache[key].definitions;
|
28
|
-
}
|
29
|
-
delete this.cache[key];
|
30
|
-
}
|
31
|
-
this.cache[key] = {
|
32
|
-
expires: Date.now() + CACHE_TTL,
|
33
|
-
definitions: getter(),
|
34
|
-
};
|
35
|
-
return this.cache[key].definitions;
|
20
|
+
getFullKey(kindFilter) {
|
21
|
+
return `DefinitionsManager:${this.getHash(kindFilter)}`;
|
36
22
|
}
|
37
23
|
getDefinitions(kindFilter) {
|
38
|
-
const key = this.
|
39
|
-
return
|
24
|
+
const key = this.getFullKey(kindFilter);
|
25
|
+
return (0, cacheManager_1.doCached)(key, () => local_cluster_config_1.default.getDefinitions(kindFilter));
|
40
26
|
}
|
41
27
|
exists(ref) {
|
42
28
|
return !!this.getDefinition(ref);
|
43
29
|
}
|
44
30
|
getProviderDefinitions() {
|
45
|
-
return
|
31
|
+
return (0, cacheManager_1.doCached)('providers', () => local_cluster_config_1.default.getProviderDefinitions());
|
46
32
|
}
|
47
33
|
getDefinition(ref) {
|
48
34
|
const uri = (0, nodejs_utils_1.parseKapetaUri)(ref);
|
@@ -57,7 +57,10 @@ class InstanceManager {
|
|
57
57
|
return [];
|
58
58
|
}
|
59
59
|
const plan = planInfo.definition;
|
60
|
-
|
60
|
+
if (!plan?.spec?.blocks) {
|
61
|
+
return [];
|
62
|
+
}
|
63
|
+
const instanceIds = plan.spec?.blocks?.map((block) => block.id) || [];
|
61
64
|
return this._instances.filter((instance) => instance.systemId === systemId && instanceIds.includes(instance.instanceId));
|
62
65
|
}
|
63
66
|
getInstance(systemId, instanceId) {
|
@@ -1,13 +1,18 @@
|
|
1
1
|
import { Task } from './taskManager';
|
2
|
+
import { SourceOfChange } from './types';
|
2
3
|
declare class RepositoryManager {
|
3
|
-
private changeEventsEnabled;
|
4
4
|
private _registryService;
|
5
5
|
private _cache;
|
6
|
-
private watcher
|
6
|
+
private watcher;
|
7
7
|
constructor();
|
8
|
-
setChangeEventsEnabled(enabled: boolean): void;
|
9
8
|
listenForChanges(): void;
|
10
|
-
stopListening(): void
|
9
|
+
stopListening(): Promise<void>;
|
10
|
+
/**
|
11
|
+
* Setting the source of change helps us know
|
12
|
+
* how to react to changes in the UI.
|
13
|
+
*/
|
14
|
+
setSourceOfChangeFor(file: string, source: SourceOfChange): Promise<void>;
|
15
|
+
clearSourceOfChangeFor(file: string): Promise<void>;
|
11
16
|
ensureDefaultProviders(): void;
|
12
17
|
private _install;
|
13
18
|
ensureAsset(handle: string, name: string, version: string, wait?: boolean): Promise<undefined | Task[]>;
|