Skip to content
This repository has been archived by the owner on Aug 1, 2024. It is now read-only.

21 expose entire sdk object #24

Merged
merged 7 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 0 additions & 26 deletions projects/angular-sdk/src/lib/angular-sdk.component.spec.ts

This file was deleted.

8 changes: 0 additions & 8 deletions projects/angular-sdk/src/lib/angular-sdk.component.ts

This file was deleted.

9 changes: 0 additions & 9 deletions projects/angular-sdk/src/lib/angular-sdk.module.ts

This file was deleted.

4 changes: 1 addition & 3 deletions projects/angular-sdk/src/lib/descope-auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,14 @@ import {
Optional,
SkipSelf
} from '@angular/core';
import { AngularSdkComponent } from './angular-sdk.component';

export class DescopeAuthConfig {
projectId = '';
}

@NgModule({
declarations: [AngularSdkComponent],
imports: [],
exports: [AngularSdkComponent]
exports: []
})
export class DescopeAuthModule {
constructor(@Optional() @SkipSelf() parentModule?: DescopeAuthModule) {
Expand Down
94 changes: 56 additions & 38 deletions projects/angular-sdk/src/lib/descope-auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Injectable } from '@angular/core';
import { DescopeAuthConfig } from './descope-auth.module';
import createSdk from '@descope/web-js-sdk';
import type { UserResponse } from '@descope/web-js-sdk';
import { BehaviorSubject, finalize, from, Observable } from 'rxjs';
import createSdk from '@descope/web-js-sdk';
import { BehaviorSubject, finalize, Observable, tap } from 'rxjs';
import { observabilify, Observablefied } from './helpers';

type DescopeSDK = ReturnType<typeof createSdk>;
type AngularDescopeSDK = Observablefied<DescopeSDK>;

export interface DescopeSession {
isAuthenticated: boolean;
Expand All @@ -18,18 +20,28 @@ export type DescopeUser = { user: UserResponse; isUserLoading: boolean };
providedIn: 'root'
})
export class DescopeAuthService {
private sdk: DescopeSDK;
public sdk: AngularDescopeSDK;
private readonly sessionSubject: BehaviorSubject<DescopeSession>;
private readonly userSubject: BehaviorSubject<DescopeUser>;
readonly descopeSession$: Observable<DescopeSession>;
readonly descopeUser$: Observable<DescopeUser>;

private readonly EMPTY_USER = {
loginIds: [],
userId: '',
createTime: 0,
TOTP: false,
SAML: false
};

constructor(config: DescopeAuthConfig) {
this.sdk = createSdk({
...config,
persistTokens: true,
autoRefresh: true
});
this.sdk = observabilify<DescopeSDK>(
createSdk({
...config,
persistTokens: true,
autoRefresh: true
})
);
this.sdk.onSessionTokenChange(this.setSession.bind(this));
this.sdk.onUserChange(this.setUser.bind(this));
this.sessionSubject = new BehaviorSubject<DescopeSession>({
Expand All @@ -40,45 +52,34 @@ export class DescopeAuthService {
this.descopeSession$ = this.sessionSubject.asObservable();
this.userSubject = new BehaviorSubject<DescopeUser>({
isUserLoading: false,
user: {
loginIds: [],
userId: '',
createTime: 0,
TOTP: false,
SAML: false
}
user: this.EMPTY_USER
});
this.descopeUser$ = this.userSubject.asObservable();
}

passwordSignUp() {
const user = {
name: 'Joe Person',
phone: '+15555555555',
email: '[email protected]'
};
return from(
this.sdk.password.signUp('[email protected]', '!QAZ2wsx', user)
);
}

passwordLogin() {
return from(
this.sdk.password.signIn('[email protected]', '!QAZ2wsx')
);
}

logout() {
return from(this.sdk.logout());
}

refreshSession() {
const beforeRefreshSession = this.sessionSubject.value;
this.sessionSubject.next({
...beforeRefreshSession,
isSessionLoading: true
});
return from(this.sdk.refresh()).pipe(
return this.sdk.refresh().pipe(
tap((data) => {
const afterRequestSession = this.sessionSubject.value;
if (data.ok && data.data) {
this.sessionSubject.next({
...afterRequestSession,
sessionToken: data.data.sessionJwt,
isAuthenticated: !!data.data.sessionJwt
});
} else {
this.sessionSubject.next({
...afterRequestSession,
sessionToken: '',
isAuthenticated: false
});
}
}),
finalize(() => {
const afterRefreshSession = this.sessionSubject.value;
this.sessionSubject.next({
Expand All @@ -95,7 +96,24 @@ export class DescopeAuthService {
...beforeRefreshUser,
isUserLoading: true
});
return from(this.sdk.me()).pipe(
return this.sdk.me().pipe(
tap((data) => {
const afterRequestUser = this.userSubject.value;
console.log(data);
if (data.data) {
this.userSubject.next({
...afterRequestUser,
user: {
...data.data
}
});
} else {
this.userSubject.next({
...afterRequestUser,
user: this.EMPTY_USER
});
}
}),
finalize(() => {
const afterRefreshUser = this.userSubject.value;
this.userSubject.next({
Expand Down
103 changes: 103 additions & 0 deletions projects/angular-sdk/src/lib/helpers.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { observabilify, Observablefied } from './helpers';
import { lastValueFrom, Observable } from 'rxjs';

describe('Helpers', () => {
describe('Observabilify', () => {
it('should not affect simple object', () => {
//GIVEN
const obj = {
field1: 'string',
field2: 123,
nested: {
field1: 'string',
field2: 123
}
};
type TestType = typeof obj;

//WHEN
const result: Observablefied<TestType> = observabilify<TestType>(obj);

//THEN
expect(result).toStrictEqual(obj);
});

it('should not affect simple object with non async functions', () => {
//GIVEN
const obj = {
field1: 'string',
field2: 123,
fn: (arg1: string, arg2: number): string => {
return arg1 + arg2.toString();
},
nested: {
fn2: (arg: string): string => {
return arg;
},
field1: 'string',
field2: 123
}
};
type TestType = typeof obj;
const expected1 = obj.fn('Test', 1);
const expected2 = obj.nested.fn2('Test');

//WHEN
const transformed: Observablefied<TestType> =
observabilify<TestType>(obj);
const actual1 = transformed.fn('Test', 1);
const actual2 = transformed.nested.fn2('Test');

//THEN
expect(expected1).toStrictEqual(actual1);
expect(expected2).toStrictEqual(actual2);
});

it('should transform async functions', async () => {
//GIVEN
const obj = {
field1: 'string',
field2: 123,
fn: (arg1: string, arg2: number): string => {
return arg1 + arg2.toString();
},
asyncFn: (arg1: string, arg2: number): Promise<string> => {
return Promise.resolve(arg1 + arg2.toString());
},
nested: {
fn2: (arg: string) => {
return arg;
},
asyncFn: (arg: string): Promise<string> => {
return Promise.resolve(arg);
},
field1: 'string',
field2: 123
}
};
type TestType = typeof obj;
const expected1 = obj.fn('Test', 1);
const expected2 = obj.nested.fn2('Test');
const expected3 = await obj.asyncFn('Test', 1);
const expected4 = await obj.nested.asyncFn('Test');

//WHEN
const transformed: Observablefied<TestType> =
observabilify<TestType>(obj);
const actual1 = transformed.fn('Test', 1);
const actual2 = transformed.nested.fn2('Test');
const actual3Async = transformed.asyncFn('Test', 1);
const actual3 = await lastValueFrom(actual3Async);
const actual4Async = transformed.nested.asyncFn('Test');
const actual4 = await lastValueFrom(actual4Async);

//THEN
expect(expected1).toStrictEqual(actual1);
expect(expected2).toStrictEqual(actual2);
expect(actual3Async).toBeInstanceOf(Observable);
expect(actual4Async).toBeInstanceOf(Observable);
expect(expected3).toStrictEqual(actual3);
expect(expected4).toStrictEqual(actual4);
});
});
});
36 changes: 36 additions & 0 deletions projects/angular-sdk/src/lib/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { from, Observable } from 'rxjs';

export type Observablefied<T> = {
[K in keyof T]: T[K] extends (...args: infer Args) => Promise<infer R>
? (...args: Args) => Observable<R>
: T[K] extends (...args: infer Args) => infer R
? (...args: Args) => R
: T[K] extends object
? Observablefied<T[K]>
: T[K];
};

export function observabilify<T>(value: T): Observablefied<T> {
/* eslint-disable @typescript-eslint/no-explicit-any */
const observableValue: any = {};

for (const key in value) {
if (typeof value[key] === 'function') {
const fn = value[key] as (...args: unknown[]) => unknown;
observableValue[key] = (...args: unknown[]) => {
const fnResult = fn(...args);
if (fnResult instanceof Promise) {
return from(fnResult);
} else {
return fnResult;
}
};
} else if (typeof value[key] === 'object' && value[key] !== null) {
observableValue[key] = observabilify(value[key]);
} else {
observableValue[key] = value[key];
}
}

return observableValue as Observablefied<T>;
}
1 change: 0 additions & 1 deletion projects/angular-sdk/src/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,5 @@
*/

export * from './lib/descope-auth.service';
export * from './lib/angular-sdk.component';
export * from './lib/descope-auth.guard';
export * from './lib/descope-auth.module';
Loading
Loading