@acodeninja/persist 2.2.1 → 2.2.3

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.
@@ -1,8 +1,21 @@
1
1
  import Engine, {EngineError, MissConfiguredError} from './Engine.js';
2
2
 
3
- export class HTTPEngineError extends EngineError {
4
- }
3
+ /**
4
+ * Represents an error specific to HTTP engine operations.
5
+ * @class HTTPEngineError
6
+ * @extends EngineError
7
+ */
8
+ export class HTTPEngineError extends EngineError {}
5
9
 
10
+ /**
11
+ * Error indicating a failed HTTP request.
12
+ * @class HTTPRequestFailedError
13
+ * @extends HTTPEngineError
14
+ *
15
+ * @param {string} url - The URL of the failed request.
16
+ * @param {Object} options - The options used in the fetch request.
17
+ * @param {Response} response - The HTTP response object.
18
+ */
6
19
  export class HTTPRequestFailedError extends HTTPEngineError {
7
20
  constructor(url, options, response) {
8
21
  const method = options.method?.toLowerCase() || 'get';
@@ -13,7 +26,25 @@ export class HTTPRequestFailedError extends HTTPEngineError {
13
26
  }
14
27
  }
15
28
 
16
- export default class HTTPEngine extends Engine {
29
+ /**
30
+ * HTTPEngine is an extension of the Engine class that provides methods for interacting with HTTP-based APIs.
31
+ * It uses the Fetch API for sending and receiving data.
32
+ *
33
+ * @class HTTPEngine
34
+ * @extends Engine
35
+ */
36
+ class HTTPEngine extends Engine {
37
+
38
+ /**
39
+ * Configures the HTTP engine with additional fetch options.
40
+ * Sets the Accept header to 'application/json' by default.
41
+ *
42
+ * @param {Object} configuration - Configuration object containing fetch options and other settings.
43
+ * @param {string} [configuration.host] - Hostname and protocol of the HTTP service to use (ie: https://example.com).
44
+ * @param {string?} [configuration.prefix] - The prefix on the host to perform operations against.
45
+ * @param {Object} [configuration.fetchOptions] - Fetch overrides to attach to all requests sent to the HTTP service.
46
+ * @returns {Object} The configured settings for the HTTP engine.
47
+ */
17
48
  static configure(configuration = {}) {
18
49
  configuration.fetchOptions = {
19
50
  ...(configuration.fetchOptions ?? {}),
@@ -26,16 +57,30 @@ export default class HTTPEngine extends Engine {
26
57
  return super.configure(configuration);
27
58
  }
28
59
 
60
+ /**
61
+ * Validates the engine's configuration, ensuring that the host is defined.
62
+ *
63
+ * @throws {MissConfiguredError} Thrown if the configuration is missing a host.
64
+ */
29
65
  static checkConfiguration() {
30
- if (
31
- !this._configuration?.host
32
- ) throw new MissConfiguredError(this._configuration);
66
+ if (!this.configuration?.host) throw new MissConfiguredError(this.configuration);
33
67
  }
34
68
 
69
+ /**
70
+ * Returns the fetch options for reading operations.
71
+ *
72
+ * @returns {Object} The fetch options for reading.
73
+ */
35
74
  static _getReadOptions() {
36
- return this._configuration.fetchOptions;
75
+ return this.configuration.fetchOptions;
37
76
  }
38
77
 
78
+ /**
79
+ * Returns the fetch options for writing (PUT) operations.
80
+ * Sets the method to PUT and adds a Content-Type header of 'application/json'.
81
+ *
82
+ * @returns {Object} The fetch options for writing.
83
+ */
39
84
  static _getWriteOptions() {
40
85
  return {
41
86
  ...this._getReadOptions(),
@@ -47,8 +92,18 @@ export default class HTTPEngine extends Engine {
47
92
  };
48
93
  }
49
94
 
50
- static async _processFetch(url, options, defaultValue = undefined) {
51
- return this._configuration.fetch(url, options)
95
+ /**
96
+ * Processes a fetch request with error handling. Throws an error if the response is not successful.
97
+ *
98
+ * @param {string|URL} url - The URL to fetch.
99
+ * @param {Object} options - The fetch options.
100
+ * @param {*} [defaultValue] - A default value to return if the fetch fails.
101
+ * @returns {Promise<Object>} The parsed JSON response.
102
+ *
103
+ * @throws {HTTPRequestFailedError} Thrown if the fetch request fails.
104
+ */
105
+ static _processFetch(url, options, defaultValue = undefined) {
106
+ return this.configuration.fetch(url, options)
52
107
  .then(response => {
53
108
  if (!response.ok) {
54
109
  if (defaultValue !== undefined) {
@@ -63,40 +118,64 @@ export default class HTTPEngine extends Engine {
63
118
  .then(r => r.json());
64
119
  }
65
120
 
121
+ /**
122
+ * Retrieves an object by its ID from the server.
123
+ *
124
+ * @param {string} id - The ID of the object to retrieve.
125
+ * @returns {Promise<Object>} The retrieved object in JSON format.
126
+ *
127
+ * @throws {HTTPRequestFailedError} Thrown if the fetch request fails.
128
+ */
66
129
  static async getById(id) {
67
130
  this.checkConfiguration();
68
131
 
69
132
  const url = new URL([
70
- this._configuration.host,
71
- this._configuration.prefix,
133
+ this.configuration.host,
134
+ this.configuration.prefix,
72
135
  `${id}.json`,
73
- ].filter(e => !!e).join('/'));
136
+ ].filter(e => Boolean(e)).join('/'));
74
137
 
75
138
  return await this._processFetch(url, this._getReadOptions());
76
139
  }
77
140
 
78
- static async putModel(model) {
141
+ /**
142
+ * Uploads (puts) a model object to the server.
143
+ *
144
+ * @param {Model} model - The model object to upload.
145
+ * @returns {Promise<Object>} The response from the server.
146
+ *
147
+ * @throws {HTTPRequestFailedError} Thrown if the PUT request fails.
148
+ */
149
+ static putModel(model) {
79
150
  const url = new URL([
80
- this._configuration.host,
81
- this._configuration.prefix,
151
+ this.configuration.host,
152
+ this.configuration.prefix,
82
153
  `${model.id}.json`,
83
- ].filter(e => !!e).join('/'));
154
+ ].filter(e => Boolean(e)).join('/'));
84
155
 
85
- return await this._processFetch(url, {
156
+ return this._processFetch(url, {
86
157
  ...this._getWriteOptions(),
87
158
  body: JSON.stringify(model.toData()),
88
159
  });
89
160
  }
90
161
 
162
+ /**
163
+ * Uploads (puts) an index object to the server.
164
+ *
165
+ * @param {Object} index - The index data to upload, organized by location.
166
+ * @returns {Promise<void>}
167
+ *
168
+ * @throws {HTTPRequestFailedError} Thrown if the PUT request fails.
169
+ */
91
170
  static async putIndex(index) {
92
171
  const processIndex = async (location, models) => {
93
172
  const modelIndex = Object.fromEntries(models.map(m => [m.id, m.toIndexData()]));
94
173
  const url = new URL([
95
- this._configuration.host,
96
- this._configuration.prefix,
174
+ this.configuration.host,
175
+ this.configuration.prefix,
97
176
  location,
98
177
  '_index.json',
99
- ].filter(e => !!e).join('/'));
178
+ ].filter(e => Boolean(e)).join('/'));
100
179
 
101
180
  return await this._processFetch(url, {
102
181
  ...this._getWriteOptions(),
@@ -114,16 +193,33 @@ export default class HTTPEngine extends Engine {
114
193
  await processIndex(null, Object.values(index).flat());
115
194
  }
116
195
 
117
- static async getIndex(location) {
118
- const url = new URL([this._configuration.host, this._configuration.prefix, location, '_index.json'].filter(e => !!e).join('/'));
196
+ /**
197
+ * Retrieves the index object from the server for the specified location.
198
+ *
199
+ * @param {Model.constructor?} model - The model in the host where the index is stored.
200
+ * @returns {Promise<Object>} The index data in JSON format.
201
+ */
202
+ static async getIndex(model) {
203
+ const url = new URL([
204
+ this.configuration.host,
205
+ this.configuration.prefix,
206
+ model?.toString(),
207
+ '_index.json',
208
+ ].filter(e => Boolean(e)).join('/'));
119
209
 
120
210
  return await this._processFetch(url, this._getReadOptions(), {});
121
211
  }
122
212
 
213
+ /**
214
+ * Retrieves the compiled search index for a model from the server.
215
+ *
216
+ * @param {Model.constructor} model - The model whose compiled search index to retrieve.
217
+ * @returns {Promise<Object>} The compiled search index in JSON format.
218
+ */
123
219
  static async getSearchIndexCompiled(model) {
124
220
  const url = new URL([
125
- this._configuration.host,
126
- this._configuration.prefix,
221
+ this.configuration.host,
222
+ this.configuration.prefix,
127
223
  model.toString(),
128
224
  '_search_index.json',
129
225
  ].join('/'));
@@ -131,10 +227,16 @@ export default class HTTPEngine extends Engine {
131
227
  return await this._processFetch(url, this._getReadOptions());
132
228
  }
133
229
 
230
+ /**
231
+ * Retrieves the raw (uncompiled) search index for a model from the server.
232
+ *
233
+ * @param {Model.constructor} model - The model whose raw search index to retrieve.
234
+ * @returns {Promise<Object>} The raw search index in JSON format, or an empty object if not found.
235
+ */
134
236
  static async getSearchIndexRaw(model) {
135
237
  const url = new URL([
136
- this._configuration.host,
137
- this._configuration.prefix,
238
+ this.configuration.host,
239
+ this.configuration.prefix,
138
240
  model.toString(),
139
241
  '_search_index_raw.json',
140
242
  ].join('/'));
@@ -142,13 +244,22 @@ export default class HTTPEngine extends Engine {
142
244
  return await this._processFetch(url, this._getReadOptions()).catch(() => ({}));
143
245
  }
144
246
 
247
+ /**
248
+ * Uploads (puts) a compiled search index for a model to the server.
249
+ *
250
+ * @param {Model.constructor} model - The model whose compiled search index to upload.
251
+ * @param {Object} compiledIndex - The compiled search index data.
252
+ * @returns {Promise<Object>} The response from the server.
253
+ *
254
+ * @throws {HTTPRequestFailedError} Thrown if the PUT request fails.
255
+ */
145
256
  static async putSearchIndexCompiled(model, compiledIndex) {
146
257
  const url = new URL([
147
- this._configuration.host,
148
- this._configuration.prefix,
149
- model.name,
258
+ this.configuration.host,
259
+ this.configuration.prefix,
260
+ model.toString(),
150
261
  '_search_index.json',
151
- ].filter(e => !!e).join('/'));
262
+ ].filter(e => Boolean(e)).join('/'));
152
263
 
153
264
  return this._processFetch(url, {
154
265
  ...this._getWriteOptions(),
@@ -156,13 +267,22 @@ export default class HTTPEngine extends Engine {
156
267
  });
157
268
  }
158
269
 
270
+ /**
271
+ * Uploads (puts) a raw search index for a model to the server.
272
+ *
273
+ * @param {Model.constructor} model - The model whose raw search index to upload.
274
+ * @param {Object} rawIndex - The raw search index data.
275
+ * @returns {Promise<Object>} The response from the server.
276
+ *
277
+ * @throws {HTTPRequestFailedError} Thrown if the PUT request fails.
278
+ */
159
279
  static async putSearchIndexRaw(model, rawIndex) {
160
280
  const url = new URL([
161
- this._configuration.host,
162
- this._configuration.prefix,
163
- model.name,
281
+ this.configuration.host,
282
+ this.configuration.prefix,
283
+ model.toString(),
164
284
  '_search_index_raw.json',
165
- ].filter(e => !!e).join('/'));
285
+ ].filter(e => Boolean(e)).join('/'));
166
286
 
167
287
  return await this._processFetch(url, {
168
288
  ...this._getWriteOptions(),
@@ -170,3 +290,5 @@ export default class HTTPEngine extends Engine {
170
290
  });
171
291
  }
172
292
  }
293
+
294
+ export default HTTPEngine;
@@ -1,68 +1,134 @@
1
1
  import Engine, {EngineError, MissConfiguredError} from './Engine.js';
2
2
  import {GetObjectCommand, PutObjectCommand} from '@aws-sdk/client-s3';
3
3
 
4
+ /**
5
+ * Represents an error specific to the S3 engine operations.
6
+ * @class S3EngineError
7
+ * @extends EngineError
8
+ */
4
9
  class S3EngineError extends EngineError {}
5
10
 
11
+ /**
12
+ * Error indicating a failure when putting an object to S3.
13
+ * @class FailedPutS3EngineError
14
+ * @extends S3EngineError
15
+ */
6
16
  class FailedPutS3EngineError extends S3EngineError {}
7
17
 
8
- export default class S3Engine extends Engine {
18
+ /**
19
+ * S3Engine is an extension of the Engine class that provides methods for interacting with AWS S3.
20
+ * It allows for storing, retrieving, and managing model data in an S3 bucket.
21
+ *
22
+ * @class S3Engine
23
+ * @extends Engine
24
+ */
25
+ class S3Engine extends Engine {
26
+ /**
27
+ * Configures the S3 engine with additional options.
28
+ *
29
+ * @param {Object} configuration - Configuration object.
30
+ * @param {S3Client} [configuration.client] - An S3 client used to process operations.
31
+ * @param {string} [configuration.bucket] - The S3 bucket to perform operations against.
32
+ * @param {string?} [configuration.prefix] - The optional prefix in the bucket to perform operations against.
33
+ * @returns {Object} The configured settings for the HTTP engine.
34
+ */
35
+ static configure(configuration = {}) {
36
+ return super.configure(configuration);
37
+ }
38
+
39
+ /**
40
+ * Validates the S3 engine configuration to ensure necessary parameters (bucket and client) are present.
41
+ * Throws an error if the configuration is invalid.
42
+ *
43
+ * @throws {MissConfiguredError} Thrown when the configuration is missing required parameters.
44
+ */
9
45
  static checkConfiguration() {
10
46
  if (
11
- !this._configuration?.bucket ||
12
- !this._configuration?.client
13
- ) throw new MissConfiguredError(this._configuration);
47
+ !this.configuration?.bucket ||
48
+ !this.configuration?.client
49
+ ) throw new MissConfiguredError(this.configuration);
14
50
  }
15
51
 
52
+ /**
53
+ * Retrieves an object from S3 by its ID.
54
+ *
55
+ * @param {string} id - The ID of the object to retrieve.
56
+ * @returns {Promise<Object>} The parsed JSON object retrieved from S3.
57
+ *
58
+ * @throws {Error} Thrown if there is an issue with the S3 client request.
59
+ */
16
60
  static async getById(id) {
17
- const objectPath = [this._configuration.prefix, `${id}.json`].join('/');
61
+ const objectPath = [this.configuration.prefix, `${id}.json`].join('/');
18
62
 
19
- const data = await this._configuration.client.send(new GetObjectCommand({
20
- Bucket: this._configuration.bucket,
63
+ const data = await this.configuration.client.send(new GetObjectCommand({
64
+ Bucket: this.configuration.bucket,
21
65
  Key: objectPath,
22
66
  }));
23
67
 
24
68
  return JSON.parse(await data.Body.transformToString());
25
69
  }
26
70
 
71
+ /**
72
+ * Puts (uploads) a model object to S3.
73
+ *
74
+ * @param {Model} model - The model object to upload.
75
+ * @returns {Promise<void>}
76
+ *
77
+ * @throws {FailedPutS3EngineError} Thrown if there is an error during the S3 PutObject operation.
78
+ */
27
79
  static async putModel(model) {
28
- const Key = [this._configuration.prefix, `${model.id}.json`].join('/');
80
+ const Key = [this.configuration.prefix, `${model.id}.json`].join('/');
29
81
 
30
82
  try {
31
- await this._configuration.client.send(new PutObjectCommand({
83
+ await this.configuration.client.send(new PutObjectCommand({
32
84
  Key,
33
85
  Body: JSON.stringify(model.toData()),
34
- Bucket: this._configuration.bucket,
86
+ Bucket: this.configuration.bucket,
35
87
  ContentType: 'application/json',
36
88
  }));
37
89
  } catch (error) {
38
- throw new FailedPutS3EngineError(`Failed to put s3://${this._configuration.bucket}/${Key}`, error);
90
+ throw new FailedPutS3EngineError(`Failed to put s3://${this.configuration.bucket}/${Key}`, error);
39
91
  }
40
92
  }
41
93
 
42
- static async getIndex(location) {
94
+ /**
95
+ * Retrieves the index object from S3 at the specified location.
96
+ *
97
+ * @param {Model.constructor?} model - The model in the bucket where the index is stored.
98
+ * @returns {Promise<Object>} The parsed index object.
99
+ */
100
+ static async getIndex(model) {
43
101
  try {
44
- const data = await this._configuration.client.send(new GetObjectCommand({
45
- Key: [this._configuration.prefix, location, '_index.json'].filter(e => !!e).join('/'),
46
- Bucket: this._configuration.bucket,
102
+ const data = await this.configuration.client.send(new GetObjectCommand({
103
+ Key: [this.configuration.prefix, model?.toString(), '_index.json'].filter(e => Boolean(e)).join('/'),
104
+ Bucket: this.configuration.bucket,
47
105
  }));
48
106
 
49
107
  return JSON.parse(await data.Body.transformToString());
50
- } catch (_) {
108
+ } catch (_error) {
51
109
  return {};
52
110
  }
53
111
  }
54
112
 
113
+ /**
114
+ * Puts (uploads) an index object to S3.
115
+ *
116
+ * @param {Object} index - The index data to upload, organized by location.
117
+ * @returns {Promise<void>}
118
+ *
119
+ * @throws {FailedPutS3EngineError} Thrown if there is an error during the S3 PutObject operation.
120
+ */
55
121
  static async putIndex(index) {
56
122
  const processIndex = async (location, models) => {
57
123
  const modelIndex = Object.fromEntries(models.map(m => [m.id, m.toIndexData()]));
58
- const Key = [this._configuration.prefix, location, '_index.json'].filter(e => !!e).join('/');
124
+ const Key = [this.configuration.prefix, location, '_index.json'].filter(e => Boolean(e)).join('/');
59
125
 
60
126
  const currentIndex = await this.getIndex(location);
61
127
 
62
128
  try {
63
- await this._configuration.client.send(new PutObjectCommand({
129
+ await this.configuration.client.send(new PutObjectCommand({
64
130
  Key,
65
- Bucket: this._configuration.bucket,
131
+ Bucket: this.configuration.bucket,
66
132
  ContentType: 'application/json',
67
133
  Body: JSON.stringify({
68
134
  ...currentIndex,
@@ -70,7 +136,7 @@ export default class S3Engine extends Engine {
70
136
  }),
71
137
  }));
72
138
  } catch (error) {
73
- throw new FailedPutS3EngineError(`Failed to put s3://${this._configuration.bucket}/${Key}`, error);
139
+ throw new FailedPutS3EngineError(`Failed to put s3://${this.configuration.bucket}/${Key}`, error);
74
140
  }
75
141
  };
76
142
 
@@ -81,50 +147,82 @@ export default class S3Engine extends Engine {
81
147
  await processIndex(null, Object.values(index).flat());
82
148
  }
83
149
 
150
+ /**
151
+ * Retrieves the compiled search index for a specific model from S3.
152
+ *
153
+ * @param {Model.constructor} model - The model whose search index to retrieve.
154
+ * @returns {Promise<Object>} The compiled search index.
155
+ */
84
156
  static async getSearchIndexCompiled(model) {
85
- return await this._configuration.client.send(new GetObjectCommand({
86
- Key: [this._configuration.prefix, model.name, '_search_index.json'].join('/'),
87
- Bucket: this._configuration.bucket,
157
+ return await this.configuration.client.send(new GetObjectCommand({
158
+ Key: [this.configuration.prefix, model.name, '_search_index.json'].join('/'),
159
+ Bucket: this.configuration.bucket,
88
160
  })).then(data => data.Body.transformToString())
89
161
  .then(JSON.parse);
90
162
  }
91
163
 
164
+ /**
165
+ * Retrieves the raw (uncompiled) search index for a specific model from S3.
166
+ *
167
+ * @param {Model.constructor} model - The model whose raw search index to retrieve.
168
+ * @returns {Promise<Object>} The raw search index, or an empty object if not found.
169
+ */
92
170
  static async getSearchIndexRaw(model) {
93
- return await this._configuration.client.send(new GetObjectCommand({
94
- Key: [this._configuration.prefix, model.name, '_search_index_raw.json'].join('/'),
95
- Bucket: this._configuration.bucket,
171
+ return await this.configuration.client.send(new GetObjectCommand({
172
+ Key: [this.configuration.prefix, model.toString(), '_search_index_raw.json'].join('/'),
173
+ Bucket: this.configuration.bucket,
96
174
  })).then(data => data.Body.transformToString())
97
175
  .then(JSON.parse)
98
176
  .catch(() => ({}));
99
177
  }
100
178
 
179
+ /**
180
+ * Puts (uploads) a compiled search index for a specific model to S3.
181
+ *
182
+ * @param {Model.constructor} model - The model whose compiled search index to upload.
183
+ * @param {Object} compiledIndex - The compiled search index data.
184
+ * @returns {Promise<void>}
185
+ *
186
+ * @throws {FailedPutS3EngineError} Thrown if there is an error during the S3 PutObject operation.
187
+ */
101
188
  static async putSearchIndexCompiled(model, compiledIndex) {
102
- const Key = [this._configuration.prefix, model.name, '_search_index.json'].join('/');
189
+ const Key = [this.configuration.prefix, model.toString(), '_search_index.json'].join('/');
103
190
 
104
191
  try {
105
- await this._configuration.client.send(new PutObjectCommand({
192
+ await this.configuration.client.send(new PutObjectCommand({
106
193
  Key,
107
194
  Body: JSON.stringify(compiledIndex),
108
- Bucket: this._configuration.bucket,
195
+ Bucket: this.configuration.bucket,
109
196
  ContentType: 'application/json',
110
197
  }));
111
198
  } catch (error) {
112
- throw new FailedPutS3EngineError(`Failed to put s3://${this._configuration.bucket}/${Key}`, error);
199
+ throw new FailedPutS3EngineError(`Failed to put s3://${this.configuration.bucket}/${Key}`, error);
113
200
  }
114
201
  }
115
202
 
203
+ /**
204
+ * Puts (uploads) a raw search index for a specific model to S3.
205
+ *
206
+ * @param {Model.constructor} model - The model whose raw search index to upload.
207
+ * @param {Object} rawIndex - The raw search index data.
208
+ * @returns {Promise<void>}
209
+ *
210
+ * @throws {FailedPutS3EngineError} Thrown if there is an error during the S3 PutObject operation.
211
+ */
116
212
  static async putSearchIndexRaw(model, rawIndex) {
117
- const Key = [this._configuration.prefix, model.name, '_search_index_raw.json'].join('/');
213
+ const Key = [this.configuration.prefix, model.toString(), '_search_index_raw.json'].join('/');
118
214
 
119
215
  try {
120
- await this._configuration.client.send(new PutObjectCommand({
216
+ await this.configuration.client.send(new PutObjectCommand({
121
217
  Key,
122
218
  Body: JSON.stringify(rawIndex),
123
- Bucket: this._configuration.bucket,
219
+ Bucket: this.configuration.bucket,
124
220
  ContentType: 'application/json',
125
221
  }));
126
222
  } catch (error) {
127
- throw new FailedPutS3EngineError(`Failed to put s3://${this._configuration.bucket}/${Key}`, error);
223
+ throw new FailedPutS3EngineError(`Failed to put s3://${this.configuration.bucket}/${Key}`, error);
128
224
  }
129
225
  }
130
226
  }
227
+
228
+ export default S3Engine;