Skip to content

Commit d85f595

Browse files
authored
feat(arcgis-rest-request): add serialization and deserialization for all auth managers
1 parent 9c665ec commit d85f595

File tree

6 files changed

+289
-11
lines changed

6 files changed

+289
-11
lines changed

packages/arcgis-rest-request/src/ApiKeyManager.ts

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ export class ApiKeyManager
3333
*/
3434
public readonly portal: string = "https://www.arcgis.com/sharing/rest";
3535

36-
private key: string;
36+
/**
37+
* The original API Key used to create this instance.
38+
*/
39+
private readonly key: string;
3740

3841
/**
3942
* The preferred method for creating an instance of `ApiKeyManager`.
@@ -52,11 +55,77 @@ export class ApiKeyManager
5255
}
5356

5457
/**
55-
* Gets a token (the API Key).
58+
* Gets the current access token (the API Key).
59+
*/
60+
get token() {
61+
return this.key;
62+
}
63+
64+
/**
65+
* Gets the current access token (the API Key).
5666
*/
5767
public getToken(url: string) {
5868
return Promise.resolve(this.key);
5969
}
70+
71+
/**
72+
* Converts the `ApiKeyManager` instance to a JSON object. This is called when the instance is serialized to JSON with [`JSON.stringify()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
73+
*
74+
* ```js
75+
* import { ApiKeyManager } from '@esri/arcgis-rest-request';
76+
*
77+
* const apiKey = new ApiKeyManager.fromKey("...")
78+
*
79+
* const json = JSON.stringify(session);
80+
* ```
81+
*
82+
* @returns A plain object representation of the instance.
83+
*/
84+
toJSON() {
85+
return {
86+
type: "ApiKeyManager",
87+
token: this.key,
88+
username: this.username,
89+
portal: this.portal
90+
};
91+
}
92+
93+
/**
94+
* Serializes the ApiKeyManager instance to a JSON string.
95+
*
96+
* ```js
97+
* import { ApiKeyManager } from '@esri/arcgis-rest-request';
98+
*
99+
* const apiKey = new ApiKeyManager.fromKey("...")
100+
*
101+
* localStorage.setItem("apiKey", apiKey.serialize());
102+
* ```
103+
* @returns {string} The serialized JSON string.
104+
*/
105+
serialize() {
106+
return JSON.stringify(this);
107+
}
108+
109+
/**
110+
* Deserializes a JSON string previously created with {@linkcode ApiKeyManager.deserialize} to an {@linkcode ApiKeyManager} instance.
111+
*
112+
* ```js
113+
* import { ApiKeyManager } from '@esri/arcgis-rest-request';
114+
*
115+
* const apiKey = ApiKeyManager.deserialize(localStorage.getItem("apiKey"));
116+
* ```
117+
* @param {string} serialized - The serialized JSON string.
118+
* @returns {ApiKeyManager} The deserialized ApiKeyManager instance.
119+
*/
120+
static deserialize(serialized: string) {
121+
const data = JSON.parse(serialized);
122+
123+
return new ApiKeyManager({
124+
key: data.token,
125+
username: data.username,
126+
portal: data.portal
127+
});
128+
}
60129
}
61130

