@l-v-yonsama/multi-platform-database-drivers 0.1.1

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 (42) hide show
  1. package/.editorconfig +15 -0
  2. package/.eslintignore +1 -0
  3. package/.eslintrc.json +30 -0
  4. package/.github/workflows/npm-publish-github-packages.yml +50 -0
  5. package/.prettierrc +12 -0
  6. package/README.md +7 -0
  7. package/__tests__/db/drivers/AwsS3Driver.test.ts +97 -0
  8. package/__tests__/db/drivers/MySQLDriver.test.ts +140 -0
  9. package/__tests__/db/drivers/PostgresDriver.test.ts +139 -0
  10. package/__tests__/db/drivers/RedisDriver.test.ts +156 -0
  11. package/jest.config.js +18 -0
  12. package/package.json +76 -0
  13. package/src/db/DBError.ts +26 -0
  14. package/src/db/drivers/AwsS3Driver.ts +539 -0
  15. package/src/db/drivers/BaseDriver.ts +256 -0
  16. package/src/db/drivers/MongoDriver.js +110 -0
  17. package/src/db/drivers/MySQLDriver.ts +318 -0
  18. package/src/db/drivers/PostgresDriver.ts +363 -0
  19. package/src/db/drivers/RedisDriver.ts +277 -0
  20. package/src/db/manager.ts +95 -0
  21. package/src/db/resource/DbResource.ts +579 -0
  22. package/src/db/resource/MultipleResultSetDataHolder.ts +119 -0
  23. package/src/db/resource/ResourceUtil.ts +366 -0
  24. package/src/db/resource/ResultSetDataHolder.ts +806 -0
  25. package/src/db/resource/types/DBType.ts +43 -0
  26. package/src/db/resource/types/GeneralColumnType.ts +321 -0
  27. package/src/db/resource/types/MySQLColumnType.ts +134 -0
  28. package/src/db/resource/types/ODBCVendorType.ts +3 -0
  29. package/src/db/resource/types/PostgresColumnType.ts +94 -0
  30. package/src/db/resource/types/RedisKeyType.ts +48 -0
  31. package/src/db/resource/types/ResourceType.ts +10 -0
  32. package/src/main.ts +1 -0
  33. package/src/service/request/general_db_request.ts +20 -0
  34. package/src/service/request/redis_request.ts +21 -0
  35. package/src/service/request/s3_request.ts +15 -0
  36. package/src/service/response/GeneralReponse.ts +10 -0
  37. package/src/types/DateModifiedType.ts +75 -0
  38. package/src/types/FileKindType.ts +80 -0
  39. package/src/util/file_util.ts +178 -0
  40. package/tsconfig.json +23 -0
  41. package/tsconfig.release.json +23 -0
  42. package/unit-test.yml +41 -0
