Skip to content

Use the Fetch API instead of SuperAgent to communicate with MS Graph #63

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

Merged
merged 12 commits into from Jun 20, 2018
Merged

Conversation

ghost
Copy link

@ghost ghost commented Feb 2, 2018

MS Graph JS SDK should break the dependency on SuperAgent

MS Graph SDK is currently using SuperAgent behind the scenes to communicate with the Microsoft Graph. This dependency should be reconsidered for the following reasons:

SuperAgent is using XHR behind the scenes.

While there isn’t anything inherently wrong with XHR, usage of this technology has been “deprecated” for the Fetch API (https://blogs.windows.com/msedgedev/2016/05/24/fetch-and-xhr-limitations/ for more information). Switching to the Fetch API would align MSGraph SDK with the industry standard method for making network requests.

SuperAgent + it’s dependencies is large.

A quick look at graph-js-sdk-web.js reveals that SuperAgent is a large majority of the file’s contents and is only being used to make XHR calls. The Fetch API polyfill is much smaller and will likely simplify the code used to make network requests to Microsoft Graph in the SDK.

Additional "Bug"
Setting the responseType to Document will result in JSON being returned.
Fetch API doesn't support this feature (whatwg/fetch#16) This return type doesn't really make sense for communicating with Microsoft Graph.

REDMOND\lahuey added 2 commits December 13, 2017 16:32
Requests are now processed using the fetch API
Response Handler needs some refactoring to handle the error vs bad response
@ghost ghost changed the base branch from dev to master February 2, 2018 17:52
"@types/mocha": "^2.2.34",
"@types/node": "^9.4.0",
Copy link
Author

Choose a reason for hiding this comment

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

Cannot successfully build this library without node typings for require statements

@MIchaelMainer
Copy link
Contributor

MIchaelMainer commented Feb 6, 2018

First off, thank you @lahuey for taking the time to propose this change. This makes sense to me. I apologize as I can't get to review this right now. We will definitely take a look at this when we get back to this repo.

Solid.

package.json Outdated
"browserify": "^13.1.0",
"mocha": "^3.2.0",
"typescript": "^2.2.1"
"typescript": "^2.2.1",
"whatwg-fetch": "^2.0.3"
Copy link
Contributor

Choose a reason for hiding this comment

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

Global: we should be using an isomorphic Fetch library so that we are covered in the browser and from Node. Consider the following:

a: 1
}
};
const _200_SUPERAGENT_RES_BODY: any = { a: 1 };
Copy link
Contributor

Choose a reason for hiding this comment

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

Global: Remove references to superagent. Change to Fetch, or leave it out all together.


const error: Error = new Error();
(error as any).statusCode = 404;

Copy link
Contributor

Choose a reason for hiding this comment

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

Were you able to run the tests? I wasn't able to from your fork. I then updated to use cross-fetch - two of the tests aren't passing.

  27 passing (19ms)
  2 failing
  1) #ParseResponse() extracts code and request-id from the JSON body of 500 errors:
     TypeError: Cannot read property 'code' of null
      at lib\spec\core\responseHandling.js:38:29
      at Function.ResponseHandler.init (lib\src\ResponseHandler.js:8:13)
      at Context.<anonymous> (lib\spec\core\responseHandling.js:37:43)
  2) #ParseError() parses a 404 superagent error:
      AssertionError: -1 == 404
      + expected - actual
      --1
      +404
      
      at lib\spec\core\responseHandling.js:52:20
      at Function.ResponseHandler.init (lib\src\ResponseHandler.js:17:17)
      at Context.<anonymous> (lib\spec\core\responseHandling.js:51:43)

Switch to isomorphic fetch
@mpasarin
Copy link

Agreed on the comments for isomorphic fetch, and ensuring that the tests pass.
After that, approved from my side.

@MIchaelMainer MIchaelMainer changed the base branch from master to dev April 17, 2018 00:14
@MIchaelMainer MIchaelMainer changed the base branch from dev to fetchApiUpdate April 17, 2018 00:16
@MIchaelMainer
Copy link
Contributor

MIchaelMainer commented Apr 17, 2018

I updated the base branch to pull into. There are some issues that are popping up when I run npm run build that need to be resolved. An array declaration like this: [string], instead of string[]. You can see it in src/GraphRequest.ts (107,32) and in spec/core/urlGeneration.ts(24,25).

Please resolve the merge conflicts and we should be good.