62131
/**

packages/arcgis-rest-request/src/ApplicationCredentialsManager.ts

Lines changed: 81 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
} from "./utils/ArcGISTokenRequestError.js";
1111
import { ArcGISRequestError } from "./utils/ArcGISRequestError.js";
1212
import { AuthenticationManagerBase } from "./AuthenticationManagerBase.js";
13+
import { Writable } from "./utils/writable.js";
1314

1415
export interface IApplicationCredentialsManagerOptions {
1516
/**
@@ -63,12 +64,12 @@ export class ApplicationCredentialsManager
6364
extends AuthenticationManagerBase
6465
implements IAuthenticationManager
6566
{
66-
public portal: string;
67-
private clientId: string;
68-
private clientSecret: string;
69-
private token: string;
70-
private expires: Date;
71-
private duration: number;
67+
public readonly portal: string;
68+
public readonly token: string;
69+
public readonly clientId: string;
70+
public readonly clientSecret: string;
71+
public readonly expires: Date;
72+
public readonly duration: number;
7273

7374
/**
7475
* Preferred method for creating an `ApplicationCredentialsManager`
@@ -127,8 +128,8 @@ export class ApplicationCredentialsManager
127128
return fetchToken(`${this.portal}/oauth2/token/`, options)
128129
.then((response) => {
129130
this._pendingTokenRequest = null;
130-
this.token = response.token;
131-
this.expires = response.expires;
131+
this.setToken(response.token);
132+
this.setExpires(response.expires);
132133
return response.token;
133134
})
134135
.catch((e: ArcGISRequestError) => {
@@ -146,6 +147,78 @@ export class ApplicationCredentialsManager
146147
this.clearCachedUserInfo();
147148
return this.refreshToken().then(() => this);
148149
}
150+
151+
/**
152+
* Converts the `ApplicationCredentialsManager` instance to a JSON object. This is called when the instance is serialized to JSON with [`JSON.stringify()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
153+
*
154+
* ```js
155+
* import { ApplicationCredentialsManager } from '@esri/arcgis-rest-request';
156+
*
157+
* const session = ApplicationCredentialsManager.fromCredentials({
158+
* clientId: "abc123",
159+
* clientSecret: "••••••"
160+
* })
161+
*
162+
* const json = JSON.stringify(session);
163+
* ```
164+
*
165+
* @returns A plain object representation of the instance.
166+
*/
167+
public toJSON() {
168+
return {
169+
type: "ApplicationCredentialsManager",
170+
clientId: this.clientId,
171+
clientSecret: this.clientSecret,
172+
token: this.token,
173+
expires: this.expires,
174+
portal: this.portal,
175+
duration: this.duration
176+
};
177+
}
178+
179+
/**
180+
* Serializes the `ApplicationCredentialsManager` instance to a JSON string.
181+
* @returns The serialized JSON string.
182+
*/
183+
public serialize(): string {
184+
return JSON.stringify(this.toJSON());
185+
}
186+
187+
/**
188+
* Deserializes a JSON string previously created with {@linkcode ApplicationCredentialsManager.serialize} to an {@linkcode ApplicationCredentialsManager} instance.
189+
* @param serialized - The serialized JSON string.
190+
* @returns An instance of `ApplicationCredentialsManager`.
191+
*/
192+
public static deserialize(serialized: string): ApplicationCredentialsManager {
193+
const data: IApplicationCredentialsManagerOptions = JSON.parse(serialized);
194+
195+
return new ApplicationCredentialsManager({
196+
clientId: data.clientId,
197+
clientSecret: data.clientSecret,
198+
token: data.token,
199+
expires: new Date(data.expires),
200+
portal: data.portal,
201+
duration: data.duration
202+
});
203+
}
204+
205+
/*
206+
* Used to update the token when the session is refreshed.
207+
* @param newToken - Sets the token for the session.
208+
* @internal
209+
*/
210+
private setToken(newToken: string) {
211+
(this as Writable<ApplicationCredentialsManager>).token = newToken;
212+
}
213+
214+
/*
215+
* Used to update the expiration date when the session is refreshed.
216+
* @param newExpires - Sets the expiration date for the session.
217+
* @internal
218+
*/
219+
private setExpires(newExpires: Date) {
220+
(this as Writable<ApplicationCredentialsManager>).expires = newExpires;
221+
}
149222
}
150223

151224
/**

packages/arcgis-rest-request/src/ArcGISIdentityManager.ts

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,21 @@ export class ArcGISIdentityManager
837837
});
838838
}
839839

840+
/**
841+
* Deserializes a JSON string previously created with {@linkcode ArcGISIdentityManager.serialize} to an {@linkcode ArcGISIdentityManager} instance.
842+
*
843+
* ```js
844+
* // create an ArcGISIdentityManager instance
845+
* const serializedString = manager.serialize();
846+
* localStorage.setItem("arcgis-identity-manager", serializedString);
847+
*
848+
* // later, you can retrieve the manager from localStorage
849+
* const serializedString = localStorage.getItem("arcgis-identity-manager");
850+
* const manager = ArcGISIdentityManager.deserialize(serializedString);
851+
* ```
852+
*
853+
* @param str A JSON string representing an instance of `ArcGISIdentityManager`. This can be created with {@linkcode ArcGISIdentityManager.serialize}.
854+
*/
840855
public static deserialize(str: string) {
841856
const options = JSON.parse(str);
842857
return new ArcGISIdentityManager({
@@ -1188,8 +1203,25 @@ export class ArcGISIdentityManager
11881203
});
11891204
}
11901205

