@object-ui/data-objectstack 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +45 -0
- package/dist/index.cjs +182 -0
- package/dist/index.d.cts +106 -0
- package/dist/index.d.ts +106 -0
- package/dist/index.js +158 -0
- package/package.json +43 -0
- package/src/index.ts +231 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 ObjectQL
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# @object-ui/data-objectstack
|
|
2
|
+
|
|
3
|
+
Official ObjectStack data adapter for Object UI.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This package provides the `ObjectStackAdapter` class, which connects Object UI's universal `DataSource` interface with the `@objectstack/client` SDK.
|
|
8
|
+
|
|
9
|
+
This enables strictly typed, metadata-driven UI components to communicate seamlessly with ObjectStack backends (Steedos, Salesforce, etc.).
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @object-ui/data-objectstack @objectstack/client
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { createObjectStackAdapter } from '@object-ui/data-objectstack';
|
|
21
|
+
import { SchemaRenderer } from '@object-ui/react';
|
|
22
|
+
|
|
23
|
+
// 1. Create the adapter
|
|
24
|
+
const dataSource = createObjectStackAdapter({
|
|
25
|
+
baseUrl: 'https://api.example.com',
|
|
26
|
+
token: 'your-api-token' // Optional if effectively handling auth elsewhere
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// 2. Pass to the Renderer
|
|
30
|
+
function App() {
|
|
31
|
+
return (
|
|
32
|
+
<SchemaRenderer
|
|
33
|
+
schema={mySchema}
|
|
34
|
+
dataSource={dataSource}
|
|
35
|
+
/>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
- ✅ **CRUD Operations**: Implements `find`, `findOne`, `create`, `update`, `delete`.
|
|
43
|
+
- ✅ **Metadata Fetching**: Implements `getObjectSchema` to power auto-generated forms and grids.
|
|
44
|
+
- ✅ **Query Translation**: Converts Object UI's OData-like query parameters to ObjectStack's native query format.
|
|
45
|
+
- ✅ **Bulk Operations**: Supports batch create/update/delete.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
21
|
+
|
|
22
|
+
// src/index.ts
|
|
23
|
+
var index_exports = {};
|
|
24
|
+
__export(index_exports, {
|
|
25
|
+
ObjectStackAdapter: () => ObjectStackAdapter,
|
|
26
|
+
createObjectStackAdapter: () => createObjectStackAdapter
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
var import_client = require("@objectstack/client");
|
|
30
|
+
var import_core = require("@object-ui/core");
|
|
31
|
+
var ObjectStackAdapter = class {
|
|
32
|
+
constructor(config) {
|
|
33
|
+
__publicField(this, "client");
|
|
34
|
+
__publicField(this, "connected", false);
|
|
35
|
+
this.client = new import_client.ObjectStackClient(config);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Ensure the client is connected to the server.
|
|
39
|
+
* Call this before making requests or it will auto-connect on first request.
|
|
40
|
+
*/
|
|
41
|
+
async connect() {
|
|
42
|
+
if (!this.connected) {
|
|
43
|
+
await this.client.connect();
|
|
44
|
+
this.connected = true;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Find multiple records with query parameters.
|
|
49
|
+
* Converts OData-style params to ObjectStack query options.
|
|
50
|
+
*/
|
|
51
|
+
async find(resource, params) {
|
|
52
|
+
await this.connect();
|
|
53
|
+
const queryOptions = this.convertQueryParams(params);
|
|
54
|
+
const result = await this.client.data.find(resource, queryOptions);
|
|
55
|
+
return {
|
|
56
|
+
data: result.value,
|
|
57
|
+
total: result.count,
|
|
58
|
+
page: params?.$skip ? Math.floor(params.$skip / (params.$top || 20)) + 1 : 1,
|
|
59
|
+
pageSize: params?.$top,
|
|
60
|
+
hasMore: result.value.length === params?.$top
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Find a single record by ID.
|
|
65
|
+
*/
|
|
66
|
+
async findOne(resource, id, _params) {
|
|
67
|
+
await this.connect();
|
|
68
|
+
try {
|
|
69
|
+
const record = await this.client.data.get(resource, String(id));
|
|
70
|
+
return record;
|
|
71
|
+
} catch (error) {
|
|
72
|
+
if (error?.status === 404) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Create a new record.
|
|
80
|
+
*/
|
|
81
|
+
async create(resource, data) {
|
|
82
|
+
await this.connect();
|
|
83
|
+
return this.client.data.create(resource, data);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Update an existing record.
|
|
87
|
+
*/
|
|
88
|
+
async update(resource, id, data) {
|
|
89
|
+
await this.connect();
|
|
90
|
+
return this.client.data.update(resource, String(id), data);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Delete a record.
|
|
94
|
+
*/
|
|
95
|
+
async delete(resource, id) {
|
|
96
|
+
await this.connect();
|
|
97
|
+
const result = await this.client.data.delete(resource, String(id));
|
|
98
|
+
return result.success;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Bulk operations (optional implementation).
|
|
102
|
+
*/
|
|
103
|
+
async bulk(resource, operation, data) {
|
|
104
|
+
await this.connect();
|
|
105
|
+
switch (operation) {
|
|
106
|
+
case "create":
|
|
107
|
+
return this.client.data.createMany(resource, data);
|
|
108
|
+
case "delete": {
|
|
109
|
+
const ids = data.map((item) => item.id).filter(Boolean);
|
|
110
|
+
await this.client.data.deleteMany(resource, ids);
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
case "update": {
|
|
114
|
+
const results = await Promise.all(
|
|
115
|
+
data.map(
|
|
116
|
+
(item) => this.client.data.update(resource, String(item.id), item)
|
|
117
|
+
)
|
|
118
|
+
);
|
|
119
|
+
return results;
|
|
120
|
+
}
|
|
121
|
+
default:
|
|
122
|
+
throw new Error(`Unsupported bulk operation: ${operation}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Convert ObjectUI QueryParams to ObjectStack QueryOptions.
|
|
127
|
+
* Maps OData-style conventions to ObjectStack conventions.
|
|
128
|
+
*/
|
|
129
|
+
convertQueryParams(params) {
|
|
130
|
+
if (!params) return {};
|
|
131
|
+
const options = {};
|
|
132
|
+
if (params.$select) {
|
|
133
|
+
options.select = params.$select;
|
|
134
|
+
}
|
|
135
|
+
if (params.$filter) {
|
|
136
|
+
options.filters = (0, import_core.convertFiltersToAST)(params.$filter);
|
|
137
|
+
}
|
|
138
|
+
if (params.$orderby) {
|
|
139
|
+
const sortArray = Object.entries(params.$orderby).map(([field, order]) => {
|
|
140
|
+
return order === "desc" ? `-${field}` : field;
|
|
141
|
+
});
|
|
142
|
+
options.sort = sortArray;
|
|
143
|
+
}
|
|
144
|
+
if (params.$skip !== void 0) {
|
|
145
|
+
options.skip = params.$skip;
|
|
146
|
+
}
|
|
147
|
+
if (params.$top !== void 0) {
|
|
148
|
+
options.top = params.$top;
|
|
149
|
+
}
|
|
150
|
+
return options;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get object schema/metadata from ObjectStack.
|
|
154
|
+
*
|
|
155
|
+
* @param objectName - Object name
|
|
156
|
+
* @returns Promise resolving to the object schema
|
|
157
|
+
*/
|
|
158
|
+
async getObjectSchema(objectName) {
|
|
159
|
+
await this.connect();
|
|
160
|
+
try {
|
|
161
|
+
const schema = await this.client.meta.getObject(objectName);
|
|
162
|
+
return schema;
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.error(`Failed to fetch schema for ${objectName}:`, error);
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get access to the underlying ObjectStack client for advanced operations.
|
|
170
|
+
*/
|
|
171
|
+
getClient() {
|
|
172
|
+
return this.client;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
function createObjectStackAdapter(config) {
|
|
176
|
+
return new ObjectStackAdapter(config);
|
|
177
|
+
}
|
|
178
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
179
|
+
0 && (module.exports = {
|
|
180
|
+
ObjectStackAdapter,
|
|
181
|
+
createObjectStackAdapter
|
|
182
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { ObjectStackClient } from '@objectstack/client';
|
|
2
|
+
import { DataSource, QueryParams, QueryResult } from '@object-ui/types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* ObjectUI
|
|
6
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
7
|
+
*
|
|
8
|
+
* This source code is licensed under the MIT license found in the
|
|
9
|
+
* LICENSE file in the root directory of this source tree.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* ObjectStack Data Source Adapter
|
|
14
|
+
*
|
|
15
|
+
* Bridges the ObjectStack Client SDK with the ObjectUI DataSource interface.
|
|
16
|
+
* This allows Object UI applications to seamlessly integrate with ObjectStack
|
|
17
|
+
* backends while maintaining the universal DataSource abstraction.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { ObjectStackAdapter } from '@object-ui/data-objectstack';
|
|
22
|
+
*
|
|
23
|
+
* const dataSource = new ObjectStackAdapter({
|
|
24
|
+
* baseUrl: 'https://api.example.com',
|
|
25
|
+
* token: 'your-api-token'
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* const users = await dataSource.find('users', {
|
|
29
|
+
* $filter: { status: 'active' },
|
|
30
|
+
* $top: 10
|
|
31
|
+
* });
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
declare class ObjectStackAdapter<T = any> implements DataSource<T> {
|
|
35
|
+
private client;
|
|
36
|
+
private connected;
|
|
37
|
+
constructor(config: {
|
|
38
|
+
baseUrl: string;
|
|
39
|
+
token?: string;
|
|
40
|
+
fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
41
|
+
});
|
|
42
|
+
/**
|
|
43
|
+
* Ensure the client is connected to the server.
|
|
44
|
+
* Call this before making requests or it will auto-connect on first request.
|
|
45
|
+
*/
|
|
46
|
+
connect(): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Find multiple records with query parameters.
|
|
49
|
+
* Converts OData-style params to ObjectStack query options.
|
|
50
|
+
*/
|
|
51
|
+
find(resource: string, params?: QueryParams): Promise<QueryResult<T>>;
|
|
52
|
+
/**
|
|
53
|
+
* Find a single record by ID.
|
|
54
|
+
*/
|
|
55
|
+
findOne(resource: string, id: string | number, _params?: QueryParams): Promise<T | null>;
|
|
56
|
+
/**
|
|
57
|
+
* Create a new record.
|
|
58
|
+
*/
|
|
59
|
+
create(resource: string, data: Partial<T>): Promise<T>;
|
|
60
|
+
/**
|
|
61
|
+
* Update an existing record.
|
|
62
|
+
*/
|
|
63
|
+
update(resource: string, id: string | number, data: Partial<T>): Promise<T>;
|
|
64
|
+
/**
|
|
65
|
+
* Delete a record.
|
|
66
|
+
*/
|
|
67
|
+
delete(resource: string, id: string | number): Promise<boolean>;
|
|
68
|
+
/**
|
|
69
|
+
* Bulk operations (optional implementation).
|
|
70
|
+
*/
|
|
71
|
+
bulk(resource: string, operation: 'create' | 'update' | 'delete', data: Partial<T>[]): Promise<T[]>;
|
|
72
|
+
/**
|
|
73
|
+
* Convert ObjectUI QueryParams to ObjectStack QueryOptions.
|
|
74
|
+
* Maps OData-style conventions to ObjectStack conventions.
|
|
75
|
+
*/
|
|
76
|
+
private convertQueryParams;
|
|
77
|
+
/**
|
|
78
|
+
* Get object schema/metadata from ObjectStack.
|
|
79
|
+
*
|
|
80
|
+
* @param objectName - Object name
|
|
81
|
+
* @returns Promise resolving to the object schema
|
|
82
|
+
*/
|
|
83
|
+
getObjectSchema(objectName: string): Promise<any>;
|
|
84
|
+
/**
|
|
85
|
+
* Get access to the underlying ObjectStack client for advanced operations.
|
|
86
|
+
*/
|
|
87
|
+
getClient(): ObjectStackClient;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Factory function to create an ObjectStack data source.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```typescript
|
|
94
|
+
* const dataSource = createObjectStackAdapter({
|
|
95
|
+
* baseUrl: process.env.API_URL,
|
|
96
|
+
* token: process.env.API_TOKEN
|
|
97
|
+
* });
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
declare function createObjectStackAdapter<T = any>(config: {
|
|
101
|
+
baseUrl: string;
|
|
102
|
+
token?: string;
|
|
103
|
+
fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
104
|
+
}): DataSource<T>;
|
|
105
|
+
|
|
106
|
+
export { ObjectStackAdapter, createObjectStackAdapter };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { ObjectStackClient } from '@objectstack/client';
|
|
2
|
+
import { DataSource, QueryParams, QueryResult } from '@object-ui/types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* ObjectUI
|
|
6
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
7
|
+
*
|
|
8
|
+
* This source code is licensed under the MIT license found in the
|
|
9
|
+
* LICENSE file in the root directory of this source tree.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* ObjectStack Data Source Adapter
|
|
14
|
+
*
|
|
15
|
+
* Bridges the ObjectStack Client SDK with the ObjectUI DataSource interface.
|
|
16
|
+
* This allows Object UI applications to seamlessly integrate with ObjectStack
|
|
17
|
+
* backends while maintaining the universal DataSource abstraction.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { ObjectStackAdapter } from '@object-ui/data-objectstack';
|
|
22
|
+
*
|
|
23
|
+
* const dataSource = new ObjectStackAdapter({
|
|
24
|
+
* baseUrl: 'https://api.example.com',
|
|
25
|
+
* token: 'your-api-token'
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* const users = await dataSource.find('users', {
|
|
29
|
+
* $filter: { status: 'active' },
|
|
30
|
+
* $top: 10
|
|
31
|
+
* });
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
declare class ObjectStackAdapter<T = any> implements DataSource<T> {
|
|
35
|
+
private client;
|
|
36
|
+
private connected;
|
|
37
|
+
constructor(config: {
|
|
38
|
+
baseUrl: string;
|
|
39
|
+
token?: string;
|
|
40
|
+
fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
41
|
+
});
|
|
42
|
+
/**
|
|
43
|
+
* Ensure the client is connected to the server.
|
|
44
|
+
* Call this before making requests or it will auto-connect on first request.
|
|
45
|
+
*/
|
|
46
|
+
connect(): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Find multiple records with query parameters.
|
|
49
|
+
* Converts OData-style params to ObjectStack query options.
|
|
50
|
+
*/
|
|
51
|
+
find(resource: string, params?: QueryParams): Promise<QueryResult<T>>;
|
|
52
|
+
/**
|
|
53
|
+
* Find a single record by ID.
|
|
54
|
+
*/
|
|
55
|
+
findOne(resource: string, id: string | number, _params?: QueryParams): Promise<T | null>;
|
|
56
|
+
/**
|
|
57
|
+
* Create a new record.
|
|
58
|
+
*/
|
|
59
|
+
create(resource: string, data: Partial<T>): Promise<T>;
|
|
60
|
+
/**
|
|
61
|
+
* Update an existing record.
|
|
62
|
+
*/
|
|
63
|
+
update(resource: string, id: string | number, data: Partial<T>): Promise<T>;
|
|
64
|
+
/**
|
|
65
|
+
* Delete a record.
|
|
66
|
+
*/
|
|
67
|
+
delete(resource: string, id: string | number): Promise<boolean>;
|
|
68
|
+
/**
|
|
69
|
+
* Bulk operations (optional implementation).
|
|
70
|
+
*/
|
|
71
|
+
bulk(resource: string, operation: 'create' | 'update' | 'delete', data: Partial<T>[]): Promise<T[]>;
|
|
72
|
+
/**
|
|
73
|
+
* Convert ObjectUI QueryParams to ObjectStack QueryOptions.
|
|
74
|
+
* Maps OData-style conventions to ObjectStack conventions.
|
|
75
|
+
*/
|
|
76
|
+
private convertQueryParams;
|
|
77
|
+
/**
|
|
78
|
+
* Get object schema/metadata from ObjectStack.
|
|
79
|
+
*
|
|
80
|
+
* @param objectName - Object name
|
|
81
|
+
* @returns Promise resolving to the object schema
|
|
82
|
+
*/
|
|
83
|
+
getObjectSchema(objectName: string): Promise<any>;
|
|
84
|
+
/**
|
|
85
|
+
* Get access to the underlying ObjectStack client for advanced operations.
|
|
86
|
+
*/
|
|
87
|
+
getClient(): ObjectStackClient;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Factory function to create an ObjectStack data source.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```typescript
|
|
94
|
+
* const dataSource = createObjectStackAdapter({
|
|
95
|
+
* baseUrl: process.env.API_URL,
|
|
96
|
+
* token: process.env.API_TOKEN
|
|
97
|
+
* });
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
declare function createObjectStackAdapter<T = any>(config: {
|
|
101
|
+
baseUrl: string;
|
|
102
|
+
token?: string;
|
|
103
|
+
fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
104
|
+
}): DataSource<T>;
|
|
105
|
+
|
|
106
|
+
export { ObjectStackAdapter, createObjectStackAdapter };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
|
|
5
|
+
// src/index.ts
|
|
6
|
+
import { ObjectStackClient } from "@objectstack/client";
|
|
7
|
+
import { convertFiltersToAST } from "@object-ui/core";
|
|
8
|
+
var ObjectStackAdapter = class {
|
|
9
|
+
constructor(config) {
|
|
10
|
+
__publicField(this, "client");
|
|
11
|
+
__publicField(this, "connected", false);
|
|
12
|
+
this.client = new ObjectStackClient(config);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Ensure the client is connected to the server.
|
|
16
|
+
* Call this before making requests or it will auto-connect on first request.
|
|
17
|
+
*/
|
|
18
|
+
async connect() {
|
|
19
|
+
if (!this.connected) {
|
|
20
|
+
await this.client.connect();
|
|
21
|
+
this.connected = true;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Find multiple records with query parameters.
|
|
26
|
+
* Converts OData-style params to ObjectStack query options.
|
|
27
|
+
*/
|
|
28
|
+
async find(resource, params) {
|
|
29
|
+
await this.connect();
|
|
30
|
+
const queryOptions = this.convertQueryParams(params);
|
|
31
|
+
const result = await this.client.data.find(resource, queryOptions);
|
|
32
|
+
return {
|
|
33
|
+
data: result.value,
|
|
34
|
+
total: result.count,
|
|
35
|
+
page: params?.$skip ? Math.floor(params.$skip / (params.$top || 20)) + 1 : 1,
|
|
36
|
+
pageSize: params?.$top,
|
|
37
|
+
hasMore: result.value.length === params?.$top
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Find a single record by ID.
|
|
42
|
+
*/
|
|
43
|
+
async findOne(resource, id, _params) {
|
|
44
|
+
await this.connect();
|
|
45
|
+
try {
|
|
46
|
+
const record = await this.client.data.get(resource, String(id));
|
|
47
|
+
return record;
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (error?.status === 404) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Create a new record.
|
|
57
|
+
*/
|
|
58
|
+
async create(resource, data) {
|
|
59
|
+
await this.connect();
|
|
60
|
+
return this.client.data.create(resource, data);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Update an existing record.
|
|
64
|
+
*/
|
|
65
|
+
async update(resource, id, data) {
|
|
66
|
+
await this.connect();
|
|
67
|
+
return this.client.data.update(resource, String(id), data);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Delete a record.
|
|
71
|
+
*/
|
|
72
|
+
async delete(resource, id) {
|
|
73
|
+
await this.connect();
|
|
74
|
+
const result = await this.client.data.delete(resource, String(id));
|
|
75
|
+
return result.success;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Bulk operations (optional implementation).
|
|
79
|
+
*/
|
|
80
|
+
async bulk(resource, operation, data) {
|
|
81
|
+
await this.connect();
|
|
82
|
+
switch (operation) {
|
|
83
|
+
case "create":
|
|
84
|
+
return this.client.data.createMany(resource, data);
|
|
85
|
+
case "delete": {
|
|
86
|
+
const ids = data.map((item) => item.id).filter(Boolean);
|
|
87
|
+
await this.client.data.deleteMany(resource, ids);
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
case "update": {
|
|
91
|
+
const results = await Promise.all(
|
|
92
|
+
data.map(
|
|
93
|
+
(item) => this.client.data.update(resource, String(item.id), item)
|
|
94
|
+
)
|
|
95
|
+
);
|
|
96
|
+
return results;
|
|
97
|
+
}
|
|
98
|
+
default:
|
|
99
|
+
throw new Error(`Unsupported bulk operation: ${operation}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Convert ObjectUI QueryParams to ObjectStack QueryOptions.
|
|
104
|
+
* Maps OData-style conventions to ObjectStack conventions.
|
|
105
|
+
*/
|
|
106
|
+
convertQueryParams(params) {
|
|
107
|
+
if (!params) return {};
|
|
108
|
+
const options = {};
|
|
109
|
+
if (params.$select) {
|
|
110
|
+
options.select = params.$select;
|
|
111
|
+
}
|
|
112
|
+
if (params.$filter) {
|
|
113
|
+
options.filters = convertFiltersToAST(params.$filter);
|
|
114
|
+
}
|
|
115
|
+
if (params.$orderby) {
|
|
116
|
+
const sortArray = Object.entries(params.$orderby).map(([field, order]) => {
|
|
117
|
+
return order === "desc" ? `-${field}` : field;
|
|
118
|
+
});
|
|
119
|
+
options.sort = sortArray;
|
|
120
|
+
}
|
|
121
|
+
if (params.$skip !== void 0) {
|
|
122
|
+
options.skip = params.$skip;
|
|
123
|
+
}
|
|
124
|
+
if (params.$top !== void 0) {
|
|
125
|
+
options.top = params.$top;
|
|
126
|
+
}
|
|
127
|
+
return options;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Get object schema/metadata from ObjectStack.
|
|
131
|
+
*
|
|
132
|
+
* @param objectName - Object name
|
|
133
|
+
* @returns Promise resolving to the object schema
|
|
134
|
+
*/
|
|
135
|
+
async getObjectSchema(objectName) {
|
|
136
|
+
await this.connect();
|
|
137
|
+
try {
|
|
138
|
+
const schema = await this.client.meta.getObject(objectName);
|
|
139
|
+
return schema;
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error(`Failed to fetch schema for ${objectName}:`, error);
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Get access to the underlying ObjectStack client for advanced operations.
|
|
147
|
+
*/
|
|
148
|
+
getClient() {
|
|
149
|
+
return this.client;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
function createObjectStackAdapter(config) {
|
|
153
|
+
return new ObjectStackAdapter(config);
|
|
154
|
+
}
|
|
155
|
+
export {
|
|
156
|
+
ObjectStackAdapter,
|
|
157
|
+
createObjectStackAdapter
|
|
158
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@object-ui/data-objectstack",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "ObjectStack Data Adapter for Object UI",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"require": "./dist/index.cjs"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"src",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@objectstack/client": "^0.3.3",
|
|
24
|
+
"@object-ui/types": "0.3.0",
|
|
25
|
+
"@object-ui/core": "0.3.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"tsup": "^8.0.1",
|
|
29
|
+
"typescript": "^5.3.3",
|
|
30
|
+
"vitest": "^1.2.0"
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
37
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
38
|
+
"clean": "rm -rf dist",
|
|
39
|
+
"type-check": "tsc --noEmit",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"lint": "eslint ."
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { ObjectStackClient, type QueryOptions as ObjectStackQueryOptions } from '@objectstack/client';
|
|
10
|
+
import type { DataSource, QueryParams, QueryResult } from '@object-ui/types';
|
|
11
|
+
import { convertFiltersToAST } from '@object-ui/core';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* ObjectStack Data Source Adapter
|
|
15
|
+
*
|
|
16
|
+
* Bridges the ObjectStack Client SDK with the ObjectUI DataSource interface.
|
|
17
|
+
* This allows Object UI applications to seamlessly integrate with ObjectStack
|
|
18
|
+
* backends while maintaining the universal DataSource abstraction.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* import { ObjectStackAdapter } from '@object-ui/data-objectstack';
|
|
23
|
+
*
|
|
24
|
+
* const dataSource = new ObjectStackAdapter({
|
|
25
|
+
* baseUrl: 'https://api.example.com',
|
|
26
|
+
* token: 'your-api-token'
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* const users = await dataSource.find('users', {
|
|
30
|
+
* $filter: { status: 'active' },
|
|
31
|
+
* $top: 10
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export class ObjectStackAdapter<T = any> implements DataSource<T> {
|
|
36
|
+
private client: ObjectStackClient;
|
|
37
|
+
private connected: boolean = false;
|
|
38
|
+
|
|
39
|
+
constructor(config: {
|
|
40
|
+
baseUrl: string;
|
|
41
|
+
token?: string;
|
|
42
|
+
fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
43
|
+
}) {
|
|
44
|
+
this.client = new ObjectStackClient(config);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Ensure the client is connected to the server.
|
|
49
|
+
* Call this before making requests or it will auto-connect on first request.
|
|
50
|
+
*/
|
|
51
|
+
async connect(): Promise<void> {
|
|
52
|
+
if (!this.connected) {
|
|
53
|
+
await this.client.connect();
|
|
54
|
+
this.connected = true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Find multiple records with query parameters.
|
|
60
|
+
* Converts OData-style params to ObjectStack query options.
|
|
61
|
+
*/
|
|
62
|
+
async find(resource: string, params?: QueryParams): Promise<QueryResult<T>> {
|
|
63
|
+
await this.connect();
|
|
64
|
+
|
|
65
|
+
const queryOptions = this.convertQueryParams(params);
|
|
66
|
+
const result = await this.client.data.find<T>(resource, queryOptions);
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
data: result.value,
|
|
70
|
+
total: result.count,
|
|
71
|
+
page: params?.$skip ? Math.floor(params.$skip / (params.$top || 20)) + 1 : 1,
|
|
72
|
+
pageSize: params?.$top,
|
|
73
|
+
hasMore: result.value.length === params?.$top,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Find a single record by ID.
|
|
79
|
+
*/
|
|
80
|
+
async findOne(resource: string, id: string | number, _params?: QueryParams): Promise<T | null> {
|
|
81
|
+
await this.connect();
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const record = await this.client.data.get<T>(resource, String(id));
|
|
85
|
+
return record;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
// If record not found, return null instead of throwing
|
|
88
|
+
if ((error as any)?.status === 404) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Create a new record.
|
|
97
|
+
*/
|
|
98
|
+
async create(resource: string, data: Partial<T>): Promise<T> {
|
|
99
|
+
await this.connect();
|
|
100
|
+
return this.client.data.create<T>(resource, data);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Update an existing record.
|
|
105
|
+
*/
|
|
106
|
+
async update(resource: string, id: string | number, data: Partial<T>): Promise<T> {
|
|
107
|
+
await this.connect();
|
|
108
|
+
return this.client.data.update<T>(resource, String(id), data);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Delete a record.
|
|
113
|
+
*/
|
|
114
|
+
async delete(resource: string, id: string | number): Promise<boolean> {
|
|
115
|
+
await this.connect();
|
|
116
|
+
const result = await this.client.data.delete(resource, String(id));
|
|
117
|
+
return result.success;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Bulk operations (optional implementation).
|
|
122
|
+
*/
|
|
123
|
+
async bulk(resource: string, operation: 'create' | 'update' | 'delete', data: Partial<T>[]): Promise<T[]> {
|
|
124
|
+
await this.connect();
|
|
125
|
+
|
|
126
|
+
switch (operation) {
|
|
127
|
+
case 'create':
|
|
128
|
+
return this.client.data.createMany<T>(resource, data);
|
|
129
|
+
case 'delete': {
|
|
130
|
+
const ids = data.map(item => (item as any).id).filter(Boolean);
|
|
131
|
+
await this.client.data.deleteMany(resource, ids);
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
case 'update': {
|
|
135
|
+
// For update, we need to handle each record individually
|
|
136
|
+
// or use the batch update if all records get the same changes
|
|
137
|
+
const results = await Promise.all(
|
|
138
|
+
data.map(item =>
|
|
139
|
+
this.client.data.update<T>(resource, String((item as any).id), item)
|
|
140
|
+
)
|
|
141
|
+
);
|
|
142
|
+
return results;
|
|
143
|
+
}
|
|
144
|
+
default:
|
|
145
|
+
throw new Error(`Unsupported bulk operation: ${operation}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Convert ObjectUI QueryParams to ObjectStack QueryOptions.
|
|
151
|
+
* Maps OData-style conventions to ObjectStack conventions.
|
|
152
|
+
*/
|
|
153
|
+
private convertQueryParams(params?: QueryParams): ObjectStackQueryOptions {
|
|
154
|
+
if (!params) return {};
|
|
155
|
+
|
|
156
|
+
const options: ObjectStackQueryOptions = {};
|
|
157
|
+
|
|
158
|
+
// Selection
|
|
159
|
+
if (params.$select) {
|
|
160
|
+
options.select = params.$select;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Filtering - convert to ObjectStack FilterNode AST format
|
|
164
|
+
if (params.$filter) {
|
|
165
|
+
options.filters = convertFiltersToAST(params.$filter);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Sorting - convert to ObjectStack format
|
|
169
|
+
if (params.$orderby) {
|
|
170
|
+
const sortArray = Object.entries(params.$orderby).map(([field, order]) => {
|
|
171
|
+
return order === 'desc' ? `-${field}` : field;
|
|
172
|
+
});
|
|
173
|
+
options.sort = sortArray;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Pagination
|
|
177
|
+
if (params.$skip !== undefined) {
|
|
178
|
+
options.skip = params.$skip;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (params.$top !== undefined) {
|
|
182
|
+
options.top = params.$top;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return options;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Get object schema/metadata from ObjectStack.
|
|
190
|
+
*
|
|
191
|
+
* @param objectName - Object name
|
|
192
|
+
* @returns Promise resolving to the object schema
|
|
193
|
+
*/
|
|
194
|
+
async getObjectSchema(objectName: string): Promise<any> {
|
|
195
|
+
await this.connect();
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
const schema = await this.client.meta.getObject(objectName);
|
|
199
|
+
return schema;
|
|
200
|
+
} catch (error) {
|
|
201
|
+
console.error(`Failed to fetch schema for ${objectName}:`, error);
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Get access to the underlying ObjectStack client for advanced operations.
|
|
208
|
+
*/
|
|
209
|
+
getClient(): ObjectStackClient {
|
|
210
|
+
return this.client;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Factory function to create an ObjectStack data source.
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* ```typescript
|
|
219
|
+
* const dataSource = createObjectStackAdapter({
|
|
220
|
+
* baseUrl: process.env.API_URL,
|
|
221
|
+
* token: process.env.API_TOKEN
|
|
222
|
+
* });
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
export function createObjectStackAdapter<T = any>(config: {
|
|
226
|
+
baseUrl: string;
|
|
227
|
+
token?: string;
|
|
228
|
+
fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
229
|
+
}): DataSource<T> {
|
|
230
|
+
return new ObjectStackAdapter<T>(config);
|
|
231
|
+
}
|