Skip to content
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

Merged
merged 2 commits into from
Mar 1, 2018

Conversation

benmvp
Copy link
Contributor

@benmvp benmvp commented Feb 28, 2018

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:

const eventbrite = require('eventbrite');

// Create configured Eventbrite SDK
const sdk = 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
  • Added a bunch of eslint-plugin-typescript rules

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 (and the one-off Typescript definition file).

Fixes #6

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();
Copy link
Contributor

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);
Copy link
Contributor

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?

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

Copy link
Contributor Author

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?

Copy link
Contributor Author

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)

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

Copy link
Contributor

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']);
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep!

Copy link
Contributor Author

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

neat!

Copy link
Contributor Author

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}))
Copy link
Contributor

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?

Copy link
Contributor Author

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;
};
}

Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor Author

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 => {
Copy link
Contributor

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

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.

Copy link
Contributor Author

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()

Copy link
Contributor Author

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

Copy link
Contributor

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);

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 => {

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"/>

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?

Copy link
Contributor Author

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.
Copy link
Contributor

@kaylie-alexa kaylie-alexa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice work @benmvp !

@BenAtEventbrite BenAtEventbrite merged commit 149b0a1 into eventbrite:master Mar 1, 2018
@benmvp benmvp deleted the request branch March 1, 2018 19:24
@benmvp benmvp mentioned this pull request Mar 2, 2018
@BenAtEventbrite BenAtEventbrite added this to the Alpha milestone Apr 5, 2018
@ebtravis
Copy link
Collaborator

🎉 This PR is included in version 1.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for low-level request method
5 participants