@beauraines/node-helpers 4.2.1 → 5.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/CHANGELOG.md CHANGED
@@ -2,6 +2,24 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ## [5.0.0](https://github.com/beauraines/node-helpers/compare/v4.3.0...v5.0.0) (2024-10-14)
6
+
7
+
8
+ ### ⚠ BREAKING CHANGES
9
+
10
+ * Updates and adds Storage Queue functions (#154)
11
+
12
+ ### Features
13
+
14
+ * Updates and adds Storage Queue functions ([#154](https://github.com/beauraines/node-helpers/issues/154)) ([7301e37](https://github.com/beauraines/node-helpers/commit/7301e37e5e819a5d9ac222602a5e4f7be8e0816c))
15
+
16
+ ## [4.3.0](https://github.com/beauraines/node-helpers/compare/v4.2.1...v4.3.0) (2024-10-14)
17
+
18
+
19
+ ### Features
20
+
21
+ * string to upper case snake case conversion function ([8cd6f85](https://github.com/beauraines/node-helpers/commit/8cd6f8516d24e8ef19c65508814b4aefd65030e7))
22
+
5
23
  ### [4.2.1](https://github.com/beauraines/node-helpers/compare/v4.2.0...v4.2.1) (2024-10-13)
6
24
 
7
25
  ## [4.2.0](https://github.com/beauraines/node-helpers/compare/v4.1.1...v4.2.0) (2024-10-06)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beauraines/node-helpers",
3
- "version": "4.2.1",
3
+ "version": "5.0.0",
4
4
  "description": "Collection of node helpers",
5
5
  "main": "index.js",
6
6
  "repository": {
package/src/azure.js CHANGED
@@ -1,8 +1,8 @@
1
1
  const dayjs = require('dayjs')
2
2
  const fs = require('fs');
3
3
  const { streamToBuffer } = require('./helpers.js')
4
- const { BlobServiceClient, StorageSharedKeyCredential } = require("@azure/storage-blob");
5
- const { QueueClient } = require("@azure/storage-queue");
4
+ const { BlobServiceClient, StorageSharedKeyCredential:BlobStorageSharedKeyCredential } = require("@azure/storage-blob");
5
+ const { QueueServiceClient ,StorageSharedKeyCredential:QueueStorageSharedKeyCredential } = require("@azure/storage-queue");
6
6
  var path = require('path');
7
7
 
8
8
  /**
@@ -74,44 +74,148 @@ class AzureStorage {
74
74
  /**
75
75
  * Sends a message to the specified storage queue. The messages are given a TTL based upon the
76
76
  * class's `queueMessageTTLSeconds` value.
77
+ *
78
+ * The response includes the `expiresOn`, `messageId` and `requestId` as well as a status code in
79
+ * `_response.status`
80
+ *
77
81
  * @param {string} queueUrl The URL to the storage queue
78
82
  * @param {string} messageContent The message to send to the queue
83
+ *
84
+ * @returns {Object} sendMessageResponse
79
85
  */
80
- async sendMessageToQueue(queueUrl, messageContent,) {
86
+ async sendMessageToQueue(queueName, messageContent) {
81
87
  try {
82
- const queueClient = new QueueClient(
83
- queueUrl,
84
- new StorageSharedKeyCredential(this.storageAccountName, this.storageAccountKey)
88
+ const queueServiceClient = new QueueServiceClient(
89
+ this.host('queue',this.cloudName),
90
+ new QueueStorageSharedKeyCredential(this.storageAccountName, this.storageAccountKey)
85
91
  );
92
+
93
+ const queueClient = queueServiceClient.getQueueClient(queueName);
94
+
86
95
  let queueOptions = {
87
96
  messageTimeToLive: this.queueMessageTTLSeconds
88
97
  };
89
- let sendMessageResponse = await queueClient.sendMessage(messageContent, queueOptions);
90
- console.log(
91
- "Sent message successfully, service assigned message Id:", sendMessageResponse.messageId,
92
- "service assigned request Id:", sendMessageResponse.requestId
93
- );
98
+
99
+ let sendMessageResponse = await queueClient.sendMessage(messageContent,queueOptions);
100
+ sendMessageResponse.status = sendMessageResponse._response.status;
101
+ delete sendMessageResponse._response;
102
+ return sendMessageResponse;
103
+
94
104
  } catch (error) {
95
- console.error(error.message)
105
+ console.log(error.message)
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Gets any number of messages from the queue. The messages themselves are in the property
111
+ * `receivedMessageItems[]messageText` Use the options to control the number of messages returned,
112
+ * defaults are 1 with a 30 second timeout. You will need the `messageId` and `popReceipt` to
113
+ * delete the message after processing.
114
+ *
115
+ * @param {string} queueName The name of the queue
116
+ * @param {object} options Any options such as `numberOfMessages` or `visibilityTimeout`
117
+ * @returns {object} The queue messages response
118
+ */
119
+ async getQueueMessages(queueName,options) {
120
+ const queueServiceClient = new QueueServiceClient(
121
+ this.host('queue',this.cloudName),
122
+ new QueueStorageSharedKeyCredential(this.storageAccountName, this.storageAccountKey)
123
+ );
124
+
125
+ const queueClient = queueServiceClient.getQueueClient(queueName);
126
+ const receivedMessagesResponse = await queueClient.receiveMessages(options);
127
+ return receivedMessagesResponse;
128
+
129
+ }
130
+
131
+
132
+ /**
133
+ * Deletes a message, by `messageId` and `popReceipt` from a named queue
134
+ *
135
+ * @param {string} queueName The name of the queue that has the message
136
+ * @param {string} messageId The message id to be deleted
137
+ * @param {string} popReceipt The popReceipt of teh message to be deleted
138
+ * @returns object with a nothing really useful
139
+ */
140
+ async deleteQueueMessage(queueName,messageId,popReceipt) {
141
+ const queueServiceClient = new QueueServiceClient(
142
+ this.host('queue',this.cloudName),
143
+ new QueueStorageSharedKeyCredential(this.storageAccountName, this.storageAccountKey)
144
+ );
145
+
146
+ const queueClient = queueServiceClient.getQueueClient(queueName);
147
+ const deleteMessageResponse = await queueClient.deleteMessage(messageId,popReceipt);
148
+ return deleteMessageResponse
149
+
150
+ }
151
+
152
+ /**
153
+ * Gets the named queues properties, which includes the approximateMessagesCount
154
+ *
155
+ * @param {string} queueName
156
+ *
157
+ * @returns {Object} the queues properties
158
+ */
159
+ async getQueueProperties(queueName) {
160
+ const queueServiceClient = new QueueServiceClient(
161
+ this.host('queue',this.cloudName),
162
+ new QueueStorageSharedKeyCredential(this.storageAccountName, this.storageAccountKey)
163
+ );
164
+
165
+ const queueClient = queueServiceClient.getQueueClient(queueName);
166
+ const properties = await queueClient.getProperties();
167
+ return properties
168
+
169
+ }
170
+
171
+ /**
172
+ * Lists storage queues in the storage account
173
+ *
174
+ * @returns Array
175
+ */
176
+ async listsQueues() {
177
+ try {
178
+ const queueServiceClient = new QueueServiceClient(
179
+ this.host('queue',this.cloudName),
180
+ new QueueStorageSharedKeyCredential(this.storageAccountName, this.storageAccountKey)
181
+ );
182
+
183
+ let queues = []
184
+ for await (const queue of queueServiceClient.listQueues()) {
185
+ queues.push(queue)
96
186
  }
187
+
188
+ return queues;
189
+
190
+ } catch (error) {
191
+ console.log(error.message)
97
192
  }
193
+ }
98
194
 
99
195
  /**
100
- * Gets a SAS token for the storage queue
196
+ * Gets a SAS URL for the storage queue
101
197
  * .
102
- * @param {string} queueUrl The URL to the storage queue
198
+ * @param {string} queueName The name of the storage queue
103
199
  * @param {object} options Should include `permissions: "raup"` or some combination thereof Any additional options supported. https://docs.microsoft.com/en-us/rest/api/storageservices/constructing-a-service-sas
200
+ *
201
+ * @returns {string} SAS URL for the specified queue
104
202
  */
105
- getStorageQueueSignedURL(queueUrl,options) {
106
-
107
- const queueClient = new QueueClient(
108
- queueUrl,
109
- new StorageSharedKeyCredential(this.storageAccountName, this.storageAccountKey)
110
- )
111
-
203
+ getStorageQueueSignedURL(queueName,options) {
204
+ const queueServiceClient = new QueueServiceClient(
205
+ this.host('queue',this.cloudName),
206
+ new QueueStorageSharedKeyCredential(this.storageAccountName, this.storageAccountKey)
207
+ );
208
+
209
+ const queueClient = queueServiceClient.getQueueClient(queueName);
210
+
112
211
  options = {
113
- startsOn: dayjs().toDate(),
114
- ...options
212
+ startsOn: dayjs().toDate(),
213
+ expiresOn: dayjs().add(this.tokenExpiry,'minutes'),
214
+ ...options
215
+ }
216
+
217
+ if (!options.permissions || !options.expiresOn) {
218
+ throw new Error("Must provide 'permissions' and 'expiresOn' for Queue SAS generation when 'identifier' is not provided");
115
219
  }
116
220
 
117
221
  return queueClient.generateSasUrl(options)
@@ -130,7 +234,7 @@ getStorageQueueSignedURL(queueUrl,options) {
130
234
 
131
235
  const blobServiceClient = new BlobServiceClient(
132
236
  this.host('blob',this.cloudName),
133
- new StorageSharedKeyCredential(this.storageAccountName, this.storageAccountKey)
237
+ new BlobStorageSharedKeyCredential(this.storageAccountName, this.storageAccountKey)
134
238
  );
135
239
  const containerClient = blobServiceClient.getContainerClient(containerName);
136
240
  const blockBlobClient = containerClient.getBlockBlobClient(blobName);
@@ -158,7 +262,7 @@ getStorageQueueSignedURL(queueUrl,options) {
158
262
  async uploadBlobFromFile(containerName,file) {
159
263
  const blobServiceClient = new BlobServiceClient(
160
264
  this.host('blob',this.cloudName),
161
- new StorageSharedKeyCredential(this.storageAccountName, this.storageAccountKey)
265
+ new BlobStorageSharedKeyCredential(this.storageAccountName, this.storageAccountKey)
162
266
  );
163
267
  const containerClient = blobServiceClient.getContainerClient(containerName);
164
268
 
@@ -194,7 +298,7 @@ getStorageQueueSignedURL(queueUrl,options) {
194
298
  async downloadBlobToFile(containerName,blobName,file) {
195
299
  const blobServiceClient = new BlobServiceClient(
196
300
  this.host('blob',this.cloudName),
197
- new StorageSharedKeyCredential(this.storageAccountName, this.storageAccountKey)
301
+ new BlobStorageSharedKeyCredential(this.storageAccountName, this.storageAccountKey)
198
302
  );
199
303
  const containerClient = blobServiceClient.getContainerClient(containerName);
200
304
  const blobClient = containerClient.getBlobClient(blobName);
@@ -219,7 +323,7 @@ getStorageQueueSignedURL(queueUrl,options) {
219
323
  async getBlob(containerName,blobName) {
220
324
  const blobServiceClient = new BlobServiceClient(
221
325
  this.host('blob',this.cloudName),
222
- new StorageSharedKeyCredential(this.storageAccountName, this.storageAccountKey)
326
+ new BlobStorageSharedKeyCredential(this.storageAccountName, this.storageAccountKey)
223
327
  );
224
328
  const containerClient = blobServiceClient.getContainerClient(containerName);
225
329
  const blobClient = containerClient.getBlobClient(blobName);
@@ -246,7 +350,7 @@ getStorageQueueSignedURL(queueUrl,options) {
246
350
  async getBinaryBlob(containerName,blobName) {
247
351
  const blobServiceClient = new BlobServiceClient(
248
352
  this.host('blob',this.cloudName),
249
- new StorageSharedKeyCredential(this.storageAccountName, this.storageAccountKey)
353
+ new BlobStorageSharedKeyCredential(this.storageAccountName, this.storageAccountKey)
250
354
  );
251
355
  const containerClient = blobServiceClient.getContainerClient(containerName);
252
356
  const blobClient = containerClient.getBlobClient(blobName);
@@ -268,7 +372,7 @@ getStorageQueueSignedURL(queueUrl,options) {
268
372
  async listBlobs(containerName) {
269
373
  const blobServiceClient = new BlobServiceClient(
270
374
  this.host('blob',this.cloudName),
271
- new StorageSharedKeyCredential(this.storageAccountName, this.storageAccountKey)
375
+ new BlobStorageSharedKeyCredential(this.storageAccountName, this.storageAccountKey)
272
376
  );
273
377
  const containerClient = blobServiceClient.getContainerClient(containerName);
274
378
  let blobs = []
package/src/azure.test.js CHANGED
@@ -77,8 +77,6 @@ describe('Azure Storage module', () => {
77
77
  expect(success)
78
78
  })
79
79
 
80
- it.todo('should send a message to the storage queue')
81
-
82
80
  it.skip('should get a blob from azure storage', async () =>{
83
81
  const account = "devstoreaccount1";
84
82
  const accountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
@@ -102,9 +100,6 @@ describe('Azure Storage module', () => {
102
100
  expect(fileExists(file))
103
101
  })
104
102
 
105
-
106
-
107
-
108
103
  it.skip('should list blobs from azure storage', async () => {
109
104
  const account = "devstoreaccount1";
110
105
  const accountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
@@ -118,4 +113,89 @@ describe('Azure Storage module', () => {
118
113
  expect(blobs.filter(b => b.name == blobName).length).toBe(1)
119
114
  })
120
115
 
116
+ it.skip('should send a message to the storage queue', async () => {
117
+ const account = "devstoreaccount1";
118
+ const accountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
119
+ let azure = new AzureStorage(account,accountKey,{cloudName:'Azurite'})
120
+ const message = {foo:"bar"}
121
+ const queueName = 'node-helpers-testing'
122
+ let response = await azure.sendMessageToQueue(queueName,JSON.stringify(message))
123
+ expect(response.status).toBe(201)
124
+ })
125
+
126
+ it.skip('should error when generating a SAS URL for the storage queue without permissions', async () => {
127
+ const account = "devstoreaccount1";
128
+ const accountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
129
+ let azure = new AzureStorage(account,accountKey,{cloudName:'Azurite'})
130
+ const queueName = 'node-helpers-testing'
131
+ expect(() => azure.getStorageQueueSignedURL(queueName)).toThrow();
132
+ })
133
+
134
+ it.skip('should generate a SAS URL for the storage queue', async () => {
135
+ const account = "devstoreaccount1";
136
+ const accountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
137
+ let azure = new AzureStorage(account,accountKey,{cloudName:'Azurite'})
138
+ const queueName = 'node-helpers-testing'
139
+ const options = {permissions:"r"}
140
+ let response = await azure.getStorageQueueSignedURL(queueName,options)
141
+ expect(response.includes('sp=r')).toBe(true) // Read permissions
142
+ expect(response.includes('http://127.0.0.1:10001/devstoreaccount1/node-helpers-testing')).toBe(true) // Azurite URL for storage queue
143
+ // TODO compute the expected expiration time
144
+ // expect(response.includes('se=2024-10-13T23%3A59%3A02Z')).toBe(true) // Expiration time defaults to 30 minutes
145
+ })
146
+
147
+
148
+ it.skip('should list the queues in the storage account', async () => {
149
+ const account = "devstoreaccount1";
150
+ const accountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
151
+ let azure = new AzureStorage(account,accountKey,{cloudName:'Azurite'})
152
+ const queueName = 'node-helpers-testing';
153
+ let queues = await azure.listsQueues();
154
+ expect(queues.map(x => x.name).indexOf(queueName)).toBeGreaterThan(-1)
155
+ })
156
+
157
+ it.skip('should get the queue properties', async () => {
158
+ const account = "devstoreaccount1";
159
+ const accountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
160
+ let azure = new AzureStorage(account,accountKey,{cloudName:'Azurite'})
161
+ const queueName = 'node-helpers-testing';
162
+ let properties = await azure.getQueueProperties(queueName);
163
+ expect(properties.approximateMessagesCount).toBeGreaterThan(0)
164
+ })
165
+
166
+ it.skip('should get a message from the queue', async () => {
167
+ const account = "devstoreaccount1";
168
+ const accountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
169
+ let azure = new AzureStorage(account,accountKey,{cloudName:'Azurite'})
170
+ const queueName = 'node-helpers-testing';
171
+ let response = await azure.getQueueMessages(queueName);
172
+ const expectedMessage = {foo:"bar"}
173
+ expect(response._response.status).toBe(200)
174
+ expect(response.receivedMessageItems.length).toBeGreaterThan(0)
175
+ expect(response.receivedMessageItems[0].messageText).toBe(JSON.stringify(expectedMessage))
176
+ })
177
+
178
+ it.skip('should get multiple messages from the queue', async () => {
179
+ const account = "devstoreaccount1";
180
+ const accountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
181
+ let azure = new AzureStorage(account,accountKey,{cloudName:'Azurite'})
182
+ const queueName = 'node-helpers-testing';
183
+ let response = await azure.getQueueMessages(queueName,{numberOfMessages:5});
184
+ expect(response._response.status).toBe(200)
185
+ expect(response.receivedMessageItems.length).toBeGreaterThan(1)
186
+ expect(response.receivedMessageItems.length).toBeLessThan(6)
187
+ })
188
+
189
+ it.skip('should delete a message from the queue',async () => {
190
+ const account = "devstoreaccount1";
191
+ const accountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
192
+ let azure = new AzureStorage(account,accountKey,{cloudName:'Azurite'})
193
+ const queueName = 'node-helpers-testing';
194
+ expect(() => azure.deleteQueueMessage(queueName,undefined,undefined)).toThrow() // popReceipt cannot be null
195
+ let addedMessage = await azure.sendMessageToQueue(queueName,'abc123')
196
+ let response = await azure.deleteQueueMessage(queueName,addedMessage.messageId,addedMessage.popReceipt);
197
+ expect(response.errorCode).toBe(undefined)
198
+ })
199
+
200
+
121
201
  })
package/src/helpers.js CHANGED
@@ -16,6 +16,14 @@ function toTitleCase(str) {
16
16
  );
17
17
  }
18
18
 
19
+ const toUpperSnakeCase = (str) => {
20
+ return str
21
+ .replace(/([a-z])([A-Z])/g, '$1_$2') // Add underscore before capital letters
22
+ .replace(/[^a-zA-Z0-9_]/g, '_') // Replace non-alphanumeric characters with underscore
23
+ .toUpperCase(); // Convert to uppercase
24
+ }
25
+
26
+
19
27
  /**
20
28
  * Removes newline characters \r and/or \n from a string
21
29
  * @param {string} string to remove newlines from
@@ -129,7 +137,7 @@ function getEpochMillis() {
129
137
  *
130
138
  * @returns
131
139
  */
132
- // eslint-disable-next-line no-unused-vars
140
+
133
141
  function sparkline(data,label,options) {
134
142
 
135
143
  options = {
@@ -213,6 +221,7 @@ module.exports = {
213
221
  streamToBuffer,
214
222
  stripNewLines,
215
223
  toTitleCase,
224
+ toUpperSnakeCase,
216
225
  unixTimestamp,
217
226
  writeFile
218
227
  }
@@ -30,5 +30,13 @@ describe('helpers',()=> {
30
30
  expect(helper.sparkline(input,label, options)).toBe(expectedOutput)
31
31
  })
32
32
 
33
+ it('should return an upper case snake case string',()=>{
34
+ const expectedOutput = 'NEXT_WEEK';
35
+ const inputs = ['nextWeek','next-week','next_week'];
36
+ inputs.forEach( i => {
37
+ expect(helper.toUpperSnakeCase(i)).toBe(expectedOutput)
38
+ })
39
+ })
40
+
33
41
  })
34
42