Skip to content

Commit d84a03a

Browse files
authored
feat: add keyValueStore.getRecordPublicUrl (#725)
This PR adds new method to KV store client: - `getRecordPublicUrl(key: string)` Note: We already have this same method ([KeyValueStore.getPublicUrl](https://github.com/apify/apify-sdk-js/blob/master/packages/apify/src/key_value_store.ts#L19)) in SDK.
1 parent 48cf639 commit d84a03a

File tree

3 files changed

+46
-3
lines changed

3 files changed

+46
-3
lines changed

src/resource_clients/dataset.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ export class DatasetClient<
168168
* Generates a URL that can be used to access dataset items.
169169
*
170170
* If the client has permission to access the dataset's URL signing key,
171-
* the URL will include a signature to verify its authenticity.
171+
* the URL will include a signature which will allow the link to work even without authentication.
172172
*
173173
* You can optionally control how long the signed URL should be valid using the `expiresInMillis` option.
174174
* This value sets the expiration duration in milliseconds from the time the URL is generated.

src/resource_clients/key_value_store.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { JsonValue } from 'type-fest';
55

66
import type { STORAGE_GENERAL_ACCESS } from '@apify/consts';
77
import log from '@apify/log';
8-
import { createStorageContentSignature } from '@apify/utilities';
8+
import { createHmacSignature, createStorageContentSignature } from '@apify/utilities';
99

1010
import type { ApifyApiError } from '../apify_api_error';
1111
import type { ApiClientSubResourceOptions } from '../base/api_client';
@@ -86,10 +86,31 @@ export class KeyValueStoreClient extends ResourceClient {
8686
}
8787

8888
/**
89-
* Generates a URL that can be used to access key-value store keys.
89+
* Generates a URL that can be used to access key-value store record.
9090
*
9191
* If the client has permission to access the key-value store's URL signing key,
9292
* the URL will include a signature to verify its authenticity.
93+
*/
94+
async getRecordPublicUrl(key: string): Promise<string> {
95+
ow(key, ow.string.nonEmpty);
96+
97+
const store = await this.get();
98+
99+
const recordPublicUrl = new URL(this._url(`records/${key}`));
100+
101+
if (store?.urlSigningSecretKey) {
102+
const signature = createHmacSignature(store.urlSigningSecretKey, key);
103+
recordPublicUrl.searchParams.append('signature', signature);
104+
}
105+
106+
return recordPublicUrl.toString();
107+
}
108+
109+
/**
110+
* Generates a URL that can be used to access key-value store keys.
111+
*
112+
* If the client has permission to access the key-value store's URL signing key,
113+
* the URL will include a signature which will allow the link to work even without authentication.
93114
*
94115
* You can optionally control how long the signed URL should be valid using the `expiresInMillis` option.
95116
* This value sets the expiration duration in milliseconds from the time the URL is generated.

test/key_value_stores.test.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,28 @@ describe('Key-Value Store methods', () => {
554554
validateRequest({}, { storeId, key });
555555
});
556556

557+
describe('getRecordPublicUrl()', () => {
558+
it('should include a signature in the URL when the caller has permission to access the signing secret key', async () => {
559+
const storeId = 'id-with-secret-key';
560+
const key = 'some-key';
561+
const res = await client.keyValueStore(storeId).getRecordPublicUrl(key);
562+
563+
const url = new URL(res);
564+
expect(url.searchParams.get('signature')).toBeDefined();
565+
expect(url.pathname).toBe(`/v2/key-value-stores/${storeId}/records/${key}`);
566+
});
567+
568+
it('should not include a signature in the URL when the caller lacks permission to access the signing secret key', async () => {
569+
const storeId = 'some-id';
570+
const key = 'some-key';
571+
const res = await client.keyValueStore(storeId).getRecordPublicUrl(key);
572+
573+
const url = new URL(res);
574+
expect(url.searchParams.get('signature')).toBeNull();
575+
expect(url.pathname).toBe(`/v2/key-value-stores/${storeId}/records/${key}`);
576+
});
577+
});
578+
557579
describe('createKeysPublicUrl()', () => {
558580
it('should include a signature in the URL when the caller has permission to access the signing secret key', async () => {
559581
const storeId = 'id-with-secret-key';

0 commit comments

Comments
 (0)