@toxplanet/pegasus-sdk 1.1.3 → 1.1.5
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/config/environment.acc.js +4 -4
- package/config/environment.prod.js +3 -3
- package/config/environment.qa.js +4 -4
- package/index.js +43 -43
- package/lib/chemicals.js +1 -1
- package/lib/connection.js +223 -223
- package/lib/documents.js +94 -94
- package/lib/search.js +56 -12
- package/lib/sync.js +43 -43
- package/lib/utils.js +49 -49
- package/package.json +1 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
environment: 'acc',
|
|
3
3
|
region: 'us-east-1',
|
|
4
|
-
secretName: 'arn:aws:secretsmanager:us-east-1:
|
|
5
|
-
openSearchEndpoint: 'https://
|
|
4
|
+
secretName: 'arn:aws:secretsmanager:us-east-1:605134466764:secret:rds!cluster-9b502dde-5e2a-49db-b2c5-9801141ee40b-gkHbLm',
|
|
5
|
+
openSearchEndpoint: 'https://1pbu0yqr197lq07hfcjh.us-east-1.aoss.amazonaws.com',
|
|
6
6
|
openSearchIndex: 'chemicals',
|
|
7
7
|
database: {
|
|
8
|
-
host: 'cr-chemicals.cluster-
|
|
8
|
+
host: 'cr-chemicals-acc.cluster-czgc4c20yerz.us-east-1.rds.amazonaws.com',
|
|
9
9
|
name: 'chemicals'
|
|
10
10
|
},
|
|
11
11
|
postgres: {
|
|
@@ -20,7 +20,7 @@ module.exports = {
|
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
22
|
indexRoutes: {
|
|
23
|
-
chemicals: ['
|
|
23
|
+
chemicals: ['*'],
|
|
24
24
|
documents: ['documents*'],
|
|
25
25
|
search: [/^(chemicals|substances|search)/]
|
|
26
26
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
environment: 'prod',
|
|
3
3
|
region: 'us-east-1',
|
|
4
|
-
secretName: '
|
|
5
|
-
openSearchEndpoint:
|
|
4
|
+
secretName: 'rds!cluster-25483b3f-3758-43ed-9548-26c91de16c2d',
|
|
5
|
+
openSearchEndpoint: 'https://odusb11s00j5hyy5r6.us-east-1.aoss.amazonaws.com',
|
|
6
6
|
openSearchIndex: 'chemicals',
|
|
7
7
|
database: {
|
|
8
|
-
host:
|
|
8
|
+
host: 'cr-chemicals-qa.cluster-c7gakqksq9m4.us-east-1.rds.amazonaws.com',
|
|
9
9
|
name: 'chemicals'
|
|
10
10
|
},
|
|
11
11
|
postgres: {
|
package/config/environment.qa.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
environment: 'qa',
|
|
3
3
|
region: 'us-east-1',
|
|
4
|
-
secretName: '
|
|
5
|
-
openSearchEndpoint:
|
|
4
|
+
secretName: 'arn:aws:secretsmanager:us-east-1:147997144422:secret:rds!cluster-25483b3f-3758-43ed-9548-26c91de16c2d-oYjysU',
|
|
5
|
+
openSearchEndpoint: 'https://odusb11s00j5hyy5r6.us-east-1.aoss.amazonaws.com',
|
|
6
6
|
openSearchIndex: 'chemicals',
|
|
7
7
|
database: {
|
|
8
|
-
host:
|
|
8
|
+
host: 'cr-chemicals-qa.cluster-c7gakqksq9m4.us-east-1.rds.amazonaws.com',
|
|
9
9
|
name: 'chemicals'
|
|
10
10
|
},
|
|
11
11
|
postgres: {
|
|
@@ -20,7 +20,7 @@ module.exports = {
|
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
22
|
indexRoutes: {
|
|
23
|
-
chemicals: ['
|
|
23
|
+
chemicals: ['*'],
|
|
24
24
|
documents: ['documents*'],
|
|
25
25
|
search: [/^(chemicals|substances|search)/]
|
|
26
26
|
}
|
package/index.js
CHANGED
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
const PegasusConnection = require('./lib/connection');
|
|
2
|
-
const ChemicalsService = require('./lib/chemicals');
|
|
3
|
-
const DocumentsService = require('./lib/documents');
|
|
4
|
-
const SearchService = require('./lib/search');
|
|
5
|
-
const SyncService = require('./lib/sync');
|
|
6
|
-
const UtilsService = require('./lib/utils');
|
|
7
|
-
const ElasticsearchService = require('./lib/elasticsearch');
|
|
8
|
-
|
|
9
|
-
class PegasusSDK {
|
|
10
|
-
constructor(config) {
|
|
11
|
-
this.connection = new PegasusConnection(config);
|
|
12
|
-
this.elasticsearch = new ElasticsearchService(this.connection);
|
|
13
|
-
this.chemicals = new ChemicalsService(this.connection);
|
|
14
|
-
this.documents = new DocumentsService(this.connection);
|
|
15
|
-
this.search = new SearchService(this.connection);
|
|
16
|
-
this.sync = new SyncService(this.connection);
|
|
17
|
-
this.utils = new UtilsService(this.connection);
|
|
18
|
-
|
|
19
|
-
this.chemicals.registerElasticsearchHandlers(this.elasticsearch);
|
|
20
|
-
this.documents.registerElasticsearchHandlers(this.elasticsearch);
|
|
21
|
-
this.search.registerElasticsearchHandlers(this.elasticsearch);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async connect() {
|
|
25
|
-
return this.connection.connect();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async disconnect() {
|
|
29
|
-
return this.connection.disconnect();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async healthCheck() {
|
|
33
|
-
return this.connection.testConnection();
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
module.exports = PegasusSDK;
|
|
38
|
-
module.exports.PegasusConnection = PegasusConnection;
|
|
39
|
-
module.exports.ChemicalsService = ChemicalsService;
|
|
40
|
-
module.exports.DocumentsService = DocumentsService;
|
|
41
|
-
module.exports.SearchService = SearchService;
|
|
42
|
-
module.exports.SyncService = SyncService;
|
|
43
|
-
module.exports.UtilsService = UtilsService;
|
|
1
|
+
const PegasusConnection = require('./lib/connection');
|
|
2
|
+
const ChemicalsService = require('./lib/chemicals');
|
|
3
|
+
const DocumentsService = require('./lib/documents');
|
|
4
|
+
const SearchService = require('./lib/search');
|
|
5
|
+
const SyncService = require('./lib/sync');
|
|
6
|
+
const UtilsService = require('./lib/utils');
|
|
7
|
+
const ElasticsearchService = require('./lib/elasticsearch');
|
|
8
|
+
|
|
9
|
+
class PegasusSDK {
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.connection = new PegasusConnection(config);
|
|
12
|
+
this.elasticsearch = new ElasticsearchService(this.connection);
|
|
13
|
+
this.chemicals = new ChemicalsService(this.connection);
|
|
14
|
+
this.documents = new DocumentsService(this.connection);
|
|
15
|
+
this.search = new SearchService(this.connection);
|
|
16
|
+
this.sync = new SyncService(this.connection);
|
|
17
|
+
this.utils = new UtilsService(this.connection);
|
|
18
|
+
|
|
19
|
+
this.chemicals.registerElasticsearchHandlers(this.elasticsearch);
|
|
20
|
+
this.documents.registerElasticsearchHandlers(this.elasticsearch);
|
|
21
|
+
this.search.registerElasticsearchHandlers(this.elasticsearch);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async connect() {
|
|
25
|
+
return this.connection.connect();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async disconnect() {
|
|
29
|
+
return this.connection.disconnect();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async healthCheck() {
|
|
33
|
+
return this.connection.testConnection();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = PegasusSDK;
|
|
38
|
+
module.exports.PegasusConnection = PegasusConnection;
|
|
39
|
+
module.exports.ChemicalsService = ChemicalsService;
|
|
40
|
+
module.exports.DocumentsService = DocumentsService;
|
|
41
|
+
module.exports.SearchService = SearchService;
|
|
42
|
+
module.exports.SyncService = SyncService;
|
|
43
|
+
module.exports.UtilsService = UtilsService;
|
|
44
44
|
module.exports.ElasticsearchService = ElasticsearchService;
|
package/lib/chemicals.js
CHANGED
package/lib/connection.js
CHANGED
|
@@ -1,223 +1,223 @@
|
|
|
1
|
-
const { Pool } = require('pg');
|
|
2
|
-
const { Client } = require('@opensearch-project/opensearch');
|
|
3
|
-
const { SecretsManagerClient, GetSecretValueCommand } = require('@aws-sdk/client-secrets-manager');
|
|
4
|
-
const { AwsSigv4Signer } = require('@opensearch-project/opensearch/aws');
|
|
5
|
-
const { fromNodeProviderChain } = require('@aws-sdk/credential-providers');
|
|
6
|
-
const { loadConfig } = require('../config');
|
|
7
|
-
const { logInfo, logError } = require('@toxplanet/tphelper/logging');
|
|
8
|
-
|
|
9
|
-
class PegasusConnection {
|
|
10
|
-
constructor(config = {}) {
|
|
11
|
-
const envConfig = loadConfig(config.environment);
|
|
12
|
-
|
|
13
|
-
this.config = { ...envConfig, ...config };
|
|
14
|
-
this.environment = this.config.environment;
|
|
15
|
-
this.region = this.config.region;
|
|
16
|
-
this.secretName = this.config.secretName;
|
|
17
|
-
this.openSearchEndpoint = this.config.openSearchEndpoint;
|
|
18
|
-
this.openSearchIndex = this.config.openSearchIndex;
|
|
19
|
-
this.databaseHost = this.config.database?.host;
|
|
20
|
-
this.databaseName = this.config.database?.name;
|
|
21
|
-
|
|
22
|
-
this.pgPool = null;
|
|
23
|
-
this.osClient = null;
|
|
24
|
-
this.secretsClient = null;
|
|
25
|
-
this.cachedSecret = null;
|
|
26
|
-
this.isConnected = false;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async getSecret() {
|
|
30
|
-
if (this.cachedSecret) {
|
|
31
|
-
return this.cachedSecret;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (!this.secretsClient) {
|
|
35
|
-
this.secretsClient = new SecretsManagerClient({ region: this.region });
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const command = new GetSecretValueCommand({ SecretId: this.secretName });
|
|
39
|
-
const response = await this.secretsClient.send(command);
|
|
40
|
-
|
|
41
|
-
if (response.SecretString) {
|
|
42
|
-
this.cachedSecret = JSON.parse(response.SecretString);
|
|
43
|
-
return this.cachedSecret;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
throw new Error(`Secret ${this.secretName} does not contain SecretString`);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async connect() {
|
|
50
|
-
if (this.isConnected) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const secret = await this.getSecret();
|
|
55
|
-
|
|
56
|
-
const poolConfig = {
|
|
57
|
-
host: this.databaseHost,
|
|
58
|
-
port: 5432,
|
|
59
|
-
database: this.databaseName,
|
|
60
|
-
user: secret.username,
|
|
61
|
-
password: secret.password,
|
|
62
|
-
max: this.config.postgres.maxConnections,
|
|
63
|
-
min: this.config.postgres.minConnections,
|
|
64
|
-
idleTimeoutMillis: this.config.postgres.idleTimeoutMillis,
|
|
65
|
-
connectionTimeoutMillis: this.config.postgres.connectionTimeoutMillis,
|
|
66
|
-
allowExitOnIdle: true,
|
|
67
|
-
ssl: this.config.postgres.ssl,
|
|
68
|
-
statement_timeout: this.config.postgres.statementTimeout,
|
|
69
|
-
query_timeout: this.config.postgres.queryTimeout
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
this.pgPool = new Pool(poolConfig);
|
|
73
|
-
|
|
74
|
-
this.pgPool.on('error', (err) => {
|
|
75
|
-
logError('pegasus-sdk', 'PegasusConnection', 'pgPool.error', err);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
this.pgPool.on('connect', () => {
|
|
79
|
-
logInfo('pegasus-sdk', 'PostgreSQL client connected');
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
this.pgPool.on('remove', () => {
|
|
83
|
-
logInfo('pegasus-sdk', 'PostgreSQL client removed from pool');
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
if (this.openSearchEndpoint) {
|
|
87
|
-
this.osClient = new Client({
|
|
88
|
-
...AwsSigv4Signer({
|
|
89
|
-
region: this.region,
|
|
90
|
-
service: 'aoss',
|
|
91
|
-
getCredentials: () => {
|
|
92
|
-
const credentialsProvider = fromNodeProviderChain();
|
|
93
|
-
return credentialsProvider();
|
|
94
|
-
}
|
|
95
|
-
}),
|
|
96
|
-
node: this.openSearchEndpoint
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
this.isConnected = true;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
async disconnect() {
|
|
104
|
-
if (!this.isConnected) {
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (this.pgPool) {
|
|
109
|
-
await this.pgPool.end();
|
|
110
|
-
this.pgPool = null;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
this.osClient = null;
|
|
114
|
-
this.isConnected = false;
|
|
115
|
-
this.cachedSecret = null;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
getPostgresClient() {
|
|
119
|
-
if (!this.pgPool) {
|
|
120
|
-
throw new Error('PostgreSQL connection not established. Call connect() first.');
|
|
121
|
-
}
|
|
122
|
-
return this.pgPool;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
getOpenSearchClient() {
|
|
126
|
-
if (!this.osClient) {
|
|
127
|
-
throw new Error('OpenSearch connection not established. Call connect() first or provide openSearchEndpoint.');
|
|
128
|
-
}
|
|
129
|
-
return this.osClient;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
getOpenSearchIndex() {
|
|
133
|
-
return this.openSearchIndex || 'chemicals';
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
async testConnection() {
|
|
137
|
-
try {
|
|
138
|
-
if (this.pgPool) {
|
|
139
|
-
const client = await this.pgPool.connect();
|
|
140
|
-
const result = await client.query('SELECT NOW() as current_time, version() as pg_version');
|
|
141
|
-
client.release();
|
|
142
|
-
|
|
143
|
-
const pgStatus = {
|
|
144
|
-
connected: true,
|
|
145
|
-
timestamp: result.rows[0].current_time,
|
|
146
|
-
version: result.rows[0].pg_version,
|
|
147
|
-
poolSize: this.pgPool.totalCount,
|
|
148
|
-
idleConnections: this.pgPool.idleCount,
|
|
149
|
-
waitingRequests: this.pgPool.waitingCount
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
let osStatus = null;
|
|
153
|
-
if (this.osClient) {
|
|
154
|
-
try {
|
|
155
|
-
const indexName = this.getOpenSearchIndex();
|
|
156
|
-
const testSearch = await this.osClient.search({
|
|
157
|
-
index: indexName,
|
|
158
|
-
body: {
|
|
159
|
-
size: 1,
|
|
160
|
-
query: {
|
|
161
|
-
match: { chemical_name: 'benzene' }
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
osStatus = {
|
|
166
|
-
connected: true,
|
|
167
|
-
resultsFound: testSearch.body.hits.total.value || 0
|
|
168
|
-
};
|
|
169
|
-
} catch (osError) {
|
|
170
|
-
osStatus = {
|
|
171
|
-
connected: false,
|
|
172
|
-
error: osError.message
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return {
|
|
178
|
-
postgres: pgStatus,
|
|
179
|
-
opensearch: osStatus,
|
|
180
|
-
environment: this.environment,
|
|
181
|
-
region: this.region
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
throw new Error('No active connections');
|
|
186
|
-
} catch (error) {
|
|
187
|
-
return {
|
|
188
|
-
postgres: { connected: false, error: error.message },
|
|
189
|
-
opensearch: null,
|
|
190
|
-
environment: this.environment,
|
|
191
|
-
region: this.region
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
async query(sql, params) {
|
|
197
|
-
const pool = this.getPostgresClient();
|
|
198
|
-
return pool.query(sql, params);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
async getClient() {
|
|
202
|
-
const pool = this.getPostgresClient();
|
|
203
|
-
return pool.connect();
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
async transaction(callback) {
|
|
207
|
-
const client = await this.getClient();
|
|
208
|
-
|
|
209
|
-
try {
|
|
210
|
-
await client.query('BEGIN');
|
|
211
|
-
const result = await callback(client);
|
|
212
|
-
await client.query('COMMIT');
|
|
213
|
-
return result;
|
|
214
|
-
} catch (error) {
|
|
215
|
-
await client.query('ROLLBACK');
|
|
216
|
-
throw error;
|
|
217
|
-
} finally {
|
|
218
|
-
client.release();
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
module.exports = PegasusConnection;
|
|
1
|
+
const { Pool } = require('pg');
|
|
2
|
+
const { Client } = require('@opensearch-project/opensearch');
|
|
3
|
+
const { SecretsManagerClient, GetSecretValueCommand } = require('@aws-sdk/client-secrets-manager');
|
|
4
|
+
const { AwsSigv4Signer } = require('@opensearch-project/opensearch/aws');
|
|
5
|
+
const { fromNodeProviderChain } = require('@aws-sdk/credential-providers');
|
|
6
|
+
const { loadConfig } = require('../config');
|
|
7
|
+
const { logInfo, logError } = require('@toxplanet/tphelper/logging');
|
|
8
|
+
|
|
9
|
+
class PegasusConnection {
|
|
10
|
+
constructor(config = {}) {
|
|
11
|
+
const envConfig = loadConfig(config.environment);
|
|
12
|
+
|
|
13
|
+
this.config = { ...envConfig, ...config };
|
|
14
|
+
this.environment = this.config.environment;
|
|
15
|
+
this.region = this.config.region;
|
|
16
|
+
this.secretName = this.config.secretName;
|
|
17
|
+
this.openSearchEndpoint = this.config.openSearchEndpoint;
|
|
18
|
+
this.openSearchIndex = this.config.openSearchIndex;
|
|
19
|
+
this.databaseHost = this.config.database?.host;
|
|
20
|
+
this.databaseName = this.config.database?.name;
|
|
21
|
+
|
|
22
|
+
this.pgPool = null;
|
|
23
|
+
this.osClient = null;
|
|
24
|
+
this.secretsClient = null;
|
|
25
|
+
this.cachedSecret = null;
|
|
26
|
+
this.isConnected = false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async getSecret() {
|
|
30
|
+
if (this.cachedSecret) {
|
|
31
|
+
return this.cachedSecret;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!this.secretsClient) {
|
|
35
|
+
this.secretsClient = new SecretsManagerClient({ region: this.region });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const command = new GetSecretValueCommand({ SecretId: this.secretName });
|
|
39
|
+
const response = await this.secretsClient.send(command);
|
|
40
|
+
|
|
41
|
+
if (response.SecretString) {
|
|
42
|
+
this.cachedSecret = JSON.parse(response.SecretString);
|
|
43
|
+
return this.cachedSecret;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
throw new Error(`Secret ${this.secretName} does not contain SecretString`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async connect() {
|
|
50
|
+
if (this.isConnected) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const secret = await this.getSecret();
|
|
55
|
+
|
|
56
|
+
const poolConfig = {
|
|
57
|
+
host: this.databaseHost,
|
|
58
|
+
port: 5432,
|
|
59
|
+
database: this.databaseName,
|
|
60
|
+
user: secret.username,
|
|
61
|
+
password: secret.password,
|
|
62
|
+
max: this.config.postgres.maxConnections,
|
|
63
|
+
min: this.config.postgres.minConnections,
|
|
64
|
+
idleTimeoutMillis: this.config.postgres.idleTimeoutMillis,
|
|
65
|
+
connectionTimeoutMillis: this.config.postgres.connectionTimeoutMillis,
|
|
66
|
+
allowExitOnIdle: true,
|
|
67
|
+
ssl: this.config.postgres.ssl,
|
|
68
|
+
statement_timeout: this.config.postgres.statementTimeout,
|
|
69
|
+
query_timeout: this.config.postgres.queryTimeout
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
this.pgPool = new Pool(poolConfig);
|
|
73
|
+
|
|
74
|
+
this.pgPool.on('error', (err) => {
|
|
75
|
+
logError('pegasus-sdk', 'PegasusConnection', 'pgPool.error', err);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
this.pgPool.on('connect', () => {
|
|
79
|
+
logInfo('pegasus-sdk', 'PostgreSQL client connected');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
this.pgPool.on('remove', () => {
|
|
83
|
+
logInfo('pegasus-sdk', 'PostgreSQL client removed from pool');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
if (this.openSearchEndpoint) {
|
|
87
|
+
this.osClient = new Client({
|
|
88
|
+
...AwsSigv4Signer({
|
|
89
|
+
region: this.region,
|
|
90
|
+
service: 'aoss',
|
|
91
|
+
getCredentials: () => {
|
|
92
|
+
const credentialsProvider = fromNodeProviderChain();
|
|
93
|
+
return credentialsProvider();
|
|
94
|
+
}
|
|
95
|
+
}),
|
|
96
|
+
node: this.openSearchEndpoint
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
this.isConnected = true;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async disconnect() {
|
|
104
|
+
if (!this.isConnected) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (this.pgPool) {
|
|
109
|
+
await this.pgPool.end();
|
|
110
|
+
this.pgPool = null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
this.osClient = null;
|
|
114
|
+
this.isConnected = false;
|
|
115
|
+
this.cachedSecret = null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
getPostgresClient() {
|
|
119
|
+
if (!this.pgPool) {
|
|
120
|
+
throw new Error('PostgreSQL connection not established. Call connect() first.');
|
|
121
|
+
}
|
|
122
|
+
return this.pgPool;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
getOpenSearchClient() {
|
|
126
|
+
if (!this.osClient) {
|
|
127
|
+
throw new Error('OpenSearch connection not established. Call connect() first or provide openSearchEndpoint.');
|
|
128
|
+
}
|
|
129
|
+
return this.osClient;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
getOpenSearchIndex() {
|
|
133
|
+
return this.openSearchIndex || 'chemicals';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async testConnection() {
|
|
137
|
+
try {
|
|
138
|
+
if (this.pgPool) {
|
|
139
|
+
const client = await this.pgPool.connect();
|
|
140
|
+
const result = await client.query('SELECT NOW() as current_time, version() as pg_version');
|
|
141
|
+
client.release();
|
|
142
|
+
|
|
143
|
+
const pgStatus = {
|
|
144
|
+
connected: true,
|
|
145
|
+
timestamp: result.rows[0].current_time,
|
|
146
|
+
version: result.rows[0].pg_version,
|
|
147
|
+
poolSize: this.pgPool.totalCount,
|
|
148
|
+
idleConnections: this.pgPool.idleCount,
|
|
149
|
+
waitingRequests: this.pgPool.waitingCount
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
let osStatus = null;
|
|
153
|
+
if (this.osClient) {
|
|
154
|
+
try {
|
|
155
|
+
const indexName = this.getOpenSearchIndex();
|
|
156
|
+
const testSearch = await this.osClient.search({
|
|
157
|
+
index: indexName,
|
|
158
|
+
body: {
|
|
159
|
+
size: 1,
|
|
160
|
+
query: {
|
|
161
|
+
match: { chemical_name: 'benzene' }
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
osStatus = {
|
|
166
|
+
connected: true,
|
|
167
|
+
resultsFound: testSearch.body.hits.total.value || 0
|
|
168
|
+
};
|
|
169
|
+
} catch (osError) {
|
|
170
|
+
osStatus = {
|
|
171
|
+
connected: false,
|
|
172
|
+
error: osError.message
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
postgres: pgStatus,
|
|
179
|
+
opensearch: osStatus,
|
|
180
|
+
environment: this.environment,
|
|
181
|
+
region: this.region
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
throw new Error('No active connections');
|
|
186
|
+
} catch (error) {
|
|
187
|
+
return {
|
|
188
|
+
postgres: { connected: false, error: error.message },
|
|
189
|
+
opensearch: null,
|
|
190
|
+
environment: this.environment,
|
|
191
|
+
region: this.region
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async query(sql, params) {
|
|
197
|
+
const pool = this.getPostgresClient();
|
|
198
|
+
return pool.query(sql, params);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async getClient() {
|
|
202
|
+
const pool = this.getPostgresClient();
|
|
203
|
+
return pool.connect();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async transaction(callback) {
|
|
207
|
+
const client = await this.getClient();
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
await client.query('BEGIN');
|
|
211
|
+
const result = await callback(client);
|
|
212
|
+
await client.query('COMMIT');
|
|
213
|
+
return result;
|
|
214
|
+
} catch (error) {
|
|
215
|
+
await client.query('ROLLBACK');
|
|
216
|
+
throw error;
|
|
217
|
+
} finally {
|
|
218
|
+
client.release();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
module.exports = PegasusConnection;
|
package/lib/documents.js
CHANGED
|
@@ -1,94 +1,94 @@
|
|
|
1
|
-
class DocumentsService {
|
|
2
|
-
constructor(connection) {
|
|
3
|
-
this.connection = connection;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
async createDocument(documentPath, casNumbers) {}
|
|
7
|
-
|
|
8
|
-
async bulkCreateDocuments(documents) {}
|
|
9
|
-
|
|
10
|
-
async updateDocument(documentId, updates) {}
|
|
11
|
-
|
|
12
|
-
async deleteDocument(documentId) {}
|
|
13
|
-
|
|
14
|
-
async deleteDocumentByPath(documentPath) {}
|
|
15
|
-
|
|
16
|
-
async getDocumentById(documentId) {}
|
|
17
|
-
|
|
18
|
-
async getDocumentByPath(documentPath) {}
|
|
19
|
-
|
|
20
|
-
async getDocumentsByCAS(casNumber) {}
|
|
21
|
-
|
|
22
|
-
async getCASByDocument(documentId) {}
|
|
23
|
-
|
|
24
|
-
async getCASByDocumentPath(documentPath) {}
|
|
25
|
-
|
|
26
|
-
async addCASToDocument(documentId, casNumber) {}
|
|
27
|
-
|
|
28
|
-
async addCASToDocumentBatch(documentId, casNumbers) {}
|
|
29
|
-
|
|
30
|
-
async removeCASFromDocument(documentId, casNumber) {}
|
|
31
|
-
|
|
32
|
-
async findDocumentsWithMultipleCAS(casNumbers, requireAll) {}
|
|
33
|
-
|
|
34
|
-
async countDocuments() {}
|
|
35
|
-
|
|
36
|
-
async countDocumentsByCAS(casNumber) {}
|
|
37
|
-
|
|
38
|
-
async countUniqueCAS() {}
|
|
39
|
-
|
|
40
|
-
async getTopCASByDocumentCount(limit) {}
|
|
41
|
-
|
|
42
|
-
async extractTextFromPDF(pdfBuffer) {}
|
|
43
|
-
|
|
44
|
-
async extractCASFromText(text) {}
|
|
45
|
-
|
|
46
|
-
async processDocument(documentPath, documentData) {}
|
|
47
|
-
|
|
48
|
-
registerElasticsearchHandlers(elasticsearchService) {
|
|
49
|
-
const indexPatterns = this.connection.config.indexRoutes?.documents || ['documents*'];
|
|
50
|
-
|
|
51
|
-
indexPatterns.forEach(pattern => {
|
|
52
|
-
elasticsearchService.registerIndexRoute(pattern, {
|
|
53
|
-
index: async (params) => {
|
|
54
|
-
const document = params.body;
|
|
55
|
-
return await this.createDocument(document.document_path, document.cas_numbers || []);
|
|
56
|
-
},
|
|
57
|
-
|
|
58
|
-
bulk: async (params) => {
|
|
59
|
-
const operations = params.body || params.operations;
|
|
60
|
-
const documents = [];
|
|
61
|
-
|
|
62
|
-
for (let i = 0; i < operations.length; i += 2) {
|
|
63
|
-
const action = operations[i];
|
|
64
|
-
const document = operations[i + 1];
|
|
65
|
-
|
|
66
|
-
if (action.index || action.create) {
|
|
67
|
-
documents.push(document);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return await this.bulkCreateDocuments(documents);
|
|
72
|
-
},
|
|
73
|
-
|
|
74
|
-
get: async (params) => {
|
|
75
|
-
return await this.getDocumentById(params.id);
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
update: async (params) => {
|
|
79
|
-
return await this.updateDocument(params.id, params.body);
|
|
80
|
-
},
|
|
81
|
-
|
|
82
|
-
delete: async (params) => {
|
|
83
|
-
return await this.deleteDocument(params.id);
|
|
84
|
-
},
|
|
85
|
-
|
|
86
|
-
count: async (params) => {
|
|
87
|
-
return await this.countDocuments();
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
module.exports = DocumentsService;
|
|
1
|
+
class DocumentsService {
|
|
2
|
+
constructor(connection) {
|
|
3
|
+
this.connection = connection;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
async createDocument(documentPath, casNumbers) {}
|
|
7
|
+
|
|
8
|
+
async bulkCreateDocuments(documents) {}
|
|
9
|
+
|
|
10
|
+
async updateDocument(documentId, updates) {}
|
|
11
|
+
|
|
12
|
+
async deleteDocument(documentId) {}
|
|
13
|
+
|
|
14
|
+
async deleteDocumentByPath(documentPath) {}
|
|
15
|
+
|
|
16
|
+
async getDocumentById(documentId) {}
|
|
17
|
+
|
|
18
|
+
async getDocumentByPath(documentPath) {}
|
|
19
|
+
|
|
20
|
+
async getDocumentsByCAS(casNumber) {}
|
|
21
|
+
|
|
22
|
+
async getCASByDocument(documentId) {}
|
|
23
|
+
|
|
24
|
+
async getCASByDocumentPath(documentPath) {}
|
|
25
|
+
|
|
26
|
+
async addCASToDocument(documentId, casNumber) {}
|
|
27
|
+
|
|
28
|
+
async addCASToDocumentBatch(documentId, casNumbers) {}
|
|
29
|
+
|
|
30
|
+
async removeCASFromDocument(documentId, casNumber) {}
|
|
31
|
+
|
|
32
|
+
async findDocumentsWithMultipleCAS(casNumbers, requireAll) {}
|
|
33
|
+
|
|
34
|
+
async countDocuments() {}
|
|
35
|
+
|
|
36
|
+
async countDocumentsByCAS(casNumber) {}
|
|
37
|
+
|
|
38
|
+
async countUniqueCAS() {}
|
|
39
|
+
|
|
40
|
+
async getTopCASByDocumentCount(limit) {}
|
|
41
|
+
|
|
42
|
+
async extractTextFromPDF(pdfBuffer) {}
|
|
43
|
+
|
|
44
|
+
async extractCASFromText(text) {}
|
|
45
|
+
|
|
46
|
+
async processDocument(documentPath, documentData) {}
|
|
47
|
+
|
|
48
|
+
registerElasticsearchHandlers(elasticsearchService) {
|
|
49
|
+
const indexPatterns = this.connection.config.indexRoutes?.documents || ['documents*'];
|
|
50
|
+
|
|
51
|
+
indexPatterns.forEach(pattern => {
|
|
52
|
+
elasticsearchService.registerIndexRoute(pattern, {
|
|
53
|
+
index: async (params) => {
|
|
54
|
+
const document = params.body;
|
|
55
|
+
return await this.createDocument(document.document_path, document.cas_numbers || []);
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
bulk: async (params) => {
|
|
59
|
+
const operations = params.body || params.operations;
|
|
60
|
+
const documents = [];
|
|
61
|
+
|
|
62
|
+
for (let i = 0; i < operations.length; i += 2) {
|
|
63
|
+
const action = operations[i];
|
|
64
|
+
const document = operations[i + 1];
|
|
65
|
+
|
|
66
|
+
if (action.index || action.create) {
|
|
67
|
+
documents.push(document);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return await this.bulkCreateDocuments(documents);
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
get: async (params) => {
|
|
75
|
+
return await this.getDocumentById(params.id);
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
update: async (params) => {
|
|
79
|
+
return await this.updateDocument(params.id, params.body);
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
delete: async (params) => {
|
|
83
|
+
return await this.deleteDocument(params.id);
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
count: async (params) => {
|
|
87
|
+
return await this.countDocuments();
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
module.exports = DocumentsService;
|
package/lib/search.js
CHANGED
|
@@ -310,22 +310,66 @@ class SearchService {
|
|
|
310
310
|
elasticsearchService.registerIndexRoute(pattern, {
|
|
311
311
|
search: async (params) => {
|
|
312
312
|
const query = params.body?.query;
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
313
|
+
const from = params.body?.from || 0;
|
|
314
|
+
const size = params.body?.size || 10;
|
|
315
|
+
|
|
316
|
+
// Determine search term and type from the ES-style query
|
|
317
|
+
let rawTerm = '';
|
|
318
|
+
let searchFn = (term, limit) => this.searchChemicals(term, { limit });
|
|
319
|
+
|
|
320
|
+
if (query?.query_string?.query) {
|
|
321
|
+
const qs = query.query_string.query;
|
|
322
|
+
if (qs.endsWith('*')) {
|
|
323
|
+
rawTerm = qs.slice(0, -1);
|
|
324
|
+
searchFn = (term, limit) => this.searchStartsWith(term, limit);
|
|
325
|
+
} else {
|
|
326
|
+
rawTerm = qs;
|
|
327
|
+
}
|
|
328
|
+
} else if (query?.match_phrase_prefix) {
|
|
329
|
+
const key = Object.keys(query.match_phrase_prefix)[0];
|
|
330
|
+
const val = query.match_phrase_prefix[key];
|
|
331
|
+
rawTerm = (typeof val === 'object' ? val.query : val) || '';
|
|
332
|
+
searchFn = (term, limit) => this.searchContains(term, limit);
|
|
333
|
+
} else if (query?.match?.chemical_name) {
|
|
334
|
+
rawTerm = query.match.chemical_name;
|
|
335
|
+
} else if (query?.term?.chemical_name) {
|
|
336
|
+
rawTerm = query.term.chemical_name;
|
|
337
|
+
} else if (query?.match_all !== undefined) {
|
|
338
|
+
rawTerm = '*';
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const pegasusResults = await searchFn(rawTerm, from + size);
|
|
342
|
+
const hits = pegasusResults.results.slice(from);
|
|
343
|
+
|
|
344
|
+
return {
|
|
345
|
+
body: {
|
|
346
|
+
hits: {
|
|
347
|
+
hits: hits.map(r => ({
|
|
348
|
+
_id: r.id,
|
|
349
|
+
_source: {
|
|
350
|
+
chemical_name: r.name,
|
|
351
|
+
chemical_name_sensitive: r.name,
|
|
352
|
+
chemical_name_sort: (r.name || '').toLowerCase(),
|
|
353
|
+
chemical_identifier: [...(r.cas || []), ...(r.identifiers || [])],
|
|
354
|
+
chemical_set_identifier: (r.cas && r.cas[0]) || r.id || '',
|
|
355
|
+
}
|
|
356
|
+
})),
|
|
357
|
+
total: { value: pegasusResults.results.length, relation: 'eq' }
|
|
358
|
+
},
|
|
359
|
+
timed_out: false,
|
|
360
|
+
_shards: { total: 1, successful: 1, failed: 0 }
|
|
361
|
+
}
|
|
362
|
+
};
|
|
320
363
|
},
|
|
321
364
|
|
|
322
365
|
count: async (params) => {
|
|
323
366
|
const query = params.body?.query;
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
367
|
+
let rawTerm = query?.match?.chemical_name ||
|
|
368
|
+
query?.term?.chemical_name ||
|
|
369
|
+
query?.query_string?.query || '';
|
|
370
|
+
if (rawTerm.endsWith('*')) rawTerm = rawTerm.slice(0, -1);
|
|
371
|
+
|
|
372
|
+
const results = await this.searchChemicals(rawTerm, { limit: 10000 });
|
|
329
373
|
return { count: results.results.length };
|
|
330
374
|
}
|
|
331
375
|
});
|
package/lib/sync.js
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
class SyncService {
|
|
2
|
-
constructor(connection) {
|
|
3
|
-
this.connection = connection;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
async syncBatch(batchSize) {}
|
|
7
|
-
|
|
8
|
-
async syncAll() {}
|
|
9
|
-
|
|
10
|
-
async syncContinuous(intervalMs) {}
|
|
11
|
-
|
|
12
|
-
async stopContinuousSync() {}
|
|
13
|
-
|
|
14
|
-
async getPendingCount() {}
|
|
15
|
-
|
|
16
|
-
async getOldestPending() {}
|
|
17
|
-
|
|
18
|
-
async getSyncLag() {}
|
|
19
|
-
|
|
20
|
-
async getFailedEntries(minRetryCount) {}
|
|
21
|
-
|
|
22
|
-
async retryFailed(outboxId) {}
|
|
23
|
-
|
|
24
|
-
async retryAllFailed() {}
|
|
25
|
-
|
|
26
|
-
async markAsProcessed(outboxId) {}
|
|
27
|
-
|
|
28
|
-
async deleteProcessedOlderThan(days) {}
|
|
29
|
-
|
|
30
|
-
async cleanupOutbox(daysToKeep) {}
|
|
31
|
-
|
|
32
|
-
async getSyncStats(timeWindowMinutes) {}
|
|
33
|
-
|
|
34
|
-
async getSyncThroughput() {}
|
|
35
|
-
|
|
36
|
-
async verifySync(chemicalId) {}
|
|
37
|
-
|
|
38
|
-
async forceResync(chemicalId) {}
|
|
39
|
-
|
|
40
|
-
async getOutboxHealth() {}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
module.exports = SyncService;
|
|
1
|
+
class SyncService {
|
|
2
|
+
constructor(connection) {
|
|
3
|
+
this.connection = connection;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
async syncBatch(batchSize) {}
|
|
7
|
+
|
|
8
|
+
async syncAll() {}
|
|
9
|
+
|
|
10
|
+
async syncContinuous(intervalMs) {}
|
|
11
|
+
|
|
12
|
+
async stopContinuousSync() {}
|
|
13
|
+
|
|
14
|
+
async getPendingCount() {}
|
|
15
|
+
|
|
16
|
+
async getOldestPending() {}
|
|
17
|
+
|
|
18
|
+
async getSyncLag() {}
|
|
19
|
+
|
|
20
|
+
async getFailedEntries(minRetryCount) {}
|
|
21
|
+
|
|
22
|
+
async retryFailed(outboxId) {}
|
|
23
|
+
|
|
24
|
+
async retryAllFailed() {}
|
|
25
|
+
|
|
26
|
+
async markAsProcessed(outboxId) {}
|
|
27
|
+
|
|
28
|
+
async deleteProcessedOlderThan(days) {}
|
|
29
|
+
|
|
30
|
+
async cleanupOutbox(daysToKeep) {}
|
|
31
|
+
|
|
32
|
+
async getSyncStats(timeWindowMinutes) {}
|
|
33
|
+
|
|
34
|
+
async getSyncThroughput() {}
|
|
35
|
+
|
|
36
|
+
async verifySync(chemicalId) {}
|
|
37
|
+
|
|
38
|
+
async forceResync(chemicalId) {}
|
|
39
|
+
|
|
40
|
+
async getOutboxHealth() {}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = SyncService;
|
package/lib/utils.js
CHANGED
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
class UtilsService {
|
|
2
|
-
constructor(connection) {
|
|
3
|
-
this.connection = connection;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
async executeBatch(operations, batchSize, concurrency) {}
|
|
7
|
-
|
|
8
|
-
async withTransaction(callback) {}
|
|
9
|
-
|
|
10
|
-
async withRetry(operation, maxRetries, backoffMs) {}
|
|
11
|
-
|
|
12
|
-
validateChemical(chemical) {}
|
|
13
|
-
|
|
14
|
-
validateDocument(document) {}
|
|
15
|
-
|
|
16
|
-
validateIdentifier(identifier) {}
|
|
17
|
-
|
|
18
|
-
validateCAS(casNumber) {}
|
|
19
|
-
|
|
20
|
-
transformForOpenSearch(chemical) {}
|
|
21
|
-
|
|
22
|
-
transformFromElasticsearch(esDocument) {}
|
|
23
|
-
|
|
24
|
-
transformFromDynamoDB(dynamoItem) {}
|
|
25
|
-
|
|
26
|
-
buildOpenSearchQuery(searchTerm, searchType) {}
|
|
27
|
-
|
|
28
|
-
buildPostgresFilter(filters) {}
|
|
29
|
-
|
|
30
|
-
parseChemicalIdentifiers(identifiers) {}
|
|
31
|
-
|
|
32
|
-
parseSynonyms(synonyms) {}
|
|
33
|
-
|
|
34
|
-
extractCASFromText(text) {}
|
|
35
|
-
|
|
36
|
-
sanitizeSearchTerm(term) {}
|
|
37
|
-
|
|
38
|
-
generateSourceId(chemical) {}
|
|
39
|
-
|
|
40
|
-
calculateChecksum(data) {}
|
|
41
|
-
|
|
42
|
-
formatError(error) {}
|
|
43
|
-
|
|
44
|
-
logOperation(operation, duration, metadata) {}
|
|
45
|
-
|
|
46
|
-
getTimestamp() {}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
module.exports = UtilsService;
|
|
1
|
+
class UtilsService {
|
|
2
|
+
constructor(connection) {
|
|
3
|
+
this.connection = connection;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
async executeBatch(operations, batchSize, concurrency) {}
|
|
7
|
+
|
|
8
|
+
async withTransaction(callback) {}
|
|
9
|
+
|
|
10
|
+
async withRetry(operation, maxRetries, backoffMs) {}
|
|
11
|
+
|
|
12
|
+
validateChemical(chemical) {}
|
|
13
|
+
|
|
14
|
+
validateDocument(document) {}
|
|
15
|
+
|
|
16
|
+
validateIdentifier(identifier) {}
|
|
17
|
+
|
|
18
|
+
validateCAS(casNumber) {}
|
|
19
|
+
|
|
20
|
+
transformForOpenSearch(chemical) {}
|
|
21
|
+
|
|
22
|
+
transformFromElasticsearch(esDocument) {}
|
|
23
|
+
|
|
24
|
+
transformFromDynamoDB(dynamoItem) {}
|
|
25
|
+
|
|
26
|
+
buildOpenSearchQuery(searchTerm, searchType) {}
|
|
27
|
+
|
|
28
|
+
buildPostgresFilter(filters) {}
|
|
29
|
+
|
|
30
|
+
parseChemicalIdentifiers(identifiers) {}
|
|
31
|
+
|
|
32
|
+
parseSynonyms(synonyms) {}
|
|
33
|
+
|
|
34
|
+
extractCASFromText(text) {}
|
|
35
|
+
|
|
36
|
+
sanitizeSearchTerm(term) {}
|
|
37
|
+
|
|
38
|
+
generateSourceId(chemical) {}
|
|
39
|
+
|
|
40
|
+
calculateChecksum(data) {}
|
|
41
|
+
|
|
42
|
+
formatError(error) {}
|
|
43
|
+
|
|
44
|
+
logOperation(operation, duration, metadata) {}
|
|
45
|
+
|
|
46
|
+
getTimestamp() {}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = UtilsService;
|
package/package.json
CHANGED