@@ -199,38 +199,40 @@ export class GraphRequest {

delete(callback?:GraphRequestCallback):Promise<any> {
let url = this.buildFullUrl();
return this.sendRequestAndRouteResponse(request.del(url), callback)
return this.sendRequestAndRouteResponse(
new Request(url, { method: RequestMethod.DELETE, headers: new Headers() }),
Copy link
Contributor

Choose a reason for hiding this comment

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

We aren't passing in the headers set by the developer. We are always setting the headers collection to an empty collection. We should set the headers passed in by the user and only set an empty header collection if it is a required argument.

For DELETE, I think we should not have a default header.

The headers are set on this._headers. We should check that first for headers.

Copy link
Author

@ghost ghost Apr 26, 2018

Choose a reason for hiding this comment

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

There are always headers present
headers.append('Authorization', 'Bearer ' + accessToken);
headers.append('SdkVersion', "graph-js-" + packageInfo.version);

request
.patch(url)
.send(content),
new Request(url, { method: RequestMethod.PATCH, body: content, headers: new Headers() }),
Copy link
Contributor

Choose a reason for hiding this comment

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

We aren't passing in the headers set by the developer. We are always setting the headers collection to an empty collection. We should set the headers passed in by the user and only set an empty header collection if it is a required argument.

For PATCH, I think we should have a default content-type: application\json header set if no headers are provided by the customer.

.post(url)
.send(content),
callback
new Request(url, { method: RequestMethod.POST, body: content, headers: new Headers() }),
Copy link
Contributor

Choose a reason for hiding this comment

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

We aren't passing in the headers set by the developer. We are always setting the headers collection to an empty collection. We should set the headers passed in by the user and only set an empty header collection if it is a required argument.

For POST, I think we should have a default content-type: application\json header set if no headers are provided by the customer.

Copy link
Author

Choose a reason for hiding this comment

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

Sound good to me

{
method: RequestMethod.PUT,
body: content,
headers: new Headers({ 'Content-Type' : 'application/octet-stream' })
Copy link
Contributor

Choose a reason for hiding this comment

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

Same notes as for the other verbs. We shouldn't set the default content-type to application/octet-stream. We have many other PUT scenarios that use different content types.

https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/profilephoto_update

Copy link
Author

Choose a reason for hiding this comment

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

@MIchaelMainer wouldn't this be a breaking change? Isn't the original code setting this header?

Copy link
Contributor

Choose a reason for hiding this comment

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

Shoot. Yes, you're right. This would be breaking. Unfortunately, this implementation is wrong. We can Put other content types in other calls plus, there is a PutStream function that also hard codes this content type. For now, we should leave this broke (not happy about this).

Example
https://developer.microsoft.com/en-us/graph/docs/api-reference/beta/api/team_put_teams

Copy link
Contributor

Choose a reason for hiding this comment

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

@lahuey Can you make it so that when the customer sets the content-type header, it checks whether it exists and it removes the existing one if it does. That way, we won't break existing customers who have taken a dependency on this behavior.

Copy link
Author

Choose a reason for hiding this comment

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

changed this.configureRequest to use Headers.set instead of Headers.append

@bullsseye
Copy link
Contributor

bullsseye commented May 31, 2018

@lahuey can you please pull below commit into your branch. It fixes the non get calls.

commit: 574974a

I tried to push it into your branch, but it is giving me permission denied. This commit is reviewed by @MIchaelMainer

Changed [string] to string[], fixed spacing, fixed double parens
@ghost
Copy link
Author

ghost commented May 31, 2018

@bullsseye I added you as a collaborator so you should be able to push now. Sorry about that!

@bullsseye
Copy link
Contributor

bullsseye commented Jun 5, 2018

@lahuey I am using the below command to push the commit: 574974a but it is still giving me permission denied.

command: git push https://github.com/lahuey/msgraph-sdk-javascript fix-POST-PATCH-PUT-request:RemoveSuperAgentDependency

error that I receive: To https://github.com/lahuey/msgraph-sdk-javascript
! [remote rejected] fix-POST-PATCH-PUT-request -> RemoveSuperAgentDependency (permission denied)
error: failed to push some refs to 'https://github.com/lahuey/msgraph-sdk-javascript'

where fix-POST-PATCH-PUT-request is my local created out of your pull request on which this commit: 574974a is created.

Please let me know if I need to do something different to push this.

Other way is that you pull this branch: https://github.com/microsoftgraph/msgraph-sdk-javascript/commits/fix-POST-PATCH-PUT-request into your local and then push that into this pull request. This branch is Master + Your pull request + fix commit

@ghost
Copy link
Author

ghost commented Jun 5, 2018

@bullsseye just merged your changes into my branch

@bullsseye
Copy link
Contributor

bullsseye commented Jun 12, 2018

@MIchaelMainer Please review this commit: 584c60f

It includes the test cases that are added to verify this pull request

@bullsseye
Copy link
Contributor

@MIchaelMainer Comments are fixed in the commit: 60270db

@MIchaelMainer
Copy link
Contributor

@bullsseye Reviewed 60270db, added a comment about where to add the access token. I also added statements for the test assumptions. I think that stating those assumptions are optional, but beneficial.

@bullsseye
Copy link
Contributor

bullsseye commented Jun 14, 2018

@MIchaelMainer I have fixed the comments in this commit 946253f

@MIchaelMainer
Copy link
Contributor

👍 Please pull it

@bullsseye
Copy link
Contributor

@lahuey Please pull this commit: 946253f

Post this we will merge this branch

@ghost
Copy link
Author

ghost commented Jun 18, 2018

@bullsseye Awesome. Just pushed the commit manually again (Need to figure out how to use GitHub at some point)

this.urlComponents.version,
this.urlComponents.path])
+ this.createQueryString();
this.urlComponents.version,
Copy link
Contributor

Choose a reason for hiding this comment

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

previous indentation was correct. Not a blocker though

);
}


// Given the built SuperAgentRequest, get an auth token from the authProvider, make the request and return a promise
private routeResponseToPromise(requestBuilder:request.SuperAgentRequest) {
private routeResponseToPromise(request: Request) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Update the above comment : not a blocker

@@ -271,12 +284,17 @@ export class GraphRequest {
}

// Given the built SuperAgentRequest, get an auth token from the authProvider, make the request and invoke the callback
private routeResponseToCallback(requestBuilder:request.SuperAgentRequest, callback: GraphRequestCallback) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Update the above comment: not a blocker

@bullsseye
Copy link
Contributor

Merging this, There are non blocker changes required on this.

@bullsseye bullsseye merged commit 55c308a into microsoftgraph:fetchApiUpdate Jun 20, 2018
@MarkHerhold
Copy link

This is really great! Any idea when we can expect a release with these changes?

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.

5 participants