adminforth 1.3.54-next.9 → 1.3.55-next.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.
Files changed (250) hide show
  1. package/dist/auth.d.ts +31 -0
  2. package/dist/auth.d.ts.map +1 -0
  3. package/dist/auth.js +42 -56
  4. package/dist/auth.js.map +1 -0
  5. package/dist/basePlugin.d.ts +18 -0
  6. package/dist/basePlugin.d.ts.map +1 -0
  7. package/dist/basePlugin.js +1 -0
  8. package/dist/basePlugin.js.map +1 -0
  9. package/dist/dataConnectors/baseConnector.d.ts +94 -0
  10. package/dist/dataConnectors/baseConnector.d.ts.map +1 -0
  11. package/dist/dataConnectors/baseConnector.js +108 -122
  12. package/dist/dataConnectors/baseConnector.js.map +1 -0
  13. package/dist/dataConnectors/clickhouse.d.ts +92 -0
  14. package/dist/dataConnectors/clickhouse.d.ts.map +1 -0
  15. package/dist/dataConnectors/clickhouse.js +132 -149
  16. package/dist/dataConnectors/clickhouse.js.map +1 -0
  17. package/dist/dataConnectors/mongo.d.ts +93 -0
  18. package/dist/dataConnectors/mongo.d.ts.map +1 -0
  19. package/dist/dataConnectors/mongo.js +75 -101
  20. package/dist/dataConnectors/mongo.js.map +1 -0
  21. package/dist/dataConnectors/postgres.d.ts +71 -0
  22. package/dist/dataConnectors/postgres.d.ts.map +1 -0
  23. package/dist/dataConnectors/postgres.js +124 -143
  24. package/dist/dataConnectors/postgres.js.map +1 -0
  25. package/dist/dataConnectors/sqlite.d.ts +67 -0
  26. package/dist/dataConnectors/sqlite.d.ts.map +1 -0
  27. package/dist/dataConnectors/sqlite.js +113 -130
  28. package/dist/dataConnectors/sqlite.js.map +1 -0
  29. package/dist/index.d.ts +92 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +197 -217
  32. package/dist/index.js.map +1 -0
  33. package/dist/modules/codeInjector.d.ts +35 -0
  34. package/dist/modules/codeInjector.d.ts.map +1 -0
  35. package/dist/modules/codeInjector.js +480 -486
  36. package/dist/modules/codeInjector.js.map +1 -0
  37. package/dist/modules/configValidator.d.ts +12 -0
  38. package/dist/modules/configValidator.d.ts.map +1 -0
  39. package/dist/modules/configValidator.js +31 -22
  40. package/dist/modules/configValidator.js.map +1 -0
  41. package/dist/modules/operationalResource.d.ts +17 -0
  42. package/dist/modules/operationalResource.d.ts.map +1 -0
  43. package/dist/modules/operationalResource.js +50 -70
  44. package/dist/modules/operationalResource.js.map +1 -0
  45. package/dist/modules/restApi.d.ts +10 -0
  46. package/dist/modules/restApi.d.ts.map +1 -0
  47. package/dist/modules/restApi.js +104 -116
  48. package/dist/modules/restApi.js.map +1 -0
  49. package/dist/modules/styleGenerator.d.ts +9 -0
  50. package/dist/modules/styleGenerator.d.ts.map +1 -0
  51. package/dist/modules/styleGenerator.js +1 -0
  52. package/dist/modules/styleGenerator.js.map +1 -0
  53. package/dist/modules/styles.d.ts +96 -0
  54. package/dist/modules/styles.d.ts.map +1 -0
  55. package/dist/modules/styles.js +1 -0
  56. package/dist/modules/styles.js.map +1 -0
  57. package/dist/modules/utils.d.ts +39 -0
  58. package/dist/modules/utils.d.ts.map +1 -0
  59. package/dist/modules/utils.js +1 -0
  60. package/dist/modules/utils.js.map +1 -0
  61. package/dist/plugins/audit-log/types.d.ts +35 -0
  62. package/dist/plugins/audit-log/types.d.ts.map +1 -0
  63. package/dist/plugins/audit-log/types.js +2 -0
  64. package/dist/plugins/audit-log/types.js.map +1 -0
  65. package/dist/plugins/chat-gpt/types.d.ts +82 -0
  66. package/dist/plugins/chat-gpt/types.d.ts.map +1 -0
  67. package/dist/plugins/chat-gpt/types.js +2 -0
  68. package/dist/plugins/chat-gpt/types.js.map +1 -0
  69. package/dist/plugins/email-password-reset/types.d.ts +28 -0
  70. package/dist/plugins/email-password-reset/types.d.ts.map +1 -0
  71. package/dist/plugins/email-password-reset/types.js +2 -0
  72. package/dist/plugins/email-password-reset/types.js.map +1 -0
  73. package/dist/plugins/foreign-inline-list/types.d.ts +19 -0
  74. package/dist/plugins/foreign-inline-list/types.d.ts.map +1 -0
  75. package/dist/plugins/foreign-inline-list/types.js +2 -0
  76. package/dist/plugins/foreign-inline-list/types.js.map +1 -0
  77. package/dist/plugins/import-export/types.d.ts +3 -0
  78. package/dist/plugins/import-export/types.d.ts.map +1 -0
  79. package/dist/plugins/import-export/types.js +2 -0
  80. package/dist/plugins/import-export/types.js.map +1 -0
  81. package/dist/plugins/rich-editor/custom/async-queue.d.ts +8 -0
  82. package/dist/plugins/rich-editor/custom/async-queue.d.ts.map +1 -0
  83. package/dist/plugins/rich-editor/custom/async-queue.js +29 -0
  84. package/dist/plugins/rich-editor/custom/async-queue.js.map +1 -0
  85. package/dist/plugins/rich-editor/dist/custom/async-queue.d.ts +8 -0
  86. package/dist/plugins/rich-editor/dist/custom/async-queue.d.ts.map +1 -0
  87. package/dist/plugins/rich-editor/dist/custom/async-queue.js +29 -0
  88. package/dist/plugins/rich-editor/dist/custom/async-queue.js.map +1 -0
  89. package/dist/plugins/rich-editor/types.d.ts +153 -0
  90. package/dist/plugins/rich-editor/types.d.ts.map +1 -0
  91. package/dist/plugins/rich-editor/types.js +16 -0
  92. package/dist/plugins/rich-editor/types.js.map +1 -0
  93. package/dist/plugins/two-factors-auth/types.d.ts +18 -0
  94. package/dist/plugins/two-factors-auth/types.d.ts.map +1 -0
  95. package/dist/plugins/two-factors-auth/types.js +2 -0
  96. package/dist/plugins/two-factors-auth/types.js.map +1 -0
  97. package/dist/plugins/upload/types.d.ts +132 -0
  98. package/dist/plugins/upload/types.d.ts.map +1 -0
  99. package/dist/plugins/upload/types.js +2 -0
  100. package/dist/plugins/upload/types.js.map +1 -0
  101. package/dist/servers/express.d.ts +18 -0
  102. package/dist/servers/express.d.ts.map +1 -0
  103. package/dist/servers/express.js +30 -42
  104. package/dist/servers/express.js.map +1 -0
  105. package/dist/types/AdminForthConfig.d.ts +1619 -0
  106. package/dist/types/AdminForthConfig.d.ts.map +1 -0
  107. package/dist/types/AdminForthConfig.js +1 -0
  108. package/dist/types/AdminForthConfig.js.map +1 -0
  109. package/{types/FrontendAPI.ts → dist/types/FrontendAPI.d.ts} +27 -52
  110. package/dist/types/FrontendAPI.d.ts.map +1 -0
  111. package/dist/types/FrontendAPI.js +1 -0
  112. package/dist/types/FrontendAPI.js.map +1 -0
  113. package/package.json +15 -5
  114. package/auth.ts +0 -140
  115. package/basePlugin.ts +0 -70
  116. package/dataConnectors/baseConnector.ts +0 -216
  117. package/dataConnectors/clickhouse.ts +0 -338
  118. package/dataConnectors/mongo.ts +0 -202
  119. package/dataConnectors/postgres.ts +0 -306
  120. package/dataConnectors/sqlite.ts +0 -254
  121. package/dist/spa/.eslintrc.cjs +0 -14
  122. package/dist/spa/README.md +0 -39
  123. package/dist/spa/env.d.ts +0 -1
  124. package/dist/spa/index.html +0 -23
  125. package/dist/spa/package-lock.json +0 -4573
  126. package/dist/spa/package.json +0 -49
  127. package/dist/spa/postcss.config.js +0 -6
  128. package/dist/spa/public/assets/favicon.png +0 -0
  129. package/dist/spa/src/App.vue +0 -314
  130. package/dist/spa/src/assets/base.css +0 -2
  131. package/dist/spa/src/assets/logo.svg +0 -19
  132. package/dist/spa/src/components/AcceptModal.vue +0 -45
  133. package/dist/spa/src/components/Breadcrumbs.vue +0 -41
  134. package/dist/spa/src/components/BreadcrumbsWithButtons.vue +0 -26
  135. package/dist/spa/src/components/CustomDatePicker.vue +0 -176
  136. package/dist/spa/src/components/CustomDateRangePicker.vue +0 -218
  137. package/dist/spa/src/components/CustomRangePicker.vue +0 -152
  138. package/dist/spa/src/components/Dropdown.vue +0 -168
  139. package/dist/spa/src/components/Filters.vue +0 -222
  140. package/dist/spa/src/components/HelloWorld.vue +0 -17
  141. package/dist/spa/src/components/MenuLink.vue +0 -24
  142. package/dist/spa/src/components/ResourceForm.vue +0 -294
  143. package/dist/spa/src/components/ResourceListTable.vue +0 -394
  144. package/dist/spa/src/components/SingleSkeletLoader.vue +0 -13
  145. package/dist/spa/src/components/SkeleteLoader.vue +0 -23
  146. package/dist/spa/src/components/Toast.vue +0 -77
  147. package/dist/spa/src/components/ValueRenderer.vue +0 -66
  148. package/dist/spa/src/components/icons/IconCalendar.vue +0 -5
  149. package/dist/spa/src/components/icons/IconCommunity.vue +0 -7
  150. package/dist/spa/src/components/icons/IconDocumentation.vue +0 -7
  151. package/dist/spa/src/components/icons/IconEcosystem.vue +0 -7
  152. package/dist/spa/src/components/icons/IconSupport.vue +0 -7
  153. package/dist/spa/src/components/icons/IconTime.vue +0 -5
  154. package/dist/spa/src/components/icons/IconTooling.vue +0 -19
  155. package/dist/spa/src/composables/useFrontendApi.ts +0 -26
  156. package/dist/spa/src/composables/useStores.ts +0 -127
  157. package/dist/spa/src/index.scss +0 -27
  158. package/dist/spa/src/main.ts +0 -18
  159. package/dist/spa/src/router/index.ts +0 -63
  160. package/dist/spa/src/spa_types/core.ts +0 -51
  161. package/dist/spa/src/stores/core.ts +0 -144
  162. package/dist/spa/src/stores/filters.ts +0 -22
  163. package/dist/spa/src/stores/modal.ts +0 -48
  164. package/dist/spa/src/stores/toast.ts +0 -15
  165. package/dist/spa/src/stores/user.ts +0 -72
  166. package/dist/spa/src/types/AdminForthConfig.ts +0 -1477
  167. package/dist/spa/src/types/FrontendAPI.ts +0 -121
  168. package/dist/spa/src/utils.ts +0 -103
  169. package/dist/spa/src/views/CreateView.vue +0 -156
  170. package/dist/spa/src/views/EditView.vue +0 -157
  171. package/dist/spa/src/views/ListView.vue +0 -258
  172. package/dist/spa/src/views/LoginView.vue +0 -160
  173. package/dist/spa/src/views/ResourceParent.vue +0 -17
  174. package/dist/spa/src/views/ShowView.vue +0 -184
  175. package/dist/spa/tailwind.config.js +0 -17
  176. package/dist/spa/tsconfig.app.json +0 -14
  177. package/dist/spa/tsconfig.json +0 -11
  178. package/dist/spa/tsconfig.node.json +0 -19
  179. package/dist/spa/vite.config.ts +0 -56
  180. package/index.ts +0 -428
  181. package/modules/codeInjector.ts +0 -736
  182. package/modules/configValidator.ts +0 -571
  183. package/modules/operationalResource.ts +0 -98
  184. package/modules/restApi.ts +0 -718
  185. package/modules/styleGenerator.ts +0 -55
  186. package/modules/styles.ts +0 -126
  187. package/modules/utils.ts +0 -472
  188. package/servers/express.ts +0 -259
  189. package/spa/.eslintrc.cjs +0 -14
  190. package/spa/README.md +0 -39
  191. package/spa/env.d.ts +0 -1
  192. package/spa/index.html +0 -23
  193. package/spa/package-lock.json +0 -4602
  194. package/spa/package.json +0 -51
  195. package/spa/postcss.config.js +0 -6
  196. package/spa/public/assets/favicon.png +0 -0
  197. package/spa/src/App.vue +0 -418
  198. package/spa/src/assets/base.css +0 -2
  199. package/spa/src/assets/logo.svg +0 -19
  200. package/spa/src/components/AcceptModal.vue +0 -45
  201. package/spa/src/components/Breadcrumbs.vue +0 -41
  202. package/spa/src/components/BreadcrumbsWithButtons.vue +0 -26
  203. package/spa/src/components/CustomDatePicker.vue +0 -176
  204. package/spa/src/components/CustomDateRangePicker.vue +0 -218
  205. package/spa/src/components/CustomRangePicker.vue +0 -156
  206. package/spa/src/components/Dropdown.vue +0 -168
  207. package/spa/src/components/Filters.vue +0 -222
  208. package/spa/src/components/HelloWorld.vue +0 -17
  209. package/spa/src/components/MenuLink.vue +0 -27
  210. package/spa/src/components/ResourceForm.vue +0 -290
  211. package/spa/src/components/ResourceListTable.vue +0 -466
  212. package/spa/src/components/SingleSkeletLoader.vue +0 -13
  213. package/spa/src/components/SkeleteLoader.vue +0 -23
  214. package/spa/src/components/ThreeDotsMenu.vue +0 -43
  215. package/spa/src/components/Toast.vue +0 -78
  216. package/spa/src/components/ValueRenderer.vue +0 -114
  217. package/spa/src/components/icons/IconCalendar.vue +0 -5
  218. package/spa/src/components/icons/IconCommunity.vue +0 -7
  219. package/spa/src/components/icons/IconDocumentation.vue +0 -7
  220. package/spa/src/components/icons/IconEcosystem.vue +0 -7
  221. package/spa/src/components/icons/IconSupport.vue +0 -7
  222. package/spa/src/components/icons/IconTime.vue +0 -5
  223. package/spa/src/components/icons/IconTooling.vue +0 -19
  224. package/spa/src/composables/useFrontendApi.ts +0 -26
  225. package/spa/src/composables/useStores.ts +0 -131
  226. package/spa/src/index.scss +0 -31
  227. package/spa/src/main.ts +0 -18
  228. package/spa/src/renderers/CompactUUID.vue +0 -48
  229. package/spa/src/renderers/CountryFlag.vue +0 -69
  230. package/spa/src/router/index.ts +0 -59
  231. package/spa/src/spa_types/core.ts +0 -53
  232. package/spa/src/stores/core.ts +0 -148
  233. package/spa/src/stores/filters.ts +0 -27
  234. package/spa/src/stores/modal.ts +0 -48
  235. package/spa/src/stores/toast.ts +0 -31
  236. package/spa/src/stores/user.ts +0 -72
  237. package/spa/src/utils.ts +0 -160
  238. package/spa/src/views/CreateView.vue +0 -167
  239. package/spa/src/views/EditView.vue +0 -170
  240. package/spa/src/views/ListView.vue +0 -352
  241. package/spa/src/views/LoginView.vue +0 -192
  242. package/spa/src/views/ResourceParent.vue +0 -17
  243. package/spa/src/views/ShowView.vue +0 -186
  244. package/spa/tailwind.config.js +0 -17
  245. package/spa/tsconfig.app.json +0 -14
  246. package/spa/tsconfig.json +0 -11
  247. package/spa/tsconfig.node.json +0 -19
  248. package/spa/vite.config.ts +0 -56
  249. package/tsconfig.json +0 -112
  250. package/types/AdminForthConfig.ts +0 -1762
