@auxilium/datalynk-client 0.1.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/README.md +66 -0
- package/cli/build-models.js +133 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Datalynk Client Library
|
|
2
|
+
|
|
3
|
+
[](https://gitlab.auxiliumgroup.com/ztimson/datalynk-client/pipelines)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Datalynk client library to integrate JavaScript clients with the Datalynk API
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
- [Datalynk Client Library](#datalynk-client-library)
|
|
11
|
+
- [Table of Contents](#Table-of-Contents)
|
|
12
|
+
- [Quick Start](#quick-start)
|
|
13
|
+
- [Examples](#examples)
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
1. Install the client library
|
|
17
|
+
```bash
|
|
18
|
+
npm install --save @auxilium/datalynk-client
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
2. _Optional: Build Datalynk models_
|
|
22
|
+
```bash
|
|
23
|
+
$ npx build-models
|
|
24
|
+
Output (src/models):
|
|
25
|
+
Spoke: sandbox
|
|
26
|
+
Login: example
|
|
27
|
+
Password: ********
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
3. Create an API object & start making requests
|
|
31
|
+
```js
|
|
32
|
+
import {API} from '@auxilium/datalynk-client'
|
|
33
|
+
import {User} from './models/User.model'; // Created automatically in Step 2
|
|
34
|
+
import {Slices} from './models/slices'; // Created automatically in Step 2
|
|
35
|
+
|
|
36
|
+
const api = new API('https://spoke.auxiliumgroup.com');
|
|
37
|
+
const resp = await api.slice<User>(Slices.User).select().exec();
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Examples
|
|
41
|
+
```js
|
|
42
|
+
// Login
|
|
43
|
+
const user = await api.request({'$/auth/login': {login: 'user', password: '123'}});
|
|
44
|
+
|
|
45
|
+
// Query
|
|
46
|
+
const row = await api.slice(12345).select(12345).exec().row();
|
|
47
|
+
|
|
48
|
+
// Insert One
|
|
49
|
+
const rows = await api.slice(12345).select().exec().rows();
|
|
50
|
+
|
|
51
|
+
// Insert Many
|
|
52
|
+
const keys = await api.slice(12345).insert([
|
|
53
|
+
{first: 'Adam', last: 'Bilbo'},
|
|
54
|
+
{first: 'Charlie', last: 'DeMont'}
|
|
55
|
+
]).exec().keys();
|
|
56
|
+
|
|
57
|
+
// Update
|
|
58
|
+
const success = await api.slice(12345).update(
|
|
59
|
+
{id: 1, first: 'Adam', last: 'Baxter'}
|
|
60
|
+
).exec().key();
|
|
61
|
+
|
|
62
|
+
// Advanced example - Add flag to underage users
|
|
63
|
+
const users = await api.slice(12345).select().exec().rows();
|
|
64
|
+
users = users.map(user => Object.assign(user, {underage: user.age < 18}));
|
|
65
|
+
api.slice(1245).update(users).exec();
|
|
66
|
+
```
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import read from 'read';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import {join} from 'path';
|
|
6
|
+
import {Api} from '../dist/index.mjs';
|
|
7
|
+
import packageFile from '../package.json' assert {type: 'json'};
|
|
8
|
+
|
|
9
|
+
const FieldType = {
|
|
10
|
+
'control_checkbox': 'checkbox',
|
|
11
|
+
'control_dropdown': 'select',
|
|
12
|
+
'control_new_date': 'Date',
|
|
13
|
+
'control_new_datetime': 'Date',
|
|
14
|
+
'control_new_time': 'Date',
|
|
15
|
+
'control_number': 'number',
|
|
16
|
+
'control_radio': 'radio',
|
|
17
|
+
'control_textbox': 'string',
|
|
18
|
+
'control_textarea': 'string',
|
|
19
|
+
'Date': 'Date',
|
|
20
|
+
'number': 'number',
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Fields that everything have
|
|
24
|
+
const universalFields = [
|
|
25
|
+
{id: 'id', readonly: true, type: 'number'},
|
|
26
|
+
{id: 'created', readonly: true, type: 'Date'},
|
|
27
|
+
{id: 'creatorRef', readonly: true, type: 'number'},
|
|
28
|
+
{id: 'modified', readonly: true, type: 'Date'},
|
|
29
|
+
{id: 'modifierRef', readonly: true, type: 'number'}
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
// Promise wrapper for reading console input
|
|
33
|
+
function ask(prompt, options = {}) {
|
|
34
|
+
options = Object.assign(options, {prompt: prompt});
|
|
35
|
+
return new Promise(async (res, rej) => {
|
|
36
|
+
read(options, (err, answer) => {
|
|
37
|
+
if(err) rej(err);
|
|
38
|
+
res(answer);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Shorthand for getting spoke credentials
|
|
44
|
+
async function getCredentials() {
|
|
45
|
+
let login = await ask('Login: ');
|
|
46
|
+
let password = await ask('Password: ', {silent: true, replace: '*'});
|
|
47
|
+
return [login, password];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Check type, if unknown mark it as any, if its a selector change the type to its equivalent enum
|
|
51
|
+
function checkType(field) {
|
|
52
|
+
if(field.type == 'checkbox' || field.type == 'radio' || field.type == 'select') {
|
|
53
|
+
return `${field.id.replace(/\W/g, '')}Options${field.type == 'checkbox' ? '[]' : ''}`;
|
|
54
|
+
}
|
|
55
|
+
return !!field.type ? field.type : 'any';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Main loop
|
|
59
|
+
(async () => {
|
|
60
|
+
try {
|
|
61
|
+
// Arguments
|
|
62
|
+
let [,, spoke, login, password, output] = process.argv;
|
|
63
|
+
if(!output) output = await ask('Output (src/models): ') || 'src/models';
|
|
64
|
+
if(!spoke) spoke = await ask('Spoke: ');
|
|
65
|
+
if(!login || !password) [login, password] = await getCredentials();
|
|
66
|
+
|
|
67
|
+
// Log in
|
|
68
|
+
let api = new Api(`https://${spoke}.auxiliumgroup.com`, spoke, {saveSession: false});
|
|
69
|
+
await api.auth.login(login, password);
|
|
70
|
+
console.log('Login Succeeded!');
|
|
71
|
+
|
|
72
|
+
// Get list of slices
|
|
73
|
+
let slices = await api.request({"$/tools/action_chain": [
|
|
74
|
+
{"!/env/me":{}},
|
|
75
|
+
{"!/slice/permissions": {}},
|
|
76
|
+
{"!/tools/column": {"col": "id", "rows": {"$_": "1:rows"}}}
|
|
77
|
+
]});
|
|
78
|
+
|
|
79
|
+
// Get the slice metadata for each slice
|
|
80
|
+
let metadata = await Promise.all(slices.map(id => api.request({'$/slice/fetch': {slice: id}})));
|
|
81
|
+
|
|
82
|
+
// Organize all the data
|
|
83
|
+
slices = metadata.filter(slice => slice.meta.presentation && slice.meta.presentation.fields)
|
|
84
|
+
.map(slice => ({name: slice.name.replace(/^\d+/gm, '').replace(/\W/g, ''), id: slice.id}))
|
|
85
|
+
.map(slice => Object.assign(slice, {name: slice.name.replace(/[\W|_]/g, '')}));
|
|
86
|
+
let sliceFields = metadata.filter(slice => slice.meta.presentation && slice.meta.presentation.fields)
|
|
87
|
+
.map(slice => slice.meta.presentation.fields);
|
|
88
|
+
sliceFields = sliceFields.map(fields => Object.keys(fields).filter(key => fields[key].id).map(key => {
|
|
89
|
+
let field = {id: fields[key].id, readonly: !!fields[key].readonly, type: FieldType[fields[key].type], required: fields[key].required == 'Yes'};
|
|
90
|
+
if(fields[key].options) field.options = fields[key].options.split('|');
|
|
91
|
+
return field;
|
|
92
|
+
}));
|
|
93
|
+
slices = slices.map((slice, i) => Object.assign(slice, {fields: universalFields.concat(sliceFields[i])}))
|
|
94
|
+
|
|
95
|
+
// Create models directory
|
|
96
|
+
let path = join(process.cwd(), output);
|
|
97
|
+
if(!fs.existsSync(path)) fs.mkdirSync(path);
|
|
98
|
+
let writingPromises = [];
|
|
99
|
+
|
|
100
|
+
// Create slice enum
|
|
101
|
+
let body = `/* Generated by the DatalynkAPI v${packageFile.version} - ${new Date().toLocaleString()} */\n\n`;
|
|
102
|
+
let props = [];
|
|
103
|
+
body += `export enum Slices {\n\t${slices.filter(slice => {
|
|
104
|
+
if(props.indexOf(slice.name) == -1) {
|
|
105
|
+
props.push(slice.name);
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
}).map(slice => `${slice.name} = ${slice.id},`).join('\n\t')}\n}`;
|
|
110
|
+
writingPromises.push(new Promise(res => fs.writeFile(`${path}/slices.ts`, body, () => res())));
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
// Create individual slice enums and interfaces
|
|
114
|
+
slices.forEach(slice => {
|
|
115
|
+
let body = `/* Generated by the DatalynkAPI v${packageFile.version} - ${new Date().toLocaleString()} */\n\n`;
|
|
116
|
+
body += slice.fields.filter(field => field.options).map(field => {
|
|
117
|
+
return `export enum ${field.id}Options {\n\t${field.options.map(option => `${option.replace(/^\d+/gm, '').replace(/\W/g, '')} = '${option}',`).join('\n\t')}\n}`
|
|
118
|
+
}).join('\n\n');
|
|
119
|
+
|
|
120
|
+
if(body) body += '\n\n';
|
|
121
|
+
|
|
122
|
+
body += `export interface ${slice.name} {\n\t${slice.fields.map(field => `${field.readonly ? 'readonly ' : ''}'${field.id}'${(!field.required || FieldType[field.type] == FieldType.control_checkbox) ? '?' : ''}: ${checkType(field)};`).join('\n\t')}\n}`;
|
|
123
|
+
|
|
124
|
+
writingPromises.push(new Promise(res => fs.writeFile(`${path}/${slice.name}.model.ts`, body, () => res())));
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
await Promise.all(writingPromises);
|
|
128
|
+
process.exit();
|
|
129
|
+
} catch(err) {
|
|
130
|
+
console.error(`\n${err.stack || err.stackTrace || err.message || err.toString()}`);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
})();
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@auxilium/datalynk-client",
|
|
3
|
+
"description": "Datalynk client library",
|
|
4
|
+
"repository": "https://gitlab.auxiliumgroup.com/ztimson/datalynk-client",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"author": "Zak Timson",
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.mjs",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"type": "module",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/index.mjs",
|
|
14
|
+
"require": "./dist/index.cjs",
|
|
15
|
+
"types": "./dist/index.d.ts"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"bin": {
|
|
19
|
+
"datalynk-models": "./cli/build-models.js"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc && npx vite build",
|
|
23
|
+
"build-models": "node --no-warnings ./cli/build-models.js",
|
|
24
|
+
"watch": "vite build --watch"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^20.14.10",
|
|
28
|
+
"@types/read": "^0.0.32",
|
|
29
|
+
"read": "^1.0.7",
|
|
30
|
+
"ts-node": "^10.9.2",
|
|
31
|
+
"typescript": "^5.5.3",
|
|
32
|
+
"vite": "^5.0.12",
|
|
33
|
+
"vite-plugin-dts": "^3.7.2"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"cli",
|
|
37
|
+
"dist"
|
|
38
|
+
]
|
|
39
|
+
}
|