-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(request): Add support for SDK request method #8
Conversation
Adds `sdk.request(url, fetchOptions)` that can be used to make any API request without having a convience method for it. It reads in the `token` & `baseUrl` properties that are passed in while creating the `sdk` object in order to generate the full URL. Sample usage: ```js const sdk = require('eventbrite')({token: 'OATH_TOKEN_HERE'}); // See: https://www.eventbrite.com/developer/v3/endpoints/users/#ebapi-get-users-id sdk.request('/users/me').then(res => { // handle response data }); ``` Also: - Renamed package from `brite-rest` to `eventbrite` - Updated README to reflect sample usage - Moved `index.spec.ts` into `__tests__` folder - Had to disable `camelcase` ESLint rule because Prettier was unquoting non-conforming property names and the `camelcase` rule doesn't have fix functionality for prettier-eslint to fix NOTE: Per https://www.eventbrite.com/developer/v3/api_overview/authentication/#ebapi-authenticating-requests we should move the token into an authorization header instead of a query parameter, which is preferred. It'd also allow us to remove the `url-lib` dependency. Fixes #6.
}); | ||
|
||
afterEach(() => { | ||
restoreMockFetch(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice!
*/ | ||
export const checkStatus = (res: Response): Promise<Response> => { | ||
if (res.status >= 300) { | ||
return Promise.reject(res); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would it be better to throw an error here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it makes sense to return a Promise because the type definition calls for it. If we didn't i'd imagine you might get you'd get an error like checkStatus.then is not a function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would think so. This is how our current code works.
I'm thinking the reason an Error
isn't thrown is in order to be able to pass the res
. A vanilla Error
only takes an error message so we wouldn't be able to pass the res
and get out the info we need in later handlers.
I guess we could:
throw res;
but we really should only be throwing error objects...
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rwholey-eb checkStatus
is used as a .then
handler so anything anything thrown would just trigger a .catch
. Technically I can just return res
instead of returning Promise.resolve(res)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah i misunderstood, carry on
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah it feels weird throwing an object. I was thinking the ideal would be returning Promise.reject(throw new Error(res.error)) but we're not guaranteed the response will contain an error and the current code may be using stuff from the raw response. Don't have a better solution yet :/
src/request.ts
Outdated
|
||
const hasArgumentsError = (responseData: JSONResponseData): boolean => | ||
isObject(responseData['error_detail']) && | ||
isObject(responseData['error_detail']['ARGUMENTS_ERROR']); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you're looking for _.isPlainObject
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although I think in this case, both would actually work
let error = { | ||
error: responseData.error, | ||
description: responseData['error_description'], | ||
} as ParsedResponseError; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
neat!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as
is my favorite part of Typescript
so far
response | ||
.json() | ||
.then((responseData) => parseError(responseData)) | ||
.then((parsedError) => reject({response, parsedError})) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason return both response and parsedError? can you do response: parsedError instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm assuming so that you can have access to the raw response
as well as the parsedError
. The response
will have other meta data (like response code) that may be needed in addition to the parsed error.
This is also how the code currently is in core.
src/request.ts
Outdated
[propName: string]: any; | ||
}; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do you think about creating a separate file for type definitions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, but I'll have to see how it works. I wouldn't want to have to import from that separate file and then export again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seemed to work ok!
README.md
Outdated
const sdk = require('eventbrite')({token: 'OATH_TOKEN_HERE'}); | ||
|
||
// See: https://www.eventbrite.com/developer/v3/endpoints/users/#ebapi-get-users-id | ||
sdk.request('/users/me').then(res => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so I think we shouldn't be encouraging people to use .request method directly and instead provide them wrappers for particular objects like getUser
or getEvents
so that when the endpoint changes the api kit will mask the change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree! I think it serves a purpose though and I think we should definitely make it available to our consumers. Maybe when the rest of our main apis get fleshed out we can move those to the forefront of our documentation, and note that they are slightly more stable and preferred.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. When we have the wrappers this example will get replaced. But the nice thing is that someone (like core) could start using it now with just .request()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I imagine a use-case for .request()
even when we do have the wrappers is if a new API is added and we haven't gotten around to building the wrapper for it. I believe it should always be made available in the SDK, just not in the forefront of docs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup, exactly. Basically it's you're on your own
method because we can change/deprecate the endpoints, whereas with the wrappers you're guaranteed to get some type of response back
*/ | ||
export const checkStatus = (res: Response): Promise<Response> => { | ||
if (res.status >= 300) { | ||
return Promise.reject(res); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it makes sense to return a Promise because the type definition calls for it. If we didn't i'd imagine you might get you'd get an error like checkStatus.then is not a function
README.md
Outdated
const sdk = require('eventbrite')({token: 'OATH_TOKEN_HERE'}); | ||
|
||
// See: https://www.eventbrite.com/developer/v3/endpoints/users/#ebapi-get-users-id | ||
sdk.request('/users/me').then(res => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree! I think it serves a purpose though and I think we should definitely make it available to our consumers. Maybe when the rest of our main apis get fleshed out we can move those to the forefront of our documentation, and note that they are slightly more stable and preferred.
@@ -1,5 +1,32 @@ | |||
export interface Sdk {} | |||
/// <reference path="../definitions/url-lib.d.ts"/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is interesting, so this is like a require for ts definitions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, that's my understanding. Should be able to get rid of it with #9 though
As a result of this change I had to add `typescript/no-unused-vars` rule, so I went ahead and added a bunch more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice work @benmvp !
🎉 This PR is included in version 1.0.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Adds
sdk.request(url, fetchOptions)
that can be used to make any API request without having a convience method for it.It reads in the
token
&baseUrl
properties that are passed in while creating thesdk
object in order to generate the full URL.Sample usage:
Also:
brite-rest
toeventbrite
index.spec.ts
into__tests__
foldercamelcase
ESLint rule because Prettier was unquoting non-conforming property names and thecamelcase
rule doesn't have fix functionality for prettier-eslint to fixeslint-plugin-typescript
rulesNOTE: Per https://www.eventbrite.com/developer/v3/api_overview/authentication/#ebapi-authenticating-requests we should move the token into an authorization header instead of a query parameter, which is preferred. It'd also allow us to remove the
url-lib
dependency (and the one-off Typescript definition file).Fixes #6