@hamak/ui-remote-resource-impl 0.4.19
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/dist/es2015/index.js +33 -0
- package/dist/es2015/middleware/entity-middleware.js +209 -0
- package/dist/es2015/middleware/entity-sync-middleware.js +36 -0
- package/dist/es2015/middleware/resource-middleware.js +111 -0
- package/dist/es2015/middleware/sync-middleware.js +85 -0
- package/dist/es2015/plugin/resource-plugin-factory.js +151 -0
- package/dist/es2015/providers/mock-resource-provider.js +215 -0
- package/dist/es2015/providers/rest-resource-provider.js +140 -0
- package/dist/es2015/registry/entity-registry.js +50 -0
- package/dist/es2015/registry/resource-registry.js +68 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/middleware/entity-middleware.d.ts +13 -0
- package/dist/middleware/entity-middleware.d.ts.map +1 -0
- package/dist/middleware/entity-middleware.js +221 -0
- package/dist/middleware/entity-sync-middleware.d.ts +7 -0
- package/dist/middleware/entity-sync-middleware.d.ts.map +1 -0
- package/dist/middleware/entity-sync-middleware.js +31 -0
- package/dist/middleware/resource-middleware.d.ts +13 -0
- package/dist/middleware/resource-middleware.d.ts.map +1 -0
- package/dist/middleware/resource-middleware.js +97 -0
- package/dist/middleware/sync-middleware.d.ts +12 -0
- package/dist/middleware/sync-middleware.d.ts.map +1 -0
- package/dist/middleware/sync-middleware.js +80 -0
- package/dist/plugin/resource-plugin-factory.d.ts +31 -0
- package/dist/plugin/resource-plugin-factory.d.ts.map +1 -0
- package/dist/plugin/resource-plugin-factory.js +131 -0
- package/dist/providers/mock-resource-provider.d.ts +147 -0
- package/dist/providers/mock-resource-provider.d.ts.map +1 -0
- package/dist/providers/mock-resource-provider.js +196 -0
- package/dist/providers/rest-resource-provider.d.ts +51 -0
- package/dist/providers/rest-resource-provider.d.ts.map +1 -0
- package/dist/providers/rest-resource-provider.js +117 -0
- package/dist/registry/entity-registry.d.ts +54 -0
- package/dist/registry/entity-registry.d.ts.map +1 -0
- package/dist/registry/entity-registry.js +46 -0
- package/dist/registry/resource-registry.d.ts +80 -0
- package/dist/registry/resource-registry.d.ts.map +1 -0
- package/dist/registry/resource-registry.js +64 -0
- package/package.json +57 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type { IResourceProvider, ResourceCallParams, ResourceCallResult } from '@hamak/ui-remote-resource-spi';
|
|
2
|
+
import type { ResourceOperation } from '@hamak/ui-remote-resource-api';
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for a single endpoint mock
|
|
5
|
+
*/
|
|
6
|
+
export interface EndpointMockConfig {
|
|
7
|
+
/** Mock response data */
|
|
8
|
+
data?: any;
|
|
9
|
+
/** Mock metadata */
|
|
10
|
+
metadata?: any;
|
|
11
|
+
/** Simulate delay in ms */
|
|
12
|
+
delay?: number;
|
|
13
|
+
/** Simulate error */
|
|
14
|
+
error?: {
|
|
15
|
+
message: string;
|
|
16
|
+
code?: string;
|
|
17
|
+
details?: any;
|
|
18
|
+
};
|
|
19
|
+
/** Dynamic response generator */
|
|
20
|
+
responseGenerator?: (endpoint: string, params: ResourceCallParams, operation: ResourceOperation) => Promise<ResourceCallResult> | ResourceCallResult;
|
|
21
|
+
/** Callback invoked when this endpoint is called */
|
|
22
|
+
onCall?: (endpoint: string, params: ResourceCallParams, operation: ResourceOperation) => void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Mock configuration map: endpoint pattern -> config
|
|
26
|
+
*/
|
|
27
|
+
export type MockConfigMap = Record<string, EndpointMockConfig>;
|
|
28
|
+
/**
|
|
29
|
+
* Call history entry
|
|
30
|
+
*/
|
|
31
|
+
export interface MockCallHistoryEntry {
|
|
32
|
+
timestamp: number;
|
|
33
|
+
endpoint: string;
|
|
34
|
+
params: ResourceCallParams;
|
|
35
|
+
operation: ResourceOperation;
|
|
36
|
+
result?: ResourceCallResult;
|
|
37
|
+
error?: any;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Mock provider configuration
|
|
41
|
+
*/
|
|
42
|
+
export interface MockProviderConfig {
|
|
43
|
+
/** Predefined mocks for specific endpoints */
|
|
44
|
+
mocks?: MockConfigMap;
|
|
45
|
+
/** Default delay for all calls (can be overridden per endpoint) */
|
|
46
|
+
defaultDelay?: number;
|
|
47
|
+
/** Default response for unmocked endpoints */
|
|
48
|
+
defaultResponse?: any;
|
|
49
|
+
/** Whether to track call history */
|
|
50
|
+
trackHistory?: boolean;
|
|
51
|
+
/** Global error to simulate (overrides all endpoint configs) */
|
|
52
|
+
globalError?: {
|
|
53
|
+
message: string;
|
|
54
|
+
code?: string;
|
|
55
|
+
details?: any;
|
|
56
|
+
};
|
|
57
|
+
/** Callback for all calls */
|
|
58
|
+
onAnyCall?: (endpoint: string, params: ResourceCallParams, operation: ResourceOperation) => void;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Mock Resource Provider for testing
|
|
62
|
+
*/
|
|
63
|
+
export declare class MockResourceProvider implements IResourceProvider {
|
|
64
|
+
readonly type = "mock";
|
|
65
|
+
private mocks;
|
|
66
|
+
private defaultDelay;
|
|
67
|
+
private defaultResponse;
|
|
68
|
+
private trackHistory;
|
|
69
|
+
private globalError?;
|
|
70
|
+
private onAnyCall?;
|
|
71
|
+
private callHistory;
|
|
72
|
+
constructor(config?: MockProviderConfig);
|
|
73
|
+
call<TData = any>(endpoint: string, params: ResourceCallParams, operation: ResourceOperation): Promise<ResourceCallResult<TData>>;
|
|
74
|
+
/**
|
|
75
|
+
* Find mock config for endpoint (supports pattern matching)
|
|
76
|
+
*/
|
|
77
|
+
private findMockConfig;
|
|
78
|
+
/**
|
|
79
|
+
* Simple pattern matching for endpoints
|
|
80
|
+
*/
|
|
81
|
+
private matchesPattern;
|
|
82
|
+
/**
|
|
83
|
+
* Generate response based on config
|
|
84
|
+
*/
|
|
85
|
+
private generateResponse;
|
|
86
|
+
/**
|
|
87
|
+
* Create error object
|
|
88
|
+
*/
|
|
89
|
+
private createError;
|
|
90
|
+
/**
|
|
91
|
+
* Sleep helper
|
|
92
|
+
*/
|
|
93
|
+
private sleep;
|
|
94
|
+
/**
|
|
95
|
+
* Get call history
|
|
96
|
+
*/
|
|
97
|
+
getCallHistory(): MockCallHistoryEntry[];
|
|
98
|
+
/**
|
|
99
|
+
* Get calls for specific endpoint
|
|
100
|
+
*/
|
|
101
|
+
getCallsForEndpoint(endpoint: string): MockCallHistoryEntry[];
|
|
102
|
+
/**
|
|
103
|
+
* Get calls for specific operation
|
|
104
|
+
*/
|
|
105
|
+
getCallsForOperation(operation: ResourceOperation): MockCallHistoryEntry[];
|
|
106
|
+
/**
|
|
107
|
+
* Clear call history
|
|
108
|
+
*/
|
|
109
|
+
clearHistory(): void;
|
|
110
|
+
/**
|
|
111
|
+
* Get last call
|
|
112
|
+
*/
|
|
113
|
+
getLastCall(): MockCallHistoryEntry | undefined;
|
|
114
|
+
/**
|
|
115
|
+
* Check if endpoint was called
|
|
116
|
+
*/
|
|
117
|
+
wasEndpointCalled(endpoint: string, operation?: ResourceOperation): boolean;
|
|
118
|
+
/**
|
|
119
|
+
* Get call count for endpoint
|
|
120
|
+
*/
|
|
121
|
+
getCallCount(endpoint?: string, operation?: ResourceOperation): number;
|
|
122
|
+
/**
|
|
123
|
+
* Add or update mock configuration
|
|
124
|
+
*/
|
|
125
|
+
setMock(endpoint: string, config: EndpointMockConfig): void;
|
|
126
|
+
/**
|
|
127
|
+
* Remove mock configuration
|
|
128
|
+
*/
|
|
129
|
+
removeMock(endpoint: string): void;
|
|
130
|
+
/**
|
|
131
|
+
* Set global error (all calls will fail)
|
|
132
|
+
*/
|
|
133
|
+
setGlobalError(error?: {
|
|
134
|
+
message: string;
|
|
135
|
+
code?: string;
|
|
136
|
+
details?: any;
|
|
137
|
+
}): void;
|
|
138
|
+
/**
|
|
139
|
+
* Clear global error
|
|
140
|
+
*/
|
|
141
|
+
clearGlobalError(): void;
|
|
142
|
+
/**
|
|
143
|
+
* Reset provider to initial state
|
|
144
|
+
*/
|
|
145
|
+
reset(): void;
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=mock-resource-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-resource-provider.d.ts","sourceRoot":"","sources":["../../src/providers/mock-resource-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock Resource Provider for testing
|
|
3
|
+
*/
|
|
4
|
+
export class MockResourceProvider {
|
|
5
|
+
constructor(config = {}) {
|
|
6
|
+
this.type = 'mock';
|
|
7
|
+
this.callHistory = [];
|
|
8
|
+
this.mocks = config.mocks || {};
|
|
9
|
+
this.defaultDelay = config.defaultDelay || 0;
|
|
10
|
+
this.defaultResponse = config.defaultResponse || { message: 'Mock response' };
|
|
11
|
+
this.trackHistory = config.trackHistory !== false; // Default true
|
|
12
|
+
this.globalError = config.globalError;
|
|
13
|
+
this.onAnyCall = config.onAnyCall;
|
|
14
|
+
}
|
|
15
|
+
async call(endpoint, params, operation) {
|
|
16
|
+
// Invoke global callback
|
|
17
|
+
this.onAnyCall?.(endpoint, params, operation);
|
|
18
|
+
// Find matching mock config
|
|
19
|
+
const mockConfig = this.findMockConfig(endpoint);
|
|
20
|
+
// Determine delay
|
|
21
|
+
const delay = mockConfig?.delay ?? this.defaultDelay;
|
|
22
|
+
// Simulate delay
|
|
23
|
+
if (delay > 0) {
|
|
24
|
+
await this.sleep(delay);
|
|
25
|
+
}
|
|
26
|
+
// Record call start
|
|
27
|
+
const historyEntry = {
|
|
28
|
+
timestamp: Date.now(),
|
|
29
|
+
endpoint,
|
|
30
|
+
params,
|
|
31
|
+
operation
|
|
32
|
+
};
|
|
33
|
+
try {
|
|
34
|
+
// Check for global error
|
|
35
|
+
if (this.globalError) {
|
|
36
|
+
throw this.createError(this.globalError);
|
|
37
|
+
}
|
|
38
|
+
// Check for endpoint-specific error
|
|
39
|
+
if (mockConfig?.error) {
|
|
40
|
+
throw this.createError(mockConfig.error);
|
|
41
|
+
}
|
|
42
|
+
// Invoke endpoint callback
|
|
43
|
+
mockConfig?.onCall?.(endpoint, params, operation);
|
|
44
|
+
// Generate response
|
|
45
|
+
const result = await this.generateResponse(endpoint, params, operation, mockConfig);
|
|
46
|
+
// Record success
|
|
47
|
+
if (this.trackHistory) {
|
|
48
|
+
historyEntry.result = result;
|
|
49
|
+
this.callHistory.push(historyEntry);
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
// Record error
|
|
55
|
+
if (this.trackHistory) {
|
|
56
|
+
historyEntry.error = error;
|
|
57
|
+
this.callHistory.push(historyEntry);
|
|
58
|
+
}
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Find mock config for endpoint (supports pattern matching)
|
|
64
|
+
*/
|
|
65
|
+
findMockConfig(endpoint) {
|
|
66
|
+
// Exact match
|
|
67
|
+
if (this.mocks[endpoint]) {
|
|
68
|
+
return this.mocks[endpoint];
|
|
69
|
+
}
|
|
70
|
+
// Pattern matching (e.g., "/api/users/:id" matches "/api/users/123")
|
|
71
|
+
for (const [pattern, config] of Object.entries(this.mocks)) {
|
|
72
|
+
if (this.matchesPattern(endpoint, pattern)) {
|
|
73
|
+
return config;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Simple pattern matching for endpoints
|
|
80
|
+
*/
|
|
81
|
+
matchesPattern(endpoint, pattern) {
|
|
82
|
+
const patternRegex = pattern.replace(/:[^/]+/g, '[^/]+');
|
|
83
|
+
const regex = new RegExp(`^${patternRegex}$`);
|
|
84
|
+
return regex.test(endpoint);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Generate response based on config
|
|
88
|
+
*/
|
|
89
|
+
async generateResponse(endpoint, params, operation, mockConfig) {
|
|
90
|
+
// Use custom response generator if provided
|
|
91
|
+
if (mockConfig?.responseGenerator) {
|
|
92
|
+
const result = await mockConfig.responseGenerator(endpoint, params, operation);
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
// Use configured data
|
|
96
|
+
const data = mockConfig?.data ?? this.defaultResponse;
|
|
97
|
+
const metadata = mockConfig?.metadata ?? {
|
|
98
|
+
status: 200,
|
|
99
|
+
headers: { 'content-type': 'application/json' }
|
|
100
|
+
};
|
|
101
|
+
return { data, metadata };
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Create error object
|
|
105
|
+
*/
|
|
106
|
+
createError(errorConfig) {
|
|
107
|
+
const error = new Error(errorConfig.message);
|
|
108
|
+
error.code = errorConfig.code;
|
|
109
|
+
error.details = errorConfig.details;
|
|
110
|
+
return error;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Sleep helper
|
|
114
|
+
*/
|
|
115
|
+
sleep(ms) {
|
|
116
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
117
|
+
}
|
|
118
|
+
// ========== Testing Utilities ==========
|
|
119
|
+
/**
|
|
120
|
+
* Get call history
|
|
121
|
+
*/
|
|
122
|
+
getCallHistory() {
|
|
123
|
+
return [...this.callHistory];
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Get calls for specific endpoint
|
|
127
|
+
*/
|
|
128
|
+
getCallsForEndpoint(endpoint) {
|
|
129
|
+
return this.callHistory.filter((entry) => entry.endpoint === endpoint);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Get calls for specific operation
|
|
133
|
+
*/
|
|
134
|
+
getCallsForOperation(operation) {
|
|
135
|
+
return this.callHistory.filter((entry) => entry.operation === operation);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Clear call history
|
|
139
|
+
*/
|
|
140
|
+
clearHistory() {
|
|
141
|
+
this.callHistory = [];
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Get last call
|
|
145
|
+
*/
|
|
146
|
+
getLastCall() {
|
|
147
|
+
return this.callHistory[this.callHistory.length - 1];
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Check if endpoint was called
|
|
151
|
+
*/
|
|
152
|
+
wasEndpointCalled(endpoint, operation) {
|
|
153
|
+
return this.callHistory.some((entry) => entry.endpoint === endpoint && (!operation || entry.operation === operation));
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Get call count for endpoint
|
|
157
|
+
*/
|
|
158
|
+
getCallCount(endpoint, operation) {
|
|
159
|
+
if (!endpoint) {
|
|
160
|
+
return this.callHistory.length;
|
|
161
|
+
}
|
|
162
|
+
return this.callHistory.filter((entry) => entry.endpoint === endpoint && (!operation || entry.operation === operation)).length;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Add or update mock configuration
|
|
166
|
+
*/
|
|
167
|
+
setMock(endpoint, config) {
|
|
168
|
+
this.mocks[endpoint] = config;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Remove mock configuration
|
|
172
|
+
*/
|
|
173
|
+
removeMock(endpoint) {
|
|
174
|
+
delete this.mocks[endpoint];
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Set global error (all calls will fail)
|
|
178
|
+
*/
|
|
179
|
+
setGlobalError(error) {
|
|
180
|
+
this.globalError = error;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Clear global error
|
|
184
|
+
*/
|
|
185
|
+
clearGlobalError() {
|
|
186
|
+
this.globalError = undefined;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Reset provider to initial state
|
|
190
|
+
*/
|
|
191
|
+
reset() {
|
|
192
|
+
this.clearHistory();
|
|
193
|
+
this.clearGlobalError();
|
|
194
|
+
this.mocks = {};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type AxiosInstance } from 'axios';
|
|
2
|
+
import type { IResourceProvider, ResourceCallParams, ResourceCallResult } from '@hamak/ui-remote-resource-spi';
|
|
3
|
+
import type { ResourceOperation } from '@hamak/ui-remote-resource-api';
|
|
4
|
+
/**
|
|
5
|
+
* Configuration for REST resource provider
|
|
6
|
+
*/
|
|
7
|
+
export interface RestProviderConfig {
|
|
8
|
+
/** Base URL for API requests */
|
|
9
|
+
baseUrl?: string;
|
|
10
|
+
/** Request timeout in milliseconds */
|
|
11
|
+
timeout?: number;
|
|
12
|
+
/** Default headers for all requests */
|
|
13
|
+
headers?: Record<string, string>;
|
|
14
|
+
/** Custom axios instance (if not provided, one will be created) */
|
|
15
|
+
axiosInstance?: AxiosInstance;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* REST API resource provider using Axios
|
|
19
|
+
*/
|
|
20
|
+
export declare class RestResourceProvider implements IResourceProvider {
|
|
21
|
+
readonly type = "rest";
|
|
22
|
+
private axios;
|
|
23
|
+
constructor(config?: RestProviderConfig);
|
|
24
|
+
/**
|
|
25
|
+
* Execute a resource call
|
|
26
|
+
*/
|
|
27
|
+
call<TData = any>(endpoint: string, params: ResourceCallParams, operation: ResourceOperation): Promise<ResourceCallResult<TData>>;
|
|
28
|
+
/**
|
|
29
|
+
* Execute fetch operation (GET)
|
|
30
|
+
*/
|
|
31
|
+
private executeFetch;
|
|
32
|
+
/**
|
|
33
|
+
* Execute create operation (POST)
|
|
34
|
+
*/
|
|
35
|
+
private executeCreate;
|
|
36
|
+
/**
|
|
37
|
+
* Execute update operation (PUT)
|
|
38
|
+
*/
|
|
39
|
+
private executeUpdate;
|
|
40
|
+
/**
|
|
41
|
+
* Execute delete operation (DELETE)
|
|
42
|
+
*/
|
|
43
|
+
private executeDelete;
|
|
44
|
+
/**
|
|
45
|
+
* Build URL with path parameters
|
|
46
|
+
* Replaces :param placeholders with actual values
|
|
47
|
+
* Example: /users/:id + { id: "123" } → /users/123
|
|
48
|
+
*/
|
|
49
|
+
private buildUrl;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=rest-resource-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rest-resource-provider.d.ts","sourceRoot":"","sources":["../../src/providers/rest-resource-provider.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAClD,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,gCAAgC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,uCAAuC;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjC,mEAAmE;IACnE,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED;;GAEG;AACH,qBAAa,oBAAqB,YAAW,iBAAiB;IAC5D,QAAQ,CAAC,IAAI,UAAU;IACvB,OAAO,CAAC,KAAK,CAAgB;gBAEjB,MAAM,GAAE,kBAAuB;IAa3C;;OAEG;IACG,IAAI,CAAC,KAAK,GAAG,GAAG,EACpB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,kBAAkB,EAC1B,SAAS,EAAE,iBAAiB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAerC;;OAEG;YACW,YAAY;IAmB1B;;OAEG;YACW,aAAa;IAkB3B;;OAEG;YACW,aAAa;IAmB3B;;OAEG;YACW,aAAa;IAmB3B;;;;OAIG;IACH,OAAO,CAAC,QAAQ;CAUjB"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
/**
|
|
3
|
+
* REST API resource provider using Axios
|
|
4
|
+
*/
|
|
5
|
+
export class RestResourceProvider {
|
|
6
|
+
constructor(config = {}) {
|
|
7
|
+
this.type = 'rest';
|
|
8
|
+
this.axios =
|
|
9
|
+
config.axiosInstance ||
|
|
10
|
+
axios.create({
|
|
11
|
+
baseURL: config.baseUrl,
|
|
12
|
+
timeout: config.timeout || 30000,
|
|
13
|
+
headers: {
|
|
14
|
+
'Content-Type': 'application/json',
|
|
15
|
+
...config.headers
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Execute a resource call
|
|
21
|
+
*/
|
|
22
|
+
async call(endpoint, params, operation) {
|
|
23
|
+
switch (operation) {
|
|
24
|
+
case 'fetch':
|
|
25
|
+
return this.executeFetch(endpoint, params);
|
|
26
|
+
case 'create':
|
|
27
|
+
return this.executeCreate(endpoint, params);
|
|
28
|
+
case 'update':
|
|
29
|
+
return this.executeUpdate(endpoint, params);
|
|
30
|
+
case 'delete':
|
|
31
|
+
return this.executeDelete(endpoint, params);
|
|
32
|
+
default:
|
|
33
|
+
throw new Error(`Unknown operation: ${operation}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Execute fetch operation (GET)
|
|
38
|
+
*/
|
|
39
|
+
async executeFetch(endpoint, params) {
|
|
40
|
+
const url = this.buildUrl(endpoint, params.params);
|
|
41
|
+
const response = await this.axios.get(url, {
|
|
42
|
+
params: params.query,
|
|
43
|
+
headers: params.headers
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
data: response.data,
|
|
47
|
+
metadata: {
|
|
48
|
+
status: response.status,
|
|
49
|
+
headers: response.headers
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Execute create operation (POST)
|
|
55
|
+
*/
|
|
56
|
+
async executeCreate(endpoint, params) {
|
|
57
|
+
const response = await this.axios.post(endpoint, params.body, {
|
|
58
|
+
params: params.query,
|
|
59
|
+
headers: params.headers
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
data: response.data,
|
|
63
|
+
metadata: {
|
|
64
|
+
status: response.status,
|
|
65
|
+
headers: response.headers
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Execute update operation (PUT)
|
|
71
|
+
*/
|
|
72
|
+
async executeUpdate(endpoint, params) {
|
|
73
|
+
const url = this.buildUrl(endpoint, params.params);
|
|
74
|
+
const response = await this.axios.put(url, params.body, {
|
|
75
|
+
params: params.query,
|
|
76
|
+
headers: params.headers
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
data: response.data,
|
|
80
|
+
metadata: {
|
|
81
|
+
status: response.status,
|
|
82
|
+
headers: response.headers
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Execute delete operation (DELETE)
|
|
88
|
+
*/
|
|
89
|
+
async executeDelete(endpoint, params) {
|
|
90
|
+
const url = this.buildUrl(endpoint, params.params);
|
|
91
|
+
const response = await this.axios.delete(url, {
|
|
92
|
+
params: params.query,
|
|
93
|
+
headers: params.headers
|
|
94
|
+
});
|
|
95
|
+
return {
|
|
96
|
+
data: response.data,
|
|
97
|
+
metadata: {
|
|
98
|
+
status: response.status,
|
|
99
|
+
headers: response.headers
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Build URL with path parameters
|
|
105
|
+
* Replaces :param placeholders with actual values
|
|
106
|
+
* Example: /users/:id + { id: "123" } → /users/123
|
|
107
|
+
*/
|
|
108
|
+
buildUrl(endpoint, params) {
|
|
109
|
+
if (!params)
|
|
110
|
+
return endpoint;
|
|
111
|
+
let url = endpoint;
|
|
112
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
113
|
+
url = url.replace(`:${key}`, String(value));
|
|
114
|
+
});
|
|
115
|
+
return url;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { EntityDefinition } from '@hamak/ui-remote-resource-spi';
|
|
2
|
+
/**
|
|
3
|
+
* Internal entity registry interface with write operations
|
|
4
|
+
*/
|
|
5
|
+
export interface IEntityRegistryInternal {
|
|
6
|
+
/**
|
|
7
|
+
* Register an entity definition
|
|
8
|
+
*/
|
|
9
|
+
registerEntity(definition: EntityDefinition): void;
|
|
10
|
+
/**
|
|
11
|
+
* Unregister an entity definition
|
|
12
|
+
*/
|
|
13
|
+
unregisterEntity(id: string): void;
|
|
14
|
+
/**
|
|
15
|
+
* Get entity definition by ID
|
|
16
|
+
*/
|
|
17
|
+
getEntity(id: string): EntityDefinition | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Get all registered entities
|
|
20
|
+
*/
|
|
21
|
+
getAllEntities(): EntityDefinition[];
|
|
22
|
+
/**
|
|
23
|
+
* Check if entity exists
|
|
24
|
+
*/
|
|
25
|
+
hasEntity(id: string): boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Entity registry implementation
|
|
29
|
+
* Manages entity definitions
|
|
30
|
+
*/
|
|
31
|
+
export declare class EntityRegistry implements IEntityRegistryInternal {
|
|
32
|
+
private entities;
|
|
33
|
+
/**
|
|
34
|
+
* Register an entity definition
|
|
35
|
+
*/
|
|
36
|
+
registerEntity(definition: EntityDefinition): void;
|
|
37
|
+
/**
|
|
38
|
+
* Unregister an entity definition
|
|
39
|
+
*/
|
|
40
|
+
unregisterEntity(id: string): void;
|
|
41
|
+
/**
|
|
42
|
+
* Get entity definition by ID
|
|
43
|
+
*/
|
|
44
|
+
getEntity(id: string): EntityDefinition | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* Get all registered entities
|
|
47
|
+
*/
|
|
48
|
+
getAllEntities(): EntityDefinition[];
|
|
49
|
+
/**
|
|
50
|
+
* Check if entity exists
|
|
51
|
+
*/
|
|
52
|
+
hasEntity(id: string): boolean;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=entity-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-registry.d.ts","sourceRoot":"","sources":["../../src/registry/entity-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAEtE;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,cAAc,CAAC,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAEnD;;OAEG;IACH,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAEnC;;OAEG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAAC;IAEpD;;OAEG;IACH,cAAc,IAAI,gBAAgB,EAAE,CAAC;IAErC;;OAEG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;CAChC;AAED;;;GAGG;AACH,qBAAa,cAAe,YAAW,uBAAuB;IAC5D,OAAO,CAAC,QAAQ,CAA4C;IAE5D;;OAEG;IACH,cAAc,CAAC,UAAU,EAAE,gBAAgB,GAAG,IAAI;IAiBlD;;OAEG;IACH,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIlC;;OAEG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAInD;;OAEG;IACH,cAAc,IAAI,gBAAgB,EAAE;IAIpC;;OAEG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;CAG/B"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entity registry implementation
|
|
3
|
+
* Manages entity definitions
|
|
4
|
+
*/
|
|
5
|
+
export class EntityRegistry {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.entities = new Map();
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Register an entity definition
|
|
11
|
+
*/
|
|
12
|
+
registerEntity(definition) {
|
|
13
|
+
// Validate key schema
|
|
14
|
+
if (!definition.keySchema.fields || definition.keySchema.fields.length === 0) {
|
|
15
|
+
throw new Error(`Entity "${definition.id}" must have at least one key field in keySchema.fields`);
|
|
16
|
+
}
|
|
17
|
+
if (this.entities.has(definition.id)) {
|
|
18
|
+
console.warn(`Entity "${definition.id}" is already registered. Overwriting.`);
|
|
19
|
+
}
|
|
20
|
+
this.entities.set(definition.id, definition);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Unregister an entity definition
|
|
24
|
+
*/
|
|
25
|
+
unregisterEntity(id) {
|
|
26
|
+
this.entities.delete(id);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get entity definition by ID
|
|
30
|
+
*/
|
|
31
|
+
getEntity(id) {
|
|
32
|
+
return this.entities.get(id);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get all registered entities
|
|
36
|
+
*/
|
|
37
|
+
getAllEntities() {
|
|
38
|
+
return Array.from(this.entities.values());
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Check if entity exists
|
|
42
|
+
*/
|
|
43
|
+
hasEntity(id) {
|
|
44
|
+
return this.entities.has(id);
|
|
45
|
+
}
|
|
46
|
+
}
|