Skip to content

Commit

Permalink
feat: file download (#858)
Browse files Browse the repository at this point in the history
  • Loading branch information
manchuck authored Aug 21, 2023
1 parent 7c75951 commit 67ec0e8
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 29 deletions.
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ You can find more information for each product below:
* [Number Insights](https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/number-insights/README.md)
* [Numbers](https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/numbers/README.md)
* [Pricing](https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/pricing/README.md)
* [Server Client](https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/server-client/README.md)
* [Server Client][server-client]
* [Server SDK](https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/server-sdk/README.md)
* [SMS][sms]
* [Sub Accounts](https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/subaccounts/README.md)
Expand Down Expand Up @@ -124,28 +124,29 @@ The following is a list of Vonage APIs and whether the Node Server SDK provides

### V2 Migrations

While most of the V2 functions have been ported into their own package, some of the functions have not been ported or were removed. Below is a list of those changes:
While most of the V2 functions have been ported into their own package, some of the functions have not been ported or were removed. Below is a list of those changes:

| V2 Function | Status | Note |
|-----------------------------|:---------------:|:------------------------------------------------------:|
| `vonage.conversion` | _REMOVED_ | |
| `vonage.conversation` | Not Implemented | This was only released as a beta package |
| `vonage.app` | _REMOVED_ | Moved to [Applications][applications] |
| `vonage.files` | Not ported | Has not been ported to V3 at this time |
| `vonage.app` | Moved | Moved to [Applications][applications] |
| `vonage.files` | Moved | Move to [ServerClient][server-client] |
| `vonage.message` | Moved | Moved to [SMS][sms] |
| `vonage.generateJwt` | Moved | Was moved to [JWT][jwt] |
| `vonage.generateJwt` | Moved | Was moved to [JWT][jwt] |
| `vonage.generateSignature` | Not Ported | Has not been ported to V3 at this time |
| `vonage.calls` | Moved | Was moved to [Voice][voice] |
| `vonage.credentials` | Updated | Options can be found in [Server Client][server-client] |
| `vonage.options` | Updated | Options can be found in [Server Client][server-client] |
| `vonage.options.httpClient` | _Removed_ | |
| `vonage.options.userAgent` | Not Ported | Has not been ported to V3 at this time |
| `vonage.options.userAgent` | Moved | Options can be found in [Server Client][server-client] |

For more information, check out each packages migration guide.

[applications]: https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/applications/README.md
[auth]: https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/auth/README.md
[sms]: https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/sms/README.md
[server-client]: https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/server-client/README.md
[jwt]: https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/jwt/README.md
[voice]: https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/voice/README.md
[signup]: https://dashboard.nexmo.com/sign-up?utm_source=DEV_REL&utm_medium=github&utm_campaign=node-server-sdk
Expand Down
43 changes: 36 additions & 7 deletions packages/server-client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@

![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/vonage/vonage-node-sdk/ci.yml?branch=3.x) [![Codecov](https://img.shields.io/codecov/c/github/vonage/vonage-node-sdk?label=Codecov&logo=codecov&style=flat-square)](https://codecov.io/gh/Vonage/vonage-server-sdk) ![Latest Release](https://img.shields.io/npm/v/@vonage/server-client?label=%40vonage%2Fserver-client&style=flat-square) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg?style=flat-square)](../../CODE_OF_CONDUCT.md) [![License](https://img.shields.io/npm/l/@vonage/accounts?label=License&style=flat-square)][license]


<img src="https://developer.nexmo.com/images/logos/vbc-logo.svg" height="48px" alt="Vonage" />

This is the Vonage Server Client SDK for Node.js used to wrap the authentication headers/signatures for use with [Vonage APIs](https://www.vonage.com/). To use it you will need a Vonage account. Sign up [for free at vonage.com][signup].
This is the Vonage Server Client SDK for Node.js used to wrap the authentication
headers/signatures for use with [Vonage APIs](https://www.vonage.com/). To use
it you will need a Vonage account. Sign up [for free at vonage.com][signup].

For full API documentation refer to [developer.vonage.com](https://developer.vonage.com/).

* [Installation](#installation)
* [Usage](#usage)
* [Options](#options)
* [Options](#options)
* [File Downloads](#file-downloads)
* [Testing](#testing)

## Installation
Expand All @@ -28,7 +30,7 @@ npm install @vonage/server-client
yarn add @vonage/server-client
```

## Using the Vonage Server-Client SDK
## Usage

To create a client, you will need to pass in a `@vonage/auth` object.

Expand All @@ -54,15 +56,42 @@ const response = await vonageClient.sendGetRequest('https://rest.nexmo.com/accou

### Options

The constructor for the client takes in two parameters `credentials` and `options`. `credentials` is either an [`Auth`](https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/auth/lib/auth.ts#L13) or an `object` containing the settings from [`AuthInterface`](https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/auth/lib/types.ts#L35).
The constructor for the client takes in two parameters `credentials` and
`options`. `credentials` is either an [`Auth`](https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/auth/lib/auth.ts#L13)
or an `object` containing the settings from [`AuthInterface`](https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/auth/lib/types.ts#L35).

`options` allows adjusting api endpoints and the request timeout.

* `restHost: string` (optional) - Allows overwriting the default `https://rest.nexmo.com`.
* `apiHost: string` (optional) - Allows overwriting the default `https://api.nexmo.com`.
* `videoHost: string` (optional) - Allows overwriting the default `https://video.api.vonage.com`.
* `timeout: int` (optional) - Set a custom timeout for requests to Vonage in milliseconds. Defaults to the standard for Node http requests, which is 120,000 ms.
* `appendUserAgent: string` (optional) - Set a custom string to be added to the `user-agent` header for the request
* `timeout: int` (optional) - Set a custom timeout for requests to Vonage in
milliseconds. Defaults to the standard for Node http requests, which is
120,000 ms.
* `appendUserAgent: string` (optional) - Set a custom string to be added to the
`user-agent` header for the request

## File Downloads

When downloading files, the request needs to be built with proper security
headers set. Inside this package is the `FileClient` which will handle the
request. You can download a file using the File Id or the FQURL.

```js
const { Auth } = require('@vonage/auth');
const { FileClient } = require('@vonage/server-client');

const fileClient = new FileClient(new Auth({
apiKey: API_KEY,
apiSecret: API_SECRET,
applicationId: APP_ID,
privateKey: PRIVATE_KEY_PATH,
}),
options,
);

await fileClient.downloadFile('the-file-id-or-url', '/paht/to/save');
```

## Testing

Expand Down
80 changes: 80 additions & 0 deletions packages/server-client/__tests__/file.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import nock from 'nock';
import { FileClient } from '../lib';
import { BASE_URL } from './common';
import { Auth } from '@vonage/auth';
import mockFs from 'mock-fs';
import { readFileSync, existsSync } from 'fs';

const FILE_PATH = '/path';

const key = readFileSync(`${__dirname}/private.test.key`).toString();

describe('File tests', () => {
let client;
let scope;

beforeEach(function () {
mockFs({
[FILE_PATH]: {},
});

client = new FileClient(
new Auth({
privateKey: key,
applicationId: 'my-application',
}),
);

scope = nock(BASE_URL, {
reqheaders: {
authorization: (value) => value.startsWith('Bearer '),
},
}).persist();
});

afterEach(function () {
client = null;
scope = null;
nock.cleanAll();
mockFs.restore();
});

test('Can download file with url', async () => {
const content = "Ford, I think I'm a couch";
const file = `${FILE_PATH}/my-file.txt`;
scope
.get(`/v1/files/00000000-0000-0000-0000-000000000001`)
.reply(200, content);

expect(existsSync(file)).toBeFalsy();

expect(
await client.downloadFile(
`https://api.nexmo.com/v1/files/00000000-0000-0000-0000-000000000001`,
file,
),
).toBeUndefined();

expect(existsSync(file)).toBeTruthy();
expect(readFileSync(file).toString()).toEqual(content);
expect(nock.isDone()).toBeTruthy();
});

test('Can download file with id', async () => {
const content = "Ford, I think I'm a couch";
const file = `${FILE_PATH}/my-file.txt`;
scope
.get(`/v1/files/00000000-0000-0000-0000-000000000001`)
.reply(200, content);

expect(existsSync(file)).toBeFalsy();

expect(
await client.downloadFile('00000000-0000-0000-0000-000000000001', file),
).toBeUndefined();

expect(existsSync(file)).toBeTruthy();
expect(readFileSync(file).toString()).toEqual(content);
expect(nock.isDone()).toBeTruthy();
});
});
10 changes: 3 additions & 7 deletions packages/server-client/lib/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ export abstract class Client {

protected config: ConfigParams;

constructor(
credentials: AuthInterface | AuthParams,
options?: ConfigParams,
) {
constructor(credentials: AuthInterface | AuthParams, options?: ConfigParams) {
// eslint-disable-next-line max-len
this.auth = !Object.prototype.hasOwnProperty.call(
credentials,
Expand All @@ -39,8 +36,7 @@ export abstract class Client {
apiHost: options?.apiHost || 'https://api.nexmo.com',
videoHost: options?.videoHost || 'https://video.api.vonage.com',
meetingsHost: options?.meetingsHost || 'https://api-eu.vonage.com',
proactiveHost:
options?.proactiveHost || 'https://api-eu.vonage.com',
proactiveHost: options?.proactiveHost || 'https://api-eu.vonage.com',
responseType: options?.responseType || ResponseTypes.json,
timeout: null,
} as ConfigParams;
Expand Down Expand Up @@ -84,7 +80,7 @@ export abstract class Client {
// This is most likely web-form
if (
!request[requestPath]
&& this.authType !== AuthenticationType.QUERY_KEY_SECRET
&& this.authType !== AuthenticationType.QUERY_KEY_SECRET
) {
requestPath = 'body';
params = new URLSearchParams({
Expand Down
30 changes: 30 additions & 0 deletions packages/server-client/lib/fileClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Client } from './client';
import { AuthenticationType } from './enums/AuthenticationType';
import { writeFileSync } from 'fs';
import debug from 'debug';

const log = debug('vonage:server-client');

export class FileClient extends Client {
protected authType = AuthenticationType.JWT;

async downloadFile(file: string, path: string): Promise<void> {
log(`Downloading file: ${file}`);
let fileId = file;
try {
const fileURL = new URL(file);
fileId = fileURL.pathname.split('/').pop();
} catch (_) {
log(`Not a url`);
}

log(`File Id ${fileId}`);
const resp = await this.sendGetRequest<string>(
`${this.config.apiHost}/v1/files/${fileId}`,
);

log(`Saving to ${path}`);
writeFileSync(path, resp.data);
log('File saved');
}
}
1 change: 1 addition & 0 deletions packages/server-client/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { Client } from './client';
export * from './enums/';
export * from './types';
export * from './transformers';
export * from './fileClient';
1 change: 1 addition & 0 deletions packages/server-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"lodash.snakecase": "^4.1.1"
},
"devDependencies": {
"mock-fs": "5.2.0",
"nock": "^13.3.1"
},
"publishConfig": {
Expand Down
15 changes: 8 additions & 7 deletions packages/server-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ You can find more information for each product below:
* [Number Insights](https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/number-insights/README.md)
* [Numbers](https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/numbers/README.md)
* [Pricing](https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/pricing/README.md)
* [Server Client](https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/server-client/README.md)
* [Server Client][server-client]
* [Server SDK](https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/server-sdk/README.md)
* [SMS][sms]
* [Sub Accounts](https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/subaccounts/README.md)
Expand Down Expand Up @@ -124,28 +124,29 @@ The following is a list of Vonage APIs and whether the Node Server SDK provides

### V2 Migrations

While most of the V2 functions have been ported into their own package, some of the functions have not been ported or were removed. Below is a list of those changes:
While most of the V2 functions have been ported into their own package, some of the functions have not been ported or were removed. Below is a list of those changes:

| V2 Function | Status | Note |
|-----------------------------|:---------------:|:------------------------------------------------------:|
| `vonage.conversion` | _REMOVED_ | |
| `vonage.conversation` | Not Implemented | This was only released as a beta package |
| `vonage.app` | _REMOVED_ | Moved to [Applications][applications] |
| `vonage.files` | Not ported | Has not been ported to V3 at this time |
| `vonage.app` | Moved | Moved to [Applications][applications] |
| `vonage.files` | Moved | Move to [ServerClient][server-client] |
| `vonage.message` | Moved | Moved to [SMS][sms] |
| `vonage.generateJwt` | Moved | Was moved to [JWT][jwt] |
| `vonage.generateJwt` | Moved | Was moved to [JWT][jwt] |
| `vonage.generateSignature` | Not Ported | Has not been ported to V3 at this time |
| `vonage.calls` | Moved | Was moved to [Voice][voice] |
| `vonage.credentials` | Updated | Options can be found in [Server Client][server-client] |
| `vonage.options` | Updated | Options can be found in [Server Client][server-client] |
| `vonage.options.httpClient` | _Removed_ | |
| `vonage.options.userAgent` | Not Ported | Has not been ported to V3 at this time |
| `vonage.options.userAgent` | Moved | Options can be found in [Server Client][server-client] |

For more information, check out each packages migration guide.

[applications]: https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/applications/README.md
[auth]: https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/auth/README.md
[sms]: https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/sms/README.md
[server-client]: https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/server-client/README.md
[jwt]: https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/jwt/README.md
[voice]: https://github.com/Vonage/vonage-node-sdk/blob/3.x/packages/voice/README.md
[signup]: https://dashboard.nexmo.com/sign-up?utm_source=DEV_REL&utm_medium=github&utm_campaign=node-server-sdk
Expand Down
8 changes: 7 additions & 1 deletion packages/voice/lib/voice.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AuthenticationType, Client } from '@vonage/server-client';
import { AuthenticationType, Client, FileClient } from '@vonage/server-client';
import {
GetCallDetailsParameters,
CallPageResponse,
Expand Down Expand Up @@ -176,6 +176,12 @@ export class Voice extends Client {
return this.callAction(uuid, 'unearmuff');
}

async downloadRecording(file: string, path: string): Promise<void> {
const client = new FileClient(this.auth, this.config);

return await client.downloadFile(file, path);
}

protected async callAction(
uuid: string,
action: string,
Expand Down

0 comments on commit 67ec0e8

Please sign in to comment.