@hahnpro/hpc-api 2025.3.2 → 2025.3.5

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 CHANGED
@@ -1,5 +1,15 @@
1
1
  # @hahnpro/hpc-api
2
2
 
3
+ ## 2025.3.4
4
+
5
+ Added `addOrganizationMembers`, `addSubscriptionToOrganization` and `getOrganizationIdByName` methods to Organizations API service
6
+
7
+ ## 2025.3.3
8
+
9
+ ### Patch Changes
10
+
11
+ - Remove dependency on nodejs modules to improve browser compatibility
12
+
3
13
  ## 2025.3.2
4
14
 
5
15
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hahnpro/hpc-api",
3
- "version": "2025.3.2",
3
+ "version": "2025.3.5",
4
4
  "description": "Module for easy access to the HahnPRO Cloud API",
5
5
  "license": "MIT",
6
6
  "author": {
package/src/index.d.ts CHANGED
@@ -11,7 +11,9 @@ export * from './lib/http.service';
11
11
  export * from './lib/label.interface';
12
12
  export * from './lib/mock';
13
13
  export * from './lib/notification.interface';
14
+ export * from './lib/organization.interface';
14
15
  export * from './lib/schema.interface';
15
16
  export * from './lib/secret.interface';
16
17
  export * from './lib/task.interface';
17
18
  export * from './lib/vault.interface';
19
+ export * from './lib/task.service';
package/src/index.js CHANGED
@@ -13,7 +13,9 @@ tslib_1.__exportStar(require("./lib/http.service"), exports);
13
13
  tslib_1.__exportStar(require("./lib/label.interface"), exports);
14
14
  tslib_1.__exportStar(require("./lib/mock"), exports);
15
15
  tslib_1.__exportStar(require("./lib/notification.interface"), exports);
16
+ tslib_1.__exportStar(require("./lib/organization.interface"), exports);
16
17
  tslib_1.__exportStar(require("./lib/schema.interface"), exports);
17
18
  tslib_1.__exportStar(require("./lib/secret.interface"), exports);
18
19
  tslib_1.__exportStar(require("./lib/task.interface"), exports);
19
20
  tslib_1.__exportStar(require("./lib/vault.interface"), exports);
21
+ tslib_1.__exportStar(require("./lib/task.service"), exports);
@@ -23,6 +23,5 @@ export declare enum ReturnType {
23
23
  JSON = 1,
24
24
  NODEBUFFER = 2,
25
25
  BLOB = 3,
26
- ARRAYBUFFER = 4,
27
- NODESTREAM = 5
26
+ ARRAYBUFFER = 4
28
27
  }
@@ -8,5 +8,4 @@ var ReturnType;
8
8
  ReturnType[ReturnType["NODEBUFFER"] = 2] = "NODEBUFFER";
9
9
  ReturnType[ReturnType["BLOB"] = 3] = "BLOB";
10
10
  ReturnType[ReturnType["ARRAYBUFFER"] = 4] = "ARRAYBUFFER";
11
- ReturnType[ReturnType["NODESTREAM"] = 5] = "NODESTREAM";
12
11
  })(ReturnType || (exports.ReturnType = ReturnType = {}));
@@ -1,4 +1,3 @@
1
- import { Readable } from 'stream';
2
1
  import FormData from 'form-data';
3
2
  import { APIBase } from './api-base';
4
3
  import { Content, ReturnType } from './content.interface';
@@ -18,6 +17,5 @@ export declare class ContentService extends BaseService {
18
17
  download(id: string, returnType: ReturnType.NODEBUFFER, options?: TokenOption): Promise<Buffer>;
19
18
  download(id: string, returnType: ReturnType.BLOB, options?: TokenOption): Promise<Blob>;
20
19
  download(id: string, returnType: ReturnType.ARRAYBUFFER, options?: TokenOption): Promise<ArrayBuffer>;
21
- download(id: string, returnType: ReturnType.NODESTREAM, options?: TokenOption): Promise<Readable>;
22
20
  }
23
21
  export {};
@@ -45,8 +45,6 @@ class ContentService extends BaseService {
45
45
  return this.httpClient.get(url, { responseType: 'blob', ...options });
46
46
  case content_interface_1.ReturnType.ARRAYBUFFER:
47
47
  return this.httpClient.get(url, { responseType: 'arraybuffer', ...options });
48
- case content_interface_1.ReturnType.NODESTREAM:
49
- return this.httpClient.get(url, { responseType: 'stream', ...options });
50
48
  }
51
49
  }
52
50
  }
@@ -23,5 +23,5 @@ export declare class DataService<T> extends APIBase implements DataInterface<T>
23
23
  [key: string]: any;
