@reldens/storage 0.80.0 → 0.82.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.
@@ -21,7 +21,7 @@ class PrismaSchemaGenerator
21
21
  this.maxWaitTime = sc.get(props, 'maxWaitTime', 30000);
22
22
  this.prismaSchemaPath = sc.get(props, 'prismaSchemaPath', FileHandler.joinPaths(process.cwd(), 'prisma'));
23
23
  this.schemaFilePath = FileHandler.joinPaths(this.prismaSchemaPath, 'schema.prisma');
24
- this.clientOutputPath = sc.get(props, 'clientOutputPath', '');
24
+ this.clientOutputPath = sc.get(props, 'clientOutputPath', './client');
25
25
  this.generateBinaryTargets = sc.get(props, 'generateBinaryTargets', ['native', 'debian-openssl-1.1.x']);
26
26
  this.dbParams = sc.get(props, 'dbParams', process.env.RELDENS_DB_PARAMS || '');
27
27
  }
@@ -30,11 +30,12 @@ class PrismaSchemaGenerator
30
30
  {
31
31
  FileHandler.createFolder(this.prismaSchemaPath);
32
32
  this.generateSchemaFile();
33
+ this.setDatabaseEnvironmentVariables();
33
34
  Logger.info('Running prisma introspect "npx prisma db pull"...');
34
35
  try {
35
- execSync('npx prisma db pull', { stdio: 'inherit' });
36
+ execSync('npx prisma db pull', {stdio: 'inherit'});
36
37
  } catch(error) {
37
- Logger.critical('Failed to pull database schema: ' + error.message);
38
+ Logger.critical('Failed to pull database schema: '+error.message);
38
39
  return false;
39
40
  }
40
41
  let generateCommand = 'npx prisma generate';
@@ -43,33 +44,42 @@ class PrismaSchemaGenerator
43
44
  }
44
45
  Logger.info('Running prisma generate "'+generateCommand+'"...');
45
46
  try {
46
- execSync(generateCommand, { stdio: 'inherit' });
47
+ execSync(generateCommand, {stdio: 'inherit'});
47
48
  } catch(error) {
48
49
  let errorString = error.toString();
49
50
  if(errorString.includes('EPERM') && errorString.includes('query_engine-windows.dll.node')){
50
51
  Logger.warning('Windows permission error detected with query engine file.');
51
52
  Logger.warning('This is a known Prisma issue on Windows.');
52
- Logger.warning('Please manually run: ' + generateCommand);
53
+ Logger.warning('Please manually run: '+generateCommand);
53
54
  Logger.warning('You may need to close any processes using the Prisma client first.');
54
55
  return false;
55
56
  }
56
- Logger.critical('Generate command failed: ' + error.message);
57
+ Logger.critical('Generate command failed: '+error.message);
57
58
  return false;
58
59
  }
59
60
  await this.waitForSchemaGeneration();
60
61
  return true;
61
62
  }
62
63
 
64
+ setDatabaseEnvironmentVariables()
65
+ {
66
+ let datasourceProvider = this.getDatasourceProvider();
67
+ process.env.RELDENS_DB_URL = this.buildConnectionString(datasourceProvider);
68
+ if(this.dataProxy){
69
+ process.env.RELDENS_DB_DIRECT_URL = this.buildDirectConnectionString(datasourceProvider);
70
+ }
71
+ }
72
+
63
73
  async waitForSchemaGeneration()
