Skip to content

Commit

Permalink
feat(:bowtie:): add new method to update a user profile
Browse files Browse the repository at this point in the history
AFFECTS PACKAGES:
@esri/arcgis-rest-users
  • Loading branch information
jgravois committed Jan 15, 2019
1 parent 88bf26b commit 33ce92d
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 6 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -85,6 +85,7 @@ Some useful commands include:

### Frequently Asked Questions

* [Why should I use this library?](https://esri.github.io/arcgis-rest-js/guides/package-overview/)
* [Is this a _supported_ Esri product?](docs/FAQ.md#is-this-a-supported-esri-product)
* [How does this project compare with the ArcGIS API for JavaScript?](docs/FAQ.md#comparison-with-the-arcgis-api-for-javascript)
* [Is this similar to the ArcGIS API for Python?](docs/FAQ.md#comparison-with-the-arcgis-api-for-python)
Expand Down
12 changes: 6 additions & 6 deletions docs/src/guides/package-overview.md
Expand Up @@ -7,9 +7,9 @@ group: 0-introduction

# Why `@esri/arcgis-rest`?

`@esri/arcgis-rest` simplifies interacting with ArcGIS Online and Enterprise in both browsers and Node.js.
`@esri/arcgis-rest-js` simplifies making requests to ArcGIS Online and Enterprise in both browsers and Node.js.

There's no better way to explain what that means than comparing an `@esri/arcgis-rest` call to the same web request made using plain old JavaScript.
There's no better way to explain what that means than comparing an `@esri/arcgis-rest-js` call to the same web request made using plain old JavaScript.

### @esri/arcgis-rest

Expand Down Expand Up @@ -37,12 +37,12 @@ xhr.open('GET', url, true);
xhr.send(null);
```

wow, thats a lot easier! `@esri/arcgis-rest` is able to intuit the actual url (by default it assumes you're interacting with ArcGIS Online) prior to making the request and internalizes a lot of tedious logic for handling the response.
wow, thats a lot easier! `@esri/arcgis-rest-js` is able to intuit the actual url (by default it assumes you're interacting with ArcGIS Online) prior to making the request and internalizes a lot of tedious logic for handling the response.

Our packages tap into a new JavaScript spec called [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) under the hood though, so lets compare 🍎s to 🍎s.

```js
import { deleteFeatures } from "@esri/arcgis/rest-feature-service";
import { deleteFeatures } from "@esri/arcgis-rest-feature-service";

const url = `http://sampleserver6.arcgisonline.com/arcgis/rest/services/SF311/FeatureServer/1/`

Expand Down Expand Up @@ -83,7 +83,7 @@ fetch(url, {
})
```

As you can see, `@esri/arcgis-rest` is still handling a _lot_ of the details internally.
As you can see, `@esri/arcgis-rest-js` is still handling a _lot_ of the details internally.

* the operation name is appended to urls
* a `"POST"` is made automatically (when appropriate)
Expand All @@ -95,7 +95,7 @@ As you can see, `@esri/arcgis-rest` is still handling a _lot_ of the details int

And we haven't even begun to discuss [authentication](../browser-authentication/).

Whether you're trying to automate interacting with premium services in Node.js or creating a website that will allow users to sign into [ArcGIS Online](https://www.arcgis.com) safely and manage their own content, `@esri/arcgis-rest` has you covered.
Whether you're trying to automate interacting with premium services in Node.js or creating a website that will allow users to sign into [ArcGIS Online](https://www.arcgis.com) safely and manage their own content, `@esri/arcgis-rest-js` has you covered.

# Package Overview

Expand Down
1 change: 1 addition & 0 deletions packages/arcgis-rest-users/src/index.ts
Expand Up @@ -2,5 +2,6 @@
* Apache-2.0 */

export * from "./get";
export * from "./update";
export * from "./notification";
export * from "./invitation";
72 changes: 72 additions & 0 deletions packages/arcgis-rest-users/src/update.ts
@@ -0,0 +1,72 @@
/* Copyright (c) 2018 Environmental Systems Research Institute, Inc.
* Apache-2.0 */

import {
request,
IRequestOptions,
getPortalUrl
} from "@esri/arcgis-rest-request";

import { IUser } from "@esri/arcgis-rest-common-types";
import { UserSession } from "@esri/arcgis-rest-auth";

export interface IUpdateUserRequestOptions extends IRequestOptions {
/**
* A session representing a logged in user.
*/
authentication: UserSession;
/**
* The user properties to be updated.
*/
user: IUser;
}

export interface IUpdateUserResponse {
success: boolean;
username: string;
}

/**
* ```js
* import { updateUser } from '@esri/arcgis-rest-users';
* // any user can update their own profile
* updateUser({
* authentication,
* user: { description: "better than the last one" }
* })
* .then(response)
* // org administrators must declare the username that will be updated explicitly
* updateUser({
* authentication,
* user: { username: "c@sey", description: "" }
* })
* .then(response)
* // => { "success": true, "username": "c@sey" }
* ```
* Update a user profile. The username will be extracted from the authentication session unless it is provided explicitly. See the [REST Documentation](https://developers.arcgis.com/rest/users-groups-and-items/update-user.htm) for more information.
*
* @param requestOptions - options to pass through in the request
* @returns A Promise that will resolve with metadata about the user
*/
export function updateUser(
requestOptions?: IUpdateUserRequestOptions
): Promise<IUpdateUserResponse> {
// default to the authenticated username unless another username is provided manually
const username =
requestOptions.user.username || requestOptions.authentication.username;

const updateUrl = `${getPortalUrl(
requestOptions
)}/community/users/${encodeURIComponent(username)}/update`;

// mixin custom params and the user information, then drop the user info
requestOptions.params = {
...requestOptions.user,
...requestOptions.params
};

delete requestOptions.user;

// send the request
return request(updateUrl, requestOptions);
}
151 changes: 151 additions & 0 deletions packages/arcgis-rest-users/test/update.test.ts
@@ -0,0 +1,151 @@
/* Copyright (c) 2018-2019 Environmental Systems Research Institute, Inc.
* Apache-2.0 */

import { updateUser, IUpdateUserResponse } from "../src/index";
import { encodeParam } from "@esri/arcgis-rest-request";
import { UserSession } from "@esri/arcgis-rest-auth";
import * as fetchMock from "fetch-mock";

const TOMORROW = (function() {
const now = new Date();
now.setDate(now.getDate() + 1);
return now;
})();

describe("updateUser", () => {
afterEach(fetchMock.restore);

const session = new UserSession({
username: "c@sey",
password: "123456",
token: "fake-token",
tokenExpires: TOMORROW,
portal: "https://myorg.maps.arcgis.com/sharing/rest"
});

it("should make an authenticated request to update the same user profile.", done => {
fetchMock.once("*", {
success: true,
username: "c@sey"
} as IUpdateUserResponse);

updateUser({
authentication: session,
user: { description: "destroyer of worlds" }
})
.then(() => {
expect(fetchMock.called()).toEqual(true);
const [url, options]: [string, RequestInit] = fetchMock.lastCall("*");
expect(url).toEqual(
"https://myorg.maps.arcgis.com/sharing/rest/community/users/c%40sey/update"
);
expect(options.method).toBe("POST");
expect(options.body).toContain("f=json");
expect(options.body).toContain("token=fake-token");
expect(options.body).toContain(
encodeParam("description", "destroyer of worlds")
);
done();
})
.catch(e => {
fail(e);
});
});

it("should make an authenticated request to update the same user profile and mixin custom params.", done => {
fetchMock.once("*", {
success: true,
username: "c@sey"
} as IUpdateUserResponse);

updateUser({
authentication: session,
user: { description: "destroyer of worlds" },
params: { foo: "bar" }
})
.then(() => {
expect(fetchMock.called()).toEqual(true);
const [url, options]: [string, RequestInit] = fetchMock.lastCall("*");
expect(url).toEqual(
"https://myorg.maps.arcgis.com/sharing/rest/community/users/c%40sey/update"
);
expect(options.method).toBe("POST");
expect(options.body).toContain("f=json");
expect(options.body).toContain("token=fake-token");
expect(options.body).toContain("foo=bar");
expect(options.body).toContain(
encodeParam("description", "destroyer of worlds")
);
done();
})
.catch(e => {
fail(e);
});
});

it("should make an org administrator authenticated request to update a different user.", done => {
fetchMock.once("*", {
success: true,
username: "jsmith"
} as IUpdateUserResponse);

updateUser({
authentication: session,
user: { username: "jsmith", description: "something different" }
})
.then(() => {
expect(fetchMock.called()).toEqual(true);
const [url, options]: [string, RequestInit] = fetchMock.lastCall("*");
expect(url).toEqual(
"https://myorg.maps.arcgis.com/sharing/rest/community/users/jsmith/update"
);
expect(options.method).toBe("POST");
expect(options.body).toContain("f=json");
expect(options.body).toContain("token=fake-token");
expect(options.body).toContain(
encodeParam("description", "something different")
);
done();
})
.catch(e => {
fail(e);
});
});

it("should throw an error when the authenticated user doesnt have permission to update the user profile in question.", done => {
fetchMock.once("*", {
error: {
code: 403,
messageCode: "GWM_0003",
message:
"You do not have permissions to access this resource or perform this operation.",
details: []
}
});

updateUser({
authentication: session,
user: { username: "fake", description: "real" }
})
.then(() => {
fail();
})
.catch(e => {
expect(fetchMock.called()).toEqual(true);
const [url, options]: [string, RequestInit] = fetchMock.lastCall("*");
expect(url).toEqual(
"https://myorg.maps.arcgis.com/sharing/rest/community/users/fake/update"
);
expect(options.method).toBe("POST");
expect(options.body).toContain("f=json");
expect(options.body).toContain("token=fake-token");
expect(options.body).toContain(encodeParam("description", "real"));
expect(e.name).toBe("ArcGISAuthError");
expect(e.code).toBe("GWM_0003");
expect(e.originalMessage).toBe(
"You do not have permissions to access this resource or perform this operation."
);
done();
});
});
});

0 comments on commit 33ce92d

Please sign in to comment.