24
24
  }): Promise<T>;
25
25
  deleteOne(id: string, force?: boolean, options?: TokenOption): Promise<any>;
26
- private getFilterString;
26
+ protected getFilterString(filter: Filter): string;
27
27
  }
@@ -35,6 +35,7 @@ export declare class HttpClient {
35
35
  get: <T>(url: string, config?: Config) => Promise<T>;
36
36
  post: <T>(url: string, data: any, config?: Config) => Promise<T>;
37
37
  put: <T>(url: string, data: any, config?: Config) => Promise<T>;
38
+ patch: <T>(url: string, data: any, config?: Config) => Promise<T>;
38
39
  protected request: <T>(method: Method, url: string, config?: Config, data?: any) => Promise<T>;
39
40
  addEventSource(url: string, listener: (event: MessageEvent) => void, errorListener?: (event: MessageEvent) => void, options?: TokenOption): Promise<string>;
40
41
  destroyEventSource(id: string): void;
@@ -22,6 +22,7 @@ class HttpClient {
22
22
  this.get = (url, config) => this.request('GET', url, config);
23
23
  this.post = (url, data, config) => this.request('POST', url, config, data);
24
24
  this.put = (url, data, config) => this.request('PUT', url, config, data);
25
+ this.patch = (url, data, config) => this.request('PATCH', url, config, data);
25
26
  this.request = (method, url, config = {}, data) => {
26
27
  return this.requestQueue.add(() => new Promise((resolve, reject) => {
27
28
  const tokenP = config.token ? Promise.resolve(config.token) : this.getAccessToken();
@@ -61,7 +62,7 @@ class HttpClient {
61
62
  this.requestQueue = new Queue_1.Queue({ concurrency: 1, timeout: 70000, throwOnTimeout: true });
62
63
  }
63
64
  async addEventSource(url, listener, errorListener, options = {}) {
64
- const id = generateUUID();
65
+ const id = globalThis.crypto.randomUUID();
65
66
  const errListener = errorListener
66
67
  ? errorListener
67
68
  : (event) => {
@@ -127,7 +128,7 @@ class HttpClient {
127
128
  const assertionPayload = {
128
129
  iat: timestamp,
129
130
  exp: timestamp + 60,
130
- jti: generateUUID(),
131
+ jti: globalThis.crypto.randomUUID(),
131
132
  iss: this.clientId,
132
133
  sub: this.clientId,
133
134
  aud: audience,
@@ -181,9 +182,3 @@ class HttpClient {
181
182
  }
182
183
  }
183
184
  exports.HttpClient = HttpClient;
184
- function generateUUID() {
185
- if (typeof globalThis.crypto?.randomUUID === 'function') {
186
- return globalThis.crypto.randomUUID();
187
- }
188
- throw new Error('randomUUID() not available in this environment. Please upgrade to Node 20+ or a modern browser.');
189
- }
@@ -1,9 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MockAPI = void 0;
4
- const crypto_1 = require("crypto");
5
- const fs_1 = require("fs");
6
- const path_1 = require("path");
7
4
  const asset_mock_service_1 = require("./asset.mock.service");
8
5
  const assetTypes_mock_service_1 = require("./assetTypes.mock.service");
9
6
  const content_mock_service_1 = require("./content.mock.service");
@@ -60,7 +57,7 @@ class MockAPI {
60
57
  fileId: v.fileId ?? '',
61
58
  mimetype: v.mimetype ?? '',
62
59
  }));
63
- const contentData = contents.map((v) => (v.data ? v.data : (0, fs_1.readFileSync)((0, path_1.join)(v.filePath, v.filename))));
60
+ const contentData = contents.map((v) => v.data);
64
61
  const secrets1 = secrets.map((v) => ({
65
62
  ...v,
66
63
  readPermissions: v.readPermissions ?? [],
@@ -155,7 +152,7 @@ class MockAPI {
155
152
  artifacts: modules[index].artifacts.map((art) => ({
156
153
  ...art,
157
154
  version: '0.0.0',
158
- id: (0, crypto_1.randomUUID)(),
155
+ id: globalThis.crypto.randomUUID(),
159
156
  mimetype: '',
160
157
  size: 0,
161
158
  createdAt: '' + Date.now(),
@@ -1,5 +1,5 @@
1
1
  import FormData from 'form-data';
2
- import { Paginated, RequestParameter } from '../data.interface';
2
+ import { Filter, Paginated, RequestParameter } from '../data.interface';
3
3
  import { TokenOption } from '../http.service';
4
4
  import { Asset, AssetRevision, Attachment, EventCause, EventLevelOverride } from '../interfaces';
5
5
  import { AssetService } from '../services';
@@ -17,6 +17,7 @@ export declare class AssetMockService extends BaseService implements AssetServic
17
17
  constructor(api: MockAPI, assets: Asset[], revisions: AssetRevision[]);
18
18
  private getAssets;
19
19
  addOne(dto: Asset): Promise<Asset>;
20
+ count(filter: Filter, options?: TokenOption): Promise<number>;
20
21
  deleteOne(assetId: string, force?: boolean): Promise<Asset>;
21
22
  getMany(params?: RequestParameter): Promise<Paginated<Asset[]>>;
22
23
  updateOne(assetId: string, dto: Asset): Promise<Asset>;
@@ -30,5 +31,6 @@ export declare class AssetMockService extends BaseService implements AssetServic
30
31
  deleteRevision(assetId: string, revisionId: string): Promise<any>;
31
32
  findOneExternal(key: string, options?: TokenOption): Promise<Asset>;
32
33
  updateOneExternal(key: string, dto: any, options?: TokenOption): Promise<Asset>;
34
+ findByName(name: string, options?: TokenOption): Promise<Paginated<Asset[]>>;
33
35
  }
34
36
  export {};
@@ -30,6 +30,9 @@ class AssetMockService extends BaseService {
30
30
  this.revisions.push({ ...dto, originalId: dto.id });
31
31
  return super.addOne(dto);
32
32
  }
33
+ count(filter, options = {}) {
34
+ return Promise.resolve(this.data.length);
35
+ }
33
36
  deleteOne(assetId, force = false) {
34
37
  const asset = this.data.find((v) => v.id === assetId);
35
38
  if (!asset?.deletedAt && !force) {
@@ -120,5 +123,13 @@ class AssetMockService extends BaseService {
120
123
  this.data[index] = { ...asset, ...dto };
121
124
  return Promise.resolve(this.data[index]);
122
125
  }
126
+ findByName(name, options = {}) {
127
+ const docs = this.data.filter((asset) => asset.name === name);
128
+ return Promise.resolve({
129
+ docs,
130
+ total: docs.length,
131
+ limit: Number.MAX_SAFE_INTEGER,
132
+ });
133
+ }
123
134
  }
124
135
  exports.AssetMockService = AssetMockService;
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AssetTypesMockService = void 0;
4
4
  const tslib_1 = require("tslib");
5
- const crypto_1 = require("crypto");
6
5
  const ts_mixer_1 = require("ts-mixer");
7
6
  const api_base_mock_1 = require("./api-base.mock");
8
7
  const data_mock_service_1 = require("./data.mock.service");
@@ -30,7 +29,7 @@ class AssetTypesMockService extends BaseService {
30
29
  return Promise.resolve(page);
31
30
  }
32
31
  addOne(dto) {
33
- const id = (0, crypto_1.randomUUID)();
32
+ const id = globalThis.crypto.randomUUID();
34
33
  this.revisions.push({ ...dto, id, originalId: dto.id });
35
34
  return super.addOne(dto);
36
35
  }
@@ -1,4 +1,3 @@
1
- import { Readable } from 'stream';
2
1
  import FormData from 'form-data';
3
2
  import { Content, ReturnType } from '../content.interface';
4
3
  import { ContentService } from '../content.service';
@@ -19,7 +18,6 @@ export declare class ContentMockService extends BaseService implements ContentSe
19
18
  download(id: string, returnType: ReturnType.NODEBUFFER): Promise<Buffer>;
20
19
  download(id: string, returnType: ReturnType.BLOB): Promise<Blob>;
21
20
  download(id: string, returnType: ReturnType.ARRAYBUFFER): Promise<ArrayBuffer>;
22
- download(id: string, returnType: ReturnType.NODESTREAM): Promise<Readable>;
23
21
  deleteOne(contentId: string, force?: boolean): Promise<Content>;
24
22
  getMany(params?: RequestParameter): Promise<Paginated<Content[]>>;
25
23
  upload(form: FormData): Promise<Content>;
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ContentMockService = void 0;
4
4
  const tslib_1 = require("tslib");
5
- const stream_1 = require("stream");
6
5
  const ts_mixer_1 = require("ts-mixer");
7
6
  const content_interface_1 = require("../content.interface");
8
7
  const api_base_mock_1 = require("./api-base.mock");
@@ -61,8 +60,6 @@ class ContentMockService extends BaseService {
61
60
  }
62
61
  case content_interface_1.ReturnType.ARRAYBUFFER:
63
62
  return Promise.resolve(Buffer.from(this.contentData.get(id)).buffer);
64
- case content_interface_1.ReturnType.NODESTREAM:
65
- return Promise.resolve(stream_1.Readable.from(this.contentData.get(id)));
66
63
  }
67
64
  }
68
65
  deleteOne(contentId, force = false) {
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FlowDeploymentMockService = void 0;
4
4
  const tslib_1 = require("tslib");
5
- const crypto_1 = require("crypto");
6
5
  const ts_mixer_1 = require("ts-mixer");
7
6
  const api_base_mock_1 = require("./api-base.mock");
8
7
  const data_mock_service_1 = require("./data.mock.service");
@@ -19,11 +18,11 @@ class FlowDeploymentMockService extends BaseService {
19
18
  }
20
19
  subscribeToStatus(id, listener, errorListener) {
21
20
  listener(new MessageEvent('message', { data: 'running' }));
22
- return Promise.resolve((0, crypto_1.randomUUID)());
21
+ return Promise.resolve(globalThis.crypto.randomUUID());
23
22
  }
24
23
  subscribeToLogs(id, listener, errorListener) {
25
24
  listener(new MessageEvent('message', { data: 'foo' }));
26
- return Promise.resolve((0, crypto_1.randomUUID)());
25
+ return Promise.resolve(globalThis.crypto.randomUUID());
27
26
  }
28
27
  async waitForRunningStatus(id) {
29
28
  const flowDeployment = this.data.find((v) => v.id === id);
@@ -84,7 +83,7 @@ class FlowDeploymentMockService extends BaseService {
84
83
  async addOne(dto) {
85
84
  const flow = await this.api.flows.getFlowWithDiagram(dto.diagramId);
86
85
  const refs = await this.getReferences(dto.properties);
87
- const id = (0, crypto_1.randomUUID)();
86
+ const id = globalThis.crypto.randomUUID();
88
87
  const newDepl = {
89
88
  actualStatus: 'generating queued',
90
89
  artifact: undefined,
@@ -113,8 +112,9 @@ class FlowDeploymentMockService extends BaseService {
113
112
  async getReferences(properties) {
114
113
  // super simplified version of real resolver
115
114
  return Promise.all(Object.keys(properties).map(async (prop) => {
116
- if (prop !== 'assetId')
115
+ if (prop !== 'assetId') {
117
116
  throw new Error('not implemented');
117
+ }
118
118
  return { id: (await this.api.assets.getOne(properties[prop])).id, resourceType: 'asset' };
119
119
  }));
120
120
  }
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FlowFunctionsMockService = void 0;
4
- const crypto_1 = require("crypto");
5
4
  const data_mock_service_1 = require("./data.mock.service");
6
5
  class FlowFunctionsMockService extends data_mock_service_1.DataMockService {
7
6
  constructor(functions, revisions) {
@@ -10,7 +9,7 @@ class FlowFunctionsMockService extends data_mock_service_1.DataMockService {
10
9
  this.data = functions;
11
10
  }
12
11
  addOne(dto) {
13
- const id = (0, crypto_1.randomUUID)();
12
+ const id = globalThis.crypto.randomUUID();
14
13
  this.revisions.push({ ...dto, id, originalId: dto.fqn });
15
14
  return super.addOne(dto);
16
15
  }
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FlowMockService = void 0;
4
4
  const tslib_1 = require("tslib");
5
- const crypto_1 = require("crypto");
6
5
  const ts_mixer_1 = require("ts-mixer");
7
6
  const api_base_mock_1 = require("./api-base.mock");
8
7
  const data_mock_service_1 = require("./data.mock.service");
@@ -34,7 +33,7 @@ class FlowMockService extends BaseService {
34
33
  return super.deleteOne(id);
35
34
  }
36
35
  addOne(dto) {
37
- const id = (0, crypto_1.randomUUID)();
36
+ const id = globalThis.crypto.randomUUID();
38
37
  this.revisions.push({ ...dto, id, originalId: dto.id });
39
38
  return super.addOne(dto);
40
39
  }
@@ -1,7 +1,10 @@
1
- import { Organization } from '../organization.interface';
1
+ import { Organization, OrganizationName, SubscriptionType, UserWithOrgRoles } from '../organization.interface';
2
2
  import { OrganizationService } from '../organization.service';
3
3
  import { DataMockService } from './data.mock.service';
4
4
  export declare class OrganizationMockService extends DataMockService<Organization> implements OrganizationService {
5
5
  constructor(organizations: Organization[]);
6
6
  addOrganizationInvitation(id: string, email: string, roles: string[]): Promise<void>;
7
+ addOrganizationMembers(id: string, usersWithOrgRoles: UserWithOrgRoles[]): Promise<void>;
8
+ addSubscriptionToOrganization(id: string, type: SubscriptionType): Promise<Organization>;
9
+ getOrganizationIdByName(name: string): Promise<OrganizationName>;
7
10
  }
@@ -9,9 +9,36 @@ class OrganizationMockService extends data_mock_service_1.DataMockService {
9
9
  }
10
10
  addOrganizationInvitation(id, email, roles) {
11
11
  if (!id || !email || !roles?.length) {
12
- return Promise.reject();
12
+ return Promise.reject('Bad Request');
13
13
  }
14
14
  return Promise.resolve();
15
15
  }
16
+ addOrganizationMembers(id, usersWithOrgRoles) {
17
+ if (!id ||
18
+ !usersWithOrgRoles?.length ||
19
+ usersWithOrgRoles.some((userWithOrgRoles) => !userWithOrgRoles.username || !userWithOrgRoles.orgRoles?.length)) {
20
+ return Promise.reject('Bad Request');
21
+ }
22
+ return Promise.resolve();
23
+ }
24
+ addSubscriptionToOrganization(id, type) {
25
+ if (!id || !type) {
26
+ return Promise.reject('Bad Request');
27
+ }
28
+ const organization = this.data.find((org) => org.id === id);
29
+ if (!organization) {
30
+ return Promise.reject('Not Found');
31
+ }
32
+ const existingId = organization.subscriptionId;
33
+ if (existingId) {
34
+ organization.previousSubscriptions = [existingId, ...(organization.previousSubscriptions || [])];
35
+ }
36
+ organization.subscriptionId = `test-subscription-${organization.previousSubscriptions?.length || 0}`;
37
+ return Promise.resolve(organization);
38
+ }
39
+ getOrganizationIdByName(name) {
40
+ const id = this.data.find((org) => org.name === name)?.id;
41
+ return Promise.resolve({ id });
42
+ }
16
43
  }
17
44
  exports.OrganizationMockService = OrganizationMockService;
@@ -1,5 +1,6 @@
1
1
  import { Paginated, RequestParameter } from '../data.interface';
2
- import { Task } from '../task.interface';
2
+ import { TokenOption } from '../http.service';
3
+ import { LogbookEntry, LogbookEntryDto, Task } from '../task.interface';
3
4
  import { TaskService } from '../task.service';
4
5
  import { APIBaseMock } from './api-base.mock';
5
6
  import { DataMockService } from './data.mock.service';
@@ -9,9 +10,21 @@ interface BaseService extends DataMockService<Task>, TrashMockService<Task> {
9
10
  declare class BaseService extends APIBaseMock<Task> {
10
11
  }
11
12
  export declare class TaskMockService extends BaseService implements TaskService {
12
- constructor(tasks: Task[]);
13
+ private entries;
14
+ constructor(tasks: Task[], logbookEntries?: LogbookEntry[]);
13
15
  createTaskAttachedToAsset(dto: any): Promise<Task>;
14
16
  deleteOne(taskId: string, force?: boolean): Promise<Task>;
15
17
  getMany(params?: RequestParameter): Promise<Paginated<Task[]>>;
18
+ findByName(name: string, options?: any): Promise<Paginated<Task[]>>;
19
+ findByAsset(assetId: string, options?: any): Promise<Paginated<Task[]>>;
20
+ getLogbookEntries(taskId: string, limit?: number, offset?: number, _options?: TokenOption): Promise<Paginated<LogbookEntry[]>>;
21
+ addLogbookEntry(taskId: string, dto: LogbookEntryDto, _options?: TokenOption): Promise<LogbookEntry>;
22
+ updateLogbookEntry(taskId: string, entryId: string, dto: Partial<LogbookEntryDto>, _options?: TokenOption): Promise<LogbookEntry>;
23
+ deleteLogbookEntry(taskId: string, entryId: string, _options?: TokenOption): Promise<{
24
+ deleted: boolean;
25
+ }>;
26
+ getUserLogbookEntries(limit?: number, offset?: number, _options?: TokenOption, userId?: string): Promise<Paginated<LogbookEntry[]>>;
27
+ seed(entries: LogbookEntry[]): void;
28
+ clear(): void;
16
29
  }
17
30
  export {};
@@ -12,8 +12,10 @@ BaseService = tslib_1.__decorate([
12
12
  (0, ts_mixer_1.mix)(data_mock_service_1.DataMockService, trash_mock_service_1.TrashMockService)
13
13
  ], BaseService);
14
14
  class TaskMockService extends BaseService {
15
- constructor(tasks) {
15
+ constructor(tasks, logbookEntries = []) {
16
16
  super(tasks);
17
+ this.entries = [];
18
+ this.entries = [...logbookEntries];
17
19
  }
18
20
  async createTaskAttachedToAsset(dto) {
19
21
  this.data.push(dto);
@@ -32,5 +34,94 @@ class TaskMockService extends BaseService {
32
34
  const page = this.getItems(params, false);
33
35
  return Promise.resolve(page);
34
36
  }
37
+ findByName(name, options = {}) {
38
+ const filtered = this.data.filter((task) => task.name === name);
39
+ const result = {
40
+ docs: filtered,
41
+ total: filtered.length,
42
+ page: 1,
43
+ limit: filtered.length,
44
+ };
45
+ return Promise.resolve(result);
46
+ }
47
+ findByAsset(assetId, options = {}) {
48
+ const filtered = this.data.filter((task) => task.assetRef === assetId);
49
+ const result = {
50
+ docs: filtered,
51
+ total: filtered.length,
52
+ page: 1,
53
+ limit: filtered.length,
54
+ };
55
+ return Promise.resolve(result);
56
+ }
57
+ // override network methods to in‑memory versions
58
+ async getLogbookEntries(taskId, limit = 50, offset = 0, _options = {}) {
59
+ const taskEntries = this.entries.filter((e) => e.taskId === taskId);
60
+ const docs = taskEntries.slice(offset, offset + limit);
61
+ return {
62
+ docs,
63
+ limit,
64
+ offset,
65
+ total: taskEntries.length,
66
+ page: Math.floor(offset / limit) + 1,
67
+ pages: Math.max(1, Math.ceil(taskEntries.length / limit)),
68
+ };
69
+ }
70
+ async addLogbookEntry(taskId, dto, _options = {}) {
71
+ const id = globalThis.crypto?.randomUUID?.() || Math.random().toString(36).slice(2);
72
+ const now = new Date().toISOString();
73
+ const entry = {
74
+ id,
75
+ taskId,
76
+ date: dto.date || now,
77
+ duration: dto.duration ?? 0,
78
+ materials: dto.materials || [],
79
+ comment: dto.comment || '',
80
+ source: 'manual',
81
+ createdAt: now,
82
+ updatedAt: now,
83
+ createdBy: dto.createdBy || { id: 'mock-user', username: 'mock-user' },
84
+ };
85
+ this.entries.push(entry);
86
+ return entry;
87
+ }
88
+ async updateLogbookEntry(taskId, entryId, dto, _options = {}) {
89
+ const idx = this.entries.findIndex((e) => e.id === entryId && e.taskId === taskId);
90
+ if (idx === -1) {
91
+ throw new Error('Not found');
92
+ }
93
+ const updated = {
94
+ ...this.entries[idx],
95
+ ...dto,
96
+ materials: dto.materials !== undefined ? dto.materials : this.entries[idx].materials,
97
+ updatedAt: new Date().toISOString(),
98
+ };
99
+ this.entries[idx] = updated;
100
+ return updated;
101
+ }
102
+ async deleteLogbookEntry(taskId, entryId, _options = {}) {
103
+ const before = this.entries.length;
104
+ this.entries = this.entries.filter((e) => !(e.id === entryId && e.taskId === taskId));
105
+ return { deleted: this.entries.length < before };
106
+ }
107
+ async getUserLogbookEntries(limit = 50, offset = 0, _options = {}, userId = 'mock-user') {
108
+ const userEntries = this.entries.filter((e) => e.createdBy?.id === userId);
109
+ const docs = userEntries.slice(offset, offset + limit);
110
+ return {
111
+ docs,
112
+ limit,
113
+ offset,
114
+ total: userEntries.length,
115
+ page: Math.floor(offset / limit) + 1,
116
+ pages: Math.max(1, Math.ceil(userEntries.length / limit)),
117
+ };
118
+ }
119
+ // test helpers
120
+ seed(entries) {
121
+ this.entries = [...entries];
122
+ }
123
+ clear() {
124
+ this.entries = [];
125
+ }
35
126
  }
36
127
  exports.TaskMockService = TaskMockService;
@@ -24,3 +24,13 @@ export interface Organization {
24
24
  createdBy?: Author;
25
25
  updatedBy?: Author;
26
26
  }
27
+ export interface OrganizationName {
28
+ id: string;
29
+ name?: string;
30
+ isDeleted?: boolean;
31
+ }
32
+ export interface UserWithOrgRoles {
33
+ username: string;
34
+ orgRoles: string[];
35
+ }
36
+ export type SubscriptionType = 'dev' | 'free' | 'inactive' | 'pro';
@@ -1,7 +1,10 @@
1
1
  import { DataService } from './data.service';
2
2
  import { HttpClient } from './http.service';
3
- import { Organization } from './organization.interface';
3
+ import { Organization, OrganizationName, SubscriptionType, UserWithOrgRoles } from './organization.interface';
4
4
  export declare class OrganizationService extends DataService<Organization> {
5
5
  constructor(httpClient: HttpClient);
6
6
  addOrganizationInvitation(id: string, email: string, roles: string[]): Promise<void>;
7
+ addOrganizationMembers(id: string, usersWithOrgRoles: UserWithOrgRoles[]): Promise<void>;
8
+ addSubscriptionToOrganization(id: string, type: SubscriptionType): Promise<Organization>;
9
+ getOrganizationIdByName(name: string): Promise<OrganizationName>;
7
10
  }
@@ -9,5 +9,14 @@ class OrganizationService extends data_service_1.DataService {
9
9
  addOrganizationInvitation(id, email, roles) {
10
10
  return this.httpClient.post(`${this.basePath}/${id}/data/invitations`, { roles, email });
11
11
  }
12
+ addOrganizationMembers(id, usersWithOrgRoles) {
13
+ return this.httpClient.post(`${this.basePath}/${id}/members`, { usersWithOrgRoles });
14
+ }
15
+ addSubscriptionToOrganization(id, type) {
16
+ return this.httpClient.post(`${this.basePath}/${id}/subscription`, { type });
17
+ }
18
+ getOrganizationIdByName(name) {
19
+ return this.httpClient.get(`${this.basePath}/names/${name}`);
20
+ }
12
21
  }
13
22
  exports.OrganizationService = OrganizationService;
@@ -1,6 +1,6 @@
1
1
  import FormData from 'form-data';
2
2
  import { APIBase } from '../api-base';
3
- import { Paginated, RequestParameter } from '../data.interface';
3
+ import { Filter, Paginated, RequestParameter } from '../data.interface';
4
4
  import { DataService } from '../data.service';
5
5
  import { HttpClient, TokenOption } from '../http.service';
6
6
  import { Asset, AssetRevision, Attachment, EventCause, EventLevelOverride } from '../interfaces';
@@ -11,6 +11,7 @@ declare class BaseService extends APIBase {
11
11
  }
12
12
  export declare class AssetService extends BaseService {
13
13
  constructor(httpClient: HttpClient);
14
+ count(filter: Filter, options?: TokenOption): Promise<number>;
14
15
  deleteOne(id: string, force?: boolean, options?: TokenOption): Promise<any>;
15
16
  addAttachment: (id: string, form: FormData, options?: TokenOption) => Promise<Asset>;
16
17
  getChildren(assetId: string, params?: RequestParameter, options?: TokenOption): Promise<Paginated<Asset[]>>;
@@ -22,5 +23,6 @@ export declare class AssetService extends BaseService {
22
23
  deleteRevision(assetId: string, revisionId: string, options?: TokenOption): Promise<any>;
23
24
  findOneExternal(key: string, options?: TokenOption): Promise<Asset>;
24
25
  updateOneExternal(key: string, dto: any, options?: TokenOption): Promise<Asset>;
26
+ findByName(name: string, options?: TokenOption): Promise<Paginated<Asset[]>>;
25
27
  }
26
28
  export {};
@@ -24,6 +24,10 @@ class AssetService extends BaseService {
24
24
  });
25
25
  };
26
26
  }
27
+ count(filter, options = {}) {
28
+ const params = { filter: this.getFilterString(filter) };
29
+ return this.httpClient.get(`${this.basePath}/count`, { params, ...options });
30
+ }
27
31
  deleteOne(id, force = false, options = {}) {
28
32
  return this.httpClient.delete(`${this.basePath}/${id}`, { params: { force }, ...options });
29
33
  }
@@ -54,5 +58,8 @@ class AssetService extends BaseService {
54
58
  updateOneExternal(key, dto, options = {}) {
55
59
  return this.httpClient.put(`${this.basePath}/external/${key}`, dto, options);
56
60
  }
61
+ findByName(name, options = {}) {
62
+ return this.httpClient.get(`${this.basePath}?name=${encodeURIComponent(name)}`, options);
63
+ }
57
64
  }
58
65
  exports.AssetService = AssetService;
@@ -1,3 +1,4 @@
1
+ import { Author } from './interfaces/resource.interface';
1
2
  export interface Task {
2
3
  id: string;
3
4
  name: string;
@@ -21,3 +22,30 @@ export interface Task {
21
22
  updatedAt?: string;
22
23
  deletedAt?: string;
23
24
  }
25
+ export interface LogbookEntry {
26
+ id: string;
27
+ taskId: string;
28
+ date: string;
29
+ duration: number;
30
+ materials: {
31
+ name: string;
32
+ quantity: number;
33
+ }[];
34
+ comment?: string;
35
+ source: 'manual' | 'voice' | 'automatic';
36
+ createdBy: Author;
37
+ createdAt: string;
38
+ updatedAt: string;
39
+ }
40
+ export interface LogbookEntryDto {
41
+ date: string;
42
+ duration: number;
43
+ materials: {
44
+ name: string;
45
+ quantity: number;
46
+ }[];
47
+ comment?: string;
48
+ source?: 'manual' | 'voice' | 'automatic';
49
+ taskId: string;
50
+ createdBy?: Author;
51
+ }
@@ -1,7 +1,8 @@
1
1
  import { APIBase } from './api-base';
2
+ import { Paginated } from './data.interface';
2
3
  import { DataService } from './data.service';
3
4
  import { HttpClient, TokenOption } from './http.service';
4
- import { Task } from './task.interface';
5
+ import { LogbookEntry, LogbookEntryDto, Task } from './task.interface';
5
6
  import { TrashService } from './trash.service';
6
7
  interface BaseService extends DataService<Task>, TrashService<Task> {
7
8
  }
@@ -10,5 +11,14 @@ declare class BaseService extends APIBase {
10
11
  export declare class TaskService extends BaseService {
11
12
  constructor(httpClient: HttpClient);
12
13
  createTaskAttachedToAsset(dto: any, options?: TokenOption): Promise<Task>;
14
+ findByName(name: string, options?: TokenOption): Promise<Paginated<Task[]>>;
15
+ findByAsset(assetId: string, options?: TokenOption): Promise<Paginated<Task[]>>;
16
+ getLogbookEntries(taskId: string, limit?: number, offset?: number, options?: TokenOption): Promise<Paginated<LogbookEntry[]>>;
17
+ addLogbookEntry(taskId: string, entry: LogbookEntryDto, options?: TokenOption): Promise<LogbookEntry>;
18
+ updateLogbookEntry(taskId: string, entryId: string, entry: Partial<LogbookEntryDto>, options?: TokenOption): Promise<LogbookEntry>;
19
+ deleteLogbookEntry(taskId: string, entryId: string, options?: TokenOption): Promise<{
20
+ deleted: boolean;
21
+ }>;
22
+ getUserLogbookEntries(limit?: number, offset?: number, options?: TokenOption): Promise<Paginated<LogbookEntry[]>>;
13
23
  }
14
24
  export {};
@@ -19,5 +19,32 @@ class TaskService extends BaseService {
19
19
  createTaskAttachedToAsset(dto, options = {}) {
20
20
  return this.httpClient.post(this.basePath, dto, options);
21
21
  }
22
+ findByName(name, options = {}) {
23
+ return this.httpClient.get(`${this.basePath}?name=${encodeURIComponent(name)}`, options);
24
+ }
25
+ findByAsset(assetId, options = {}) {
26
+ return this.httpClient.get(`${this.basePath}?assetRef=${assetId}`, options);
27
+ }
28
+ getLogbookEntries(taskId, limit = 50, offset = 0, options = {}) {
29
+ return this.httpClient.get(`${this.basePath}/${taskId}/logbook`, {
30
+ ...options,
31
+ params: { limit: String(limit), offset: String(offset) },
32
+ });
33
+ }
34
+ addLogbookEntry(taskId, entry, options = {}) {
35
+ return this.httpClient.post(`${this.basePath}/${taskId}/logbook`, entry, options);
36
+ }
37
+ updateLogbookEntry(taskId, entryId, entry, options = {}) {
38
+ return this.httpClient.patch(`${this.basePath}/${taskId}/logbook/${entryId}`, entry, options);
39
+ }
40
+ deleteLogbookEntry(taskId, entryId, options = {}) {
41
+ return this.httpClient.delete(`${this.basePath}/${taskId}/logbook/${entryId}`, options);
42
+ }
43
+ getUserLogbookEntries(limit = 50, offset = 0, options = {}) {
44
+ return this.httpClient.get(`${this.basePath}/logbook/mine`, {
45
+ ...options,
46
+ params: { limit: String(limit), offset: String(offset) },
47
+ });
48
+ }
22
49
  }
23
50
  exports.TaskService = TaskService;