-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(users): Add users collection to SDK (#52)
Added a collection of helpful wrapper methods for interacting with the Users API.
- Loading branch information
Showing
12 changed files
with
379 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
# Users | ||
|
||
This is a collection of method that are intended to be helpful wrappers around the [Users API endpoints](user-api-docs). | ||
|
||
View the [User response object](user-object-reference) for details on the properties you'll get back with each response. | ||
|
||
## Table on Contents | ||
|
||
- [`sdk.users.me()`](#me) | ||
- [`sdk.users.get(id)`](#getById) | ||
|
||
<a id="me"></a> | ||
|
||
## `sdk.users.me()` | ||
Gets details about the current logged in user. | ||
|
||
**Read [`/users/me` documentation](user-get-me) for more details.** | ||
|
||
### API | ||
```js | ||
sdk.users.me(): Promise<User> | ||
``` | ||
|
||
### Example | ||
|
||
```js | ||
const eventbrite = require('eventbrite'); | ||
|
||
// Create configured Eventbrite SDK | ||
const sdk = eventbrite({token: 'OATH_TOKEN_HERE'}); | ||
|
||
sdk.users.me().then((user) => { | ||
console.log(`Hi ${user.name}!`); | ||
}); | ||
``` | ||
|
||
<a id="getById"></a> | ||
|
||
## `sdk.users.get(id)` | ||
Gets the details for a specific user by their user id. | ||
|
||
**Read [`/users/:id` documentation](user-get-me) for more details.** | ||
|
||
### API | ||
```js | ||
sdk.users.get(id: string): Promise<User> | ||
``` | ||
|
||
### Example | ||
|
||
```js | ||
const eventbrite = require('eventbrite'); | ||
|
||
// Create configured Eventbrite SDK | ||
const sdk = eventbrite({token: 'OATH_TOKEN_HERE'}); | ||
|
||
sdk.users.get('1234567890').then((user) => { | ||
console.log(`Hi ${user.name}!`); | ||
}); | ||
``` | ||
|
||
<!-- link reference section --> | ||
[user-api-docs]: https://www.eventbrite.com/platform/api#/reference/user | ||
[user-object-reference]: https://www.eventbrite.com/platform/api#/reference/user/retrieve-a-user | ||
[user-by-id]: https://www.eventbrite.com/platform/api#/reference/user/retrieve-a-user | ||
[user-get-me]: https://www.eventbrite.com/platform/api#/reference/user/retrieve/retrieve-your-user |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,22 @@ export const MOCK_USERS_ME_RESPONSE_DATA = { | |
image_id: null as string, | ||
}; | ||
|
||
export const MOCK_TRANSFORMED_USERS_ME_RESPONSE_DATA = { | ||
emails: [ | ||
{ | ||
email: '[email protected]', | ||
verified: true, | ||
primary: true, | ||
}, | ||
], | ||
id: '142429416488', | ||
name: 'Eventbrite Engineer', | ||
firstName: 'Eventbrite', | ||
lastName: 'Engineer', | ||
isPublic: false, | ||
imageId: null as string, | ||
}; | ||
|
||
export const MOCK_INTERNAL_ERROR_RESPONSE_DATA = { | ||
status_code: 500, | ||
error: 'INTERNAL_ERROR', | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import { | ||
mockFetch, | ||
getMockFetch, | ||
getMockResponse, | ||
restoreMockFetch, | ||
} from './utils'; | ||
import { | ||
MOCK_USERS_ME_RESPONSE_DATA, | ||
MOCK_TRANSFORMED_USERS_ME_RESPONSE_DATA, | ||
} from './__fixtures__'; | ||
|
||
import request from '../request'; | ||
import {UserApi} from '../users'; | ||
|
||
describe('me()', () => { | ||
it('calls fetch and calls fetch with appropriate defaults', async() => { | ||
const users = new UserApi(request); | ||
|
||
mockFetch(getMockResponse(MOCK_USERS_ME_RESPONSE_DATA)); | ||
|
||
await expect(users.me()).resolves.toEqual( | ||
MOCK_TRANSFORMED_USERS_ME_RESPONSE_DATA | ||
); | ||
|
||
expect(getMockFetch()).toHaveBeenCalledTimes(1); | ||
expect(getMockFetch()).toHaveBeenCalledWith( | ||
'/users/me/', | ||
expect.objectContaining({}) | ||
); | ||
|
||
restoreMockFetch(); | ||
}); | ||
|
||
it('handle token missing requests', async() => { | ||
const users = new UserApi(request); | ||
|
||
mockFetch( | ||
getMockResponse( | ||
{ | ||
status_code: 401, | ||
error_description: | ||
'An OAuth token is required for all requests', | ||
error: 'NO_AUTH', | ||
}, | ||
{status: 401} | ||
) | ||
); | ||
|
||
await expect(users.me()).rejects.toMatchObject({ | ||
response: expect.objectContaining({ | ||
status: 401, | ||
statusText: 'Unauthorized', | ||
ok: false, | ||
}), | ||
parsedError: { | ||
description: 'An OAuth token is required for all requests', | ||
error: 'NO_AUTH', | ||
}, | ||
}); | ||
|
||
restoreMockFetch(); | ||
}); | ||
}); | ||
|
||
describe('get(id)', () => { | ||
it('calls fetch and calls fetch with appropriate defaults', async() => { | ||
const users = new UserApi(request); | ||
|
||
mockFetch(getMockResponse(MOCK_USERS_ME_RESPONSE_DATA)); | ||
|
||
await expect(users.get('142429416488')).resolves.toEqual( | ||
MOCK_TRANSFORMED_USERS_ME_RESPONSE_DATA | ||
); | ||
|
||
expect(getMockFetch()).toHaveBeenCalledTimes(1); | ||
expect(getMockFetch()).toHaveBeenCalledWith( | ||
'/users/142429416488/', | ||
expect.objectContaining({}) | ||
); | ||
|
||
restoreMockFetch(); | ||
}); | ||
|
||
it('should handle not found users', async() => { | ||
const users = new UserApi(request); | ||
|
||
mockFetch( | ||
getMockResponse( | ||
{ | ||
status_code: 404, | ||
error_description: 'The user you requested does not exist.', | ||
error: 'NOT_FOUND', | ||
}, | ||
{status: 404} | ||
) | ||
); | ||
|
||
await expect(users.get('123')).rejects.toMatchObject({ | ||
response: expect.objectContaining({ | ||
status: 404, | ||
statusText: 'Not Found', | ||
ok: false, | ||
}), | ||
parsedError: { | ||
description: 'The user you requested does not exist.', | ||
error: 'NOT_FOUND', | ||
}, | ||
}); | ||
|
||
restoreMockFetch(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import {JSONRequest} from './types'; | ||
|
||
const SNAKE_CASE_MATCH = /_\w/g; | ||
const snakeToCamel = (str: string) => | ||
str.replace(SNAKE_CASE_MATCH, (chars: string) => chars[1].toUpperCase()); | ||
|
||
const transformKeysSnakeToCamel = <T extends { [key: string]: any } = {}>( | ||
obj: T | ||
) => | ||
Object.keys(obj).reduce((memo, key) => { | ||
let newValue = obj[key]; | ||
const camelKey = snakeToCamel(key); | ||
|
||
if ( | ||
newValue && | ||
typeof newValue === 'object' && | ||
!Array.isArray(newValue) | ||
) { | ||
newValue = transformKeysSnakeToCamel(newValue); | ||
} | ||
|
||
return { | ||
...memo, | ||
[camelKey]: newValue, | ||
}; | ||
}, {}) as T; | ||
|
||
/** | ||
* Returns a function that sends a request, and transforms its results | ||
*/ | ||
const makeJsonRequest = <T>( | ||
request: JSONRequest, | ||
transformers: Array<(obj: T) => T> | ||
) => (url: string, options?: RequestInit) => | ||
request(url, options).then((response: T) => | ||
transformers.reduce<T>((acc, transformer) => { | ||
let memo = acc; | ||
|
||
memo = transformer(response); | ||
return memo; | ||
}, response) | ||
); | ||
|
||
/** | ||
* Base API class for creating new API Classes. | ||
* Also encapsulates default transformers such as snake to camel. | ||
*/ | ||
export abstract class BaseApi<T> { | ||
request: JSONRequest<T>; | ||
|
||
constructor(req: JSONRequest<T>) { | ||
this.request = makeJsonRequest(req, [transformKeysSnakeToCamel]); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,43 @@ | ||
import {Sdk, SdkConfig} from './types'; | ||
import {Sdk, SdkConfig, JSONRequest} from './types'; | ||
import request from './request'; | ||
import {UserApi} from './users'; | ||
|
||
export * from './constants'; | ||
|
||
const DEFAULT_API_URL = 'https://www.eventbriteapi.com/v3'; | ||
|
||
type MakeRequestFunction = (baseUrl: string, token: string) => JSONRequest; | ||
|
||
const makeRequest: MakeRequestFunction = (baseUrl: string, token: string) => ( | ||
endpoint, | ||
options = {} | ||
) => { | ||
const url = `${baseUrl}${endpoint}`; | ||
let requestOptions = options; | ||
|
||
if (token) { | ||
requestOptions = { | ||
...requestOptions, | ||
headers: { | ||
...(requestOptions.headers || {}), | ||
Authorization: `Bearer ${token}`, | ||
}, | ||
}; | ||
} | ||
|
||
return request(url, requestOptions); | ||
}; | ||
|
||
const eventbrite = ({ | ||
baseUrl = DEFAULT_API_URL, | ||
token, | ||
}: SdkConfig = {}): Sdk => ({ | ||
request: (endpoint, options = {}) => { | ||
const url = `${baseUrl}${endpoint}`; | ||
let requestOptions = options; | ||
|
||
if (token) { | ||
requestOptions = { | ||
...requestOptions, | ||
headers: { | ||
...(requestOptions.headers || {}), | ||
Authorization: `Bearer ${token}`, | ||
}, | ||
}; | ||
} | ||
|
||
return request(url, requestOptions); | ||
}, | ||
}); | ||
}: SdkConfig = {}): Sdk => { | ||
const jsonRequest = makeRequest(baseUrl, token); | ||
|
||
return { | ||
request: jsonRequest, | ||
users: new UserApi(jsonRequest), | ||
}; | ||
}; | ||
|
||
export default eventbrite; |
Oops, something went wrong.