@toxplanet/pegasus-sdk 1.0.0 → 1.0.2

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.
@@ -0,0 +1,27 @@
1
+ module.exports = {
2
+ environment: 'acc',
3
+ region: 'us-east-1',
4
+ secretName: 'arn:aws:secretsmanager:us-east-1:292931567094:secret:rds!cluster-b851c3ce-58cc-41cd-aeae-05cc7f5e031a-ZYSjiI',
5
+ openSearchEndpoint: 'https://war8lk73nzswquk8dcz1.us-east-1.aoss.amazonaws.com',
6
+ openSearchIndex: 'chemicals',
7
+ database: {
8
+ host: 'cr-chemicals.cluster-cz0iqdg8irhb.us-east-1.rds.amazonaws.com',
9
+ name: 'chemicals'
10
+ },
11
+ postgres: {
12
+ maxConnections: 2,
13
+ minConnections: 0,
14
+ idleTimeoutMillis: 30000,
15
+ connectionTimeoutMillis: 5000,
16
+ statementTimeout: 30000,
17
+ queryTimeout: 30000,
18
+ ssl: {
19
+ rejectUnauthorized: false
20
+ }
21
+ },
22
+ indexRoutes: {
23
+ chemicals: ['chemicals*'],
24
+ documents: ['documents*'],
25
+ search: [/^(chemicals|substances|search)/]
26
+ }
27
+ };
@@ -0,0 +1,27 @@
1
+ module.exports = {
2
+ environment: 'dev',
3
+ region: 'us-east-1',
4
+ secretName: 'arn:aws:secretsmanager:us-east-1:292931567094:secret:rds!cluster-b851c3ce-58cc-41cd-aeae-05cc7f5e031a-ZYSjiI',
5
+ openSearchEndpoint: 'https://war8lk73nzswquk8dcz1.us-east-1.aoss.amazonaws.com',
6
+ openSearchIndex: 'chemicals',
7
+ database: {
8
+ host: 'cr-chemicals.cluster-cz0iqdg8irhb.us-east-1.rds.amazonaws.com',
9
+ name: 'chemicals'
10
+ },
11
+ postgres: {
12
+ maxConnections: 2,
13
+ minConnections: 0,
14
+ idleTimeoutMillis: 30000,
15
+ connectionTimeoutMillis: 5000,
16
+ statementTimeout: 30000,
17
+ queryTimeout: 30000,
18
+ ssl: {
19
+ rejectUnauthorized: false
20
+ }
21
+ },
22
+ indexRoutes: {
23
+ chemicals: ['chemicals*'],
24
+ documents: ['documents*'],
25
+ search: [/^(chemicals|substances|search)/]
26
+ }
27
+ };
@@ -0,0 +1,27 @@
1
+ module.exports = {
2
+ environment: 'prod',
3
+ region: 'us-east-1',
4
+ secretName: 'pegasus/production/database',
5
+ openSearchEndpoint: null,
6
+ openSearchIndex: 'chemicals',
7
+ database: {
8
+ host: null,
9
+ name: 'chemicals'
10
+ },
11
+ postgres: {
12
+ maxConnections: 10,
13
+ minConnections: 2,
14
+ idleTimeoutMillis: 120000,
15
+ connectionTimeoutMillis: 15000,
16
+ statementTimeout: 120000,
17
+ queryTimeout: 120000,
18
+ ssl: {
19
+ rejectUnauthorized: true
20
+ }
21
+ },
22
+ indexRoutes: {
23
+ chemicals: ['chemicals*'],
24
+ documents: ['documents*'],
25
+ search: [/^(chemicals|substances|search)/]
26
+ }
27
+ };
@@ -0,0 +1,27 @@
1
+ module.exports = {
2
+ environment: 'qa',
3
+ region: 'us-east-1',
4
+ secretName: 'pegasus/qa/database',
5
+ openSearchEndpoint: null,
6
+ openSearchIndex: 'chemicals',
7
+ database: {
8
+ host: null,
9
+ name: 'chemicals'
10
+ },
11
+ postgres: {
12
+ maxConnections: 5,
13
+ minConnections: 1,
14
+ idleTimeoutMillis: 60000,
15
+ connectionTimeoutMillis: 10000,
16
+ statementTimeout: 60000,
17
+ queryTimeout: 60000,
18
+ ssl: {
19
+ rejectUnauthorized: true
20
+ }
21
+ },
22
+ indexRoutes: {
23
+ chemicals: ['chemicals*'],
24
+ documents: ['documents*'],
25
+ search: [/^(chemicals|substances|search)/]
26
+ }
27
+ };
@@ -0,0 +1,67 @@
1
+ const { logInfo } = require('@toxplanet/tphelper/logging');
2
+
3
+ function loadConfig(envOverride = null) {
4
+ const env = envOverride || process.env.NODE_ENV || 'dev';
5
+
6
+ let envConfig;
7
+ try {
8
+ envConfig = require(`./environment.${env}.js`);
9
+ } catch (error) {
10
+ logInfo('pegasus-sdk', `Warning: Could not load config for environment "${env}", falling back to development`);
11
+ envConfig = require('./environment.dev.js');
12
+ }
13
+
14
+ const config = { ...envConfig };
15
+
16
+ if (process.env.PEGASUS_SDK_DB_SECRET_ARN) {
17
+ config.secretName = process.env.PEGASUS_SDK_DB_SECRET_ARN;
18
+ }
19
+
20
+ if (process.env.PEGASUS_SDK_OPENSEARCH_ENDPOINT) {
21
+ config.openSearchEndpoint = process.env.PEGASUS_SDK_OPENSEARCH_ENDPOINT;
22
+ }
23
+
24
+ if (process.env.PEGASUS_SDK_OPENSEARCH_INDEX) {
25
+ config.openSearchIndex = process.env.PEGASUS_SDK_OPENSEARCH_INDEX;
26
+ }
27
+
28
+ if (process.env.PEGASUS_SDK_DATABASE_HOST) {
29
+ config.database.host = process.env.PEGASUS_SDK_DATABASE_HOST;
30
+ }
31
+
32
+ if (process.env.PEGASUS_SDK_DATABASE_NAME) {
33
+ config.database.name = process.env.PEGASUS_SDK_DATABASE_NAME;
34
+ }
35
+
36
+ if (process.env.PEGASUS_SDK_AWS_REGION) {
37
+ config.region = process.env.PEGASUS_SDK_AWS_REGION;
38
+ }
39
+
40
+ if (process.env.PEGASUS_SDK_MAX_CONNECTIONS) {
41
+ config.postgres.maxConnections = parseInt(process.env.PEGASUS_SDK_MAX_CONNECTIONS, 10);
42
+ }
43
+
44
+ if (process.env.PEGASUS_SDK_MIN_CONNECTIONS) {
45
+ config.postgres.minConnections = parseInt(process.env.PEGASUS_SDK_MIN_CONNECTIONS, 10);
46
+ }
47
+
48
+ if (process.env.PEGASUS_SDK_IDLE_TIMEOUT) {
49
+ config.postgres.idleTimeoutMillis = parseInt(process.env.PEGASUS_SDK_IDLE_TIMEOUT, 10);
50
+ }
51
+
52
+ if (process.env.PEGASUS_SDK_CONNECTION_TIMEOUT) {
53
+ config.postgres.connectionTimeoutMillis = parseInt(process.env.PEGASUS_SDK_CONNECTION_TIMEOUT, 10);
54
+ }
55
+
56
+ if (process.env.PEGASUS_SDK_STATEMENT_TIMEOUT) {
57
+ config.postgres.statementTimeout = parseInt(process.env.PEGASUS_SDK_STATEMENT_TIMEOUT, 10);
58
+ }
59
+
60
+ if (process.env.PEGASUS_SDK_QUERY_TIMEOUT) {
61
+ config.postgres.queryTimeout = parseInt(process.env.PEGASUS_SDK_QUERY_TIMEOUT, 10);
62
+ }
63
+
64
+ return config;
65
+ }
66
+
67
+ module.exports = { loadConfig };
package/index.js CHANGED
@@ -4,15 +4,21 @@ const DocumentsService = require('./lib/documents');
4
4
  const SearchService = require('./lib/search');