@@ -0,0 +1,15 @@
1
+ export interface S3Request {
2
+ connection_id: string;
3
+ command: S3CommandType;
4
+ Bucket: string;
5
+ key?: string;
6
+ options?: any;
7
+ }
8
+
9
+ export enum S3CommandType {
10
+ PutObject = "PutObject",
11
+ Search = "Search",
12
+ AbortSearch = "AbortSearch",
13
+ listObject = "listObject",
14
+ getObject = "getObject"
15
+ }
@@ -0,0 +1,10 @@
1
+ export default class GeneralResponse {
2
+ public ok = true;
3
+ public message = '';
4
+ public result: any;
5
+
6
+ constructor(ok = true, message = '') {
7
+ this.ok = ok;
8
+ this.message = message;
9
+ }
10
+ }
@@ -0,0 +1,75 @@
1
+ export enum DateModifiedType {
2
+ None = '-',
3
+ Today = 'Today',
4
+ Yesterday = 'Yesterday',
5
+ ThisWeek = 'This week',
6
+ LastWeek = 'Last week',
7
+ ThisMonth = 'This month',
8
+ LastMonth = 'Last month',
9
+ ThisYear = 'This year',
10
+ LastYear = 'Last year',
11
+ }
12
+ const DAY1 = 1000 * 60 * 60 * 24;
13
+ const cutoffHours = (dt: Date): Date => {
14
+ const r = new Date();
15
+ r.setTime(dt.getTime());
16
+ r.setHours(0);
17
+ r.setMinutes(0);
18
+ r.setSeconds(0);
19
+ r.setMilliseconds(0);
20
+ return r;
21
+ };
22
+ export namespace DateModifiedType {
23
+ export function isValid(
24
+ type: DateModifiedType,
25
+ inDate: number | Date,
26
+ ): boolean {
27
+ const now = new Date();
28
+ const today = cutoffHours(now).getTime();
29
+ if (typeof inDate === 'number') {
30
+ inDate = new Date(inDate);
31
+ }
32
+ switch (type) {
33
+ case DateModifiedType.Today:
34
+ return today <= inDate.getTime() && inDate.getTime() <= today + DAY1;
35
+ case DateModifiedType.Yesterday:
36
+ return today - DAY1 <= inDate.getTime() && inDate.getTime() <= today;
37
+ case DateModifiedType.ThisWeek: {
38
+ const d = new Date();
39
+ d.setTime(
40
+ d.getTime() - (d.getDay() ? d.getDay() : 7) * 24 * 60 * 60 * 1000,
41
+ );
42
+ const endOfWeek = cutoffHours(d).getTime();
43
+ d.setTime(d.getTime() - 6 * 24 * 60 * 60 * 1000);
44
+ const startOfWeek = cutoffHours(d).getTime();
45
+ return (
46
+ endOfWeek <= inDate.getTime() &&
47
+ inDate.getTime() <= startOfWeek + DAY1
48
+ );
49
+ }
50
+ case DateModifiedType.LastWeek: {
51
+ const d = new Date();
52
+ d.setTime(
53
+ d.getTime() -
54
+ (d.getDay() ? d.getDay() : 7) * 24 * 60 * 60 * 1000 -
55
+ DAY1 * 7,
56
+ );
57
+ const endOfWeek = cutoffHours(d).getTime();
58
+ d.setTime(d.getTime() - 6 * 24 * 60 * 60 * 1000);
59
+ const startOfWeek = cutoffHours(d).getTime();
60
+ return (
61
+ endOfWeek <= inDate.getTime() &&
62
+ inDate.getTime() <= startOfWeek + DAY1
63
+ );
64
+ }
65
+ case DateModifiedType.ThisMonth:
66
+ return (
67
+ now.getFullYear() === inDate.getFullYear() &&
68
+ now.getMonth() === inDate.getMonth()
69
+ );
70
+ case DateModifiedType.ThisYear:
71
+ return now.getFullYear() === inDate.getFullYear();
72
+ }
73
+ return true;
74
+ }
75
+ }
@@ -0,0 +1,80 @@
1
+ export enum FileKindType {
2
+ All = 'All',
3
+ Image = 'Image',
4
+ Video = 'Video',
5
+ Music = 'Music',
6
+ Document = 'Document',
7
+ Program = 'Program',
8
+ Text = 'Text',
9
+ }
10
+
11
+ export namespace FileKindType {
12
+ export function isValid(type: FileKindType, file_name: string): boolean {
13
+ if (FileKindType.All === type) {
14
+ return true;
15
+ }
16
+ file_name = file_name.toLocaleLowerCase().trim();
17
+ const idx = file_name.lastIndexOf('.');
18
+ if (file_name === '' || idx <= 0) {
19
+ return false;
20
+ }
21
+ const ext = file_name.substring(idx + 1);
22
+ switch (type) {
23
+ case FileKindType.Image:
24
+ return (
25
+ ext === 'jpg' ||
26
+ ext === 'jpeg' ||
27
+ ext === 'png' ||
28
+ ext === 'gif' ||
29
+ ext === 'tiff' ||
30
+ ext === 'tif' ||
31
+ ext === 'bmp' ||
32
+ ext === 'webp'
33
+ );
34
+ case FileKindType.Video:
35
+ return (
36
+ ext === 'mov' || ext === 'mp4' || ext === 'webm' || ext === 'avi'
37
+ );
38
+ case FileKindType.Music:
39
+ return ext === 'wav' || ext === 'mp3';
40
+ case FileKindType.Document:
41
+ return (
42
+ ext === 'xls' ||
43
+ ext === 'doc' ||
44
+ ext === 'ppt' ||
45
+ ext === 'pdf' ||
46
+ ext === 'xlsx' ||
47
+ ext === 'docx' ||
48
+ ext === 'pptx'
49
+ );
50
+ case FileKindType.Text:
51
+ if (ext === 'txt' || ext === 'json') {
52
+ return true;
53
+ }
54
+ return (
55
+ ext === 'js' ||
56
+ ext === 'ts' ||
57
+ ext === 'py' ||
58
+ ext === 'pyx' ||
59
+ ext === 'java' ||
60
+ ext === 'html' ||
61
+ ext === 'xml' ||
62
+ ext === 'vue' ||
63
+ ext === 'c'
64
+ );
65
+ case FileKindType.Program:
66
+ return (
67
+ ext === 'js' ||
68
+ ext === 'ts' ||
69
+ ext === 'py' ||
70
+ ext === 'pyx' ||
71
+ ext === 'java' ||
72
+ ext === 'html' ||
73
+ ext === 'xml' ||
74
+ ext === 'vue' ||
75
+ ext === 'c'
76
+ );
77
+ }
78
+ return false;
79
+ }
80
+ }
@@ -0,0 +1,178 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import * as path from 'path';
3
+ import * as fs from 'fs';
4
+ import prettyBytes from 'pretty-bytes';
5
+ import { promisify } from 'util';
6
+ import * as jconv from 'jconv';
7
+ import GeneralResponse from '../service/response/GeneralReponse';
8
+
9
+ export {
10
+ replaceSafeFileName,
11
+ createKeyFolder,
12
+ getAppPath,
13
+ createAbsoluteFilePath,
14
+ getAbsTmpDirPath,
15
+ readFile,
16
+ syncReadFile,
17
+ isAsyncFileExists,
18
+ sizeToString,
19
+ stats,
20
+ getKeyFile,
21
+ };
22
+
23
+ const AWS = 'aws';
24
+ const TMP = 'tmp';
25
+
26
+ function sizeToString(len?: number): string {
27
+ if (len === undefined) {
28
+ return '';
29
+ }
30
+ return prettyBytes(len);
31
+ }
32
+
33
+ function stats(file_path: string): StatsExtra {
34
+ const stat = fs.statSync(file_path);
35
+ const r = <StatsExtra>Object.assign({}, stat);
36
+ r.size_string = sizeToString(stat.size);
37
+ r.type = '';
38
+ r.extension = '';
39
+ r.basename = path.basename(file_path);
40
+ const dot = r.basename.lastIndexOf('.');
41
+ if (dot >= 0) {
42
+ r.extension = r.basename.substring(dot + 1);
43
+ if (r.extension.match(/(jpg|jpeg|png|gif|bmp|tiff|tif)/i)) {
44
+ r.type = 'image/' + r.extension;
45
+ }
46
+ }
47
+ return r;
48
+ }
49
+
50
+ export interface StatsExtra extends fs.Stats {
51
+ size_string: string;
52
+ type: string;
53
+ basename: string;
54
+ extension: string;
55
+ }
56
+
57
+ function getAppPath(): string {
58
+ return './';
59
+ }
60
+
61
+ function createAbsoluteFilePath(
62
+ category: string,
63
+ relative_file_path: string,
64
+ ): string {
65
+ const userDataDir = getAppPath();
66
+ return path.join(userDataDir, category, relative_file_path);
67
+ }
68
+
69
+ const lpad = (n: number): string => (n < 10 ? '0' + n : String(n));
70
+
71
+ function replaceSafeFileName(name = ''): string {
72
+ return name.replace(/[<>#%{}|\\^~[]`;\?:@=& ]/gi, '_').trim();
73
+ }
74
+
75
+ async function createKeyFolder(
76
+ bucket_name: string,
77
+ key: string,
78
+ ): Promise<string> {
79
+ if (key.startsWith('/')) {
80
+ // remove.
81
+ key = key.substring(1);
82
+ }
83
+ let folders = [];
84
+ if (key.endsWith('/')) {
85
+ // this is folder
86
+ // remove.
87
+ key = key.substring(0, key.length - 1);
88
+ folders = key.split('/');
89
+ } else {
90
+ // this is file
91
+ folders = key.split('/');
92
+ folders.pop(); // remove filename
93
+ }
94
+
95
+ const userDataDir = getAppPath();
96
+ const output_dir_path = path.join(userDataDir, AWS, bucket_name, ...folders);
97
+ fs.promises.mkdir(output_dir_path, { recursive: true });
98
+ return output_dir_path;
99
+ }
100
+
101
+ function getAbsTmpDirPath(): string {
102
+ const userDataDir = getAppPath();
103
+ return path.join(userDataDir, TMP);
104
+ }
105
+
106
+ async function isAsyncFileExists(full_path: string): Promise<boolean> {
107
+ const stat = promisify(fs.stat);
108
+ try {
109
+ await stat(full_path);
110
+ return true;
111
+ } catch (e) {
112
+ return false;
113
+ }
114
+ }
115
+
116
+ async function readFile(
117
+ full_path: string,
118
+ encoding: string | null = null,
119
+ ): Promise<string | Buffer> {
120
+ return new Promise<string | Buffer>((resolve, reject) => {
121
+ if (encoding && encoding.match(/sjis|jis|eucjp|ucs2|utf16le/i)) {
122
+ fs.readFile(full_path, (err, buf: Buffer) => {
123
+ if (err) {
124
+ reject(err);
125
+ } else {
126
+ // var buf = new Buffer(data, 'binary');
127
+ const retStr = jconv.decode(buf, encoding);
128
+ resolve(retStr);
129
+ }
130
+ });
131
+ } else {
132
+ // log.info(LOG_PREFIX, '#readFile', full_path)
133
+ fs.readFile(full_path, encoding as BufferEncoding, (err, data) => {
134
+ if (err) {
135
+ reject(err);
136
+ } else {
137
+ resolve(data);
138
+ }
139
+ });
140
+ }
141
+ });
142
+ }
143
+
144
+ function syncReadFile(
145
+ full_path: string,
146
+ encoding: BufferEncoding | null = null,
147
+ ): string | Buffer {
148
+ if (encoding) {
149
+ return fs.readFileSync(full_path, encoding);
150
+ }
151
+ return fs.readFileSync(full_path);
152
+ }
153
+
154
+ async function getKeyFile(
155
+ bucket_name: string,
156
+ key: string,
157
+ ): Promise<GeneralResponse> {
158
+ if (key.startsWith('/')) {
159
+ key = key.substring(1);
160
+ }
161
+ if (key.endsWith('/')) {
162
+ // this is folder
163
+ // remove.
164
+ key = key.substring(0, key.length - 1);
165
+ }
166
+ const userDataDir = getAppPath();
167
+ const key_file_path = path.join(
168
+ userDataDir,
169
+ AWS,
170
+ bucket_name,
171
+ ...key.split('/'),
172
+ );
173
+ const exists = await isAsyncFileExists(key_file_path);
174
+ const res = new GeneralResponse(exists, '');
175
+ res.result = key_file_path;
176
+ // log.info(LOG_PREFIX, "key_file_path:res=", res);
177
+ return res;
178
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es2017",
4
+ "module": "commonjs",
5
+ "declaration": false,
6
+ "moduleResolution": "node",
7
+ "rootDir": ".",
8
+ "outDir": "build",
9
+ "allowSyntheticDefaultImports": true,
10
+ "importHelpers": true,
11
+ "alwaysStrict": true,
12
+ "sourceMap": true,
13
+ "forceConsistentCasingInFileNames": true,
14
+ "noFallthroughCasesInSwitch": true,
15
+ "noImplicitReturns": true,
16
+ "noUnusedLocals": false,
17
+ "noUnusedParameters": false,
18
+ "noImplicitAny": false,
19
+ "noImplicitThis": false,
20
+ "strictNullChecks": false,
21
+ "skipLibCheck": true
22
+ }
23
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es2017",
4
+ "module": "commonjs",
5
+ "declaration": true,
6
+ "moduleResolution": "node",
7
+ "rootDir": ".",
8
+ "outDir": "build",
9
+ "allowSyntheticDefaultImports": true,
10
+ "importHelpers": true,
11
+ "alwaysStrict": true,
12
+ "sourceMap": true,
13
+ "forceConsistentCasingInFileNames": true,
14
+ "noFallthroughCasesInSwitch": true,
15
+ "noImplicitReturns": true,
16
+ "noUnusedLocals": false,
17
+ "noUnusedParameters": false,
18
+ "noImplicitAny": false,
19
+ "noImplicitThis": false,
20
+ "strictNullChecks": false,
21
+ "skipLibCheck": true
22
+ }
23
+ }
package/unit-test.yml ADDED
@@ -0,0 +1,41 @@
1
+ version: '3'
2
+
3
+ services:
4
+ redis:
5
+ image: redis:5.0
6
+ container_name: redis_container
7
+ command: redis-server --requirepass testpass
8
+ ports:
9
+ - '6000:6379'
10
+
11
+ mysql:
12
+ image: mysql:8.0
13
+ command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
14
+ container_name: mysql_container
15
+ ports:
16
+ - 6001:3306
17
+ environment:
18
+ TZ: 'Asia/Tokyo'
19
+ MYSQL_ROOT_PASSWORD: p@ssw0rd
20
+ MYSQL_DATABASE: testdb
21
+ MYSQL_USER: testuser
22
+ MYSQL_PASSWORD: testpass
23
+ postgres:
24
+ image: postgres:14
25
+ container_name: postgres_container
26
+ ports:
27
+ - 6002:5432
28
+ environment:
29
+ - POSTGRES_USER=testuser
30
+ - POSTGRES_PASSWORD=testpass
31
+ - POSTGRES_DB=testdb
32
+ minio:
33
+ image: quay.io/minio/minio:latest
34
+ container_name: minio_container
35
+ environment:
36
+ MINIO_ROOT_USER: testuser
37
+ MINIO_ROOT_PASSWORD: testpass
38
+ command: server --console-address ":9001" /data
39
+ ports:
40
+ - 6003:9000
41
+ - 6004:9001