Skip to content

Commit

Permalink
Resolve PR conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
trunges21 committed Mar 7, 2024
2 parents 39ba497 + 71ef7fb commit 1631ac6
Show file tree
Hide file tree
Showing 11 changed files with 219 additions and 38 deletions.
21 changes: 21 additions & 0 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: run-tests

on:
push:
branches: [master]
pull_request:
branches: [master]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node.js

uses: actions/setup-node@v4
- name: Install dependencies
run: npm install # `npm ci` (won't work lock files are .gitignored)

- name: Run tests
run: npm test
130 changes: 129 additions & 1 deletion __tests__/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,16 @@ const fakeTokenResponse = {
};

function FormDataMock() {
this.append = jest.fn();
this[Symbol.for('state')] = [] as Array<{
name: string;
value: string;
}>;
}

FormDataMock.prototype.append = function (key: string, value: string) {
this[Symbol.for('state')].push({ key, value });
};

jest.mock('react-native-keychain', () => ({
setGenericPassword: jest.fn().mockResolvedValue(),
getGenericPassword: jest.fn().mockResolvedValue(),
Expand Down Expand Up @@ -1058,4 +1065,125 @@ describe('KindeSDK', () => {
);
});
});

describe('forceTokenRefresh', () => {
test(`[RNStorage] Throws an error if no refresh token found in storage`, async () => {
RNStorage.prototype.getItem = jest.fn().mockReturnValue({
password: JSON.stringify({ refresh_token: undefined })
});
await expect(globalClient.forceTokenRefresh()).rejects.toThrow(
'No refresh token available to perform token refresh.'
);
});

test(`[ExpoStorage] Throws an error if no refresh token found in storage`, async () => {
Constants.executionEnvironment = 'storeClient';
ExpoStorage.prototype.getItem = jest
.fn()
.mockReturnValue(JSON.stringify({ refresh_token: undefined }));

await expect(globalClient.forceTokenRefresh()).rejects.toThrow(
'No refresh token available to perform token refresh.'
);
});

test('[RNStorage] Stores newly fetched tokens in storage', async () => {
let storage = {
username: 'kinde',
password: JSON.stringify(fakeTokenResponse)
};
RNStorage.prototype.getItem = jest.fn(() => storage);
RNStorage.prototype.setItem = jest.fn((value: unknown) => {
storage.password = JSON.stringify(value);
});

const formData = new FormData();
const { refresh_token } = JSON.parse(storage.password);
formData.append('client_id', configuration.clientId);
formData.append('grant_type', 'refresh_token');
formData.append('refresh_token', refresh_token);

const newTokensResponse = {
...fakeTokenResponse,
access_token: 'this_is_new_access_token',
refresh_token: 'this_is_new_refresh_token',
id_token: 'this_is_new_id_token'
};
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve(newTokensResponse)
})
);

await globalClient.forceTokenRefresh();
expect(global.fetch).toHaveBeenCalled();
expect(global.fetch.mock.calls[0][1].body).toEqual(formData);
expect(storage.password).toBe(JSON.stringify(newTokensResponse));
});

test('[ExpoStorage] Stores newly fetched tokens in storage', async () => {
let storage = { ...fakeTokenResponse };
Constants.executionEnvironment = 'storeClient';
ExpoStorage.prototype.getItem = jest.fn(() =>
JSON.stringify(storage)
);
ExpoStorage.prototype.setItem = jest.fn((value: unknown) => {
storage = { ...value };
});

const formData = new FormData();
const { refresh_token } = storage;
formData.append('client_id', configuration.clientId);
formData.append('grant_type', 'refresh_token');
formData.append('refresh_token', refresh_token);

const newTokensResponse = {
...fakeTokenResponse,
access_token: 'this_is_new_access_token',
refresh_token: 'this_is_new_refresh_token',
id_token: 'this_is_new_id_token'
};
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve(newTokensResponse)
})
);

await globalClient.forceTokenRefresh();
expect(global.fetch).toHaveBeenCalled();
expect(global.fetch.mock.calls[0][1].body).toEqual(formData);
expect(storage).toEqual(newTokensResponse);
});

test(`[RNStorage] returns "null" in the event network call rejects`, async () => {
RNStorage.prototype.getItem = jest.fn().mockReturnValue({
password: JSON.stringify(fakeTokenResponse)
});
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ error: 'error' })
})
);

const response = await globalClient.forceTokenRefresh();
expect(response).toBe(null);
expect(global.fetch).toHaveBeenCalled();
});

test(`[ExpoStorage] returns "null" in the event network call rejects`, async () => {
Constants.executionEnvironment = 'storeClient';
ExpoStorage.prototype.getItem = jest
.fn()
.mockReturnValue(JSON.stringify(fakeTokenResponse));
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ error: 'error' })
})
);