@@ -1,216 +0,0 @@
1
- import { get } from "http";
2
- import { AdminForthResource, IAdminForthDataSourceConnectorBase, AdminForthSortDirections, AdminForthFilterOperators, AdminForthResourceColumn, IAdminForthSort, IAdminForthFilter } from "../types/AdminForthConfig.js";
3
- import { suggestIfTypo } from "../modules/utils.js";
4
-
5
-
6
- export default class AdminForthBaseConnector implements IAdminForthDataSourceConnectorBase {
7
- getPrimaryKey(resource: AdminForthResource): string {
8
- for (const col of resource.dataSourceColumns) {
9
- if (col.primaryKey) {
10
- return col.name;
11
- }
12
- }
13
- }
14
-
15
- async getRecordByPrimaryKeyWithOriginalTypes(resource: AdminForthResource, id: string): Promise<any> {
16
- const data = await this.getDataWithOriginalTypes({
17
- resource,
18
- limit: 1,
19
- offset: 0,
20
- sort: [],
21
- filters: [{ field: this.getPrimaryKey(resource), operator: AdminForthFilterOperators.EQ, value: id }],
22
- });
23
- return data.length > 0 ? data[0] : null;
24
- }
25
-
26
- getDataWithOriginalTypes({ resource, limit, offset, sort, filters }: {
27
- resource: AdminForthResource,
28
- limit: number,
29
- offset: number,
30
- sort: IAdminForthSort[],
31
- filters: IAdminForthFilter[],
32
- }): Promise<any[]> {
33
- throw new Error('Method not implemented.');
34
- }
35
-
36
- getCount({ resource, filters }: { resource: AdminForthResource; filters: { field: string; operator: AdminForthFilterOperators; value: any; }[]; }): Promise<number> {
37
- throw new Error('Method not implemented.');
38
- }
39
-
40
- discoverFields(resource: AdminForthResource): Promise<{ [key: string]: AdminForthResourceColumn; }> {
41
- throw new Error('Method not implemented.');
42
- }
43
-
44
- getFieldValue(field: AdminForthResourceColumn, value: any) {
45
- throw new Error('Method not implemented.');
46
- }
47
-
48
- setFieldValue(field: AdminForthResourceColumn, value: any) {
49
- throw new Error('Method not implemented.');
50
- }
51
-
52
- getMinMaxForColumnsWithOriginalTypes({ resource, columns }: { resource: AdminForthResource; columns: AdminForthResourceColumn[]; }): Promise<{ [key: string]: { min: any; max: any; }; }> {
53
- throw new Error('Method not implemented.');
54
- }
55
-
56
- createRecordOriginalValues({ resource, record }: { resource: AdminForthResource; record: any; }): Promise<void> {
57
- throw new Error('Method not implemented.');
58
- }
59
-
60
- async checkUnique(resource: AdminForthResource, column: AdminForthResourceColumn, value: any) {
61
- process.env.HEAVY_DEBUG && console.log('☝️🪲🪲🪲🪲 checkUnique|||', column, value);
62
- const existingRecord = await this.getData({
63
- resource,
64
- filters: [{ field: column.name, operator: AdminForthFilterOperators.EQ, value }],
65
- limit: 1,
66
- sort: [],
67
- offset: 0,
68
- getTotals: false
69
- });
70
-
71
- return existingRecord.data.length > 0;
72
- }
73
-
74
- async createRecord({ resource, record, adminUser }: {
75
- resource: AdminForthResource; record: any; adminUser: any;
76
- }): Promise<{ error?: string; ok: boolean; createdRecord?: any; }> {
77
- // transform value using setFieldValue and call createRecordOriginalValues
78
- const filledRecord = {...record};
79
- const recordWithOriginalValues = {...record};
80
-
81
- for (const col of resource.dataSourceColumns) {
82
- if (col.fillOnCreate) {
83
- if (filledRecord[col.name] === undefined) {
84
- filledRecord[col.name] = col.fillOnCreate({
85
- initialRecord: record,
86
- adminUser
87
- });
88
- }
89
- }
90
- recordWithOriginalValues[col.name] = this.setFieldValue(col, filledRecord[col.name]);
91
- }
92
-
93
- let error: string | null = null;
94
- await Promise.all(
95
- resource.dataSourceColumns.map(async (col) => {
96
- if (col.isUnique && !col.virtual && !error) {
97
- const exists = await this.checkUnique(resource, col, recordWithOriginalValues[col.name]);
98
- if (exists) {
99
- error = `Record with ${col.name} ${recordWithOriginalValues[col.name]} already exists`;
100
- }
101
- }
102
- })
103
- );
104
- if (error) {
105
- process.env.HEAVY_DEBUG && console.log('🪲🆕 check unique error', error);
106
- return { error, ok: false };
107
- }
108
-
109
- process.env.HEAVY_DEBUG && console.log('🪲🆕 creating record', recordWithOriginalValues);
110
- await this.createRecordOriginalValues({ resource, record: recordWithOriginalValues });
111
-
112
- return {
113
- ok: true,
114
- createdRecord: recordWithOriginalValues,
115
- }
116
- }
117
-
118
- updateRecordOriginalValues({ resource, recordId, newValues }: { resource: AdminForthResource; recordId: string; newValues: any; }): Promise<void> {
119
- throw new Error('Method not implemented.');
120
- }
121
-
122
- async updateRecord({ resource, recordId, newValues }: { resource: AdminForthResource; recordId: string; newValues: any; }): Promise<{ error?: string; ok: boolean; }> {
123
- // transform value using setFieldValue and call updateRecordOriginalValues
124
- const recordWithOriginalValues = {...newValues};
125
-
126
- for (const field of Object.keys(newValues)) {
127
- const col = resource.dataSourceColumns.find((col) => col.name == field);
128
- // todo instead of throwing error, we can just not use setFieldValue here, and pass original value to updateRecordOriginalValues
129
- // we might consider this because some users might not want to define all columns in resource with showIn:[], but still want to use them in hooks
130
- if (!col) {
131
- const similar = suggestIfTypo(resource.dataSourceColumns.map((col) => col.name), field);
132
- throw new Error(`
133
- Update record received field '${field}' (with value ${newValues[field]}), but such column not found in resource '${resource.resourceId}'. ${similar ? `Did you mean '${similar}'?` : ''}
134
- `);
135
- }
136
- recordWithOriginalValues[col.name] = this.setFieldValue(col, newValues[col.name]);
137
- }
138
-
139
- process.env.HEAVY_DEBUG && console.log(`🪲✏️ updating record id:${recordId}, values: ${JSON.stringify(recordWithOriginalValues)}`);
140
-
141
- await this.updateRecordOriginalValues({ resource, recordId, newValues: recordWithOriginalValues });
142
-
143
- return { ok: true };
144
- }
145
-
146
- deleteRecord({ resource, recordId }: { resource: AdminForthResource; recordId: string; }): Promise<boolean> {
147
- throw new Error('Method not implemented.');
148
- }
149
-
150
-
151
- async getData({ resource, limit, offset, sort, filters, getTotals }: {
152
- resource: AdminForthResource,
153
- limit: number,
154
- offset: number,
155
- sort: { field: string, direction: AdminForthSortDirections }[],
156
- filters: { field: string, operator: AdminForthFilterOperators, value: any }[],
157
- getTotals: boolean,
158
- }): Promise<{ data: any[], total: number }> {
159
- if (filters) {
160
- filters.map((f) => {
161
- const fieldObj = resource.dataSourceColumns.find((col) => col.name == f.field);
162
- if (!fieldObj) {
163
- const similar = suggestIfTypo(resource.dataSourceColumns.map((col) => col.name), f.field);
164
- throw new Error(`Field '${f.field}' not found in resource '${resource.resourceId}'. ${similar ? `Did you mean '${similar}'?` : ''}`);
165
- }
166
- if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
167
- f.value = f.value.map((val) => this.setFieldValue(fieldObj, val));
168
- } else {
169
- f.value = this.setFieldValue(fieldObj, f.value);
170
- }
171
- });
172
- }
173
-
174
- const promises: Promise<any>[] = [this.getDataWithOriginalTypes({ resource, limit, offset, sort, filters })];
175
- if (getTotals) {
176
- promises.push(this.getCount({ resource, filters }));
177
- } else {
178
- promises.push(Promise.resolve(undefined));
179
- }
180
-
181
- const [data, total] = await Promise.all(promises);
182
-
183
- // call getFieldValue for each field
184
- data.map((record) => {
185
- for (const col of resource.dataSourceColumns) {
186
- record[col.name] = this.getFieldValue(col, record[col.name]);
187
- }
188
- });
189
-
190
- return { data, total };
191
- }
192
-
193
- async getMinMaxForColumns({ resource, columns }: { resource: AdminForthResource; columns: AdminForthResourceColumn[]; }): Promise<{ [key: string]: { min: any; max: any; }; }> {
194
- const mm = await this.getMinMaxForColumnsWithOriginalTypes({ resource, columns });
195
- const result = {};
196
- for (const col of columns) {
197
- result[col.name] = {
198
- min: this.getFieldValue(col, mm[col.name].min),
199
- max: this.getFieldValue(col, mm[col.name].max),
200
- };
201
- }
202
- return result;
203
- }
204
-
205
- getRecordByPrimaryKey(resource: AdminForthResource, recordId: string): Promise<any> {
206
- return this.getRecordByPrimaryKeyWithOriginalTypes(resource, recordId).then((record) => {
207
- const newRecord = {};
208
- for (const col of resource.dataSourceColumns) {
209
- newRecord[col.name] = this.getFieldValue(col, record[col.name]);
210
- }
211
- return newRecord;
212
- });
213
- }
214
-
215
-
216
- }
@@ -1,338 +0,0 @@
1
- import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections, IAdminForthDataSourceConnector, AdminForthResource, AdminForthResourceColumn } from '../types/AdminForthConfig.js';
2
- import AdminForthBaseConnector from './baseConnector.js';
3
- import dayjs from 'dayjs';
4
- import { createClient } from '@clickhouse/client'
5
-
6
-
7
-
8
- class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForthDataSourceConnector {
9
-
10
- client: any;
11
- dbName: string;
12
- url: string;
13
-
14
- /**
15
- * url: http[s]://[username:password@]hostname:port[/database][?param1=value1&param2=value2]
16
- * @param param0
17
- */
18
- constructor({ url }: { url: string }) {
19
- super();
20
- this.dbName = new URL(url).pathname.replace('/', '');
21
- this.url = url;;
22
- // create connection here
23
- this.client = createClient({
24
- url: url.replace('clickhouse://', 'http://'),
25
- clickhouse_settings: {
26
- // Allows to insert serialized JS Dates (such as '2023-12-06T10:54:48.000Z')
27
- date_time_input_format: 'best_effort',
28
-
29
- // Recommended for cluster usage to avoid situations where a query processing error occurred after the response code,
30
- // and HTTP headers were already sent to the client.
31
- // See https://clickhouse.com/docs/en/interfaces/http/#response-buffering
32
- wait_end_of_query: 1,
33
- },
34
- // log:{
35
- // level: ClickHouseLogLevel.TRACE,
36
- // }
37
- });
38
-
39
- }
40
-
41
- async discoverFields(resource: AdminForthResource): Promise<{[key: string]: AdminForthResourceColumn}> {
42
- const tableName = resource.table;
43
-
44
- let rows;
45
- try {
46
- const q = await this.client.query({
47
- query: `SELECT * FROM system.columns WHERE table = '${tableName}' and database = '${this.dbName}'`,
48
- format: 'JSONEachRow',
49
- });
50
- rows = await q.json();
51
- } catch (e) {
52
- console.error(` 🛑Error connecting to datasource URL ${this.url}:`, e);
53
- return null;
54
- }
55
-
56
- const fieldTypes = {};
57
- rows.forEach((row) => {
58
- const field: any = {};
59
- const baseType = row.type;
60
- if (baseType.startsWith('Int') || baseType.startsWith('UInt')) {
61
- field.type = AdminForthDataTypes.INTEGER;
62
- } else if (baseType === 'FixedString' || baseType === 'String') {
63
- field.type = AdminForthDataTypes.STRING;
64
- // TODO
65
- // const length = baseType.match(/\d+/g);
66
- // field.maxLength = length ? parseInt(length[0]) : null;
67
- } else if (baseType == 'UUID') {
68
- field.type = AdminForthDataTypes.STRING;
69
- } else if (baseType.startsWith('Decimal')) {
70
- field.type = AdminForthDataTypes.DECIMAL;
71
- const [precision, scale] = baseType.match(/\d+/g);
72
- field.precision = parseInt(precision);
73
- field.scale = parseInt(scale);
74
- } else if (baseType.startsWith('Float')) {
75
- field.type = AdminForthDataTypes.FLOAT;
76
- } else if (baseType == 'DateTime64' || baseType == 'DateTime') {
77
- field.type = AdminForthDataTypes.DATETIME;
78
- } else if (baseType == 'Date' || baseType == 'Date64') {
79
- field.type = AdminForthDataTypes.DATE;
80
- } else if (baseType == 'Boolean') {
81
- field.type = AdminForthDataTypes.BOOLEAN;
82
- field._underlineType = 'boolean';
83
- } else {
84
- field.type = 'unknown'
85
- }
86
- field._underlineType = baseType;
87
- field._baseTypeDebug = baseType;
88
- field.required = row.notnull == 1;
89
- field.primaryKey = row.pk == 1;
90
- field.default = row.dflt_value;
91
- fieldTypes[row.name] = field
92
- });
93
- return fieldTypes;
94
- }
95
-
96
- getFieldValue(field: AdminForthResourceColumn, value: any): any {
97
- if (field.type == AdminForthDataTypes.DATETIME) {
98
- if (!value) {
99
- return null;
100
- }
101
- if (field._underlineType.startsWith('Int') || field._underlineType.startsWith('UInt')) {
102
- return dayjs.unix(+value).toISOString();
103
- } else if (field._underlineType.startsWith('DateTime')
104
- || field._underlineType.startsWith('String')
105
- || field._underlineType.startsWith('FixedString')) {
106
- const v = dayjs(value).toISOString();
107
- return v;
108
- } else {
109
- throw new Error(`AdminForth does not support row type: ${field._underlineType} for timestamps, use VARCHAR (with iso strings) or TIMESTAMP/INT (with unix timestamps). Issue in field "${field.name}"`);
110
- }
111
- } else if (field.type == AdminForthDataTypes.DATE) {
112
- if (!value) {
113
- return null;
114
- }
115
- return dayjs(value).toISOString().split('T')[0];
116
- } else if (field.type == AdminForthDataTypes.BOOLEAN) {
117
- return !!value;
118
- } else if (field.type == AdminForthDataTypes.JSON) {
119
- if (field._underlineType.startsWith('String') || field._underlineType.startsWith('FixedString')) {
120
- return JSON.parse(value);
121
- } else {
122
- console.error(`AdminForth: JSON field is not a string but ${field._underlineType}, this is not supported yet`);
123
- }
124
- }
125
- return value;
126
- }
127
-
128
- setFieldValue(field: AdminForthResourceColumn, value: any): any {
129
- if (field.type == AdminForthDataTypes.DATETIME) {
130
- if (!value) {
131
- return null;
132
- }
133
- if (field._underlineType.startsWith('Int') || field._underlineType.startsWith('UInt')) {
134
- // value is iso string now, convert to unix timestamp
135
- return dayjs(value).unix();
136
- } else if (field._underlineType.startsWith('DateTime')
137
- || field._underlineType.startsWith('String')
138
- || field._underlineType.startsWith('FixedString')) {
139
- // value is iso string now, convert to unix timestamp
140
- const iso = dayjs(value).toISOString();
141
- return iso;
142
- }
143
- } else if (field.type == AdminForthDataTypes.BOOLEAN) {
144
- return value ? 1 : 0;
145
- } else if (field.type == AdminForthDataTypes.JSON) {
146
- // check underline type is text or string
147
- if (field._underlineType.startsWith('String') || field._underlineType.startsWith('FixedString')) {
148
- return JSON.stringify(value);
149
- } else {
150
- console.error(`AdminForth: JSON field is not a string/text but ${field._underlineType}, this is not supported yet`);
151
- }
152
- }
153
-
154
- return value;
155
- }
156
-
157
- OperatorsMap = {
158
- [AdminForthFilterOperators.EQ]: '=',
159
- [AdminForthFilterOperators.NE]: '!=',
160
- [AdminForthFilterOperators.GT]: '>',
161
- [AdminForthFilterOperators.LT]: '<',
162
- [AdminForthFilterOperators.GTE]: '>=',
163
- [AdminForthFilterOperators.LTE]: '<=',
164
- [AdminForthFilterOperators.LIKE]: 'LIKE',
165
- [AdminForthFilterOperators.ILIKE]: 'ILIKE',
166
- [AdminForthFilterOperators.IN]: 'IN',
167
- [AdminForthFilterOperators.NIN]: 'NOT IN',
168
- };
169
-
170
- SortDirectionsMap = {
171
- [AdminForthSortDirections.asc]: 'ASC',
172
- [AdminForthSortDirections.desc]: 'DESC',
173
- };
174
-
175
- whereClause(
176
- resource: AdminForthResource,
177
- filters: { field: string, operator: AdminForthFilterOperators, value: any }[]
178
- ): string {
179
- return filters.length ? `WHERE ${filters.map((f, i) => {
180
- const column = resource.dataSourceColumns.find((col) => col.name == f.field);
181
- let placeholder = `{f${i}:${column._underlineType}}`;
182
- let field = f.field;
183
- let operator = this.OperatorsMap[f.operator];
184
- if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
185
- placeholder = `(${f.value.map((_, j) => `{p${i}_${j}:${
186
- column._underlineType
187
- }}`).join(', ')})`;
188
- }
189
-
190
- return `${field} ${operator} ${placeholder}`
191
- }).join(' AND ')}` : '';
192
- }
193
-
194
- whereParams(
195
- filters: { field: string, operator: AdminForthFilterOperators, value: any }[]
196
- ): any {
197
- const params = {};
198
- filters.length ? filters.forEach((f, i) => {
199
- // for arrays do set in map
200
- const v = f.value;
201
-
202
- if (f.operator == AdminForthFilterOperators.LIKE || f.operator == AdminForthFilterOperators.ILIKE) {
203
- params[`f${i}`] = `%${v}%`;
204
- } else if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
205
- v.forEach((_, j) => {
206
- params[`p${i}_${j}`] = v[j];
207
- });
208
- } else {
209
- params[`f${i}`] = v;
210
- }
211
- }) : [];
212
-
213
- return params;
214
- }
215
-
216
- async getDataWithOriginalTypes({ resource, limit, offset, sort, filters }: {
217
- resource: AdminForthResource,
218
- limit: number,
219
- offset: number,
220
- sort: { field: string, direction: AdminForthSortDirections }[],
221
- filters: { field: string, operator: AdminForthFilterOperators, value: any }[],
222
- }): Promise<any[]> {
223
- console.log('getDataWithOriginalTypes', resource, limit, offset, sort, filters);
224
- const columns = resource.dataSourceColumns.map((col) => col.name).join(', ');
225
- const tableName = resource.table;
226
-
227
- const where = this.whereClause(resource, filters);
228
-
229
- const params = this.whereParams(filters);
230
-
231
- const orderBy = sort.length ? `ORDER BY ${sort.map((s) => `${s.field} ${this.SortDirectionsMap[s.direction]}`).join(', ')}` : '';
232
-
233
-
234
- const q = `SELECT ${columns} FROM ${tableName} ${where} ${orderBy} LIMIT {limit:Int} OFFSET {offset:Int}`;
235
- const d = {
236
- ...params,
237
- limit,
238
- offset,
239
- };
240
-
241
- const stmt = await this.client.query({
242
- query: q,
243
- format: 'JSONEachRow',
244
- query_params: d,
245
- });
246
-
247
- const rows = await stmt.json();
248
-
249
- return rows.map((row) => {
250
- const newRow = {};
251
- for (const [key, value] of Object.entries(row)) {
252
- newRow[key] = value;
253
- }
254
- return newRow;
255
- });
256
- }
257
-
258
- async getCount({
259
- resource,
260
- filters,
261
- }: {
262
- resource: AdminForthResource;
263
- filters: { field: string, operator: AdminForthFilterOperators, value: any }[];
264
- }): Promise<number> {
265
- const tableName = resource.table;
266
- const where = this.whereClause(resource, filters);
267
- const d = this.whereParams(filters);
268
-
269
- const countQ = await this.client.query({
270
- query: `SELECT COUNT(*) as count FROM ${tableName} ${where}`,
271
- format: 'JSONEachRow',
272
- query_params: d,
273
- });
274
- const countResp = await countQ.json()
275
- return +countResp[0]['count'];
276
- }
277
-
278
- async getMinMaxForColumnsWithOriginalTypes({ resource, columns }: { resource: AdminForthResource, columns: AdminForthResourceColumn[] }): Promise<{ [key: string]: { min: any, max: any } }> {
279
- const tableName = resource.table;
280
- const result = {};
281
- await Promise.all(columns.map(async (col) => {
282
- const stmt = await this.client.query({
283
- query: `SELECT MIN(${col.name}) as min, MAX(${col.name}) as max FROM ${tableName}`,
284
- format: 'JSONEachRow',
285
- });
286
- const rows = await stmt.json();
287
- result[col.name] = {
288
- min: rows[0].min,
289
- max: rows[0].max,
290
- };
291
-
292
- }))
293
- return result;
294
- }
295
- async createRecordOriginalValues({ resource, record }: { resource: AdminForthResource, record: any }) {
296
- const tableName = resource.table;
297
- const columns = Object.keys(record);
298
- await this.client.insert({
299
- database: this.dbName,
300
- table: tableName,
301
- columns: columns,
302
- values: [Object.values(record)],
303
- });
304
- }
305
-
306
- async updateRecordOriginalValues({ resource, recordId, newValues }: { resource: AdminForthResource, recordId: any, newValues: any }) {
307
- const columnsWithPlaceholders = Object.keys(newValues).map((col) => {
308
- return `${col} = {${col}:${resource.dataSourceColumns.find((c) => c.name == col)._underlineType}}`
309
- });
310
-
311
- await this.client.command(
312
- {
313
- query: `ALTER TABLE ${this.dbName}.${resource.table} UPDATE ${columnsWithPlaceholders.join(', ')} WHERE ${this.getPrimaryKey(resource)} = {recordId:${resource.dataSourceColumns.find((c) => c.primaryKey)._underlineType}}`,
314
- query_params: { ...newValues, recordId },
315
- }
316
- );
317
- }
318
-
319
- async deleteRecord({ resource, recordId }: { resource: AdminForthResource, recordId: any }): Promise<boolean> {
320
- const pkColumn = resource.dataSourceColumns.find((col) => col.primaryKey);
321
- const res = await this.client.command(
322
- {
323
- query: `ALTER TABLE ${this.dbName}.${resource.table} DELETE WHERE ${
324
- pkColumn.name
325
- } = {recordId:${pkColumn._underlineType}}`,
326
- query_params: { recordId },
327
- }
328
- );
329
- // todo test what is in res
330
- return res;
331
- }
332
-
333
- close() {
334
- this.client.disconnect();
335
- }
336
- }
337
-
338
- export default ClickhouseConnector;