@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 ADDED
@@ -0,0 +1,66 @@
1
+ # Datalynk Client Library
2
+
3
+ [![pipeline status](https://gitlab.auxiliumgroup.com/ztimson/datalynk-client/badges/master/pipeline.svg)](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
+ }