-
Notifications
You must be signed in to change notification settings - Fork 59
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: separate api auth to plugin folder
Signed-off-by: Nastya <[email protected]>
- Loading branch information
Showing
11 changed files
with
208 additions
and
89 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import * as React from 'react'; | ||
import { createContext, useContext } from 'react'; | ||
import { getAdminApiUrl, getEndpointUrl } from '../utils'; | ||
import { AdminEndpoint, RawEndpoint } from '../utils/constants'; | ||
import { defaultLoginStatus, getLoginUrl, LoginStatus } from './login'; | ||
|
||
export interface FlyteApiContextState { | ||
loginStatus: LoginStatus; | ||
getLoginUrl: (redirect?: string) => string; | ||
getProfileUrl: () => string; | ||
getAdminApiUrl: (endpoint: AdminEndpoint) => string; | ||
} | ||
|
||
const FlyteApiContext = createContext<FlyteApiContextState>({ | ||
// default values - used when Provider wrapper is not found | ||
loginStatus: defaultLoginStatus, | ||
getLoginUrl: () => '#', | ||
getProfileUrl: () => '#', | ||
getAdminApiUrl: () => '#', | ||
}); | ||
|
||
interface FlyteApiProviderProps { | ||
flyteApiDomain: string; | ||
children?: React.ReactNode; | ||
} | ||
|
||
export const useFlyteApi = () => useContext(FlyteApiContext); | ||
|
||
export const FlyteApiProvider = (props: FlyteApiProviderProps) => { | ||
const { flyteApiDomain } = props; | ||
|
||
const [loginExpired, setLoginExpired] = React.useState(false); | ||
|
||
// Whenever we detect expired credentials, trigger a login redirect automatically | ||
React.useEffect(() => { | ||
if (loginExpired) { | ||
window.location.href = getLoginUrl(flyteApiDomain); | ||
} | ||
}, [loginExpired]); | ||
|
||
return ( | ||
<FlyteApiContext.Provider | ||
value={{ | ||
loginStatus: { | ||
expired: loginExpired, | ||
setExpired: setLoginExpired, | ||
}, | ||
getLoginUrl: (redirect) => getLoginUrl(flyteApiDomain, redirect), | ||
getProfileUrl: () => getEndpointUrl(RawEndpoint.Profile, flyteApiDomain), | ||
getAdminApiUrl: (endpoint) => getAdminApiUrl(endpoint, flyteApiDomain), | ||
}} | ||
> | ||
{props.children} | ||
</FlyteApiContext.Provider> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { getEndpointUrl } from '../utils'; | ||
import { RawEndpoint } from '../utils/constants'; | ||
|
||
export interface LoginStatus { | ||
expired: boolean; | ||
setExpired(expired: boolean): void; | ||
} | ||
|
||
export const defaultLoginStatus: LoginStatus = { | ||
expired: true, | ||
setExpired: () => { | ||
/** Do nothing */ | ||
}, | ||
}; | ||
|
||
/** Constructs a url for redirecting to the Admin login endpoint and returning | ||
* to the current location after completing the flow. | ||
*/ | ||
export function getLoginUrl(adminUrl?: string, redirectUrl: string = window.location.href) { | ||
const baseUrl = getEndpointUrl(RawEndpoint.Login, adminUrl); | ||
return `${baseUrl}?$redirect_url=${redirectUrl}`; | ||
} |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,4 @@ | ||
export { SampleComponent } from './Sample'; | ||
export { FlyteApiProvider, useFlyteApi, type FlyteApiContextState } from './ApiProvider'; | ||
|
||
export { AdminEndpoint, RawEndpoint } from './utils/constants'; | ||
export { getAxiosApiCall, defaultAxiosConfig } from './utils'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
export enum RawEndpoint { | ||
Login = '/login', | ||
Profile = '/me', | ||
} | ||
|
||
export const adminApiPrefix = '/api/v1'; | ||
|
||
export enum AdminEndpoint { | ||
Version = '/version', | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/* eslint-disable max-classes-per-file */ | ||
import { AxiosError } from 'axios'; | ||
|
||
export class NotFoundError extends Error { | ||
constructor(public override name: string, msg = 'The requested item could not be found') { | ||
super(msg); | ||
} | ||
} | ||
|
||
/** Indicates failure to fetch a resource because the user is not authorized (401) */ | ||
export class NotAuthorizedError extends Error { | ||
constructor(msg = 'User is not authorized to view this resource') { | ||
super(msg); | ||
} | ||
} | ||
|
||
/** Detects special cases for errors returned from Axios and lets others pass through. */ | ||
export function transformRequestError(err: unknown, path: string) { | ||
const error = err as AxiosError; | ||
|
||
if (!error.response) { | ||
return error; | ||
} | ||
|
||
// For some status codes, we'll throw a special error to allow | ||
// client code and components to handle separately | ||
if (error.response.status === 404) { | ||
return new NotFoundError(path); | ||
} | ||
if (error.response.status === 401) { | ||
return new NotAuthorizedError(); | ||
} | ||
|
||
// this error is not decoded. | ||
return error; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import axios, { | ||
AxiosRequestConfig, | ||
AxiosRequestTransformer, | ||
AxiosResponseTransformer, | ||
} from 'axios'; | ||
import * as snakecaseKeys from 'snakecase-keys'; | ||
import * as camelcaseKeys from 'camelcase-keys'; | ||
import { AdminEndpoint, adminApiPrefix, RawEndpoint } from './constants'; | ||
import { isObject } from './nodeChecks'; | ||
import { transformRequestError } from './errors'; | ||
|
||
/** Ensures that a string is slash-prefixed */ | ||
function ensureSlashPrefixed(path: string) { | ||
return path.startsWith('/') ? path : `/${path}`; | ||
} | ||
|
||
/** Creates a URL to the same host with a given path */ | ||
function createLocalURL(path: string) { | ||
return `${window.location.origin}${ensureSlashPrefixed(path)}`; | ||
} | ||
|
||
/** Updates Enpoint url depending on admin domain */ | ||
export function getEndpointUrl(endpoint: RawEndpoint | string, adminUrl?: string) { | ||
if (adminUrl) { | ||
return `${adminUrl}${endpoint}`; | ||
} | ||
|
||
return createLocalURL(endpoint); | ||
} | ||
|
||
/** Adds admin api prefix to domain Url */ | ||
export function getAdminApiUrl(endpoint: AdminEndpoint | string, adminUrl?: string) { | ||
const finalUrl = `${adminApiPrefix}${ensureSlashPrefixed(endpoint)}`; | ||
|
||
if (adminUrl) { | ||
return `${adminUrl}${finalUrl}`; | ||
} | ||
|
||
return createLocalURL(finalUrl); | ||
} | ||
|
||
/** Config object that can be used for requests that are not sent to | ||
* the Admin entity API (`/api/v1/...`), such as the `/me` endpoint. This config | ||
* ensures that requests/responses are correctly converted and that cookies are | ||
* included. | ||
*/ | ||
export const defaultAxiosConfig: AxiosRequestConfig = { | ||
transformRequest: [ | ||
(data: any) => (isObject(data) ? snakecaseKeys(data) : data), | ||
...(axios.defaults.transformRequest as AxiosRequestTransformer[]), | ||
], | ||
transformResponse: [ | ||
...(axios.defaults.transformResponse as AxiosResponseTransformer[] as any), | ||
camelcaseKeys, | ||
], | ||
withCredentials: true, | ||
}; | ||
|
||
/** | ||
* @deprecated Please use `axios-hooks` instead, it will allow you to get full call status. | ||
* example usage https://www.npmjs.com/package/axios-hooks: | ||
* const [{ data: profile, loading, errot }] = useAxios({url: path, method: 'GET', ...defaultAxiosConfig}); | ||
*/ | ||
export const getAxiosApiCall = async <T>(path: string): Promise<T | null> => { | ||
try { | ||
const { data } = await axios.get<T>(path, defaultAxiosConfig); | ||
return data; | ||
} catch (e) { | ||
const { message } = transformRequestError(e, path); | ||
// eslint-disable-next-line no-console | ||
console.error(`Failed to fetch data: ${message}`); | ||
return null; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
// recommended util.d.ts implementation | ||
export const isObject = (value: unknown): boolean => { | ||
return value !== null && typeof value === 'object'; | ||
}; |