@onivoro/server-aws-redshift 22.0.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/dist/cjs/index.d.ts +3 -0
- package/dist/cjs/index.js +19 -0
- package/dist/cjs/lib/classes/server-aws-redshift-config.class.d.ts +7 -0
- package/dist/cjs/lib/classes/server-aws-redshift-config.class.js +11 -0
- package/dist/cjs/lib/server-aws-redshift.module.d.ts +4 -0
- package/dist/cjs/lib/server-aws-redshift.module.js +63 -0
- package/dist/cjs/lib/services/redshift.service.d.ts +34 -0
- package/dist/cjs/lib/services/redshift.service.js +188 -0
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +19 -0
- package/dist/esm/lib/classes/server-aws-redshift-config.class.d.ts +7 -0
- package/dist/esm/lib/classes/server-aws-redshift-config.class.js +11 -0
- package/dist/esm/lib/server-aws-redshift.module.d.ts +4 -0
- package/dist/esm/lib/server-aws-redshift.module.js +63 -0
- package/dist/esm/lib/services/redshift.service.d.ts +34 -0
- package/dist/esm/lib/services/redshift.service.js +188 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/lib/classes/server-aws-redshift-config.class.d.ts +7 -0
- package/dist/types/lib/server-aws-redshift.module.d.ts +4 -0
- package/dist/types/lib/services/redshift.service.d.ts +34 -0
- package/package.json +49 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./lib/classes/server-aws-redshift-config.class"), exports);
|
|
18
|
+
__exportStar(require("./lib/services/redshift.service"), exports);
|
|
19
|
+
__exportStar(require("./lib/server-aws-redshift.module"), exports);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ServerAwsRedshiftDataConfig = void 0;
|
|
4
|
+
class ServerAwsRedshiftDataConfig {
|
|
5
|
+
AWS_ACCESS_KEY_ID;
|
|
6
|
+
AWS_KINESIS_NAME;
|
|
7
|
+
AWS_REGION;
|
|
8
|
+
AWS_SECRET_ACCESS_KEY;
|
|
9
|
+
NODE_ENV;
|
|
10
|
+
}
|
|
11
|
+
exports.ServerAwsRedshiftDataConfig = ServerAwsRedshiftDataConfig;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var ServerAwsRedshiftDataModule_1;
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.ServerAwsRedshiftDataModule = void 0;
|
|
11
|
+
const common_1 = require("@nestjs/common");
|
|
12
|
+
const server_common_1 = require("@onivoro/server-common");
|
|
13
|
+
const client_redshift_data_1 = require("@aws-sdk/client-redshift-data");
|
|
14
|
+
const redshift_service_1 = require("./services/redshift.service");
|
|
15
|
+
const server_aws_redshift_config_class_1 = require("./classes/server-aws-redshift-config.class");
|
|
16
|
+
const client_redshift_serverless_1 = require("@aws-sdk/client-redshift-serverless");
|
|
17
|
+
let redshiftClient = null;
|
|
18
|
+
let redshiftServerlessClient = null;
|
|
19
|
+
let ServerAwsRedshiftDataModule = ServerAwsRedshiftDataModule_1 = class ServerAwsRedshiftDataModule {
|
|
20
|
+
static configure(config) {
|
|
21
|
+
return (0, server_common_1.moduleFactory)({
|
|
22
|
+
module: ServerAwsRedshiftDataModule_1,
|
|
23
|
+
providers: [
|
|
24
|
+
{
|
|
25
|
+
provide: client_redshift_data_1.RedshiftDataClient,
|
|
26
|
+
useFactory: () => redshiftClient
|
|
27
|
+
? redshiftClient
|
|
28
|
+
: redshiftClient = new client_redshift_data_1.RedshiftDataClient({
|
|
29
|
+
region: config.AWS_REGION,
|
|
30
|
+
logger: console,
|
|
31
|
+
credentials: config.NODE_ENV === 'production'
|
|
32
|
+
? undefined
|
|
33
|
+
: {
|
|
34
|
+
accessKeyId: config.AWS_ACCESS_KEY_ID,
|
|
35
|
+
secretAccessKey: config.AWS_SECRET_ACCESS_KEY
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
provide: client_redshift_serverless_1.RedshiftServerlessClient,
|
|
41
|
+
useFactory: () => redshiftClient
|
|
42
|
+
? redshiftServerlessClient
|
|
43
|
+
: redshiftServerlessClient = new client_redshift_serverless_1.RedshiftServerlessClient({
|
|
44
|
+
region: config.AWS_REGION,
|
|
45
|
+
logger: console,
|
|
46
|
+
credentials: config.NODE_ENV === 'production'
|
|
47
|
+
? undefined
|
|
48
|
+
: {
|
|
49
|
+
accessKeyId: config.AWS_ACCESS_KEY_ID,
|
|
50
|
+
secretAccessKey: config.AWS_SECRET_ACCESS_KEY
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
},
|
|
54
|
+
{ provide: server_aws_redshift_config_class_1.ServerAwsRedshiftDataConfig, useValue: config },
|
|
55
|
+
redshift_service_1.RedshiftDataService
|
|
56
|
+
]
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
exports.ServerAwsRedshiftDataModule = ServerAwsRedshiftDataModule;
|
|
61
|
+
exports.ServerAwsRedshiftDataModule = ServerAwsRedshiftDataModule = ServerAwsRedshiftDataModule_1 = __decorate([
|
|
62
|
+
(0, common_1.Module)({})
|
|
63
|
+
], ServerAwsRedshiftDataModule);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { RedshiftDataClient } from "@aws-sdk/client-redshift-data";
|
|
2
|
+
import { ServerAwsRedshiftDataConfig } from '../classes/server-aws-redshift-config.class';
|
|
3
|
+
import { RedshiftServerlessClient } from '@aws-sdk/client-redshift-serverless';
|
|
4
|
+
export declare class RedshiftDataService {
|
|
5
|
+
private redshiftDataClient;
|
|
6
|
+
private redshiftServerlessClient;
|
|
7
|
+
private config;
|
|
8
|
+
constructor(redshiftDataClient: RedshiftDataClient, redshiftServerlessClient: RedshiftServerlessClient, config: ServerAwsRedshiftDataConfig);
|
|
9
|
+
waitForStatement(statementId: string): Promise<void>;
|
|
10
|
+
verifyEndpointAccess(workgroupName: string): Promise<import("@aws-sdk/client-redshift-serverless").GetWorkgroupCommandOutput>;
|
|
11
|
+
addIamUserToDatabaseGroup(params: {
|
|
12
|
+
Database: string;
|
|
13
|
+
WorkgroupName: string;
|
|
14
|
+
User: string;
|
|
15
|
+
Group: string;
|
|
16
|
+
}): Promise<void>;
|
|
17
|
+
createDatabaseUser(params: {
|
|
18
|
+
Database: string;
|
|
19
|
+
WorkgroupName: string;
|
|
20
|
+
User: string;
|
|
21
|
+
}): Promise<boolean>;
|
|
22
|
+
grantUsageOnSchema(params: {
|
|
23
|
+
GroupName: string;
|
|
24
|
+
Database: string;
|
|
25
|
+
WorkgroupName: string;
|
|
26
|
+
Schema: string;
|
|
27
|
+
Group: string;
|
|
28
|
+
}): Promise<void>;
|
|
29
|
+
createDbGroupFromIamGroupIfNotExists(params: {
|
|
30
|
+
iamGroup: string;
|
|
31
|
+
Database: string;
|
|
32
|
+
WorkgroupName: string;
|
|
33
|
+
}): Promise<any>;
|
|
34
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.RedshiftDataService = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const client_redshift_data_1 = require("@aws-sdk/client-redshift-data");
|
|
15
|
+
const server_aws_redshift_config_class_1 = require("../classes/server-aws-redshift-config.class");
|
|
16
|
+
const client_redshift_serverless_1 = require("@aws-sdk/client-redshift-serverless");
|
|
17
|
+
const crypto_1 = require("crypto");
|
|
18
|
+
let RedshiftDataService = class RedshiftDataService {
|
|
19
|
+
redshiftDataClient;
|
|
20
|
+
redshiftServerlessClient;
|
|
21
|
+
config;
|
|
22
|
+
constructor(redshiftDataClient, redshiftServerlessClient, config) {
|
|
23
|
+
this.redshiftDataClient = redshiftDataClient;
|
|
24
|
+
this.redshiftServerlessClient = redshiftServerlessClient;
|
|
25
|
+
this.config = config;
|
|
26
|
+
}
|
|
27
|
+
async waitForStatement(statementId) {
|
|
28
|
+
const maxAttempts = 10;
|
|
29
|
+
let attempts = 0;
|
|
30
|
+
while (attempts < maxAttempts) {
|
|
31
|
+
const describeStatementCommand = new client_redshift_data_1.DescribeStatementCommand({
|
|
32
|
+
Id: statementId
|
|
33
|
+
});
|
|
34
|
+
const status = await this.redshiftDataClient.send(describeStatementCommand);
|
|
35
|
+
if (status.Status === 'FINISHED') {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (status.Status === 'FAILED') {
|
|
39
|
+
throw new Error(`SQL statement failed: ${status.Error}`);
|
|
40
|
+
}
|
|
41
|
+
attempts++;
|
|
42
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
43
|
+
}
|
|
44
|
+
throw new Error('Timeout waiting for SQL statement to complete');
|
|
45
|
+
}
|
|
46
|
+
async verifyEndpointAccess(workgroupName) {
|
|
47
|
+
const workgroupResponse = await this.redshiftServerlessClient.send(new client_redshift_serverless_1.GetWorkgroupCommand({ workgroupName }));
|
|
48
|
+
if (!workgroupResponse?.workgroup?.endpoint?.address) {
|
|
49
|
+
console.error(`Could not retrieve endpoint for workgroup "${workgroupName}"`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
return workgroupResponse;
|
|
53
|
+
}
|
|
54
|
+
async addIamUserToDatabaseGroup(params) {
|
|
55
|
+
const { Database, WorkgroupName, User, Group } = params;
|
|
56
|
+
try {
|
|
57
|
+
const groupResult = await this.redshiftDataClient.send(new client_redshift_data_1.ExecuteStatementCommand({
|
|
58
|
+
Database,
|
|
59
|
+
Sql: `ALTER GROUP ${Group} ADD USER "${User}";`,
|
|
60
|
+
WorkgroupName
|
|
61
|
+
}));
|
|
62
|
+
await this.waitForStatement(groupResult.Id);
|
|
63
|
+
console.log(`Added user "${User}" to group "${Group}`);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.warn({ detail: `Warning adding ${User} to group:`, error });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async createDatabaseUser(params) {
|
|
70
|
+
const { Database, WorkgroupName, User } = params;
|
|
71
|
+
try {
|
|
72
|
+
const checkResult = await this.redshiftDataClient.send(new client_redshift_data_1.ExecuteStatementCommand({
|
|
73
|
+
Database,
|
|
74
|
+
Sql: `SELECT u.usename as username FROM pg_user u;`,
|
|
75
|
+
WorkgroupName
|
|
76
|
+
}));
|
|
77
|
+
await this.waitForStatement(checkResult.Id);
|
|
78
|
+
const describeCommand = new client_redshift_data_1.DescribeStatementCommand({ Id: checkResult.Id });
|
|
79
|
+
const checkStatus = await this.redshiftDataClient.send(describeCommand);
|
|
80
|
+
if (!checkStatus.ResultRows) {
|
|
81
|
+
const placeholderPassword = `IAM_${(0, crypto_1.randomUUID)().replace(/-/g, '_')}`;
|
|
82
|
+
const createUserCommand = new client_redshift_data_1.ExecuteStatementCommand({
|
|
83
|
+
Database,
|
|
84
|
+
Sql: `CREATE USER ${User} PASSWORD '${placeholderPassword}';`,
|
|
85
|
+
WorkgroupName
|
|
86
|
+
});
|
|
87
|
+
const createResult = await this.redshiftDataClient.send(createUserCommand);
|
|
88
|
+
await this.waitForStatement(createResult.Id);
|
|
89
|
+
console.log(`Created user: ${User}`);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
console.log(`User "${User} already exists`);
|
|
93
|
+
}
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
console.warn({ detail: `Warning creating user ${User}`, error });
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async grantUsageOnSchema(params) {
|
|
102
|
+
const { GroupName, Database, WorkgroupName, Schema, Group } = params;
|
|
103
|
+
try {
|
|
104
|
+
const checkResult = await this.redshiftDataClient.send(new client_redshift_data_1.ExecuteStatementCommand({
|
|
105
|
+
Database,
|
|
106
|
+
Sql: `SELECT 1 FROM pg_group WHERE groname = '${Group}';`,
|
|
107
|
+
WorkgroupName
|
|
108
|
+
}));
|
|
109
|
+
await this.waitForStatement(checkResult.Id);
|
|
110
|
+
const describeCommand = new client_redshift_data_1.DescribeStatementCommand({ Id: checkResult.Id });
|
|
111
|
+
const checkStatus = await this.redshiftDataClient.send(describeCommand);
|
|
112
|
+
if (!checkStatus.ResultRows) {
|
|
113
|
+
const createGroupCommand = new client_redshift_data_1.ExecuteStatementCommand({
|
|
114
|
+
Database,
|
|
115
|
+
Sql: 'CREATE GROUP ${Group};',
|
|
116
|
+
WorkgroupName
|
|
117
|
+
});
|
|
118
|
+
const createResult = await this.redshiftDataClient.send(createGroupCommand);
|
|
119
|
+
await this.waitForStatement(createResult.Id);
|
|
120
|
+
console.log('Created "${Group}" group');
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
console.log('"${Group}" group already exists');
|
|
124
|
+
}
|
|
125
|
+
const setupStatements = [
|
|
126
|
+
`GRANT USAGE ON SCHEMA ${Schema} TO GROUP ${Group};`,
|
|
127
|
+
`GRANT SELECT ON ALL TABLES IN SCHEMA ${Schema} TO GROUP ${Group};`,
|
|
128
|
+
`ALTER DEFAULT PRIVILEGES IN SCHEMA ${Schema} GRANT SELECT ON TABLES TO GROUP ${Group};`,
|
|
129
|
+
];
|
|
130
|
+
for (const Sql of setupStatements) {
|
|
131
|
+
const executeStatementCommand = new client_redshift_data_1.ExecuteStatementCommand({
|
|
132
|
+
Database,
|
|
133
|
+
Sql,
|
|
134
|
+
WorkgroupName
|
|
135
|
+
});
|
|
136
|
+
try {
|
|
137
|
+
const result = await this.redshiftDataClient.send(executeStatementCommand);
|
|
138
|
+
await this.waitForStatement(result.Id);
|
|
139
|
+
console.log(`Executed SQL: ${Sql}`);
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
console.warn(`Warning executing SQL: ${Sql}`, error.message);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
console.error({ detail: 'Error in grantUsageOnSchema:', error });
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async createDbGroupFromIamGroupIfNotExists(params) {
|
|
152
|
+
const { iamGroup, Database, WorkgroupName } = params;
|
|
153
|
+
try {
|
|
154
|
+
const checkResult = await this.redshiftDataClient.send(new client_redshift_data_1.ExecuteStatementCommand({
|
|
155
|
+
Database,
|
|
156
|
+
Sql: `SELECT 1 FROM pg_group WHERE groname = '${iamGroup}';`,
|
|
157
|
+
WorkgroupName
|
|
158
|
+
}));
|
|
159
|
+
await this.waitForStatement(checkResult.Id);
|
|
160
|
+
const describeCommand = new client_redshift_data_1.DescribeStatementCommand({ Id: checkResult.Id });
|
|
161
|
+
const checkStatus = await this.redshiftDataClient.send(describeCommand);
|
|
162
|
+
if (!checkStatus.ResultRows) {
|
|
163
|
+
const createGroupCommand = new client_redshift_data_1.ExecuteStatementCommand({
|
|
164
|
+
Database,
|
|
165
|
+
Sql: `CREATE GROUP ${iamGroup};`,
|
|
166
|
+
WorkgroupName
|
|
167
|
+
});
|
|
168
|
+
const createResult = await this.redshiftDataClient.send(createGroupCommand);
|
|
169
|
+
await this.waitForStatement(createResult.Id);
|
|
170
|
+
console.log(`Created "${iamGroup}" group`);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
console.log(`"${iamGroup}" group already exists`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
console.error({ error });
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
exports.RedshiftDataService = RedshiftDataService;
|
|
183
|
+
exports.RedshiftDataService = RedshiftDataService = __decorate([
|
|
184
|
+
(0, common_1.Injectable)(),
|
|
185
|
+
__metadata("design:paramtypes", [client_redshift_data_1.RedshiftDataClient,
|
|
186
|
+
client_redshift_serverless_1.RedshiftServerlessClient,
|
|
187
|
+
server_aws_redshift_config_class_1.ServerAwsRedshiftDataConfig])
|
|
188
|
+
], RedshiftDataService);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./lib/classes/server-aws-redshift-config.class"), exports);
|
|
18
|
+
__exportStar(require("./lib/services/redshift.service"), exports);
|
|
19
|
+
__exportStar(require("./lib/server-aws-redshift.module"), exports);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ServerAwsRedshiftDataConfig = void 0;
|
|
4
|
+
class ServerAwsRedshiftDataConfig {
|
|
5
|
+
AWS_ACCESS_KEY_ID;
|
|
6
|
+
AWS_KINESIS_NAME;
|
|
7
|
+
AWS_REGION;
|
|
8
|
+
AWS_SECRET_ACCESS_KEY;
|
|
9
|
+
NODE_ENV;
|
|
10
|
+
}
|
|
11
|
+
exports.ServerAwsRedshiftDataConfig = ServerAwsRedshiftDataConfig;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var ServerAwsRedshiftDataModule_1;
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.ServerAwsRedshiftDataModule = void 0;
|
|
11
|
+
const common_1 = require("@nestjs/common");
|
|
12
|
+
const server_common_1 = require("@onivoro/server-common");
|
|
13
|
+
const client_redshift_data_1 = require("@aws-sdk/client-redshift-data");
|
|
14
|
+
const redshift_service_1 = require("./services/redshift.service");
|
|
15
|
+
const server_aws_redshift_config_class_1 = require("./classes/server-aws-redshift-config.class");
|
|
16
|
+
const client_redshift_serverless_1 = require("@aws-sdk/client-redshift-serverless");
|
|
17
|
+
let redshiftClient = null;
|
|
18
|
+
let redshiftServerlessClient = null;
|
|
19
|
+
let ServerAwsRedshiftDataModule = ServerAwsRedshiftDataModule_1 = class ServerAwsRedshiftDataModule {
|
|
20
|
+
static configure(config) {
|
|
21
|
+
return (0, server_common_1.moduleFactory)({
|
|
22
|
+
module: ServerAwsRedshiftDataModule_1,
|
|
23
|
+
providers: [
|
|
24
|
+
{
|
|
25
|
+
provide: client_redshift_data_1.RedshiftDataClient,
|
|
26
|
+
useFactory: () => redshiftClient
|
|
27
|
+
? redshiftClient
|
|
28
|
+
: redshiftClient = new client_redshift_data_1.RedshiftDataClient({
|
|
29
|
+
region: config.AWS_REGION,
|
|
30
|
+
logger: console,
|
|
31
|
+
credentials: config.NODE_ENV === 'production'
|
|
32
|
+
? undefined
|
|
33
|
+
: {
|
|
34
|
+
accessKeyId: config.AWS_ACCESS_KEY_ID,
|
|
35
|
+
secretAccessKey: config.AWS_SECRET_ACCESS_KEY
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
provide: client_redshift_serverless_1.RedshiftServerlessClient,
|
|
41
|
+
useFactory: () => redshiftClient
|
|
42
|
+
? redshiftServerlessClient
|
|
43
|
+
: redshiftServerlessClient = new client_redshift_serverless_1.RedshiftServerlessClient({
|
|
44
|
+
region: config.AWS_REGION,
|
|
45
|
+
logger: console,
|
|
46
|
+
credentials: config.NODE_ENV === 'production'
|
|
47
|
+
? undefined
|
|
48
|
+
: {
|
|
49
|
+
accessKeyId: config.AWS_ACCESS_KEY_ID,
|
|
50
|
+
secretAccessKey: config.AWS_SECRET_ACCESS_KEY
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
},
|
|
54
|
+
{ provide: server_aws_redshift_config_class_1.ServerAwsRedshiftDataConfig, useValue: config },
|
|
55
|
+
redshift_service_1.RedshiftDataService
|
|
56
|
+
]
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
exports.ServerAwsRedshiftDataModule = ServerAwsRedshiftDataModule;
|
|
61
|
+
exports.ServerAwsRedshiftDataModule = ServerAwsRedshiftDataModule = ServerAwsRedshiftDataModule_1 = __decorate([
|
|
62
|
+
(0, common_1.Module)({})
|
|
63
|
+
], ServerAwsRedshiftDataModule);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { RedshiftDataClient } from "@aws-sdk/client-redshift-data";
|
|
2
|
+
import { ServerAwsRedshiftDataConfig } from '../classes/server-aws-redshift-config.class';
|
|
3
|
+
import { RedshiftServerlessClient } from '@aws-sdk/client-redshift-serverless';
|
|
4
|
+
export declare class RedshiftDataService {
|
|
5
|
+
private redshiftDataClient;
|
|
6
|
+
private redshiftServerlessClient;
|
|
7
|
+
private config;
|
|
8
|
+
constructor(redshiftDataClient: RedshiftDataClient, redshiftServerlessClient: RedshiftServerlessClient, config: ServerAwsRedshiftDataConfig);
|
|
9
|
+
waitForStatement(statementId: string): Promise<void>;
|
|
10
|
+
verifyEndpointAccess(workgroupName: string): Promise<import("@aws-sdk/client-redshift-serverless").GetWorkgroupCommandOutput>;
|
|
11
|
+
addIamUserToDatabaseGroup(params: {
|
|
12
|
+
Database: string;
|
|
13
|
+
WorkgroupName: string;
|
|
14
|
+
User: string;
|
|
15
|
+
Group: string;
|
|
16
|
+
}): Promise<void>;
|
|
17
|
+
createDatabaseUser(params: {
|
|
18
|
+
Database: string;
|
|
19
|
+
WorkgroupName: string;
|
|
20
|
+
User: string;
|
|
21
|
+
}): Promise<boolean>;
|
|
22
|
+
grantUsageOnSchema(params: {
|
|
23
|
+
GroupName: string;
|
|
24
|
+
Database: string;
|
|
25
|
+
WorkgroupName: string;
|
|
26
|
+
Schema: string;
|
|
27
|
+
Group: string;
|
|
28
|
+
}): Promise<void>;
|
|
29
|
+
createDbGroupFromIamGroupIfNotExists(params: {
|
|
30
|
+
iamGroup: string;
|
|
31
|
+
Database: string;
|
|
32
|
+
WorkgroupName: string;
|
|
33
|
+
}): Promise<any>;
|
|
34
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.RedshiftDataService = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const client_redshift_data_1 = require("@aws-sdk/client-redshift-data");
|
|
15
|
+
const server_aws_redshift_config_class_1 = require("../classes/server-aws-redshift-config.class");
|
|
16
|
+
const client_redshift_serverless_1 = require("@aws-sdk/client-redshift-serverless");
|
|
17
|
+
const crypto_1 = require("crypto");
|
|
18
|
+
let RedshiftDataService = class RedshiftDataService {
|
|
19
|
+
redshiftDataClient;
|
|
20
|
+
redshiftServerlessClient;
|
|
21
|
+
config;
|
|
22
|
+
constructor(redshiftDataClient, redshiftServerlessClient, config) {
|
|
23
|
+
this.redshiftDataClient = redshiftDataClient;
|
|
24
|
+
this.redshiftServerlessClient = redshiftServerlessClient;
|
|
25
|
+
this.config = config;
|
|
26
|
+
}
|
|
27
|
+
async waitForStatement(statementId) {
|
|
28
|
+
const maxAttempts = 10;
|
|
29
|
+
let attempts = 0;
|
|
30
|
+
while (attempts < maxAttempts) {
|
|
31
|
+
const describeStatementCommand = new client_redshift_data_1.DescribeStatementCommand({
|
|
32
|
+
Id: statementId
|
|
33
|
+
});
|
|
34
|
+
const status = await this.redshiftDataClient.send(describeStatementCommand);
|
|
35
|
+
if (status.Status === 'FINISHED') {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (status.Status === 'FAILED') {
|
|
39
|
+
throw new Error(`SQL statement failed: ${status.Error}`);
|
|
40
|
+
}
|
|
41
|
+
attempts++;
|
|
42
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
43
|
+
}
|
|
44
|
+
throw new Error('Timeout waiting for SQL statement to complete');
|
|
45
|
+
}
|
|
46
|
+
async verifyEndpointAccess(workgroupName) {
|
|
47
|
+
const workgroupResponse = await this.redshiftServerlessClient.send(new client_redshift_serverless_1.GetWorkgroupCommand({ workgroupName }));
|
|
48
|
+
if (!workgroupResponse?.workgroup?.endpoint?.address) {
|
|
49
|
+
console.error(`Could not retrieve endpoint for workgroup "${workgroupName}"`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
return workgroupResponse;
|
|
53
|
+
}
|
|
54
|
+
async addIamUserToDatabaseGroup(params) {
|
|
55
|
+
const { Database, WorkgroupName, User, Group } = params;
|
|
56
|
+
try {
|
|
57
|
+
const groupResult = await this.redshiftDataClient.send(new client_redshift_data_1.ExecuteStatementCommand({
|
|
58
|
+
Database,
|
|
59
|
+
Sql: `ALTER GROUP ${Group} ADD USER "${User}";`,
|
|
60
|
+
WorkgroupName
|
|
61
|
+
}));
|
|
62
|
+
await this.waitForStatement(groupResult.Id);
|
|
63
|
+
console.log(`Added user "${User}" to group "${Group}`);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.warn({ detail: `Warning adding ${User} to group:`, error });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async createDatabaseUser(params) {
|
|
70
|
+
const { Database, WorkgroupName, User } = params;
|
|
71
|
+
try {
|
|
72
|
+
const checkResult = await this.redshiftDataClient.send(new client_redshift_data_1.ExecuteStatementCommand({
|
|
73
|
+
Database,
|
|
74
|
+
Sql: `SELECT u.usename as username FROM pg_user u;`,
|
|
75
|
+
WorkgroupName
|
|
76
|
+
}));
|
|
77
|
+
await this.waitForStatement(checkResult.Id);
|
|
78
|
+
const describeCommand = new client_redshift_data_1.DescribeStatementCommand({ Id: checkResult.Id });
|
|
79
|
+
const checkStatus = await this.redshiftDataClient.send(describeCommand);
|
|
80
|
+
if (!checkStatus.ResultRows) {
|
|
81
|
+
const placeholderPassword = `IAM_${(0, crypto_1.randomUUID)().replace(/-/g, '_')}`;
|
|
82
|
+
const createUserCommand = new client_redshift_data_1.ExecuteStatementCommand({
|
|
83
|
+
Database,
|
|
84
|
+
Sql: `CREATE USER ${User} PASSWORD '${placeholderPassword}';`,
|
|
85
|
+
WorkgroupName
|
|
86
|
+
});
|
|
87
|
+
const createResult = await this.redshiftDataClient.send(createUserCommand);
|
|
88
|
+
await this.waitForStatement(createResult.Id);
|
|
89
|
+
console.log(`Created user: ${User}`);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
console.log(`User "${User} already exists`);
|
|
93
|
+
}
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
console.warn({ detail: `Warning creating user ${User}`, error });
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async grantUsageOnSchema(params) {
|
|
102
|
+
const { GroupName, Database, WorkgroupName, Schema, Group } = params;
|
|
103
|
+
try {
|
|
104
|
+
const checkResult = await this.redshiftDataClient.send(new client_redshift_data_1.ExecuteStatementCommand({
|
|
105
|
+
Database,
|
|
106
|
+
Sql: `SELECT 1 FROM pg_group WHERE groname = '${Group}';`,
|
|
107
|
+
WorkgroupName
|
|
108
|
+
}));
|
|
109
|
+
await this.waitForStatement(checkResult.Id);
|
|
110
|
+
const describeCommand = new client_redshift_data_1.DescribeStatementCommand({ Id: checkResult.Id });
|
|
111
|
+
const checkStatus = await this.redshiftDataClient.send(describeCommand);
|
|
112
|
+
if (!checkStatus.ResultRows) {
|
|
113
|
+
const createGroupCommand = new client_redshift_data_1.ExecuteStatementCommand({
|
|
114
|
+
Database,
|
|
115
|
+
Sql: 'CREATE GROUP ${Group};',
|
|
116
|
+
WorkgroupName
|
|
117
|
+
});
|
|
118
|
+
const createResult = await this.redshiftDataClient.send(createGroupCommand);
|
|
119
|
+
await this.waitForStatement(createResult.Id);
|
|
120
|
+
console.log('Created "${Group}" group');
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
console.log('"${Group}" group already exists');
|
|
124
|
+
}
|
|
125
|
+
const setupStatements = [
|
|
126
|
+
`GRANT USAGE ON SCHEMA ${Schema} TO GROUP ${Group};`,
|
|
127
|
+
`GRANT SELECT ON ALL TABLES IN SCHEMA ${Schema} TO GROUP ${Group};`,
|
|
128
|
+
`ALTER DEFAULT PRIVILEGES IN SCHEMA ${Schema} GRANT SELECT ON TABLES TO GROUP ${Group};`,
|
|
129
|
+
];
|
|
130
|
+
for (const Sql of setupStatements) {
|
|
131
|
+
const executeStatementCommand = new client_redshift_data_1.ExecuteStatementCommand({
|
|
132
|
+
Database,
|
|
133
|
+
Sql,
|
|
134
|
+
WorkgroupName
|
|
135
|
+
});
|
|
136
|
+
try {
|
|
137
|
+
const result = await this.redshiftDataClient.send(executeStatementCommand);
|
|
138
|
+
await this.waitForStatement(result.Id);
|
|
139
|
+
console.log(`Executed SQL: ${Sql}`);
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
console.warn(`Warning executing SQL: ${Sql}`, error.message);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
console.error({ detail: 'Error in grantUsageOnSchema:', error });
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async createDbGroupFromIamGroupIfNotExists(params) {
|
|
152
|
+
const { iamGroup, Database, WorkgroupName } = params;
|
|
153
|
+
try {
|
|
154
|
+
const checkResult = await this.redshiftDataClient.send(new client_redshift_data_1.ExecuteStatementCommand({
|
|
155
|
+
Database,
|
|
156
|
+
Sql: `SELECT 1 FROM pg_group WHERE groname = '${iamGroup}';`,
|
|
157
|
+
WorkgroupName
|
|
158
|
+
}));
|
|
159
|
+
await this.waitForStatement(checkResult.Id);
|
|
160
|
+
const describeCommand = new client_redshift_data_1.DescribeStatementCommand({ Id: checkResult.Id });
|
|
161
|
+
const checkStatus = await this.redshiftDataClient.send(describeCommand);
|
|
162
|
+
if (!checkStatus.ResultRows) {
|
|
163
|
+
const createGroupCommand = new client_redshift_data_1.ExecuteStatementCommand({
|
|
164
|
+
Database,
|
|
165
|
+
Sql: `CREATE GROUP ${iamGroup};`,
|
|
166
|
+
WorkgroupName
|
|
167
|
+
});
|
|
168
|
+
const createResult = await this.redshiftDataClient.send(createGroupCommand);
|
|
169
|
+
await this.waitForStatement(createResult.Id);
|
|
170
|
+
console.log(`Created "${iamGroup}" group`);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
console.log(`"${iamGroup}" group already exists`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
console.error({ error });
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
exports.RedshiftDataService = RedshiftDataService;
|
|
183
|
+
exports.RedshiftDataService = RedshiftDataService = __decorate([
|
|
184
|
+
(0, common_1.Injectable)(),
|
|
185
|
+
__metadata("design:paramtypes", [client_redshift_data_1.RedshiftDataClient,
|
|
186
|
+
client_redshift_serverless_1.RedshiftServerlessClient,
|
|
187
|
+
server_aws_redshift_config_class_1.ServerAwsRedshiftDataConfig])
|
|
188
|
+
], RedshiftDataService);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { RedshiftDataClient } from "@aws-sdk/client-redshift-data";
|
|
2
|
+
import { ServerAwsRedshiftDataConfig } from '../classes/server-aws-redshift-config.class';
|
|
3
|
+
import { RedshiftServerlessClient } from '@aws-sdk/client-redshift-serverless';
|
|
4
|
+
export declare class RedshiftDataService {
|
|
5
|
+
private redshiftDataClient;
|
|
6
|
+
private redshiftServerlessClient;
|
|
7
|
+
private config;
|
|
8
|
+
constructor(redshiftDataClient: RedshiftDataClient, redshiftServerlessClient: RedshiftServerlessClient, config: ServerAwsRedshiftDataConfig);
|
|
9
|
+
waitForStatement(statementId: string): Promise<void>;
|
|
10
|
+
verifyEndpointAccess(workgroupName: string): Promise<import("@aws-sdk/client-redshift-serverless").GetWorkgroupCommandOutput>;
|
|
11
|
+
addIamUserToDatabaseGroup(params: {
|
|
12
|
+
Database: string;
|
|
13
|
+
WorkgroupName: string;
|
|
14
|
+
User: string;
|
|
15
|
+
Group: string;
|
|
16
|
+
}): Promise<void>;
|
|
17
|
+
createDatabaseUser(params: {
|
|
18
|
+
Database: string;
|
|
19
|
+
WorkgroupName: string;
|
|
20
|
+
User: string;
|
|
21
|
+
}): Promise<boolean>;
|
|
22
|
+
grantUsageOnSchema(params: {
|
|
23
|
+
GroupName: string;
|
|
24
|
+
Database: string;
|
|
25
|
+
WorkgroupName: string;
|
|
26
|
+
Schema: string;
|
|
27
|
+
Group: string;
|
|
28
|
+
}): Promise<void>;
|
|
29
|
+
createDbGroupFromIamGroupIfNotExists(params: {
|
|
30
|
+
iamGroup: string;
|
|
31
|
+
Database: string;
|
|
32
|
+
WorkgroupName: string;
|
|
33
|
+
}): Promise<any>;
|
|
34
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@onivoro/server-aws-redshift",
|
|
3
|
+
"version": "22.0.0",
|
|
4
|
+
"repository": {
|
|
5
|
+
"url": "git+https://github.com/onivoro/server-aws-redshift.git"
|
|
6
|
+
},
|
|
7
|
+
"main": "dist/cjs/index.js",
|
|
8
|
+
"module": "dist/esm/index.js",
|
|
9
|
+
"types": "dist/types/index.d.ts",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/*"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"onx": "onx",
|
|
15
|
+
"build": "onx Build",
|
|
16
|
+
"deploy": "onx Publish",
|
|
17
|
+
"test": "onx Test",
|
|
18
|
+
"update": "onx Update"
|
|
19
|
+
},
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/types/index.d.ts",
|
|
23
|
+
"require": "./dist/cjs/index.js",
|
|
24
|
+
"import": "./dist/esm/index.js",
|
|
25
|
+
"default": "./dist/esm/lib.js"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"onx": {
|
|
29
|
+
"platform": "server",
|
|
30
|
+
"module": "commonjs"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@onivoro/cli": "^22.0.8",
|
|
34
|
+
"@types/jest": "*",
|
|
35
|
+
"@types/node": "22.8.1",
|
|
36
|
+
"typescript": "*"
|
|
37
|
+
},
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": "22.10.0",
|
|
40
|
+
"npm": "10.9.0"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@aws-sdk/client-redshift": "^3.699.0",
|
|
44
|
+
"@aws-sdk/client-redshift-data": "^3.709.0",
|
|
45
|
+
"@aws-sdk/client-redshift-serverless": "^3.709.0",
|
|
46
|
+
"@nestjs/common": "^10.4.6",
|
|
47
|
+
"@onivoro/server-common": "^22.0.3"
|
|
48
|
+
}
|
|
49
|
+
}
|