Repository: parse-community/Parse-SDK-JS
Pull request #1298 (2021-02-13 01:37:11)

fix: User management on React-Native
Closes: https://github.com/parse-community/Parse-SDK-JS/issues/1296

This addresses issues using AsyncStorage.

* Introduces new `user.isCurrentAsync` function

peril-parse-community[bot]: 
<!--
  0 failure: 
  0 warning: 
  
  1 markdown notices
  DangerID: danger-id-Peril;
-->



Danger run resulted in 1 warning; to find out more, see the [checks page](https://github.com/parse-community/Parse-SDK-JS/runs/1891628834).
<p align="right">
  Generated by :no_entry_sign: <a href="https://danger.systems/js">dangerJS</a>
</p>

codecov[bot]: # [Codecov](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1298?src=pr&el=h1) Report
> Merging [#1298](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1298?src=pr&el=desc) (13894a5) into [master](https://codecov.io/gh/parse-community/Parse-SDK-JS/commit/4caede2e4948f9eff8cf9e174d4d3b2c3b842c98?el=desc) (4caede2) will **not change** coverage.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1298/graphs/tree.svg?width=650&height=150&src=pr&token=8nCENM0QHj)](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1298?src=pr&el=tree)

```diff
@@            Coverage Diff            @@
##            master     #1298   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           59        59           
  Lines         5800      5804    +4     
  Branches      1305      1306    +1     
=========================================
+ Hits          5800      5804    +4     
```


| [Impacted Files](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1298?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/ParseUser.js](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1298/diff?src=pr&el=tree#diff-c3JjL1BhcnNlVXNlci5qcw==) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1298?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1298?src=pr&el=footer). Last update [4caede2...081d763](https://codecov.io/gh/parse-community/Parse-SDK-JS/pull/1298?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

dplewis: @davimacedo I just realized that this new `isCurrentAsync` could be used in other places. Should I add them to this PR or another?

Patch:
diff --git a/src/ParseUser.js b/src/ParseUser.js
index 9ea7f14..69d702f 100644
--- a/src/ParseUser.js
+++ b/src/ParseUser.js
@@ -308,6 +308,16 @@ class ParseUser extends ParseObject {
     return !!current && current.id === this.id;
   }
 
+  /**
+   * Returns true if <code>current</code> would return this user.
+   *
+   * @returns {Promise<boolean>} true if user is cached on disk
+   */
+  async isCurrentAsync(): Promise<boolean> {
+    const current = await ParseUser.currentAsync();
+    return !!current && current.id === this.id;
+  }
+
   /**
    * Returns get("username").
    *
@@ -458,13 +468,13 @@ class ParseUser extends ParseObject {
    * @param {...any} args
    * @returns {Promise}
    */
-  save(...args: Array<any>): Promise<ParseUser> {
-    return super.save.apply(this, args).then(() => {
-      if (this.isCurrent()) {
-        return CoreManager.getUserController().updateUserOnDisk(this);
-      }
-      return this;
-    });
+  async save(...args: Array<any>): Promise<ParseUser> {
+    await super.save.apply(this, args);
+    const current = await this.isCurrentAsync();
+    if (current) {
+      return CoreManager.getUserController().updateUserOnDisk(this);
+    }
+    return this;
   }
 
   /**
@@ -474,13 +484,13 @@ class ParseUser extends ParseObject {
    * @param {...any} args
    * @returns {Parse.User}
    */
-  destroy(...args: Array<any>): Promise<ParseUser> {
-    return super.destroy.apply(this, args).then(() => {
-      if (this.isCurrent()) {
-        return CoreManager.getUserController().removeUserFromDisk();
-      }
-      return this;
-    });
+  async destroy(...args: Array<any>): Promise<ParseUser> {
+    await super.destroy.apply(this, args);
+    const current = await this.isCurrentAsync();
+    if (current) {
+      return CoreManager.getUserController().removeUserFromDisk();
+    }
+    return this;
   }
 
   /**
@@ -490,13 +500,13 @@ class ParseUser extends ParseObject {
    * @param {...any} args
    * @returns {Parse.User}
    */
-  fetch(...args: Array<any>): Promise<ParseUser> {
-    return super.fetch.apply(this, args).then(() => {
-      if (this.isCurrent()) {
-        return CoreManager.getUserController().updateUserOnDisk(this);
-      }
-      return this;
-    });
+  async fetch(...args: Array<any>): Promise<ParseUser> {
+    await super.fetch.apply(this, args);
+    const current = await this.isCurrentAsync();
+    if (current) {
+      return CoreManager.getUserController().updateUserOnDisk(this);
+    }
+    return this;
   }
 
   /**
@@ -506,13 +516,13 @@ class ParseUser extends ParseObject {
    * @param {...any} args
    * @returns {Parse.User}
    */
-  fetchWithInclude(...args: Array<any>): Promise<ParseUser> {
-    return super.fetchWithInclude.apply(this, args).then(() => {
-      if (this.isCurrent()) {
-        return CoreManager.getUserController().updateUserOnDisk(this);
-      }
-      return this;
-    });
+  async fetchWithInclude(...args: Array<any>): Promise<ParseUser> {
+    await super.fetchWithInclude.apply(this, args);
+    const current = await this.isCurrentAsync();
+    if (current) {
+      return CoreManager.getUserController().updateUserOnDisk(this);
+    }
+    return this;
   }
 
   /**
@@ -1161,7 +1171,7 @@ const DefaultController = {
     return RESTController.request('POST', 'requestPasswordReset', { email: email }, options);
   },
 
-  upgradeToRevocableSession(user: ParseUser, options: RequestOptions) {
+  async upgradeToRevocableSession(user: ParseUser, options: RequestOptions) {
     const token = user.getSessionToken();
     if (!token) {
       return Promise.reject(
@@ -1172,15 +1182,15 @@ const DefaultController = {
     options.sessionToken = token;
 
     const RESTController = CoreManager.getRESTController();
-    return RESTController.request('POST', 'upgradeToRevocableSession', {}, options).then(result => {
-      const session = new ParseSession();
-      session._finishFetch(result);
-      user._finishFetch({ sessionToken: session.getSessionToken() });
-      if (user.isCurrent()) {
-        return DefaultController.setCurrentUser(user);
-      }
-      return Promise.resolve(user);
-    });
+    const result = await RESTController.request('POST', 'upgradeToRevocableSession', {}, options);
+    const session = new ParseSession();
+    session._finishFetch(result);
+    user._finishFetch({ sessionToken: session.getSessionToken() });
+    const current = await user.isCurrentAsync();
+    if (current) {
+      return DefaultController.setCurrentUser(user);
+    }
+    return Promise.resolve(user);
   },
 
   linkWith(user: ParseUser, authData: AuthData, options: FullOptions) {


Test patch:
diff --git a/src/__tests__/ParseUser-test.js b/src/__tests__/ParseUser-test.js
index 1e85375..828b14b 100644
--- a/src/__tests__/ParseUser-test.js
+++ b/src/__tests__/ParseUser-test.js
@@ -1280,6 +1280,54 @@ describe('ParseUser', () => {
     });
   });
 
+  it('can linkWith async', async () => {
+    const currentStorage = CoreManager.getStorageController();
+    CoreManager.setStorageController(mockAsyncStorage);
+    ParseUser.enableUnsafeCurrentUser();
+    ParseUser._clearCache();
+    CoreManager.setRESTController({
+      request() {
+        return Promise.resolve(
+          {
+            objectId: 'uid5Async',
+            sessionToken: 'r:123abc',
+            authData: {
+              test: {
+                id: 'id',
+                access_token: 'access_token',
+              },
+            },
+          },
+          200
+        );
+      },
+      ajax() {},
+    });
+    const provider = {
+      authenticate(options) {
+        if (options.success) {
+          options.success(this, {
+            id: 'id',
+            access_token: 'access_token',
+          });
+        }
+      },
+      restoreAuthentication() {},
+      getAuthType() {
+        return 'test';
+      },
+      deauthenticate() {},
+    };
+
+    const user = new ParseUser();
+    await user.linkWith(provider, null, { useMasterKey: true });
+
+    expect(user.get('authData')).toEqual({
+      test: { id: 'id', access_token: 'access_token' },
+    });
+    CoreManager.setStorageController(currentStorage);
+  });
+
   it('handle linkWith authentication failure', async () => {
     const provider = {
       authenticate(options) {