const response = await globalClient.forceTokenRefresh();
expect(response).toBe(null);
expect(global.fetch).toHaveBeenCalled();
});
});
});
18 changes: 9 additions & 9 deletions dist/ApiClient.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,24 +80,24 @@ export declare const COLLECTION_FORMATS: {
tsv: string;
pipes: string;
};
export declare type FetchAPI = WindowOrWorkerGlobalScope['fetch'];
export declare type Json = any;
export declare type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD';
export declare type HTTPHeaders = {
export type FetchAPI = WindowOrWorkerGlobalScope['fetch'];
export type Json = any;
export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD';
export type HTTPHeaders = {
[key: string]: string;
};
export declare type HTTPQuery = {
export type HTTPQuery = {
[key: string]: string | number | null | boolean | Array<string | number | null | boolean> | Set<string | number | null | boolean> | HTTPQuery;
};
export declare type HTTPBody = Json | FormData | URLSearchParams;
export declare type HTTPRequestInit = {
export type HTTPBody = Json | FormData | URLSearchParams;
export type HTTPRequestInit = {
headers?: HTTPHeaders;
method: HTTPMethod;
credentials?: RequestCredentials_;
body?: HTTPBody;
};
export declare type ModelPropertyNaming = 'camelCase' | 'snake_case' | 'PascalCase' | 'original';
export declare type InitOverrideFunction = (requestContext: {
export type ModelPropertyNaming = 'camelCase' | 'snake_case' | 'PascalCase' | 'original';
export type InitOverrideFunction = (requestContext: {
init: HTTPRequestInit;
context: RequestOpts;
}) => Promise<RequestInit>;
Expand Down
15 changes: 11 additions & 4 deletions dist/SDK/KindeSDK.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ declare class KindeSDK extends runtime.BaseAPI {
* token.
*/
useRefreshToken(token?: TokenResponse | null): Promise<TokenResponse>;
/**
* This function refreshes the access token using the current refresh token
* in storage and updates the storage with new tokens.
* @returns A Promise that resolves to `TokenResponse` if attempted refresh
* succeeds or `null` in the event the attempted token refresh fails.
*/
forceTokenRefresh(): Promise<TokenResponse | null>;
/**
* This function fetches a token from a server using a POST request with form data and stores it in
* local storage.
Expand Down Expand Up @@ -179,7 +186,7 @@ declare class KindeSDK extends runtime.BaseAPI {
* specified flag as a number. If the flag is not found, it returns the `defaultValue` parameter
* (if provided) as a number.
*/
getIntegerFlag(flagName: string, defaultValue?: number): Promise<string | number | boolean>;
getIntegerFlag(flagName: string, defaultValue?: number): Promise<import("../types/KindeSDK").FeatureFlagValue>;
/**
* This is an asynchronous function that retrieves a boolean flag value by name, with an optional
* default value.
Expand All @@ -192,7 +199,7 @@ declare class KindeSDK extends runtime.BaseAPI {
* argument to indicate that the flag value should be interpreted as a boolean. The `await` keyword
* is used to wait for the `getFlag` method to complete before accessing the `value` property
*/
getBooleanFlag(flagName: string, defaultValue?: boolean): Promise<string | number | boolean>;
getBooleanFlag(flagName: string, defaultValue?: boolean): Promise<import("../types/KindeSDK").FeatureFlagValue>;
/**
* This is an asynchronous function that retrieves a string flag value with an optional default
* value.
Expand All @@ -207,7 +214,7 @@ declare class KindeSDK extends runtime.BaseAPI {
* flag. If no flag value is found, it will return the `defaultValue` parameter passed to the
* function.
*/
getStringFlag(flagName: string, defaultValue?: string): Promise<string | number | boolean>;
getStringFlag(flagName: string, defaultValue?: string): Promise<import("../types/KindeSDK").FeatureFlagValue>;
/**
* This function retrieves a feature flag and its value, with the option to provide a default value
* and check its type.
Expand All @@ -227,7 +234,7 @@ declare class KindeSDK extends runtime.BaseAPI {
getFlag(flagName: string, options?: OptionalFlag, flagType?: FeatureFlag['t']): Promise<{
code: string;
type: string;
value: string | number | boolean;
value: import("../types/KindeSDK").FeatureFlagValue;
is_default: boolean;
}>;
/**
Expand Down
2 changes: 1 addition & 1 deletion dist/SDK/KindeSDK.js

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions dist/SDK/Utils.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/
/// <reference types="react-native-inappbrowser-reborn" />
import * as WebBrowser from 'expo-web-browser';
import { AuthBrowserOptions } from '../types/Auth';
import { AdditionalParameters } from '../types/KindeSDK';
import KindeSDK from './KindeSDK';
/**
Expand Down Expand Up @@ -51,6 +52,6 @@ export declare const checkAdditionalParameters: (additionalParameters?: Addition
*/
export declare const addAdditionalParameters: (target: Record<string, string | undefined>, additionalParameters?: AdditionalParameters) => Record<string, string | undefined>;
export declare const isExpoGo: boolean;
export declare const OpenWebInApp: (url: string, kindeSDK: KindeSDK, options?: WebBrowser.AuthSessionOpenOptions | import("react-native-inappbrowser-reborn").InAppBrowseriOSOptions | import("react-native-inappbrowser-reborn").InAppBrowserAndroidOptions | undefined) => Promise<import("../types/KindeSDK").TokenResponse | null>;
export declare const openWebBrowser: (url: string, redirectUri: string, options?: WebBrowser.AuthSessionOpenOptions | import("react-native-inappbrowser-reborn").InAppBrowseriOSOptions | import("react-native-inappbrowser-reborn").InAppBrowserAndroidOptions | undefined) => Promise<WebBrowser.WebBrowserRedirectResult | WebBrowser.WebBrowserResult | import("react-native-inappbrowser-reborn").BrowserResult>;
export declare const OpenWebInApp: (url: string, kindeSDK: KindeSDK, options?: AuthBrowserOptions) => Promise<import("../types/KindeSDK").TokenResponse | null>;
export declare const openWebBrowser: (url: string, redirectUri: string, options?: AuthBrowserOptions) => Promise<WebBrowser.WebBrowserRedirectResult | WebBrowser.WebBrowserResult | import("react-native-inappbrowser-reborn").BrowserResult>;
export declare const convertObject2FormData: (obj: Record<string, any>) => FormData;
2 changes: 1 addition & 1 deletion dist/types/Auth.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { AuthSessionOpenOptions } from 'expo-web-browser';
import { InAppBrowserOptions } from 'react-native-inappbrowser-reborn';
export declare type AuthBrowserOptions = InAppBrowserOptions | AuthSessionOpenOptions;
export type AuthBrowserOptions = InAppBrowserOptions | AuthSessionOpenOptions;
24 changes: 12 additions & 12 deletions dist/types/KindeSDK.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,29 @@
* @module SDK/Types
* @version 1.2.0
*/
export declare type AdditionalParameters = {
export type AdditionalParameters = {
audience?: string;
is_create_org?: boolean;
org_code?: string;
org_name?: string;
};
export declare type OrgAdditionalParams = Omit<AdditionalParameters, 'audience'>;
export declare type UserProfile = {
export type OrgAdditionalParams = Omit<AdditionalParameters, 'audience'>;
export type UserProfile = {
id: string;
given_name: string;
family_name: string;
email: string;
picture: string;
};
export declare type TokenResponse = {
export type TokenResponse = {
access_token: string;
refresh_token: string;
id_token: string;
scope: string;
token_type: string;
expires_in: number;
};
export declare type AccessTokenDecoded = {
export type AccessTokenDecoded = {
aud: string[];
azp: string;
exp: number;
Expand All @@ -47,25 +47,25 @@ export declare type AccessTokenDecoded = {
gty?: string[];
scp?: string[];
} & Record<string, any>;
export declare type IdTokenDecoded = {
export type IdTokenDecoded = {
sub: string;
given_name: string;
family_name: string;
email: string;
picture: string;
} & Record<string, any>;
export declare type OptionalFlag = {
export type OptionalFlag = {
defaultValue?: string | boolean | number;
};
export declare type FeatureFlagType = 's' | 'b' | 'i';
export declare type FeatureFlagValue = string | boolean | number;
export declare type FeatureFlag = {
export type FeatureFlagType = 's' | 'b' | 'i';
export type FeatureFlagValue = string | boolean | number;
export type FeatureFlag = {
v?: FeatureFlagValue;
t?: FeatureFlagType;
};
export declare type LoginAdditionalParameters = Omit<OrgAdditionalParams, 'is_create_org'> & {
export type LoginAdditionalParameters = Omit<OrgAdditionalParams, 'is_create_org'> & {
[key: string]: unknown;
};
export declare type RegisterAdditionalParameters = OrgAdditionalParams & {
export type RegisterAdditionalParameters = OrgAdditionalParams & {
[key: string]: unknown;
};
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@
"crypto-js": "3.3.0",
"expo-constants": "^14.2.1",
"expo-modules-core": "^1.2.6",
"expo-web-browser": "12.1.1",
"expo-secure-store": "^12.1.1",
"expo-secure-store": "*",
"expo-web-browser": "*",
"jwt-decode": "^3.1.2",
"qs": "^6.11.0",
"react-native-inappbrowser-reborn": "^3.7.0",
"react-native-inappbrowser-reborn": ">= 3.7",
"react-native-keychain": ">= 8.0",
"superagent": "^7.0.2",
"url-parse": "^1.5.10"
Expand Down Expand Up @@ -88,14 +88,14 @@
"react-test-renderer": "16.0.0",
"rimraf": "^3.0.2",
"ts-jest": "^23.10.5",
"typescript": "3.9.7"
"typescript": "^4.8.4"
},
"peerDependencies": {
"expo-secure-store": "*",
"react-native": ">= 0.60",
"react-native-keychain": ">= 8.0",
"expo-web-browser": "*",
"react-native-inappbrowser-reborn": ">= 3.7"
"react-native": ">= 0.60",
"react-native-inappbrowser-reborn": ">= 3.7",
"react-native-keychain": ">= 8.0"
},
"files": [
"dist"
Expand Down
Loading

0 comments on commit 1631ac6

Please sign in to comment.