@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.
- package/.editorconfig +15 -0
- package/.eslintignore +1 -0
- package/.eslintrc.json +30 -0
- package/.github/workflows/npm-publish-github-packages.yml +50 -0
- package/.prettierrc +12 -0
- package/README.md +7 -0
- package/__tests__/db/drivers/AwsS3Driver.test.ts +97 -0
- package/__tests__/db/drivers/MySQLDriver.test.ts +140 -0
- package/__tests__/db/drivers/PostgresDriver.test.ts +139 -0
- package/__tests__/db/drivers/RedisDriver.test.ts +156 -0
- package/jest.config.js +18 -0
- package/package.json +76 -0
- package/src/db/DBError.ts +26 -0
- package/src/db/drivers/AwsS3Driver.ts +539 -0
- package/src/db/drivers/BaseDriver.ts +256 -0
- package/src/db/drivers/MongoDriver.js +110 -0
- package/src/db/drivers/MySQLDriver.ts +318 -0
- package/src/db/drivers/PostgresDriver.ts +363 -0
- package/src/db/drivers/RedisDriver.ts +277 -0
- package/src/db/manager.ts +95 -0
- package/src/db/resource/DbResource.ts +579 -0
- package/src/db/resource/MultipleResultSetDataHolder.ts +119 -0
- package/src/db/resource/ResourceUtil.ts +366 -0
- package/src/db/resource/ResultSetDataHolder.ts +806 -0
- package/src/db/resource/types/DBType.ts +43 -0
- package/src/db/resource/types/GeneralColumnType.ts +321 -0
- package/src/db/resource/types/MySQLColumnType.ts +134 -0
- package/src/db/resource/types/ODBCVendorType.ts +3 -0
- package/src/db/resource/types/PostgresColumnType.ts +94 -0
- package/src/db/resource/types/RedisKeyType.ts +48 -0
- package/src/db/resource/types/ResourceType.ts +10 -0
- package/src/main.ts +1 -0
- package/src/service/request/general_db_request.ts +20 -0
- package/src/service/request/redis_request.ts +21 -0
- package/src/service/request/s3_request.ts +15 -0
- package/src/service/response/GeneralReponse.ts +10 -0
- package/src/types/DateModifiedType.ts +75 -0
- package/src/types/FileKindType.ts +80 -0
- package/src/util/file_util.ts +178 -0
- package/tsconfig.json +23 -0
- package/tsconfig.release.json +23 -0
- package/unit-test.yml +41 -0
package/.editorconfig
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
root = true
|
|
2
|
+
|
|
3
|
+
[*]
|
|
4
|
+
charset = utf-8
|
|
5
|
+
end_of_line = lf
|
|
6
|
+
trim_trailing_whitespace = true
|
|
7
|
+
insert_final_newline = true
|
|
8
|
+
|
|
9
|
+
[*.md]
|
|
10
|
+
insert_final_newline = false
|
|
11
|
+
trim_trailing_whitespace = false
|
|
12
|
+
|
|
13
|
+
[*.{js,json,ts,mts,yml,yaml}]
|
|
14
|
+
indent_size = 2
|
|
15
|
+
indent_style = space
|
package/.eslintignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/**/*.js
|
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"env": {
|
|
3
|
+
"browser": false,
|
|
4
|
+
"jest": true,
|
|
5
|
+
"node": true
|
|
6
|
+
},
|
|
7
|
+
"parser": "@typescript-eslint/parser",
|
|
8
|
+
"parserOptions": {
|
|
9
|
+
"project": "tsconfig.json",
|
|
10
|
+
"sourceType": "module"
|
|
11
|
+
},
|
|
12
|
+
"plugins": ["@typescript-eslint", "jest"],
|
|
13
|
+
"extends": [
|
|
14
|
+
"eslint:recommended",
|
|
15
|
+
"plugin:@typescript-eslint/recommended",
|
|
16
|
+
"plugin:jest/recommended",
|
|
17
|
+
"prettier"
|
|
18
|
+
],
|
|
19
|
+
"rules": {
|
|
20
|
+
// The following rule is enabled only to supplement the inline suppression
|
|
21
|
+
// examples, and because it is not a recommended rule, you should either
|
|
22
|
+
// disable it, or understand what it enforces.
|
|
23
|
+
// https://typescript-eslint.io/rules/explicit-function-return-type/
|
|
24
|
+
"@typescript-eslint/explicit-function-return-type": "warn",
|
|
25
|
+
"no-async-promise-executor": "warn",
|
|
26
|
+
"@typescript-eslint/no-namespace": "off",
|
|
27
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
28
|
+
"@typescript-eslint/ban-types": "off"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
|
|
3
|
+
|
|
4
|
+
name: Node.js Package
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
release:
|
|
8
|
+
types: [created]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v3
|
|
15
|
+
- uses: actions/setup-node@v3
|
|
16
|
+
with:
|
|
17
|
+
node-version: 16
|
|
18
|
+
# E2E テストを Docker Compose で実行する
|
|
19
|
+
- name: run test on docker-compose
|
|
20
|
+
run: |
|
|
21
|
+
docker-compose -f ./unit-test.yml build
|
|
22
|
+
docker-compose -f ./unit-test.yml up -d
|
|
23
|
+
- name: wait
|
|
24
|
+
run: docker-compose -f unit-test.yml exec -T mysql bash -c "mysqladmin --wait --count 60 ping -pp@ssw0rd --protocol=tcp || exit 1"
|
|
25
|
+
- name: wait2
|
|
26
|
+
run: |
|
|
27
|
+
while [ "$(curl -I http://localhost:6003/minio/health/live -o /dev/null -w '%{http_code}\n' -s)" != "200" ]
|
|
28
|
+
do
|
|
29
|
+
echo "waiting"
|
|
30
|
+
sleep 2
|
|
31
|
+
done
|
|
32
|
+
- run: npm ci
|
|
33
|
+
- run: npm test
|
|
34
|
+
|
|
35
|
+
publish-gpr:
|
|
36
|
+
needs: build
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
permissions:
|
|
39
|
+
contents: read
|
|
40
|
+
packages: write
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v3
|
|
43
|
+
- uses: actions/setup-node@v3
|
|
44
|
+
with:
|
|
45
|
+
node-version: 16
|
|
46
|
+
registry-url: https://npm.pkg.github.com/
|
|
47
|
+
- run: npm ci
|
|
48
|
+
- run: npm publish
|
|
49
|
+
env:
|
|
50
|
+
NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
package/.prettierrc
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import AwsS3Driver from '../../../src/db/drivers/AwsS3Driver';
|
|
2
|
+
import {
|
|
3
|
+
DbConnection,
|
|
4
|
+
DbDatabase,
|
|
5
|
+
DbS3Bucket,
|
|
6
|
+
DbS3Owner,
|
|
7
|
+
} from '../../../src/db/resource/DbResource';
|
|
8
|
+
import { DBType } from '../../../src/db/resource/types/DBType';
|
|
9
|
+
|
|
10
|
+
const connectOption = {
|
|
11
|
+
port: 6003,
|
|
12
|
+
user: 'testuser', // aws:accessKeyId
|
|
13
|
+
password: 'testpass', // aws:secretAccessKey
|
|
14
|
+
url: 'http://127.0.0.1:6003',
|
|
15
|
+
region: 'us-west-1',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
describe('AwsS3Driver', () => {
|
|
19
|
+
let driver: AwsS3Driver;
|
|
20
|
+
const bucket = 'testbucket';
|
|
21
|
+
|
|
22
|
+
beforeAll(async () => {
|
|
23
|
+
driver = createDriver();
|
|
24
|
+
await driver.asyncConnect();
|
|
25
|
+
try {
|
|
26
|
+
await driver.removeBucket({ bucket });
|
|
27
|
+
} catch (e) {
|
|
28
|
+
// The specified bucket does not exist
|
|
29
|
+
// console.error('error:' + e.message);
|
|
30
|
+
}
|
|
31
|
+
await driver.createBucket({ bucket });
|
|
32
|
+
await driver.putObject({ bucket, key: 'text/abc.txt', body: 'abc' });
|
|
33
|
+
await driver.putObject({
|
|
34
|
+
bucket,
|
|
35
|
+
key: 'text/folder/abc.txt',
|
|
36
|
+
body: 'abc',
|
|
37
|
+
});
|
|
38
|
+
await driver.putObject({ bucket, key: 'text/empty.txt', body: '' });
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
afterAll(async () => {
|
|
42
|
+
await driver.asyncClose();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('getName', () => {
|
|
46
|
+
it('should return constructor name', () => {
|
|
47
|
+
expect(driver.getName()).toBe('AwsS3Driver');
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('asyncGetResouces', () => {
|
|
52
|
+
let testDbRes: DbDatabase;
|
|
53
|
+
let testBucketRes: DbS3Bucket;
|
|
54
|
+
|
|
55
|
+
it('should return Database resource', async () => {
|
|
56
|
+
const dbRootRes = await driver.getResouces({});
|
|
57
|
+
expect(dbRootRes).toHaveLength(1);
|
|
58
|
+
testDbRes = dbRootRes[0] as DbDatabase;
|
|
59
|
+
expect(testDbRes.getName()).toBe('S3');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should have DbS3Bucket resource', async () => {
|
|
63
|
+
testBucketRes = testDbRes.getChildByName(bucket) as DbS3Bucket;
|
|
64
|
+
expect(testBucketRes.getName()).toBe(bucket);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should have DbS3Owner resource', async () => {
|
|
68
|
+
const owner = testDbRes.getChildByName('minio') as DbS3Owner;
|
|
69
|
+
expect(owner.getName()).toBe('minio');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should have values', async () => {
|
|
73
|
+
const textValue = await driver.getValueByKey({
|
|
74
|
+
bucket,
|
|
75
|
+
key: 'text/abc.txt',
|
|
76
|
+
});
|
|
77
|
+
expect(textValue).toBe('abc');
|
|
78
|
+
|
|
79
|
+
const textValue2 = await driver.getValueByKey({
|
|
80
|
+
bucket,
|
|
81
|
+
key: 'text/folder/abc.txt',
|
|
82
|
+
});
|
|
83
|
+
expect(textValue2).toBe('abc');
|
|
84
|
+
|
|
85
|
+
const textValue3 = await driver.getValueByKey({
|
|
86
|
+
bucket,
|
|
87
|
+
key: 'text/empty.txt',
|
|
88
|
+
});
|
|
89
|
+
expect(textValue3).toBe('');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
function createDriver(): AwsS3Driver {
|
|
94
|
+
const con = new DbConnection({ ...connectOption, db_type: DBType.Minio });
|
|
95
|
+
return new AwsS3Driver(con);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import MySQLDriver from '../../../src/db/drivers/MySQLDriver';
|
|
2
|
+
import {
|
|
3
|
+
DbConnection,
|
|
4
|
+
DbResource,
|
|
5
|
+
DbSchema,
|
|
6
|
+
DbTable,
|
|
7
|
+
DbColumn,
|
|
8
|
+
} from '../../../src/db/resource/DbResource';
|
|
9
|
+
import { DBType } from '../../../src/db/resource/types/DBType';
|
|
10
|
+
import * as mysql from 'mysql2/promise';
|
|
11
|
+
import { ResultSetHeader } from 'mysql2/promise';
|
|
12
|
+
import { GeneralColumnType } from '../../../src/db/resource/types/GeneralColumnType';
|
|
13
|
+
|
|
14
|
+
const baseConnectOption = {
|
|
15
|
+
host: '127.0.0.1',
|
|
16
|
+
port: 6001,
|
|
17
|
+
user: 'testuser',
|
|
18
|
+
password: 'testpass',
|
|
19
|
+
database: 'testdb',
|
|
20
|
+
};
|
|
21
|
+
const connectOption = {
|
|
22
|
+
...baseConnectOption,
|
|
23
|
+
db_type: DBType.MySQL,
|
|
24
|
+
enviroment: 'ut',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const CREATE_TABLE_STATEMENT = `
|
|
28
|
+
CREATE TABLE testtable (
|
|
29
|
+
ID int auto_increment PRIMARY KEY,
|
|
30
|
+
n0 BIT,
|
|
31
|
+
n1 TINYINT COMMENT 'MAX 127',
|
|
32
|
+
n2 SMALLINT COMMENT 'MAX 32767',
|
|
33
|
+
n3 MEDIUMINT COMMENT 'MAX 8388607',
|
|
34
|
+
n4 BIGINT COMMENT 'MAX 9223372036854775807',
|
|
35
|
+
f1 DECIMAL(6,4),
|
|
36
|
+
f2 FLOAT,
|
|
37
|
+
f3 DOUBLE,
|
|
38
|
+
|
|
39
|
+
d1 DATE COMMENT '“Zero” Value 0000-00-00',
|
|
40
|
+
d2 TIME COMMENT '“Zero” Value 00:00:00',
|
|
41
|
+
d3 DATETIME COMMENT '“Zero” Value 0000-00-00 00:00:00',
|
|
42
|
+
d4 TIMESTAMP COMMENT '“Zero” Value 0000-00-00 00:00:00',
|
|
43
|
+
d5 YEAR COMMENT '“Zero” Value 0000',
|
|
44
|
+
|
|
45
|
+
s1 CHAR(10),
|
|
46
|
+
s2 VARCHAR(10),
|
|
47
|
+
s3 TEXT,
|
|
48
|
+
s4 ENUM('a','b','c') COMMENT 'A list of a,b or c',
|
|
49
|
+
s5 BINARY(10),
|
|
50
|
+
s6 VARBINARY(10),
|
|
51
|
+
s7 BLOB,
|
|
52
|
+
s8 SET('a','b','c'),
|
|
53
|
+
|
|
54
|
+
g1 geometry NOT NULL,
|
|
55
|
+
|
|
56
|
+
j1 JSON COMMENT 'JSON data type'
|
|
57
|
+
|
|
58
|
+
) COMMENT='table with various data types'
|
|
59
|
+
`;
|
|
60
|
+
|
|
61
|
+
describe('MySQLDriver', () => {
|
|
62
|
+
let driver: MySQLDriver;
|
|
63
|
+
let conRes: DbConnection;
|
|
64
|
+
let con: mysql.Connection;
|
|
65
|
+
|
|
66
|
+
beforeAll(async () => {
|
|
67
|
+
driver = createDriver();
|
|
68
|
+
|
|
69
|
+
con = await mysql.createConnection(baseConnectOption);
|
|
70
|
+
|
|
71
|
+
await con.connect();
|
|
72
|
+
|
|
73
|
+
await con.execute('DROP TABLE IF EXISTS testtable');
|
|
74
|
+
|
|
75
|
+
await con.execute<ResultSetHeader>(CREATE_TABLE_STATEMENT);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
afterAll(async () => {
|
|
79
|
+
await driver.asyncClose();
|
|
80
|
+
con.destroy();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('asyncConnect', async () => {
|
|
84
|
+
expect(await driver.asyncConnect()).toBe('');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('getName', () => {
|
|
88
|
+
it('should return constructor name', () => {
|
|
89
|
+
expect(driver.getName()).toBe('MySQLDriver');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('asyncGetResouces', () => {
|
|
94
|
+
let testDbRes: DbResource;
|
|
95
|
+
let testSchemaRes: DbSchema;
|
|
96
|
+
let testTableRes: DbTable;
|
|
97
|
+
|
|
98
|
+
it('should return Database resource', async () => {
|
|
99
|
+
const dbRootRes = await driver.getResouces({});
|
|
100
|
+
expect(dbRootRes).toHaveLength(1);
|
|
101
|
+
testDbRes = dbRootRes[0];
|
|
102
|
+
expect(testDbRes.getName()).toBe(driver.getConnectionRes().database);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should have Schema resource', async () => {
|
|
106
|
+
expect(testDbRes.getChildren()).toHaveLength(1);
|
|
107
|
+
testSchemaRes = testDbRes.getChildren()[0];
|
|
108
|
+
expect(testSchemaRes.getName()).toBe('testdb');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should have Table resource', async () => {
|
|
112
|
+
testTableRes = testSchemaRes.getChildByName('testtable') as DbTable;
|
|
113
|
+
expect(testTableRes.getName()).toBe('testtable');
|
|
114
|
+
expect(testTableRes.table_type).toBe('TABLE');
|
|
115
|
+
expect(testTableRes.comment).toBe('table with various data types');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should have Column resource', async () => {
|
|
119
|
+
// ID
|
|
120
|
+
const idRes = testTableRes.getChildByName('ID') as DbColumn;
|
|
121
|
+
expect(idRes.col_type).toBe(GeneralColumnType.INTEGER);
|
|
122
|
+
expect(idRes.nullable).toBe(false);
|
|
123
|
+
expect(idRes.key).toBe('PRI');
|
|
124
|
+
expect(idRes.extra).toBe('auto_increment');
|
|
125
|
+
// n0
|
|
126
|
+
const n0Res = testTableRes.getChildByName('n0') as DbColumn;
|
|
127
|
+
expect(n0Res.col_type).toBe(GeneralColumnType.BIT);
|
|
128
|
+
expect(n0Res.nullable).toBe(true);
|
|
129
|
+
// n1
|
|
130
|
+
const n1Res = testTableRes.getChildByName('n1') as DbColumn;
|
|
131
|
+
expect(n1Res.col_type).toBe(GeneralColumnType.TINYINT);
|
|
132
|
+
expect(n1Res.nullable).toBe(true);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
function createDriver(): MySQLDriver {
|
|
137
|
+
conRes = new DbConnection(connectOption);
|
|
138
|
+
return new MySQLDriver(conRes);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import PostgresDriver from '../../../src/db/drivers/PostgresDriver';
|
|
2
|
+
import {
|
|
3
|
+
DbConnection,
|
|
4
|
+
DbResource,
|
|
5
|
+
DbSchema,
|
|
6
|
+
DbTable,
|
|
7
|
+
DbColumn,
|
|
8
|
+
} from '../../../src/db/resource/DbResource';
|
|
9
|
+
import { DBType } from '../../../src/db/resource/types/DBType';
|
|
10
|
+
import { default as pg } from 'pg';
|
|
11
|
+
import { GeneralColumnType } from '../../../src/db/resource/types/GeneralColumnType';
|
|
12
|
+
|
|
13
|
+
const baseConnectOption = {
|
|
14
|
+
host: '127.0.0.1',
|
|
15
|
+
port: 6002,
|
|
16
|
+
user: 'testuser',
|
|
17
|
+
password: 'testpass',
|
|
18
|
+
database: 'testdb',
|
|
19
|
+
};
|
|
20
|
+
const connectOption = {
|
|
21
|
+
...baseConnectOption,
|
|
22
|
+
db_type: DBType.Postgres,
|
|
23
|
+
enviroment: 'ut',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const CREATE_TABLE_STATEMENT = `
|
|
27
|
+
CREATE TABLE testtable (
|
|
28
|
+
ID SERIAL NOT NULL PRIMARY KEY,
|
|
29
|
+
n0 BIT,
|
|
30
|
+
n1 INT,
|
|
31
|
+
n2 BIGINT,
|
|
32
|
+
n3 SMALLSERIAL,
|
|
33
|
+
n4 BIGSERIAL,
|
|
34
|
+
f1 NUMERIC(6,4),
|
|
35
|
+
f2 DOUBLE PRECISION,
|
|
36
|
+
f3 REAL,
|
|
37
|
+
|
|
38
|
+
d1 DATE,
|
|
39
|
+
d2 TIME,
|
|
40
|
+
d3 TIMESTAMP,
|
|
41
|
+
d4 TIMESTAMP WITH TIME ZONE,
|
|
42
|
+
d5 INTERVAL YEAR,
|
|
43
|
+
|
|
44
|
+
s1 CHAR(10),
|
|
45
|
+
s2 VARCHAR(10),
|
|
46
|
+
s3 TEXT,
|
|
47
|
+
s4 mood,
|
|
48
|
+
s5 BYTEA,
|
|
49
|
+
s6 uuid,
|
|
50
|
+
|
|
51
|
+
j1 JSON
|
|
52
|
+
|
|
53
|
+
)`;
|
|
54
|
+
|
|
55
|
+
describe('PostgresDriver', () => {
|
|
56
|
+
let driver: PostgresDriver;
|
|
57
|
+
let conRes: DbConnection;
|
|
58
|
+
let client: pg.Client;
|
|
59
|
+
|
|
60
|
+
beforeAll(async () => {
|
|
61
|
+
driver = createDriver();
|
|
62
|
+
|
|
63
|
+
client = new pg.Client(baseConnectOption);
|
|
64
|
+
|
|
65
|
+
await client.connect();
|
|
66
|
+
|
|
67
|
+
await client.query('DROP TABLE IF EXISTS testtable');
|
|
68
|
+
await client.query('DROP TYPE IF EXISTS mood');
|
|
69
|
+
await client.query("CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy')");
|
|
70
|
+
|
|
71
|
+
await client.query(CREATE_TABLE_STATEMENT);
|
|
72
|
+
|
|
73
|
+
await client.query(
|
|
74
|
+
"COMMENT ON TABLE testtable IS 'table with various data types'",
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
afterAll(async () => {
|
|
79
|
+
await driver.asyncClose();
|
|
80
|
+
client.end();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('asyncConnect', async () => {
|
|
84
|
+
expect(await driver.asyncConnect()).toBe('');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('getName', () => {
|
|
88
|
+
it('should return constructor name', () => {
|
|
89
|
+
expect(driver.getName()).toBe('PostgresDriver');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('asyncGetResouces', () => {
|
|
94
|
+
let testDbRes: DbResource;
|
|
95
|
+
let testSchemaRes: DbSchema;
|
|
96
|
+
let testTableRes: DbTable;
|
|
97
|
+
|
|
98
|
+
it('should return Database resource', async () => {
|
|
99
|
+
const dbRootRes = await driver.getResouces({});
|
|
100
|
+
testDbRes = dbRootRes.find((it) => it.name === 'testdb');
|
|
101
|
+
expect(testDbRes.getName()).toBe(driver.getConnectionRes().database);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should have Schema resource', async () => {
|
|
105
|
+
expect(testDbRes.getChildren()).toHaveLength(1);
|
|
106
|
+
testSchemaRes = testDbRes.getChildren()[0];
|
|
107
|
+
expect(testSchemaRes.getName()).toBe('public');
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should have Table resource', async () => {
|
|
111
|
+
testTableRes = testSchemaRes.getChildByName('testtable') as DbTable;
|
|
112
|
+
expect(testTableRes.getName()).toBe('testtable');
|
|
113
|
+
expect(testTableRes.table_type).toBe('TABLE');
|
|
114
|
+
expect(testTableRes.comment).toBe('table with various data types');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should have Column resource', async () => {
|
|
118
|
+
// ID
|
|
119
|
+
const idRes = testTableRes.getChildByName('ID') as DbColumn;
|
|
120
|
+
expect(idRes.col_type).toBe(GeneralColumnType.INTEGER);
|
|
121
|
+
expect(idRes.nullable).toBe(false);
|
|
122
|
+
expect(idRes.key).toBe('PRI');
|
|
123
|
+
expect(idRes.default).toContain('nextval');
|
|
124
|
+
// n0
|
|
125
|
+
const n0Res = testTableRes.getChildByName('n0') as DbColumn;
|
|
126
|
+
expect(n0Res.col_type).toBe(GeneralColumnType.BIT);
|
|
127
|
+
expect(n0Res.nullable).toBe(true);
|
|
128
|
+
// n1
|
|
129
|
+
const n1Res = testTableRes.getChildByName('n1') as DbColumn;
|
|
130
|
+
expect(n1Res.col_type).toBe(GeneralColumnType.INTEGER);
|
|
131
|
+
expect(n1Res.nullable).toBe(true);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
function createDriver(): PostgresDriver {
|
|
136
|
+
conRes = new DbConnection(connectOption);
|
|
137
|
+
return new PostgresDriver(conRes);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import RedisDriver from '../../../src/db/drivers/RedisDriver';
|
|
2
|
+
import { Redis, RedisOptions } from 'ioredis';
|
|
3
|
+
import {
|
|
4
|
+
DbConnection,
|
|
5
|
+
DbKey,
|
|
6
|
+
RedisDatabase,
|
|
7
|
+
} from '../../../src/db/resource/DbResource';
|
|
8
|
+
import {
|
|
9
|
+
RedisCommandType,
|
|
10
|
+
RedisRequest,
|
|
11
|
+
} from '../../../src/service/request/redis_request';
|
|
12
|
+
import { RedisKeyType } from '../../../src/db/resource/types/RedisKeyType';
|
|
13
|
+
|
|
14
|
+
const connectOption: RedisOptions = {
|
|
15
|
+
port: 6000, // Redis port
|
|
16
|
+
host: '127.0.0.1', // Redis host
|
|
17
|
+
password: 'testpass',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
describe('RedisDriver', () => {
|
|
21
|
+
let redisClient: Redis;
|
|
22
|
+
let driver: RedisDriver;
|
|
23
|
+
|
|
24
|
+
beforeAll(async () => {
|
|
25
|
+
redisClient = new Redis({ ...connectOption, db: 0 });
|
|
26
|
+
driver = createDriver();
|
|
27
|
+
await redisClient.flushall();
|
|
28
|
+
await redisClient.set('s1', 'text', 'EX', 100);
|
|
29
|
+
const user = {
|
|
30
|
+
name: 'Bob',
|
|
31
|
+
// The value of a Redis key can not be a number.
|
|
32
|
+
// We can write `age: 20` here but ioredis will convert it to a string anyway.
|
|
33
|
+
age: '20',
|
|
34
|
+
description: 'I am a programmer',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
await redisClient.mset(user);
|
|
38
|
+
await redisClient.hmset('user-hash', user);
|
|
39
|
+
await redisClient.zadd(
|
|
40
|
+
'sortedSet',
|
|
41
|
+
1,
|
|
42
|
+
'one',
|
|
43
|
+
2,
|
|
44
|
+
'dos',
|
|
45
|
+
4,
|
|
46
|
+
'quatro',
|
|
47
|
+
3,
|
|
48
|
+
'three',
|
|
49
|
+
);
|
|
50
|
+
await redisClient.set('n1', 1);
|
|
51
|
+
await redisClient.rpush('list3', '1', '2', '3');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
afterAll(async () => {
|
|
55
|
+
await redisClient.quit();
|
|
56
|
+
await driver.asyncClose();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('asyncConnect', async () => {
|
|
60
|
+
expect(await driver.asyncConnect()).toBe('');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('getName', () => {
|
|
64
|
+
it('should return constructor name', () => {
|
|
65
|
+
expect(driver.getName()).toBe('RedisDriver');
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe('asyncGetResouces', () => {
|
|
70
|
+
let testRedisDb0Res: RedisDatabase;
|
|
71
|
+
|
|
72
|
+
it('should return Database resource', async () => {
|
|
73
|
+
const dbRootRes = await driver.getResouces({});
|
|
74
|
+
expect(dbRootRes).toHaveLength(16);
|
|
75
|
+
testRedisDb0Res = dbRootRes[0] as RedisDatabase;
|
|
76
|
+
expect(testRedisDb0Res.getName()).toBe(
|
|
77
|
+
driver.getConnectionRes().database,
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('asyncScan', () => {
|
|
83
|
+
it('should return values', async () => {
|
|
84
|
+
const keys = await driver.scan(
|
|
85
|
+
0,
|
|
86
|
+
'*',
|
|
87
|
+
1000,
|
|
88
|
+
true,
|
|
89
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
90
|
+
() => {},
|
|
91
|
+
);
|
|
92
|
+
let key = keys.find((it) => it.name === 'n1');
|
|
93
|
+
expect(key.val).toBe('1');
|
|
94
|
+
expect(key.type).toBe(RedisKeyType.string);
|
|
95
|
+
expect(key.ttl).toBe(-1);
|
|
96
|
+
|
|
97
|
+
key = keys.find((it) => it.name === 'age');
|
|
98
|
+
expect(key.val).toBe('20');
|
|
99
|
+
expect(key.type).toBe(RedisKeyType.string);
|
|
100
|
+
|
|
101
|
+
key = keys.find((it) => it.name === 'name');
|
|
102
|
+
expect(key.val).toBe('Bob');
|
|
103
|
+
expect(key.type).toBe(RedisKeyType.string);
|
|
104
|
+
|
|
105
|
+
key = keys.find((it) => it.name === 'description');
|
|
106
|
+
expect(key.val).toBe('I am a programmer');
|
|
107
|
+
expect(key.type).toBe(RedisKeyType.string);
|
|
108
|
+
|
|
109
|
+
key = keys.find((it) => it.name === 'user-hash');
|
|
110
|
+
expect(key.val).toEqual({
|
|
111
|
+
name: 'Bob',
|
|
112
|
+
age: '20',
|
|
113
|
+
description: 'I am a programmer',
|
|
114
|
+
});
|
|
115
|
+
expect(key.type).toBe(RedisKeyType.hash);
|
|
116
|
+
|
|
117
|
+
key = keys.find((it) => it.name === 'list3');
|
|
118
|
+
expect(key.val).toEqual(['1', '2', '3']);
|
|
119
|
+
expect(key.type).toBe(RedisKeyType.list);
|
|
120
|
+
|
|
121
|
+
key = keys.find((it) => it.name === 'sortedSet');
|
|
122
|
+
expect(key.val).toEqual(['one', 'dos', 'three', 'quatro']);
|
|
123
|
+
expect(key.type).toBe(RedisKeyType.zset);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('RedisCommandType.GetValue', () => {
|
|
128
|
+
it('should return values', async () => {
|
|
129
|
+
const key = (await driver.executeCommand(
|
|
130
|
+
createRedisRequest(RedisCommandType.GetValue, {
|
|
131
|
+
key: 'n1',
|
|
132
|
+
type: RedisKeyType.string,
|
|
133
|
+
}),
|
|
134
|
+
)) as DbKey;
|
|
135
|
+
expect(key.val).toBe('1');
|
|
136
|
+
expect(key.ttl).toBe(-1);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
function createDriver(): RedisDriver {
|
|
141
|
+
const con = new DbConnection({ ...connectOption, database: '0' });
|
|
142
|
+
return new RedisDriver(con);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function createRedisRequest(
|
|
146
|
+
command: RedisCommandType,
|
|
147
|
+
options: { key?: string; type?: RedisKeyType },
|
|
148
|
+
): RedisRequest {
|
|
149
|
+
return {
|
|
150
|
+
connection_id: '1',
|
|
151
|
+
index: 0,
|
|
152
|
+
command,
|
|
153
|
+
...options,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
});
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
testEnvironment: 'node',
|
|
3
|
+
preset: 'ts-jest/presets/default-esm',
|
|
4
|
+
transform: {
|
|
5
|
+
'^.+\\.m?[tj]s?$': ['ts-jest', { useESM: true }],
|
|
6
|
+
},
|
|
7
|
+
moduleNameMapper: {
|
|
8
|
+
'^(\\.{1,2}/.*)\\.(m)?js$': '$1',
|
|
9
|
+
},
|
|
10
|
+
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(m)?ts$',
|
|
11
|
+
coverageDirectory: 'coverage',
|
|
12
|
+
collectCoverageFrom: [
|
|
13
|
+
'src/**/*.ts',
|
|
14
|
+
'src/**/*.mts',
|
|
15
|
+
'!src/**/*.d.ts',
|
|
16
|
+
'!src/**/*.d.mts',
|
|
17
|
+
],
|
|
18
|
+
};
|