64
74
  {
65
- let clientPath = this.clientOutputPath || 'node_modules/.prisma/client';
75
+ let clientPath = this.clientOutputPath || './client';
66
76
  let generatedClientPath = this.resolveClientPath(clientPath);
67
77
  let clientIndexPath = FileHandler.joinPaths(generatedClientPath, 'index.js');
68
78
  let startTime = Date.now();
69
79
  return new Promise((resolve) => {
70
80
  let interval = setInterval(() => {
71
81
  let awaitTime = Date.now() - startTime;
72
- Logger.info('Awaiting on schema generation: ' + clientIndexPath, (awaitTime/1000).toFixed(0));
82
+ Logger.info('Awaiting on schema generation: '+clientIndexPath, (awaitTime/1000).toFixed(0));
73
83
  if(FileHandler.exists(clientIndexPath)){
74
84
  clearInterval(interval);
75
85
  resolve();
@@ -98,11 +108,7 @@ class PrismaSchemaGenerator
98
108
  generateSchemaFile()
99
109
  {
100
110
  let datasourceProvider = this.getDatasourceProvider();
101
- let schemaContent = this.buildSchemaContent(
102
- datasourceProvider,
103
- this.buildConnectionString(datasourceProvider),
104
- this.buildDirectConnectionString(datasourceProvider)
105
- );
111
+ let schemaContent = this.buildSchemaContent(datasourceProvider);
106
112
  FileHandler.writeFile(this.schemaFilePath, schemaContent);
107
113
  Logger.info('Generated Prisma schema file at: '+this.schemaFilePath);
108
114
  }
@@ -123,7 +129,7 @@ class PrismaSchemaGenerator
123
129
  if(this.dataProxy){
124
130
  datasourceProvider = 'prisma';
125
131
  }
126
- return datasourceProvider + this.buildConnectionDataString();
132
+ return datasourceProvider+this.buildConnectionDataString();
127
133
  }
128
134
 
129
135
  buildDirectConnectionString(datasourceProvider)
@@ -131,47 +137,48 @@ class PrismaSchemaGenerator
131
137
  if(!this.dataProxy){
132
138
  return '';
133
139
  }
134
- return datasourceProvider + this.buildConnectionDataString();
140
+ return datasourceProvider+this.buildConnectionDataString();
135
141
  }
136
142
 
137
143
  buildConnectionDataString()
138
144
  {
139
- return '://' + this.config.user + ':' + this.config.password
140
- + '@' + this.config.host+':' + this.config.port
141
- + '/' + this.config.database
142
- + (this.dbParams ? '?' + this.dbParams : '');
145
+ return '://'+this.config.user+':'+this.config.password
146
+ +'@'+this.config.host+':'+this.config.port
147
+ +'/'+this.config.database
148
+ +(this.dbParams ? '?'+this.dbParams : '');
143
149
  }
144
150
 
145
- buildSchemaContent(datasourceProvider, connectionString, directConnectionString)
151
+ buildSchemaContent(datasourceProvider)
146
152
  {
147
- return this.buildGeneratorBlock() + '\n\n'
148
- + this.buildDatasourceBlock(datasourceProvider, connectionString, directConnectionString) + '\n';
153
+ return this.buildGeneratorBlock()+'\n\n'
154
+ +this.buildDatasourceBlock(datasourceProvider)+'\n';
149
155
  }
150
156
 
151
157
  buildGeneratorBlock()
152
158
  {
153
159
  let generatorContent = 'generator client {\n';
154
160
  generatorContent += ' provider = "prisma-client-js"\n';
155
- if(this.clientOutputPath){
156
- let normalizedPath = this.clientOutputPath.replace(/\\/g, '/');
157
- generatorContent += ' output = "' + normalizedPath + '"\n';
161
+ let outputPath = this.clientOutputPath || './client';
162
+ if(!outputPath.startsWith('./')){
163
+ outputPath = './client';
158
164
  }
165
+ generatorContent += ' output = "'+outputPath+'"\n';
159
166
  if(0 < this.generateBinaryTargets.length){
160
- generatorContent += ' binaryTargets = [' +
161
- this.generateBinaryTargets.map(target => '"' + target + '"').join(', ')
162
- + ']\n';
167
+ generatorContent += ' binaryTargets = ['
168
+ +this.generateBinaryTargets.map(target => '"'+target+'"').join(', ')
169
+ +']\n';
163
170
  }
164
171
  generatorContent += '}';
165
172
  return generatorContent;
166
173
  }
167
174
 
168
- buildDatasourceBlock(datasourceProvider, connectionString, directConnectionString)
175
+ buildDatasourceBlock(datasourceProvider)
169
176
  {
170
177
  let datasourceContent = 'datasource db {\n';
171
- datasourceContent += ' provider = "' + datasourceProvider + '"\n';
172
- datasourceContent += ' url = "' + connectionString + '"\n';
173
- if('' !== directConnectionString){
174
- datasourceContent += ' directUrl = "' + directConnectionString + '"\n';
178
+ datasourceContent += ' provider = "'+datasourceProvider+'"\n';
179
+ datasourceContent += ' url = env("RELDENS_DB_URL")\n';
180
+ if(this.dataProxy){
181
+ datasourceContent += ' directUrl = env("RELDENS_DB_DIRECT_URL")\n';
175
182
  }
176
183
  datasourceContent += '}';
177
184
  return datasourceContent;
@@ -0,0 +1,260 @@
1
+ /**
2
+ *
3
+ * Reldens - PrismaTypeCaster
4
+ *
5
+ */
6
+
7
+ const { Logger, sc } = require('@reldens/utils');
8
+ const { Prisma } = require('@prisma/client');
9
+
10
+ class PrismaTypeCaster
11
+ {
12
+
13
+ constructor(props)
14
+ {
15
+ this.idFieldType = sc.get(props, 'idFieldType', 'String');
16
+ this.jsonFields = sc.get(props, 'jsonFields', new Set());
17
+ this.fieldTypes = sc.get(props, 'fieldTypes', {});
18
+ this.fieldDefaults = sc.get(props, 'fieldDefaults', {});
19
+ this.requiredFields = sc.get(props, 'requiredFields', []);
20
+ this.optionalFields = sc.get(props, 'optionalFields', []);
21
+ this.rawModel = sc.get(props, 'rawModel', false);
22
+ }
23
+
24
+ updateMetadata(metadata)
25
+ {
26
+ this.idFieldType = sc.get(metadata, 'idFieldType', this.idFieldType);
27
+ this.jsonFields = sc.get(metadata, 'jsonFields', this.jsonFields);
28
+ this.fieldTypes = sc.get(metadata, 'fieldTypes', this.fieldTypes);
29
+ this.fieldDefaults = sc.get(metadata, 'fieldDefaults', this.fieldDefaults);
30
+ this.requiredFields = sc.get(metadata, 'requiredFields', this.requiredFields);
31
+ this.optionalFields = sc.get(metadata, 'optionalFields', this.optionalFields);
32
+ }
33
+
34
+ isNumericFieldType(fieldType)
35
+ {
36
+ return ('Int' === fieldType || 'BigInt' === fieldType || 'Float' === fieldType || 'Decimal' === fieldType);
37
+ }
38
+
39
+ isBooleanFieldType(fieldType)
40
+ {
41
+ return 'Boolean' === fieldType;
42
+ }
43
+
44
+ isDateTimeFieldType(fieldType)
45
+ {
46
+ return 'DateTime' === fieldType || 'Date' === fieldType;
47
+ }
48
+
49
+ isJsonFieldType(fieldType)
50
+ {
51
+ return 'Json' === fieldType;
52
+ }
53
+
54
+ isJsonFieldByName(fieldName)
55
+ {
56
+ if(this.jsonFields.has(fieldName)){
57
+ return true;
58
+ }
59
+ if(!this.rawModel || !this.rawModel.properties){
60
+ return false;
61
+ }
62
+ return 'json' === sc.get(this.rawModel.properties[fieldName], 'dbType', '');
63
+ }
64
+
65
+ isStringFieldType(fieldType)
66
+ {
67
+ return 'String' === fieldType;
68
+ }
69
+
70
+ isNullOrEmpty(value)
71
+ {
72
+ return null === value || undefined === value || '' === value;
73
+ }
74
+
75
+ isValidCastedValue(value)
76
+ {
77
+ return undefined !== value;
78
+ }
79
+
80
+ castValue(value, fieldType, fieldName = null)
81
+ {
82
+ if('id' === fieldName){
83
+ return this.castToIdType(value);
84
+ }
85
+ if(this.isNullOrEmpty(value)){
86
+ if(sc.hasOwn(this.fieldDefaults, fieldName)){
87
+ return undefined;
88
+ }
89
+ if(-1 !== this.optionalFields.indexOf(fieldName)){
90
+ return null;
91
+ }
92
+ if(-1 !== this.requiredFields.indexOf(fieldName)){
93
+ Logger.warning('Required field '+fieldName+' cannot be null or empty');
94
+ return undefined;
95
+ }
96
+ return null;
97
+ }
98
+ if(this.isJsonFieldByName(fieldName)){
99
+ return this.castToJsonType(value);
100
+ }
101
+ if(this.isNumericFieldType(fieldType)){
102
+ return this.castToNumericType(value, fieldType);
103
+ }
104
+ if(this.isBooleanFieldType(fieldType)){
105
+ return this.castToBooleanType(value);
106
+ }
107
+ if(this.isDateTimeFieldType(fieldType)){
108
+ return this.castToDateType(value);
109
+ }
110
+ if(this.isStringFieldType(fieldType)){
111
+ return this.castToStringType(value);
112
+ }
113
+ return value;
114
+ }
115
+
116
+ castToIdType(value)
117
+ {
118
+ if(this.isNullOrEmpty(value)){
119
+ return value;
120
+ }
121
+ if(this.isNumericFieldType(this.idFieldType)){
122
+ let numeric = Number(value);
123
+ if(!isNaN(numeric)){
124
+ return ('Int' === this.idFieldType || 'BigInt' === this.idFieldType) ? Math.floor(numeric) : numeric;
125
+ }
126
+ Logger.warning('Cannot cast non-numeric value "'+value+'" to ID type '+this.idFieldType);
127
+ return null;
128
+ }
129
+ return String(value);
130
+ }
131
+
132
+ castToNumericType(value, fieldType)
133
+ {
134
+ if(this.isNullOrEmpty(value)){
135
+ return null;
136
+ }
137
+ let numericValue = Number(value);
138
+ if(isNaN(numericValue)){
139
+ Logger.warning('Cannot convert value to numeric: '+value);
140
+ return null;
141
+ }
142
+ if('Int' === fieldType || 'BigInt' === fieldType){
143
+ return Math.floor(numericValue);
144
+ }
145
+ return numericValue;
146
+ }
147
+
148
+ castToBooleanType(value)
149
+ {
150
+ if(this.isNullOrEmpty(value)){
151
+ return null;
152
+ }
153
+ if('boolean' === typeof value){
154
+ return value;
155
+ }
156
+ if('number' === typeof value){
157
+ return 0 !== value;
158
+ }
159
+ if('string' === typeof value){
160
+ let lowerValue = value.toLowerCase();
161
+ if('true' === lowerValue || '1' === lowerValue || 'yes' === lowerValue){
162
+ return true;
163
+ }
164
+ if('false' === lowerValue || '0' === lowerValue || 'no' === lowerValue){
165
+ return false;
166
+ }
167
+ }
168
+ return Boolean(value);
169
+ }
170
+
171
+ castToDateType(dateValue)
172
+ {
173
+ if(dateValue instanceof Date){
174
+ return dateValue;
175
+ }
176
+ if(this.isNullOrEmpty(dateValue)){
177
+ return null;
178
+ }
179
+ let dateString = String(dateValue);
180
+ if(dateString.match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/)){
181
+ return new Date(dateString.replace(' ', 'T')+'Z');
182
+ }
183
+ if(dateString.match(/^\d{4}-\d{2}-\d{2}$/)){
184
+ return new Date(dateString+'T00:00:00Z');
185
+ }
186
+ try {
187
+ let parsedDate = new Date(dateString);
188
+ if(isNaN(parsedDate.getTime())){
189
+ Logger.warning('Invalid date value: '+dateString);
190
+ return null;
191
+ }
192
+ return parsedDate;
193
+ } catch(error) {
194
+ Logger.warning('Failed to convert date value: '+dateString);
195
+ return null;
196
+ }
197
+ }
198
+
199
+ castToJsonType(value)
200
+ {
201
+ if(this.isNullOrEmpty(value)){
202
+ return Prisma.DbNull;
203
+ }
204
+ if('object' === typeof value){
205
+ return value;
206
+ }
207
+ if('string' === typeof value){
208
+ if(value.startsWith('%') && value.endsWith('%')){
209
+ return value;
210
+ }
211
+ try {
212
+ return JSON.parse(value);
213
+ } catch(error) {
214
+ return value;
215
+ }
216
+ }
217
+ return value;
218
+ }
219
+
220
+ castToStringType(value)
221
+ {
222
+ if(null === value || undefined === value){
223
+ return null;
224
+ }
225
+ if('string' === typeof value){
226
+ if('null' === value){
227
+ return null;
228
+ }
229
+ return value;
230
+ }
231
+ return String(value);
232
+ }
233
+
234
+ normalizeReturnData(data)
235
+ {
236
+ if(null === data || undefined === data){
237
+ return data;
238
+ }
239
+ if('bigint' === typeof data){
240
+ return Number(data);
241
+ }
242
+ if(data instanceof Date){
243
+ return data;
244
+ }
245
+ if(sc.isArray(data)){
246
+ return data.map(item => this.normalizeReturnData(item));
247
+ }
248
+ if(sc.isObject(data)){
249
+ let result = {};
250
+ for(let key of Object.keys(data)){
251
+ result[key] = this.normalizeReturnData(data[key]);
252
+ }
253
+ return result;
254
+ }
255
+ return data;
256
+ }
257
+
258
+ }
259
+
260
+ module.exports.PrismaTypeCaster = PrismaTypeCaster;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@reldens/storage",
3
3
  "scope": "@reldens",
4
- "version": "0.80.0",
4
+ "version": "0.82.0",
5
5
  "description": "Reldens - Storage",
6
6
  "author": "Damian A. Pastorini",
7
7
  "license": "MIT",