1191-
public toJSON(): IArcGISIdentityManagerOptions {
1206+
/**
1207+
* Converts the `ArcGISIdentityManager` instance to a JSON object. This is called when the instance is serialized to JSON with [`JSON.stringify()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
1208+
*
1209+
* ```js
1210+
* import { ArcGISIdentityManager } from '@esri/arcgis-rest-request';
1211+
*
1212+
* const session = ArcGISIdentityManager.fromCredentials({
1213+
* clientId: "abc123",
1214+
* clientSecret: "••••••"
1215+
* })
1216+
*
1217+
* const json = JSON.stringify(session);
1218+
* ```
1219+
*
1220+
* @returns A plain object representation of the instance.
1221+
*/
1222+
public toJSON(): IArcGISIdentityManagerOptions & { type: string } {
11921223
return {
1224+
type: "ArcGISIdentityManager",
11931225
clientId: this.clientId,
11941226
refreshToken: this.refreshToken,
11951227
refreshTokenExpires: this.refreshTokenExpires || undefined,
@@ -1205,9 +1237,25 @@ export class ArcGISIdentityManager
12051237
};
12061238
}
12071239

1240+
/**
1241+
* Serializes the `ArcGISIdentityManager` instance to a JSON string.
1242+
*
1243+
* ```js
1244+
* // create an ArcGISIdentityManager instance
1245+
* const serializedString = manager.serialize();
1246+
* localStorage.setItem("arcgis-identity-manager", serializedString);
1247+
*
1248+
* // later, you can retrieve the manager from localStorage
1249+
* const serializedString = localStorage.getItem("arcgis-identity-manager");
1250+
* const manager = ArcGISIdentityManager.deserialize(serializedString);
1251+
* ```
1252+
*
1253+
* @returns The serialized JSON string.
1254+
*/
12081255
public serialize() {
12091256
return JSON.stringify(this);
12101257
}
1258+
12111259
/**
12121260
* For a "Host" app that embeds other platform apps via iframes, after authenticating the user
12131261
* and creating a ArcGISIdentityManager, the app can then enable "post message" style authentication by calling
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Allows stripping readonly from types, making them writable.
2+
// From https://stackoverflow.com/questions/46634876/how-can-i-change-a-readonly-property-in-typescript#comment101446253_57485043
3+
export type Writable<T> = { -readonly [K in keyof T]: T[K] };

packages/arcgis-rest-request/test/ApiKey.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ describe("ApiKeyManager", () => {
88
afterEach(() => {
99
fetchMock.restore();
1010
});
11+
1112
describe(".getToken()", () => {
1213
it("should return the Api Key", (done) => {
1314
const session = new ApiKeyManager({
@@ -133,4 +134,39 @@ describe("ApiKeyManager", () => {
133134
});
134135
});
135136
});
137+
138+
describe(".serialize() and .deserialize()", () => {
139+
it("should serialize to a string", () => {
140+
const session = new ApiKeyManager({
141+
key: "123456",
142+
username: "c@sey"
143+
});
144+
145+
const serialized = session.serialize();
146+
expect(serialized).toBe(
147+
JSON.stringify({
148+
type: "ApiKeyManager",
149+
token: "123456",
150+
username: "c@sey",
151+
portal: "https://www.arcgis.com/sharing/rest"
152+
})
153+
);
154+
});
155+
156+
it("should deserialize to an object", () => {
157+
const session = new ApiKeyManager({
158+
key: "123456",
159+
username: "c@sey"
160+
});
161+
162+
const serialized = session.serialize();
163+
const deserialized = ApiKeyManager.deserialize(serialized);
164+
expect(deserialized.token).toEqual("123456");
165+
expect(deserialized.username).toEqual("c@sey");
166+
expect(deserialized.portal).toEqual(
167+
"https://www.arcgis.com/sharing/rest"
168+
);
169+
expect(deserialized instanceof ApiKeyManager).toBeTruthy();
170+
});
171+
});
136172
});

packages/arcgis-rest-request/test/ApplicationCredentialsManager.test.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,4 +175,53 @@ describe("ApplicationCredentialsManager", () => {
175175
);
176176
});
177177
});
178+
179+
describe(".serialize() and .deserialize()", () => {
180+
it("should serialize to a string", () => {
181+
const session = new ApplicationCredentialsManager({
182+
clientId: "id",
183+
clientSecret: "secret",
184+
token: "token",
185+
expires: YESTERDAY
186+
});
187+
188+
const serialized = session.serialize();
189+
190+
expect(serialized).toBe(
191+
JSON.stringify({
192+
type: "ApplicationCredentialsManager",
193+
clientId: "id",
194+
clientSecret: "secret",
195+
token: "token",
196+
expires: YESTERDAY,
197+
portal: "https://www.arcgis.com/sharing/rest",
198+
duration: 7200
199+
})
200+
);
201+
});
202+
203+
it("should deserialize to an object", () => {
204+
const session = new ApplicationCredentialsManager({
205+
token: "token",
206+
clientId: "id",
207+
clientSecret: "secret",
208+
expires: YESTERDAY,
209+
portal: "https://www.arcgis.com/sharing/rest"
210+
});
211+
212+
const serialized = session.serialize();
213+
const deserialized =
214+
ApplicationCredentialsManager.deserialize(serialized);
215+
expect(deserialized.token).toEqual("token");
216+
expect(deserialized.clientId).toEqual("id");
217+
expect(deserialized.clientSecret).toEqual("secret");
218+
expect(deserialized.expires).toEqual(YESTERDAY);
219+
expect(deserialized.portal).toEqual(
220+
"https://www.arcgis.com/sharing/rest"
221+
);
222+
expect(
223+
deserialized instanceof ApplicationCredentialsManager
224+
).toBeTruthy();
225+
});
226+
});
178227
});

0 commit comments

Comments
 (0)