Skip to content

Commit

Permalink
fix(ums): allow fetching user by email
Browse files Browse the repository at this point in the history
  • Loading branch information
KutsenkoA committed Oct 9, 2019
1 parent d08a3a0 commit 194e1ae
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 29 deletions.
5 changes: 2 additions & 3 deletions e2e/stories/ums/ums.e2e.ts
Expand Up @@ -25,7 +25,7 @@ describe("User Management Service", () => {
describe("initialize without API key", () => {

const wrongCredentialsError = new Error(
"There was an error on the back-end as a result of your request: 400 Bad Request",
"There was an error on the back-end as a result of your request: 401 Unauthorized",
);

beforeAll(async () => await initWithUMS());
Expand Down Expand Up @@ -105,8 +105,7 @@ describe("User Management Service", () => {
}));
});

// TODO Does not work until alias/email token key will be developed
xit("should fetch himself by email", async () => {
it("should fetch himself by email", async () => {
const fetchedUSer = await ums.getUser(credentials.email);
joiAssert(fetchedUSer, UserSchema);
});
Expand Down
2 changes: 1 addition & 1 deletion src/api/core/componentStorage.ts
Expand Up @@ -18,7 +18,7 @@ export interface IStorageComponent {
/**
* Return token pair by their alias
* if alias not provided, returns the default pair
* @param auth {string} Authentication alias
* @param auth {string} Authentication alias or user email
*/
getTokens(auth?: string): Tokens;

Expand Down
29 changes: 21 additions & 8 deletions src/api/core/tokenManager.spec.ts
Expand Up @@ -104,15 +104,15 @@ describe("TokenManager", () => {
const { subject, validOptions } = createSubject();
jest.spyOn(subject as any, "startRefreshDigest");
await subject.init(validOptions);
expect((subject as any).startRefreshDigest).toHaveBeenCalledWith("apikey");
expect((subject as any).startRefreshDigest).toHaveBeenCalledWith(["apikey"]);
});

it("should schedule token refreshing for the authentication by alias", async () => {
const { subject, validOptions } = createSubject();
const auth = faker.random.word();
jest.spyOn(subject as any, "startRefreshDigest");
await subject.init({ ...validOptions, auth });
expect((subject as any).startRefreshDigest).toHaveBeenCalledWith(auth);
expect((subject as any).startRefreshDigest).toHaveBeenCalledWith([auth]);
});
});

Expand All @@ -138,11 +138,24 @@ describe("TokenManager", () => {
refresh_token: faker.random.word(),
};
await subject.init(validOptions);
subject.addTokens(randomAlias, anotherTokens);
subject.addTokens([randomAlias], anotherTokens);
subject.setDefault(randomAlias);
expect(await subject.token()).toEqual(anotherTokens.access_token);
});

it("should return a token by any of aliases if there are few", async () => {
const { subject, validOptions } = createSubject();
const randomAliases = [faker.random.word(), faker.random.word(), faker.random.word()];
const randomAlias = faker.helpers.randomize(randomAliases);
const anotherTokens = {
access_token: faker.random.word(),
refresh_token: faker.random.word(),
};
await subject.init(validOptions);
subject.addTokens(randomAliases, anotherTokens);
expect(await subject.token(randomAlias)).toEqual(anotherTokens.access_token);
});

it("should return default token after reset to default", async () => {
const { subject, validOptions, tokens } = createSubject();
const randomAlias = faker.random.word();
Expand All @@ -151,7 +164,7 @@ describe("TokenManager", () => {
refresh_token: faker.random.word(),
};
await subject.init(validOptions);
subject.addTokens(randomAlias, anotherTokens);
subject.addTokens([randomAlias], anotherTokens);
subject.setDefault(randomAlias);
subject.resetDefault();
expect(await subject.token()).toEqual(tokens.access_token);
Expand Down Expand Up @@ -201,7 +214,7 @@ describe("TokenManager", () => {
const { subject, validOptions } = createSubject();
const clearIntervalSpy = jest.spyOn(global, "clearInterval");
await subject.init(validOptions);
await subject.addTokens(faker.random.word(), {
await subject.addTokens([faker.random.word()], {
access_token: faker.random.word(),
refresh_token: faker.random.word(),
});
Expand Down Expand Up @@ -243,7 +256,7 @@ describe("TokenManager", () => {
await subject.init(validOptions);
jest.spyOn(subject as any, "refresh");
jest.runOnlyPendingTimers();
expect((subject as any).refresh).toHaveBeenCalledWith("apikey");
expect((subject as any).refresh).toHaveBeenCalledWith(["apikey"]);
});

it("should terminate itself if there is an error during refresh", async () => {
Expand Down Expand Up @@ -275,7 +288,7 @@ describe("TokenManager", () => {
(subject as any).storage.setTokens("testRefresh",
{ access_token: "access_token", refresh_token: "refresh_token" });
try {
await (subject as any).refresh("randomAuth");
await (subject as any).refresh(["randomAuth"]);
} catch (error) {
expect(error.message).toEqual("There is no refresh token for randomAuth");
}
Expand All @@ -288,7 +301,7 @@ describe("TokenManager", () => {
(subject as any).storage.setTokens("testRefresh",
{ access_token: "access_token", refresh_token: "refresh_token" });
try {
await (subject as any).refresh("testRefresh");
await (subject as any).refresh(["testRefresh"]);
} catch (error) {
expect(error.message).toEqual("Unable to get tokens: refresh error");
}
Expand Down
36 changes: 26 additions & 10 deletions src/api/core/tokenManager.ts
Expand Up @@ -53,6 +53,15 @@ export interface IAuthOptions {

export const AuthOptions = new InjectionToken<IAuthOptions>("IAuthOptions");

/**
* Not undefined type guard
* @internal
* @param x
*/
function notUndefined<T>(x: T | undefined): x is T {
return x !== undefined;
}

/**
* Keep token pairs and refresh them when its needed
*/
Expand Down Expand Up @@ -149,24 +158,31 @@ export class TokenManager {

/**
* Add new token pair and run refresh digest
* @param {string} auth Authenticate alias
* @param {Array<string | undefined>} aliases an array of authenticate aliases
* @param {Tokens} tokens Token pair
* @param {boolean} defaults Whether to use this token by default
*/
public addTokens(auth: string, tokens: Tokens, defaults?: boolean) {
this.storage.setTokens(auth, tokens, defaults);
this.startRefreshDigest(auth);
public addTokens(aliases: Array<string | undefined>, tokens: Tokens, defaults?: boolean) {

const definedAliases: string[] = aliases.filter(notUndefined);

/* Store token pairs for each alias but make default the first one only */
definedAliases.forEach((alias, index) => {
this.storage.setTokens(alias, tokens, !index && defaults);
});

this.startRefreshDigest(definedAliases);
}

/**
* Start refreshing digest for the specific auth
* @ignore
*/
private startRefreshDigest(auth: string) {
private startRefreshDigest(aliases: string[]) {
this.refreshes.push(
setInterval(() => {
this.logger.debug("tokenManager", `refresh ${auth} token`);
this.refresh(auth)
this.logger.debug("tokenManager", `refresh ${aliases[0]} token`);
this.refresh(aliases)
.catch(() => this.terminate());
}, DELAY)
);
Expand All @@ -179,7 +195,7 @@ export class TokenManager {
private login({auth = APIKEY_DEFAULT_ALIAS, key, secret}: IAuthOptions): Promise<this> {
return this.obtainTokens(auth, this.authUrl, { method: "apk", key, secret })
.then((tokens) => {
this.addTokens(auth, tokens, true);
this.addTokens([auth], tokens, true);
return this;
});
}
Expand All @@ -188,7 +204,7 @@ export class TokenManager {
* Refresh the token
* @ignore
*/
private refresh(auth: string): Promise<this> {
private refresh([auth, ...restAliases]: string[] = []): Promise<this> {

const tokens = this.storage.getTokens(auth);

Expand All @@ -198,7 +214,7 @@ export class TokenManager {

return this.obtainTokens(auth, this.refreshUrl, { refresh_token: tokens.refresh_token })
.then((refreshedTokens) => {
this.storage.setTokens(auth, refreshedTokens);
[auth, ...restAliases].forEach((alias) => this.storage.setTokens(alias, refreshedTokens));
return this;
});
}
Expand Down
8 changes: 1 addition & 7 deletions src/api/ums/umsModule.ts
Expand Up @@ -60,13 +60,7 @@ export class UMSModule implements IModule {
this.getUrl(API.AUTH, false),
{ body, method: RequestMethod.POST }
).then((tokens) => {

this.tokenManager.addTokens(
user.alias || user.email,
tokens,
user.default,
);

this.tokenManager.addTokens([user.email, user.alias], tokens, user.default);
return tokens.access_token;
});
}
Expand Down

0 comments on commit 194e1ae

Please sign in to comment.