5
5
  const SyncService = require('./lib/sync');
6
6
  const UtilsService = require('./lib/utils');
7
+ const ElasticsearchService = require('./lib/elasticsearch');
7
8
 
8
9
  class PegasusSDK {
9
10
  constructor(config) {
10
11
  this.connection = new PegasusConnection(config);
12
+ this.elasticsearch = new ElasticsearchService(this.connection);
11
13
  this.chemicals = new ChemicalsService(this.connection);
12
14
  this.documents = new DocumentsService(this.connection);
13
15
  this.search = new SearchService(this.connection);
14
16
  this.sync = new SyncService(this.connection);
15
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);
16
22
  }
17
23
 
18
24
  async connect() {
@@ -35,3 +41,4 @@ module.exports.DocumentsService = DocumentsService;
35
41
  module.exports.SearchService = SearchService;
36
42
  module.exports.SyncService = SyncService;
37
43
  module.exports.UtilsService = UtilsService;
44
+ module.exports.ElasticsearchService = ElasticsearchService;
package/lib/chemicals.js CHANGED
@@ -1,5 +1,19 @@
1
+ const { logError } = require('@toxplanet/tphelper/logging');
2
+ const { getDrizzle, schema } = require('./db');
3
+ const { eq } = require('drizzle-orm');
4
+
1
5
  class ChemicalsService {
2
- constructor(connection) {}
6
+ constructor(connection) {
7
+ this.connection = connection;
8
+ this.db = null;
9
+ }
10
+
11
+ getDb() {
12
+ if (!this.db) {
13
+ this.db = getDrizzle(this.connection.pgPool);
14
+ }
15
+ return this.db;
16
+ }
3
17
 
4
18
  async bulkIndexFielded(documents) {}
5
19
 
@@ -7,11 +21,50 @@ class ChemicalsService {
7
21
 
8
22
  async bulkIndexSubstances(substances) {}
9
23
 
10
- async createChemical(chemical) {}
24
+ async createChemical(chemical) {
25
+ try {
26
+ const db = this.getDb();
27
+
28
+ const [result] = await db
29
+ .insert(schema.chemicals)
30
+ .values({
31
+ sourceId: chemical.source_id,
32
+ chemicalName: chemical.chemical_name,
33
+ chemicalMeta: chemical.chemical_meta,
34
+ chemicalIdentifiers: chemical.chemical_identifiers,
35
+ chemicalSynonyms: chemical.chemical_synonyms,
36
+ chemicalCategories: chemical.chemical_categories,
37
+ createdAt: chemical.created_at || new Date(),
38
+ updatedAt: chemical.updated_at || new Date(),
39
+ ...(chemical.imported_at && { importedAt: chemical.imported_at }),
40
+ ...(chemical.chemical_id && { chemicalId: chemical.chemical_id })
41
+ })
42
+ .returning();
43
+
44
+ return result;
45
+ } catch (error) {
46
+ logError('pegasus-sdk', 'ChemicalsService', 'createChemical', error);
47
+ throw error;
48
+ }
49
+ }
11
50
 
12
51
  async updateChemical(chemicalId, updates) {}
13
52
 
14
- async deleteChemical(chemicalId) {}
53
+ async deleteChemical(chemicalId) {
54
+ try {
55
+ const db = this.getDb();
56
+
57
+ const [deleted] = await db
58
+ .delete(schema.chemicals)
59
+ .where(eq(schema.chemicals.chemicalId, chemicalId))
60
+ .returning();
61
+
62
+ return deleted || null;
63
+ } catch (error) {
64
+ logError('pegasus-sdk', 'ChemicalsService', 'deleteChemical', error);
65
+ throw error;
66
+ }
67
+ }
15
68
 
16
69
  async deleteBySourceId(sourceId) {}
17
70
 
@@ -21,7 +74,22 @@ class ChemicalsService {
21
74
 
22
75
  async bulkUpdateProperty(filter, propertyPath, newValue) {}
23
76
 
24
- async getChemicalById(chemicalId) {}
77
+ async getChemicalById(chemicalId) {
78
+ try {
79
+ const db = this.getDb();
80
+
81
+ const [result] = await db
82
+ .select()
83
+ .from(schema.chemicals)
84
+ .where(eq(schema.chemicals.chemicalId, chemicalId))
85
+ .limit(1);
86
+
87
+ return result || null;
88
+ } catch (error) {
89
+ logError('pegasus-sdk', 'ChemicalsService', 'getChemicalById', error);
90
+ throw error;
91
+ }
92
+ }
25
93
 
26
94
  async getChemicalBySourceId(sourceId) {}
27
95
 
@@ -29,8 +97,6 @@ class ChemicalsService {
29
97
 
30
98
  async getChemicalsByIdentifier(identifierType, identifierValue) {}
31
99
 
32
- async countAll() {}
33
-
34
100
  async countByCollection(collectionName) {}
35
101
 
36
102
  async countByIdentifier(identifierValue) {}
@@ -45,13 +111,183 @@ class ChemicalsService {
45
111
 
46
112
  async convertIdentifiersBatch(fromIdentifiers, toIdentifierType) {}
47
113
 
48
- async searchByName(searchTerm, limit) {}
114
+ /**
115
+ * Search for chemicals by name using OpenSearch
116
+ * @param {string} searchTerm - Name to search for
117
+ * @param {number} limit - Maximum number of results (default: 10)
118
+ * @returns {Promise<Object>} Search results
119
+ */
120
+ async searchByName(searchTerm, limit = 10) {
121
+ if (!searchTerm) {
122
+ return { results: [] };
123
+ }
124
+
125
+ try {
126
+ const opensearchClient = this.connection.getOpenSearchClient();
127
+ const indexName = this.connection.getOpenSearchIndex();
128
+
129
+ const response = await opensearchClient.search({
130
+ index: indexName,
131
+ body: {
132
+ size: limit,
133
+ query: {
134
+ bool: {
135
+ should: [
136
+ // Prioritize exact name matches
137
+ { term: { 'chemical_name.keyword': { value: searchTerm, boost: 100, case_insensitive: true } } },
138
+ // Then prefix matches
139
+ { prefix: { 'chemical_name.keyword': { value: searchTerm, boost: 50, case_insensitive: true } } },
140
+ // Include synonym matches as secondary
141
+ { term: { 'synonyms.keyword': { value: searchTerm, boost: 30, case_insensitive: true } } },
142
+ { prefix: { 'synonyms.keyword': { value: searchTerm, boost: 10, case_insensitive: true } } }
143
+ ],
144
+ minimum_should_match: 1
145
+ }
146
+ },
147
+ _source: ['postgres_id', 'chemical_name', 'cas_numbers', 'identifier_values', 'synonyms']
148
+ }
149
+ });
49
150
 
50
- async searchBySynonym(synonymTerm, limit) {}
151
+ const hits = response.body?.hits?.hits || [];
152
+ const results = hits.map((hit) => ({
153
+ id: hit._source.postgres_id,
154
+ name: hit._source.chemical_name,
155
+ cas: hit._source.cas_numbers || [],
156
+ identifiers: hit._source.identifier_values || [],
157
+ synonyms: hit._source.synonyms || [],
158
+ score: hit._score
159
+ }));
160
+
161
+ return { results };
162
+ } catch (error) {
163
+ logError('pegasus-sdk', 'ChemicalsService', 'searchByName', error);
164
+ throw error;
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Search for chemicals by synonym using OpenSearch
170
+ * @param {string} synonymTerm - Synonym to search for
171
+ * @param {number} limit - Maximum number of results (default: 10)
172
+ * @returns {Promise<Object>} Search results
173
+ */
174
+ async searchBySynonym(synonymTerm, limit = 10) {
175
+ if (!synonymTerm) {
176
+ return { results: [] };
177
+ }
178
+
179
+ try {
180
+ const opensearchClient = this.connection.getOpenSearchClient();
181
+ const indexName = this.connection.getOpenSearchIndex();
182
+
183
+ const response = await opensearchClient.search({
184
+ index: indexName,
185
+ body: {
186
+ size: limit,
187
+ query: {
188
+ bool: {
189
+ should: [
190
+ // Prioritize exact synonym matches
191
+ { term: { 'synonyms.keyword': { value: synonymTerm, boost: 100, case_insensitive: true } } },
192
+ // Then prefix matches
193
+ { prefix: { 'synonyms.keyword': { value: synonymTerm, boost: 50, case_insensitive: true } } },
194
+ // Include name matches as secondary
195
+ { term: { 'chemical_name.keyword': { value: synonymTerm, boost: 30, case_insensitive: true } } },
196
+ { prefix: { 'chemical_name.keyword': { value: synonymTerm, boost: 10, case_insensitive: true } } }
197
+ ],
198
+ minimum_should_match: 1
199
+ }
200
+ },
201
+ _source: ['postgres_id', 'chemical_name', 'cas_numbers', 'identifier_values', 'synonyms']
202
+ }
203
+ });
204
+
205
+ const hits = response.body?.hits?.hits || [];
206
+ const results = hits.map((hit) => ({
207
+ id: hit._source.postgres_id,
208
+ name: hit._source.chemical_name,
209
+ cas: hit._source.cas_numbers || [],
210
+ identifiers: hit._source.identifier_values || [],
211
+ synonyms: hit._source.synonyms || [],
212
+ score: hit._score
213
+ }));
214
+
215
+ return { results };
216
+ } catch (error) {
217
+ logError('pegasus-sdk', 'ChemicalsService', 'searchBySynonym', error);
218
+ throw error;
219
+ }
220
+ }
221
+
222
+ async countAll() {
223
+ try {
224
+ const db = this.getDb();
225
+ const result = await db.select({ count: sql`count(*)::int` }).from(schema.chemicals);
226
+ return { count: result[0].count };
227
+ } catch (error) {
228
+ logError('pegasus-sdk', 'ChemicalsService', 'countAll', error);
229
+ throw error;
230
+ }
231
+ }
51
232
 
52
233
  async findChemicalsWithoutDocuments(collectionName, searchTerm, pageSize) {}
53
234
 
54
235
  async countChemicalsWithoutDocuments(collectionName) {}
236
+
237
+ registerElasticsearchHandlers(elasticsearchService) {
238
+ const indexPatterns = this.connection.config.indexRoutes?.chemicals || ['chemicals*'];
239
+
240
+ indexPatterns.forEach(pattern => {
241
+ elasticsearchService.registerIndexRoute(pattern, {
242
+ index: async (params) => {
243
+ const chemical = params.body;
244
+ return await this.createChemical(chemical);
245
+ },
246
+
247
+ bulk: async (params) => {
248
+ const operations = params.body || params.operations;
249
+ const documents = [];
250
+
251
+ for (let i = 0; i < operations.length; i += 2) {
252
+ const action = operations[i];
253
+ const document = operations[i + 1];
254
+
255
+ if (action.index || action.create) {
256
+ documents.push(document);
257
+ }
258
+ }
259
+
260
+ return await this.bulkIndexFielded(documents);
261
+ },
262
+
263
+ get: async (params) => {
264
+ return await this.getChemicalById(params.id);
265
+ },
266
+
267
+ update: async (params) => {
268
+ return await this.updateChemical(params.id, params.body);
269
+ },
270
+
271
+ delete: async (params) => {
272
+ return await this.deleteChemical(params.id);
273
+ },
274
+
275
+ search: async (params) => {
276
+ const query = params.body?.query;
277
+ const searchTerm = query?.match?.chemical_name ||
278
+ query?.term?.chemical_name ||
279
+ query?.query_string?.query || '';
280
+ const limit = params.body?.size || 10;
281
+
282
+ return await this.searchByName(searchTerm, limit);
283
+ },
284
+
285
+ count: async (params) => {
286
+ return await this.countAll();
287
+ }
288
+ });
289
+ });
290
+ }
55
291
  }
56
292
 
57
293
  module.exports = ChemicalsService;
package/lib/connection.js CHANGED
@@ -2,15 +2,22 @@ const { Pool } = require('pg');
2
2
  const { Client } = require('@opensearch-project/opensearch');
3
3
  const { SecretsManagerClient, GetSecretValueCommand } = require('@aws-sdk/client-secrets-manager');
4
4
  const { AwsSigv4Signer } = require('@opensearch-project/opensearch/aws');
5
- const { defaultProvider } = require('@aws-sdk/credential-providers');
5
+ const { fromNodeProviderChain } = require('@aws-sdk/credential-providers');
6
+ const { loadConfig } = require('../config');
7
+ const { logInfo, logError } = require('@toxplanet/tphelper/logging');
6
8
 
7
9
  class PegasusConnection {
8
10
  constructor(config = {}) {
9
- this.config = config;
10
- this.environment = config.environment || process.env.NODE_ENV || 'development';
11
- this.region = config.region || process.env.AWS_REGION || 'us-east-1';
12
- this.secretName = config.secretName || `pegasus/${this.environment}/database`;
13
- this.openSearchEndpoint = config.openSearchEndpoint || process.env.OPENSEARCH_ENDPOINT;
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;
14
21
 
15
22
  this.pgPool = null;
16
23
  this.osClient = null;
@@ -47,35 +54,33 @@ class PegasusConnection {
47
54
  const secret = await this.getSecret();
48
55
 
49
56
  const poolConfig = {
50
- host: secret.host || secret.endpoint,
51
- port: secret.port || 5432,
52
- database: secret.database || secret.dbname || 'chemicals',
53
- user: secret.username || secret.user,
57
+ host: this.databaseHost,
58
+ port: 5432,
59
+ database: this.databaseName,
60
+ user: secret.username,
54
61
  password: secret.password,
55
- max: this.config.maxConnections || 2,
56
- min: this.config.minConnections || 0,
57
- idleTimeoutMillis: this.config.idleTimeoutMillis || 30000,
58
- connectionTimeoutMillis: this.config.connectionTimeoutMillis || 5000,
62
+ max: this.config.postgres.maxConnections,
63
+ min: this.config.postgres.minConnections,
64
+ idleTimeoutMillis: this.config.postgres.idleTimeoutMillis,
65
+ connectionTimeoutMillis: this.config.postgres.connectionTimeoutMillis,
59
66
  allowExitOnIdle: true,
60
- ssl: {
61
- rejectUnauthorized: true
62
- },
63
- statement_timeout: this.config.statementTimeout || 30000,
64
- query_timeout: this.config.queryTimeout || 30000
67
+ ssl: this.config.postgres.ssl,
68
+ statement_timeout: this.config.postgres.statementTimeout,
69
+ query_timeout: this.config.postgres.queryTimeout
65
70
  };
66
71
 
67
72
  this.pgPool = new Pool(poolConfig);
68
73
 
69
74
  this.pgPool.on('error', (err) => {
70
- console.error('Unexpected PostgreSQL pool error:', err);
75
+ logError('pegasus-sdk', 'PegasusConnection', 'pgPool.error', err);
71
76
  });
72
77
 
73
78
  this.pgPool.on('connect', () => {
74
- console.log('PostgreSQL client connected');
79
+ logInfo('pegasus-sdk', 'PostgreSQL client connected');
75
80
  });
76
81
 
77
82
  this.pgPool.on('remove', () => {
78
- console.log('PostgreSQL client removed from pool');
83
+ logInfo('pegasus-sdk', 'PostgreSQL client removed from pool');
79
84
  });
80
85
 
81
86
  if (this.openSearchEndpoint) {
@@ -84,7 +89,7 @@ class PegasusConnection {
84
89
  region: this.region,
85
90
  service: 'aoss',
86
91
  getCredentials: () => {
87
- const credentialsProvider = defaultProvider();
92
+ const credentialsProvider = fromNodeProviderChain();
88
93
  return credentialsProvider();
89
94
  }
90
95
  }),
@@ -124,6 +129,10 @@ class PegasusConnection {
124
129
  return this.osClient;
125
130
  }
126
131
 
132
+ getOpenSearchIndex() {
133
+ return this.openSearchIndex || 'chemicals';
134
+ }
135
+
127
136
  async testConnection() {
128
137
  try {
129
138
  if (this.pgPool) {
@@ -143,11 +152,19 @@ class PegasusConnection {
143
152
  let osStatus = null;
144
153
  if (this.osClient) {
145
154
  try {
146
- const osInfo = await this.osClient.info();
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
+ });
147
165
  osStatus = {
148
166
  connected: true,
149
- version: osInfo.body.version.number,
150
- cluster: osInfo.body.cluster_name
167
+ resultsFound: testSearch.body.hits.total.value || 0
151
168
  };
152
169
  } catch (osError) {
153
170
  osStatus = {
@@ -0,0 +1,11 @@
1
+ const { drizzle } = require('drizzle-orm/node-postgres');
2
+ const schema = require('./schema');
3
+
4
+ function getDrizzle(pgPool) {
5
+ return drizzle(pgPool, { schema });
6
+ }
7
+
8
+ module.exports = {
9
+ getDrizzle,
10
+ schema
11
+ };
@@ -0,0 +1,28 @@
1
+ const { pgTable, uuid, text, jsonb, timestamp, index, uniqueIndex } = require('drizzle-orm/pg-core');
2
+ const { sql } = require('drizzle-orm');
3
+
4
+ const chemicals = pgTable('chemicals', {
5
+ chemicalId: uuid('chemical_id').defaultRandom().primaryKey(),
6
+ sourceId: text('source_id').notNull().unique(),
7
+ chemicalName: text('chemical_name').notNull(),
8
+ chemicalMeta: jsonb('chemical_meta'),
9
+ chemicalIdentifiers: jsonb('chemical_identifiers'),
10
+ chemicalSynonyms: text('chemical_synonyms').array(),
11
+ chemicalCategories: text('chemical_categories').array(),
12
+ createdAt: timestamp('created_at', { withTimezone: true }).notNull(),
13
+ updatedAt: timestamp('updated_at', { withTimezone: true }).notNull(),
14
+ importedAt: timestamp('imported_at', { withTimezone: true }).defaultNow()
15
+ }, (table) => {
16
+ return {
17
+ sourceIdIdx: uniqueIndex('idx_chemicals_source_id').on(table.sourceId),
18
+ nameIdx: index('idx_chemicals_name').on(table.chemicalName),
19
+ createdAtIdx: index('idx_chemicals_created_at').on(table.createdAt),
20
+ updatedAtIdx: index('idx_chemicals_updated_at').on(table.updatedAt),
21
+ identifiersGinIdx: index('idx_chemicals_identifiers_gin').on(table.chemicalIdentifiers),
22
+ synonymsGinIdx: index('idx_chemicals_synonyms_gin').on(table.chemicalSynonyms)
23
+ };
24
+ });
25
+
26
+ module.exports = {
27
+ chemicals
28
+ };