diff --git a/frontend/.prettierignore b/frontend/.prettierignore index 20ab352d5b..a06b11e1a7 100644 --- a/frontend/.prettierignore +++ b/frontend/.prettierignore @@ -1,2 +1,3 @@ +.github/* +/src/openapi CHANGELOG.md -.github/* \ No newline at end of file diff --git a/frontend/README.md b/frontend/README.md index 749c8d8976..cdddbbc674 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -23,10 +23,10 @@ yarn install yarn run start:heroku ``` -## Running end-to-end Tests - -We have a set of Cypress tests that run on the build before a PR can be merged so it's important that you check these yourself before submitting a PR. +## Running end-to-end tests +We have a set of Cypress tests that run on the build before a PR can be merged +so it's important that you check these yourself before submitting a PR. On the server the tests will run against the deployed Heroku app so this is what you probably want to test against: ``` @@ -39,15 +39,30 @@ In a different shell, you can run the tests themselves: yarn run e2e:heroku ``` -If you need to test against patches against a local server instance, you'll need to run that, and then run the end to end tests using: +If you need to test against patches against a local server instance, +you'll need to run that, and then run the end to end tests using: ``` yarn run e2e ``` -You may also need to test that a feature works against the enterprise version of unleash. Assuming the Heroku instance is still running, this can be done by: +You may also need to test that a feature works against the enterprise version of unleash. +Assuming the Heroku instance is still running, this can be done by: ``` yarn run start:enterprise yarn run e2e ``` + +## Generating the OpenAPI client + +The frontend uses an OpenAPI client generated from the backend's OpenAPI spec. +Whenever there are changes to the backend API, the client should be regenerated: + +``` +./scripts/generate-openapi.sh +``` + +This script assumes that you have a running instance of the enterprise backend at `http://localhost:4242`. +The new OpenAPI client will be generated from the runtime schema of this instance. +The target URL can be changed by setting the `UNLEASH_OPENAPI_URL` env var. diff --git a/frontend/package.json b/frontend/package.json index d3f5b2a3e0..edd8805ce8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -40,6 +40,7 @@ "@material-ui/core": "4.12.4", "@material-ui/icons": "4.11.3", "@material-ui/lab": "4.0.0-alpha.61", + "@openapitools/openapi-generator-cli": "^2.4.26", "@testing-library/dom": "8.12.0", "@testing-library/jest-dom": "5.16.3", "@testing-library/react": "12.1.5", diff --git a/frontend/scripts/generate-openapi.sh b/frontend/scripts/generate-openapi.sh new file mode 100755 index 0000000000..19d68b94d8 --- /dev/null +++ b/frontend/scripts/generate-openapi.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +# Generate OpenAPI bindings for the Unleash API. +# https://openapi-generator.tech/docs/generators/typescript-fetch + +set -feux +cd "$(dirname "$0")" + +# URL to the generated open API spec. +# Set the UNLEASH_OPENAPI_URL environment variable to override. +UNLEASH_OPENAPI_URL="${UNLEASH_OPENAPI_URL:-http://localhost:4242/docs/openapi.json}" + +rm -rf "../src/openapi" +mkdir "../src/openapi" + +npx @openapitools/openapi-generator-cli generate \ + -g "typescript-fetch" \ + -i "$UNLEASH_OPENAPI_URL" \ + -o "../src/openapi" + +# Remove unused files. +rm "openapitools.json" +rm "../src/openapi/.openapi-generator-ignore" +rm -r "../src/openapi/.openapi-generator" diff --git a/frontend/src/component/admin/auth/AutoCreateForm/AutoCreateForm.tsx b/frontend/src/component/admin/auth/AutoCreateForm/AutoCreateForm.tsx index 373cf547ae..3353c01597 100644 --- a/frontend/src/component/admin/auth/AutoCreateForm/AutoCreateForm.tsx +++ b/frontend/src/component/admin/auth/AutoCreateForm/AutoCreateForm.tsx @@ -10,7 +10,7 @@ import { TextField, } from '@material-ui/core'; -interface Props { +interface IAutoCreateFormProps { data?: { enabled: boolean; autoCreate: boolean; @@ -23,7 +23,7 @@ interface Props { export const AutoCreateForm = ({ data = { enabled: false, autoCreate: false }, setValue, -}: Props) => { +}: IAutoCreateFormProps) => { const updateAutoCreate = () => { setValue('autoCreate', !data.autoCreate); }; diff --git a/frontend/src/component/archive/ArchiveListContainer.tsx b/frontend/src/component/archive/ArchiveListContainer.tsx index 1031b18de8..b345294d6d 100644 --- a/frontend/src/component/archive/ArchiveListContainer.tsx +++ b/frontend/src/component/archive/ArchiveListContainer.tsx @@ -10,7 +10,13 @@ export const ArchiveListContainer = () => { const { setToastData, setToastApiError } = useToast(); const { uiConfig } = useUiConfig(); const { reviveFeature } = useFeatureArchiveApi(); - const { archivedFeatures, loading, refetchArchived } = useFeaturesArchive(); + + const { + archivedFeatures = [], + refetchArchived, + loading, + } = useFeaturesArchive(); + const { filtered, filter, setFilter } = useFeaturesFilter(archivedFeatures); const { sorted, sort, setSort } = useFeaturesSort(filtered); diff --git a/frontend/src/component/environments/EnvironmentList/EnvironmentListItem/EnvironmentListItem.tsx b/frontend/src/component/environments/EnvironmentList/EnvironmentListItem/EnvironmentListItem.tsx index f5628f0a35..6fe04e47cc 100644 --- a/frontend/src/component/environments/EnvironmentList/EnvironmentListItem/EnvironmentListItem.tsx +++ b/frontend/src/component/environments/EnvironmentList/EnvironmentListItem/EnvironmentListItem.tsx @@ -37,13 +37,13 @@ interface IEnvironmentListItemProps { moveListItemApi: (dragIndex: number, hoverIndex: number) => Promise; } -interface DragItem { +interface IDragItem { index: number; id: string; type: string; } -interface CollectedProps { +interface ICollectedProps { handlerId: Identifier | null; } @@ -69,19 +69,19 @@ const EnvironmentListItem = ({ }), }); - const [{ handlerId }, drop] = useDrop({ + const [{ handlerId }, drop] = useDrop({ accept: ACCEPT_TYPE, collect(monitor) { return { handlerId: monitor.getHandlerId(), }; }, - drop(item: DragItem, monitor: DropTargetMonitor) { + drop(item: IDragItem, monitor: DropTargetMonitor) { const dragIndex = item.index; const hoverIndex = index; moveListItemApi(dragIndex, hoverIndex); }, - hover(item: DragItem, monitor: DropTargetMonitor) { + hover(item: IDragItem, monitor: DropTargetMonitor) { if (!ref.current) { return; } diff --git a/frontend/src/component/feature/CreateFeature/CreateFeature.tsx b/frontend/src/component/feature/CreateFeature/CreateFeature.tsx index 2cd2946b8a..b827be959a 100644 --- a/frontend/src/component/feature/CreateFeature/CreateFeature.tsx +++ b/frontend/src/component/feature/CreateFeature/CreateFeature.tsx @@ -45,7 +45,6 @@ const CreateFeature = () => { if (validToggleName) { const payload = getTogglePayload(); try { - // @ts-expect-error await createFeatureToggle(project, payload); history.push(`/projects/${project}/features/${name}`); setToastData({ diff --git a/frontend/src/component/feature/FeatureToggleList/FeatureToggleListContainer.tsx b/frontend/src/component/feature/FeatureToggleList/FeatureToggleListContainer.tsx index 067b4756fa..13cbe7a3d6 100644 --- a/frontend/src/component/feature/FeatureToggleList/FeatureToggleListContainer.tsx +++ b/frontend/src/component/feature/FeatureToggleList/FeatureToggleListContainer.tsx @@ -6,7 +6,7 @@ import { useFeaturesSort } from 'hooks/useFeaturesSort'; export const FeatureToggleListContainer = () => { const { uiConfig } = useUiConfig(); - const { features, loading } = useFeatures(); + const { features = [], loading } = useFeatures(); const { filtered, filter, setFilter } = useFeaturesFilter(features); const { sorted, sort, setSort } = useFeaturesSort(filtered); diff --git a/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNewItem/CreatedAt.tsx b/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNewItem/CreatedAt.tsx index 2cc3cc2a6e..088d406b48 100644 --- a/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNewItem/CreatedAt.tsx +++ b/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNewItem/CreatedAt.tsx @@ -2,11 +2,11 @@ import { Tooltip } from '@material-ui/core'; import { useLocationSettings } from 'hooks/useLocationSettings'; import { formatDateYMD, formatDateYMDHMS } from 'utils/formatDate'; -interface CreatedAtProps { +interface ICreatedAtProps { time: string; } -const CreatedAt = ({ time }: CreatedAtProps) => { +const CreatedAt = ({ time }: ICreatedAtProps) => { const { locationSettings } = useLocationSettings(); return ( diff --git a/frontend/src/component/feature/FeatureView/FeatureNotFound/FeatureNotFound.tsx b/frontend/src/component/feature/FeatureView/FeatureNotFound/FeatureNotFound.tsx index 0fb06592d9..6779ae358c 100644 --- a/frontend/src/component/feature/FeatureView/FeatureNotFound/FeatureNotFound.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureNotFound/FeatureNotFound.tsx @@ -18,6 +18,10 @@ export const FeatureNotFound = () => { { name: featureId } ); + if (!archivedFeatures) { + return null; + } + const isArchived = archivedFeatures.some(archivedFeature => { return archivedFeature.name === featureId; }); diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentStrategies/FeatureOverviewEnvironmentStrategies.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentStrategies/FeatureOverviewEnvironmentStrategies.tsx index fff5abc801..782e33fff1 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentStrategies/FeatureOverviewEnvironmentStrategies.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentStrategies/FeatureOverviewEnvironmentStrategies.tsx @@ -1,7 +1,7 @@ import { IFeatureStrategy } from 'interfaces/strategy'; import FeatureOverviewEnvironmentStrategy from './FeatureOverviewEnvironmentStrategy/FeatureOverviewEnvironmentStrategy'; -interface FeatureOverviewEnvironmentStrategiesProps { +interface IFeatureOverviewEnvironmentStrategiesProps { strategies: IFeatureStrategy[]; environmentName: string; } @@ -9,7 +9,7 @@ interface FeatureOverviewEnvironmentStrategiesProps { const FeatureOverviewEnvironmentStrategies = ({ strategies, environmentName, -}: FeatureOverviewEnvironmentStrategiesProps) => { +}: IFeatureOverviewEnvironmentStrategiesProps) => { return ( <> {strategies.map(strategy => ( diff --git a/frontend/src/component/feature/FeatureView/FeatureStatus/FeatureStatus.tsx b/frontend/src/component/feature/FeatureView/FeatureStatus/FeatureStatus.tsx index 062f4969b5..dc3a6ad4a2 100644 --- a/frontend/src/component/feature/FeatureView/FeatureStatus/FeatureStatus.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureStatus/FeatureStatus.tsx @@ -46,7 +46,7 @@ function getColor(unit?: string): string { } } -interface FeatureStatusProps { +interface IFeatureStatusProps { lastSeenAt?: string; tooltipPlacement?: TooltipProps['placement']; } @@ -54,7 +54,7 @@ interface FeatureStatusProps { const FeatureStatus = ({ lastSeenAt, tooltipPlacement, -}: FeatureStatusProps) => { +}: IFeatureStatusProps) => { const styles = useStyles(); const Wrapper = ( diff --git a/frontend/src/component/feature/FeatureView/FeatureType/FeatureType.tsx b/frontend/src/component/feature/FeatureView/FeatureType/FeatureType.tsx index 8e995c1aea..ed0b03eadd 100644 --- a/frontend/src/component/feature/FeatureView/FeatureType/FeatureType.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureType/FeatureType.tsx @@ -3,11 +3,11 @@ import { Tooltip } from '@material-ui/core'; import { getFeatureTypeIcons } from 'utils/getFeatureTypeIcons'; import useFeatureTypes from 'hooks/api/getters/useFeatureTypes/useFeatureTypes'; -interface FeatureTypeProps { +interface IFeatureTypeProps { type: string; } -const FeatureStatus = ({ type }: FeatureTypeProps) => { +const FeatureStatus = ({ type }: IFeatureTypeProps) => { const styles = useStyles(); const { featureTypes } = useFeatureTypes(); const IconComponent = getFeatureTypeIcons(type); diff --git a/frontend/src/component/feature/RedirectFeatureView/RedirectFeatureView.tsx b/frontend/src/component/feature/RedirectFeatureView/RedirectFeatureView.tsx index 4c5a2f912b..14c2dc6a79 100644 --- a/frontend/src/component/feature/RedirectFeatureView/RedirectFeatureView.tsx +++ b/frontend/src/component/feature/RedirectFeatureView/RedirectFeatureView.tsx @@ -1,8 +1,8 @@ import { useEffect, useState } from 'react'; import { Redirect, useParams } from 'react-router-dom'; import { useFeatures } from 'hooks/api/getters/useFeatures/useFeatures'; -import { IFeatureToggle } from 'interfaces/featureToggle'; import { getTogglePath } from 'utils/routePathHelpers'; +import { FeatureSchema } from 'openapi'; interface IRedirectParams { name: string; @@ -10,12 +10,12 @@ interface IRedirectParams { const RedirectFeatureView = () => { const { name } = useParams(); - const { features } = useFeatures(); - const [featureToggle, setFeatureToggle] = useState(); + const { features = [] } = useFeatures(); + const [featureToggle, setFeatureToggle] = useState(); useEffect(() => { const toggle = features.find( - (toggle: IFeatureToggle) => toggle.name === name + (toggle: FeatureSchema) => toggle.name === name ); setFeatureToggle(toggle); diff --git a/frontend/src/component/project/Project/ProjectHealth/ProjectHealth.tsx b/frontend/src/component/project/Project/ProjectHealth/ProjectHealth.tsx index 21489200bd..82a0ec40cb 100644 --- a/frontend/src/component/project/Project/ProjectHealth/ProjectHealth.tsx +++ b/frontend/src/component/project/Project/ProjectHealth/ProjectHealth.tsx @@ -4,11 +4,11 @@ import ConditionallyRender from 'component/common/ConditionallyRender'; import ReportToggleList from 'component/Reporting/ReportToggleList/ReportToggleList'; import { ReportCard } from 'component/Reporting/ReportCard/ReportCard'; -interface ProjectHealthProps { +interface IProjectHealthProps { projectId: string; } -const ProjectHealth = ({ projectId }: ProjectHealthProps) => { +const ProjectHealth = ({ projectId }: IProjectHealthProps) => { const { healthReport, refetchHealthReport, error } = useHealthReport(projectId); diff --git a/frontend/src/component/project/Project/ProjectOverview.tsx b/frontend/src/component/project/Project/ProjectOverview.tsx index c84608cdc1..386a73205b 100644 --- a/frontend/src/component/project/Project/ProjectOverview.tsx +++ b/frontend/src/component/project/Project/ProjectOverview.tsx @@ -3,11 +3,11 @@ import { ProjectFeatureToggles } from './ProjectFeatureToggles/ProjectFeatureTog import ProjectInfo from './ProjectInfo/ProjectInfo'; import { useStyles } from './Project.styles'; -interface ProjectOverviewProps { +interface IProjectOverviewProps { projectId: string; } -const ProjectOverview = ({ projectId }: ProjectOverviewProps) => { +const ProjectOverview = ({ projectId }: IProjectOverviewProps) => { const { project, loading } = useProject(projectId, { refreshInterval: 10000, }); diff --git a/frontend/src/component/project/ProjectEnvironment/ProjectEnvironment.tsx b/frontend/src/component/project/ProjectEnvironment/ProjectEnvironment.tsx index 6f378fe825..7f88387054 100644 --- a/frontend/src/component/project/ProjectEnvironment/ProjectEnvironment.tsx +++ b/frontend/src/component/project/ProjectEnvironment/ProjectEnvironment.tsx @@ -23,11 +23,13 @@ import { getEnabledEnvs } from './helpers'; import StringTruncator from 'component/common/StringTruncator/StringTruncator'; import { useCommonStyles } from 'themes/commonStyles'; -interface ProjectEnvironmentListProps { +interface IProjectEnvironmentListProps { projectId: string; } -const ProjectEnvironmentList = ({ projectId }: ProjectEnvironmentListProps) => { +const ProjectEnvironmentList = ({ + projectId, +}: IProjectEnvironmentListProps) => { // api state const [envs, setEnvs] = useState([]); const { setToastData, setToastApiError } = useToast(); diff --git a/frontend/src/component/strategies/StrategyView/StrategyDetails/StrategyDetails.tsx b/frontend/src/component/strategies/StrategyView/StrategyDetails/StrategyDetails.tsx index 1f6e84b84c..dfdd01827e 100644 --- a/frontend/src/component/strategies/StrategyView/StrategyDetails/StrategyDetails.tsx +++ b/frontend/src/component/strategies/StrategyView/StrategyDetails/StrategyDetails.tsx @@ -13,12 +13,12 @@ import styles from '../../strategies.module.scss'; import { TogglesLinkList } from 'component/strategies/TogglesLinkList/TogglesLinkList'; import { IParameter, IStrategy } from 'interfaces/strategy'; import { IApplication } from 'interfaces/application'; -import { IFeatureToggle } from 'interfaces/featureToggle'; +import { FeatureSchema } from 'openapi'; interface IStrategyDetailsProps { strategy: IStrategy; applications: IApplication[]; - toggles: IFeatureToggle[]; + toggles: FeatureSchema[]; } export const StrategyDetails = ({ diff --git a/frontend/src/component/strategies/StrategyView/StrategyView.tsx b/frontend/src/component/strategies/StrategyView/StrategyView.tsx index 2996a8b656..e18488c9f2 100644 --- a/frontend/src/component/strategies/StrategyView/StrategyView.tsx +++ b/frontend/src/component/strategies/StrategyView/StrategyView.tsx @@ -14,7 +14,7 @@ import ConditionallyRender from 'component/common/ConditionallyRender'; export const StrategyView = () => { const { name } = useParams<{ name: string }>(); const { strategies } = useStrategies(); - const { features } = useFeatures(); + const { features = [] } = useFeatures(); const { applications } = useApplications(); const history = useHistory(); diff --git a/frontend/src/component/strategies/TogglesLinkList/TogglesLinkList.tsx b/frontend/src/component/strategies/TogglesLinkList/TogglesLinkList.tsx index c7a7eb5d13..9b9ec45b2b 100644 --- a/frontend/src/component/strategies/TogglesLinkList/TogglesLinkList.tsx +++ b/frontend/src/component/strategies/TogglesLinkList/TogglesLinkList.tsx @@ -9,10 +9,10 @@ import { Pause, PlayArrow } from '@material-ui/icons'; import styles from 'component/common/common.module.scss'; import { Link } from 'react-router-dom'; import ConditionallyRender from 'component/common/ConditionallyRender'; -import { IFeatureToggle } from 'interfaces/featureToggle'; +import { FeatureSchema } from 'openapi'; interface ITogglesLinkListProps { - toggles: IFeatureToggle[]; + toggles: FeatureSchema[]; } export const TogglesLinkList = ({ toggles }: ITogglesLinkListProps) => ( diff --git a/frontend/src/hooks/__snapshots__/useFeaturesFilter.test.ts.snap b/frontend/src/hooks/__snapshots__/useFeaturesFilter.test.ts.snap index 59157fe615..9b1f588d76 100644 --- a/frontend/src/hooks/__snapshots__/useFeaturesFilter.test.ts.snap +++ b/frontend/src/hooks/__snapshots__/useFeaturesFilter.test.ts.snap @@ -8,13 +8,11 @@ Object { }, "filtered": Array [ Object { - "archived": false, - "createdAt": "2006-01-02T15:04:05Z", + "createdAt": 2006-01-02T15:04:05.000Z, "description": "1", "enabled": false, - "environments": Array [], "impressionData": false, - "lastSeenAt": "2006-01-02T15:04:05Z", + "lastSeenAt": 2006-01-02T15:04:05.000Z, "name": "1", "project": "a", "stale": false, @@ -80,13 +78,11 @@ Object { }, "filtered": Array [ Object { - "archived": false, - "createdAt": "2006-01-02T15:04:05Z", + "createdAt": 2006-01-02T15:04:05.000Z, "description": "1", "enabled": false, - "environments": Array [], "impressionData": false, - "lastSeenAt": "2006-01-02T15:04:05Z", + "lastSeenAt": 2006-01-02T15:04:05.000Z, "name": "1", "project": "1", "stale": false, @@ -95,13 +91,11 @@ Object { "variants": Array [], }, Object { - "archived": false, - "createdAt": "2006-01-02T15:04:05Z", + "createdAt": 2006-01-02T15:04:05.000Z, "description": "1", "enabled": false, - "environments": Array [], "impressionData": false, - "lastSeenAt": "2006-01-02T15:04:05Z", + "lastSeenAt": 2006-01-02T15:04:05.000Z, "name": "1", "project": "1", "stale": false, @@ -110,13 +104,11 @@ Object { "variants": Array [], }, Object { - "archived": false, - "createdAt": "2006-01-02T15:04:05Z", + "createdAt": 2006-01-02T15:04:05.000Z, "description": "1", "enabled": false, - "environments": Array [], "impressionData": false, - "lastSeenAt": "2006-01-02T15:04:05Z", + "lastSeenAt": 2006-01-02T15:04:05.000Z, "name": "1", "project": "1", "stale": false, @@ -136,13 +128,11 @@ Object { }, "filtered": Array [ Object { - "archived": false, - "createdAt": "2006-01-02T15:04:05Z", + "createdAt": 2006-01-02T15:04:05.000Z, "description": "1", "enabled": false, - "environments": Array [], "impressionData": false, - "lastSeenAt": "2006-01-02T15:04:05Z", + "lastSeenAt": 2006-01-02T15:04:05.000Z, "name": "1", "project": "2", "stale": false, @@ -151,13 +141,11 @@ Object { "variants": Array [], }, Object { - "archived": false, - "createdAt": "2006-01-02T15:04:05Z", + "createdAt": 2006-01-02T15:04:05.000Z, "description": "1", "enabled": false, - "environments": Array [], "impressionData": false, - "lastSeenAt": "2006-01-02T15:04:05Z", + "lastSeenAt": 2006-01-02T15:04:05.000Z, "name": "1", "project": "2", "stale": false, @@ -178,13 +166,11 @@ Object { }, "filtered": Array [ Object { - "archived": false, - "createdAt": "2006-01-02T15:04:05Z", + "createdAt": 2006-01-02T15:04:05.000Z, "description": "1", "enabled": false, - "environments": Array [], "impressionData": false, - "lastSeenAt": "2006-01-02T15:04:05Z", + "lastSeenAt": 2006-01-02T15:04:05.000Z, "name": "1", "project": "abc", "stale": false, @@ -193,13 +179,11 @@ Object { "variants": Array [], }, Object { - "archived": false, - "createdAt": "2006-01-02T15:04:05Z", + "createdAt": 2006-01-02T15:04:05.000Z, "description": "1", "enabled": false, - "environments": Array [], "impressionData": false, - "lastSeenAt": "2006-01-02T15:04:05Z", + "lastSeenAt": 2006-01-02T15:04:05.000Z, "name": "1", "project": "abcd", "stale": false, diff --git a/frontend/src/hooks/api/actions/useFeatureApi/useFeatureApi.ts b/frontend/src/hooks/api/actions/useFeatureApi/useFeatureApi.ts index ca05839e43..3e0e493dc0 100644 --- a/frontend/src/hooks/api/actions/useFeatureApi/useFeatureApi.ts +++ b/frontend/src/hooks/api/actions/useFeatureApi/useFeatureApi.ts @@ -1,8 +1,9 @@ -import { IFeatureTogglePayload } from 'interfaces/featureToggle'; import { ITag } from 'interfaces/tags'; import useAPI from '../useApi/useApi'; import { Operation } from 'fast-json-patch'; import { IConstraint } from 'interfaces/strategy'; +import { CreateFeatureSchema } from 'openapi'; +import { openApiAdmin } from 'utils/openapiClient'; const useFeatureApi = () => { const { makeRequest, createRequest, errors, loading } = useAPI({ @@ -38,21 +39,12 @@ const useFeatureApi = () => { const createFeatureToggle = async ( projectId: string, - featureToggle: IFeatureTogglePayload + createFeatureSchema: CreateFeatureSchema ) => { - const path = `api/admin/projects/${projectId}/features`; - const req = createRequest(path, { - method: 'POST', - body: JSON.stringify(featureToggle), + return openApiAdmin.apiAdminProjectsProjectIdFeaturesPost({ + projectId, + createFeatureSchema, }); - - try { - const res = await makeRequest(req.caller, req.id); - - return res; - } catch (e) { - throw e; - } }; const toggleFeatureEnvironmentOn = async ( diff --git a/frontend/src/hooks/api/getters/useApiGetter/useApiGetter.ts b/frontend/src/hooks/api/getters/useApiGetter/useApiGetter.ts new file mode 100644 index 0000000000..88c00d59f3 --- /dev/null +++ b/frontend/src/hooks/api/getters/useApiGetter/useApiGetter.ts @@ -0,0 +1,30 @@ +import useSWR, { SWRConfiguration, mutate } from 'swr'; +import { useCallback } from 'react'; + +type CacheKey = 'apiAdminFeaturesGet' | 'apiAdminArchiveFeaturesGet'; + +interface IUseApiGetterOutput { + data?: T; + refetch: () => void; + error?: Error | undefined; + loading: boolean; +} + +export const useApiGetter = ( + cacheKey: CacheKey, + fetcher: () => Promise, + options?: SWRConfiguration +): IUseApiGetterOutput => { + const { data, error } = useSWR(cacheKey, fetcher, options); + + const refetch = useCallback(() => { + mutate(cacheKey).catch(console.warn); + }, [cacheKey]); + + return { + data, + error, + refetch, + loading: !error && !data, + }; +}; diff --git a/frontend/src/hooks/api/getters/useFeatures/useFeatures.ts b/frontend/src/hooks/api/getters/useFeatures/useFeatures.ts index 6571864e7b..a65dd738fc 100644 --- a/frontend/src/hooks/api/getters/useFeatures/useFeatures.ts +++ b/frontend/src/hooks/api/getters/useFeatures/useFeatures.ts @@ -1,39 +1,24 @@ -import useSWR, { mutate, SWRConfiguration } from 'swr'; -import { useCallback } from 'react'; -import { formatApiPath } from 'utils/formatPath'; -import handleErrorResponses from '../httpErrorResponseHandler'; -import { IFeatureToggle } from 'interfaces/featureToggle'; - -const PATH = formatApiPath('api/admin/features'); +import { FeatureSchema } from 'openapi'; +import { openApiAdmin } from 'utils/openapiClient'; +import { useApiGetter } from 'hooks/api/getters/useApiGetter/useApiGetter'; export interface IUseFeaturesOutput { - features: IFeatureToggle[]; + features?: FeatureSchema[]; refetchFeatures: () => void; loading: boolean; error?: Error; } -export const useFeatures = (options?: SWRConfiguration): IUseFeaturesOutput => { - const { data, error } = useSWR<{ features: IFeatureToggle[] }>( - PATH, - fetchFeatures, - options +export const useFeatures = (): IUseFeaturesOutput => { + const { data, refetch, loading, error } = useApiGetter( + 'apiAdminFeaturesGet', + () => openApiAdmin.apiAdminFeaturesGet() ); - const refetchFeatures = useCallback(() => { - mutate(PATH).catch(console.warn); - }, []); - return { - features: data?.features || [], - loading: !error && !data, - refetchFeatures, + features: data?.features, + refetchFeatures: refetch, + loading, error, }; }; - -const fetchFeatures = () => { - return fetch(PATH, { method: 'GET' }) - .then(handleErrorResponses('Features')) - .then(res => res.json()); -}; diff --git a/frontend/src/hooks/api/getters/useFeaturesArchive/useFeaturesArchive.ts b/frontend/src/hooks/api/getters/useFeaturesArchive/useFeaturesArchive.ts index 0b95fcca83..d89fc86faa 100644 --- a/frontend/src/hooks/api/getters/useFeaturesArchive/useFeaturesArchive.ts +++ b/frontend/src/hooks/api/getters/useFeaturesArchive/useFeaturesArchive.ts @@ -1,41 +1,24 @@ -import useSWR, { mutate, SWRConfiguration } from 'swr'; -import { useCallback } from 'react'; -import { formatApiPath } from 'utils/formatPath'; -import handleErrorResponses from '../httpErrorResponseHandler'; -import { IFeatureToggle } from 'interfaces/featureToggle'; +import { openApiAdmin } from 'utils/openapiClient'; +import { FeatureSchema } from 'openapi'; +import { useApiGetter } from 'hooks/api/getters/useApiGetter/useApiGetter'; -const PATH = formatApiPath('api/admin/archive/features'); - -export interface UseFeaturesArchiveOutput { - archivedFeatures: IFeatureToggle[]; +export interface IUseFeaturesArchiveOutput { + archivedFeatures?: FeatureSchema[]; refetchArchived: () => void; loading: boolean; error?: Error; } -export const useFeaturesArchive = ( - options?: SWRConfiguration -): UseFeaturesArchiveOutput => { - const { data, error } = useSWR<{ features: IFeatureToggle[] }>( - PATH, - fetchArchivedFeatures, - options +export const useFeaturesArchive = (): IUseFeaturesArchiveOutput => { + const { data, refetch, loading, error } = useApiGetter( + 'apiAdminArchiveFeaturesGet', + () => openApiAdmin.apiAdminArchiveFeaturesGet() ); - const refetchArchived = useCallback(() => { - mutate(PATH).catch(console.warn); - }, []); - return { - archivedFeatures: data?.features || [], - refetchArchived, - loading: !error && !data, + archivedFeatures: data?.features, + refetchArchived: refetch, + loading, error, }; }; - -const fetchArchivedFeatures = () => { - return fetch(PATH, { method: 'GET' }) - .then(handleErrorResponses('Archive')) - .then(res => res.json()); -}; diff --git a/frontend/src/hooks/api/getters/useSegment/useSegment.ts b/frontend/src/hooks/api/getters/useSegment/useSegment.ts index e3506c9784..57e79f3696 100644 --- a/frontend/src/hooks/api/getters/useSegment/useSegment.ts +++ b/frontend/src/hooks/api/getters/useSegment/useSegment.ts @@ -4,14 +4,14 @@ import { formatApiPath } from 'utils/formatPath'; import handleErrorResponses from '../httpErrorResponseHandler'; import { ISegment } from 'interfaces/segment'; -export interface UseSegmentOutput { +export interface IUseSegmentOutput { segment?: ISegment; refetchSegment: () => void; loading: boolean; error?: Error; } -export const useSegment = (id: number): UseSegmentOutput => { +export const useSegment = (id: number): IUseSegmentOutput => { const path = formatApiPath(`api/admin/segments/${id}`); const { data, error } = useSWR(path, () => fetchSegment(path)); diff --git a/frontend/src/hooks/api/getters/useSegments/useSegments.ts b/frontend/src/hooks/api/getters/useSegments/useSegments.ts index 41ef8483be..4eecced526 100644 --- a/frontend/src/hooks/api/getters/useSegments/useSegments.ts +++ b/frontend/src/hooks/api/getters/useSegments/useSegments.ts @@ -6,14 +6,14 @@ import { ISegment } from 'interfaces/segment'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import { IFlags } from 'interfaces/uiConfig'; -export interface UseSegmentsOutput { +export interface IUseSegmentsOutput { segments?: ISegment[]; refetchSegments: () => void; loading: boolean; error?: Error; } -export const useSegments = (strategyId?: string): UseSegmentsOutput => { +export const useSegments = (strategyId?: string): IUseSegmentsOutput => { const { uiConfig } = useUiConfig(); const { data, error, mutate } = useSWR( diff --git a/frontend/src/hooks/api/getters/useStrategiesBySegment/useStrategiesBySegment.ts b/frontend/src/hooks/api/getters/useStrategiesBySegment/useStrategiesBySegment.ts index 7999065720..54efbd8d50 100644 --- a/frontend/src/hooks/api/getters/useStrategiesBySegment/useStrategiesBySegment.ts +++ b/frontend/src/hooks/api/getters/useStrategiesBySegment/useStrategiesBySegment.ts @@ -4,7 +4,7 @@ import { formatApiPath } from 'utils/formatPath'; import handleErrorResponses from '../httpErrorResponseHandler'; import { IFeatureStrategy } from 'interfaces/strategy'; -export interface useStrategiesBySegmentOutput { +export interface IUseStrategiesBySegmentOutput { strategies?: IFeatureStrategy[]; refetchUsedSegments: () => void; loading: boolean; @@ -13,7 +13,7 @@ export interface useStrategiesBySegmentOutput { export const useStrategiesBySegment = ( id: number -): useStrategiesBySegmentOutput => { +): IUseStrategiesBySegmentOutput => { const path = formatApiPath(`api/admin/segments/${id}/strategies`); const { data, error } = useSWR(path, () => fetchUsedSegment(path)); diff --git a/frontend/src/hooks/useFeaturesFilter.test.ts b/frontend/src/hooks/useFeaturesFilter.test.ts index 09339b98c7..1b8bdc894d 100644 --- a/frontend/src/hooks/useFeaturesFilter.test.ts +++ b/frontend/src/hooks/useFeaturesFilter.test.ts @@ -1,7 +1,8 @@ import { renderHook, act } from '@testing-library/react-hooks'; import { useFeaturesFilter } from 'hooks/useFeaturesFilter'; -import { IFeatureToggle } from 'interfaces/featureToggle'; import { IConstraint, IFeatureStrategy } from 'interfaces/strategy'; +import { FeatureSchema } from 'openapi'; +import parseISO from 'date-fns/parseISO'; test('useFeaturesFilter empty', () => { const { result } = renderHook(() => useFeaturesFilter([])); @@ -88,22 +89,20 @@ test('useFeaturesFilter constraints', () => { }); const mockFeatureToggle = ( - overrides?: Partial -): IFeatureToggle => { + overrides?: Partial +): FeatureSchema => { return { name: '1', description: '1', type: '1', project: '1', - archived: false, enabled: false, stale: false, impressionData: false, strategies: [], variants: [], - environments: [], - createdAt: '2006-01-02T15:04:05Z', - lastSeenAt: '2006-01-02T15:04:05Z', + createdAt: parseISO('2006-01-02T15:04:05Z'), + lastSeenAt: parseISO('2006-01-02T15:04:05Z'), ...overrides, }; }; diff --git a/frontend/src/hooks/useFeaturesFilter.ts b/frontend/src/hooks/useFeaturesFilter.ts index a2afed09db..a1510d71c4 100644 --- a/frontend/src/hooks/useFeaturesFilter.ts +++ b/frontend/src/hooks/useFeaturesFilter.ts @@ -1,6 +1,6 @@ -import { IFeatureToggle } from 'interfaces/featureToggle'; import React, { useMemo } from 'react'; import { createGlobalStateHook } from 'hooks/useGlobalState'; +import { FeatureSchema } from 'openapi'; export interface IFeaturesFilter { query?: string; @@ -8,7 +8,7 @@ export interface IFeaturesFilter { } export interface IFeaturesSortOutput { - filtered: IFeatureToggle[]; + filtered: FeatureSchema[]; filter: IFeaturesFilter; setFilter: React.Dispatch>; } @@ -21,7 +21,7 @@ const useFeaturesFilterState = createGlobalStateHook( ); export const useFeaturesFilter = ( - features: IFeatureToggle[] + features: FeatureSchema[] ): IFeaturesSortOutput => { const [filter, setFilter] = useFeaturesFilterState(); @@ -37,9 +37,9 @@ export const useFeaturesFilter = ( }; const filterFeatures = ( - features: IFeatureToggle[], + features: FeatureSchema[], filter: IFeaturesFilter -): IFeatureToggle[] => { +): FeatureSchema[] => { return filterFeaturesByQuery( filterFeaturesByProject(features, filter), filter @@ -47,18 +47,18 @@ const filterFeatures = ( }; const filterFeaturesByProject = ( - features: IFeatureToggle[], + features: FeatureSchema[], filter: IFeaturesFilter -): IFeatureToggle[] => { +): FeatureSchema[] => { return filter.project === '*' ? features : features.filter(f => f.project === filter.project); }; const filterFeaturesByQuery = ( - features: IFeatureToggle[], + features: FeatureSchema[], filter: IFeaturesFilter -): IFeatureToggle[] => { +): FeatureSchema[] => { if (!filter.query) { return features; } @@ -78,11 +78,14 @@ const filterFeaturesByQuery = ( }; const filterFeatureByRegExp = ( - feature: IFeatureToggle, + feature: FeatureSchema, filter: IFeaturesFilter, regExp: RegExp ): boolean => { - if (regExp.test(feature.name) || regExp.test(feature.description)) { + if ( + regExp.test(feature.name) || + (feature.description && regExp.test(feature.description)) + ) { return true; } diff --git a/frontend/src/hooks/useFeaturesSort.ts b/frontend/src/hooks/useFeaturesSort.ts index 89f7c0c843..cdfb00e346 100644 --- a/frontend/src/hooks/useFeaturesSort.ts +++ b/frontend/src/hooks/useFeaturesSort.ts @@ -1,13 +1,12 @@ -import { IFeatureToggle } from '../interfaces/featureToggle'; import React, { useMemo } from 'react'; import { getBasePath } from 'utils/formatPath'; import { createPersistentGlobalStateHook } from './usePersistentGlobalState'; -import { parseISO } from 'date-fns'; import { expired, getDiffInDays, toggleExpiryByTypeMap, } from 'component/Reporting/utils'; +import { FeatureSchema } from 'openapi'; type FeaturesSortType = | 'name' @@ -27,7 +26,7 @@ interface IFeaturesSort { export interface IFeaturesSortOutput { sort: IFeaturesSort; - sorted: IFeatureToggle[]; + sorted: FeatureSchema[]; setSort: React.Dispatch>; } @@ -44,7 +43,7 @@ const useFeaturesSortState = createPersistentGlobalStateHook( ); export const useFeaturesSort = ( - features: IFeatureToggle[] + features: FeatureSchema[] ): IFeaturesSortOutput => { const [sort, setSort] = useFeaturesSortState(); @@ -73,9 +72,9 @@ export const createFeaturesFilterSortOptions = }; const sortAscendingFeatures = ( - features: IFeatureToggle[], + features: FeatureSchema[], sort: IFeaturesSort -): IFeatureToggle[] => { +): FeatureSchema[] => { switch (sort.type) { case 'enabled': return sortByEnabled(features); @@ -102,9 +101,9 @@ const sortAscendingFeatures = ( }; const sortFeatures = ( - features: IFeatureToggle[], + features: FeatureSchema[], sort: IFeaturesSort -): IFeatureToggle[] => { +): FeatureSchema[] => { const sorted = sortAscendingFeatures(features, sort); if (sort.desc) { @@ -115,84 +114,97 @@ const sortFeatures = ( }; const sortByEnabled = ( - features: Readonly -): IFeatureToggle[] => { + features: Readonly +): FeatureSchema[] => { return [...features].sort((a, b) => a.enabled === b.enabled ? 0 : a.enabled ? -1 : 1 ); }; -const sortByStale = ( - features: Readonly -): IFeatureToggle[] => { +const sortByStale = (features: Readonly): FeatureSchema[] => { return [...features].sort((a, b) => a.stale === b.stale ? 0 : a.stale ? -1 : 1 ); }; const sortByLastSeen = ( - features: Readonly -): IFeatureToggle[] => { + features: Readonly +): FeatureSchema[] => { return [...features].sort((a, b) => a.lastSeenAt && b.lastSeenAt - ? b.lastSeenAt.localeCompare(a.lastSeenAt) + ? compareNullableDates(b.lastSeenAt, a.lastSeenAt) : a.lastSeenAt ? -1 : b.lastSeenAt ? 1 - : b.createdAt.localeCompare(a.createdAt) + : compareNullableDates(b.createdAt, a.createdAt) ); }; const sortByCreated = ( - features: Readonly -): IFeatureToggle[] => { - return [...features].sort((a, b) => b.createdAt.localeCompare(a.createdAt)); + features: Readonly +): FeatureSchema[] => { + return [...features].sort((a, b) => + compareNullableDates(b.createdAt, a.createdAt) + ); }; -const sortByName = (features: Readonly): IFeatureToggle[] => { +const sortByName = (features: Readonly): FeatureSchema[] => { return [...features].sort((a, b) => a.name.localeCompare(b.name)); }; const sortByProject = ( - features: Readonly -): IFeatureToggle[] => { + features: Readonly +): FeatureSchema[] => { return [...features].sort((a, b) => a.project.localeCompare(b.project)); }; -const sortByType = (features: Readonly): IFeatureToggle[] => { - return [...features].sort((a, b) => a.type.localeCompare(b.type)); +const sortByType = (features: Readonly): FeatureSchema[] => { + return [...features].sort((a, b) => + a.type && b.type + ? a.type.localeCompare(b.type) + : a.type + ? 1 + : b.type + ? -1 + : 0 + ); +}; + +const compareNullableDates = ( + a: Date | null | undefined, + b: Date | null | undefined +): number => { + return a && b ? a.getTime() - b.getTime() : a ? 1 : b ? -1 : 0; }; const sortByExpired = ( - features: Readonly -): IFeatureToggle[] => { + features: Readonly +): FeatureSchema[] => { return [...features].sort((a, b) => { const now = new Date(); - const dateA = parseISO(a.createdAt); - const dateB = parseISO(b.createdAt); + const dateA = a.createdAt!; + const dateB = b.createdAt!; const diffA = getDiffInDays(dateA, now); const diffB = getDiffInDays(dateB, now); - if (!expired(diffA, a.type) && expired(diffB, b.type)) { + if (!expired(diffA, a.type!) && expired(diffB, b.type!)) { return 1; } - if (expired(diffA, a.type) && !expired(diffB, b.type)) { + if (expired(diffA, a.type!) && !expired(diffB, b.type!)) { return -1; } const expiration = toggleExpiryByTypeMap as Record; - const expiredByA = diffA - expiration[a.type]; - const expiredByB = diffB - expiration[b.type]; + const expiredByA = a.type ? diffA - expiration[a.type] : 0; + const expiredByB = b.type ? diffB - expiration[b.type] : 0; return expiredByB - expiredByA; }); }; -const sortByStatus = ( - features: Readonly -): IFeatureToggle[] => { +const sortByStatus = (features: Readonly): FeatureSchema[] => { return [...features].sort((a, b) => { if (a.stale) { return 1; diff --git a/frontend/src/openapi/apis/AdminApi.ts b/frontend/src/openapi/apis/AdminApi.ts new file mode 100644 index 0000000000..77caf5e2ec --- /dev/null +++ b/frontend/src/openapi/apis/AdminApi.ts @@ -0,0 +1,262 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Unleash API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 4.10.0-beta.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import * as runtime from '../runtime'; +import { + ChangeProjectSchema, + ChangeProjectSchemaFromJSON, + ChangeProjectSchemaToJSON, + CreateFeatureSchema, + CreateFeatureSchemaFromJSON, + CreateFeatureSchemaToJSON, + FeatureSchema, + FeatureSchemaFromJSON, + FeatureSchemaToJSON, + FeaturesSchema, + FeaturesSchemaFromJSON, + FeaturesSchemaToJSON, +} from '../models'; + +export interface ApiAdminProjectsProjectIdFeaturesFeatureNameChangeProjectPostRequest { + projectId: string; + featureName: string; + changeProjectSchema: ChangeProjectSchema; +} + +export interface ApiAdminProjectsProjectIdFeaturesFeatureNameGetRequest { + projectId: string; + featureName: string; +} + +export interface ApiAdminProjectsProjectIdFeaturesGetRequest { + projectId: string; +} + +export interface ApiAdminProjectsProjectIdFeaturesPostRequest { + projectId: string; + createFeatureSchema: CreateFeatureSchema; +} + +/** + * + */ +export class AdminApi extends runtime.BaseAPI { + + /** + */ + async apiAdminArchiveFeaturesGetRaw(initOverrides?: RequestInit): Promise> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Authorization"] = this.configuration.apiKey("Authorization"); // apiKey authentication + } + + const response = await this.request({ + path: `/api/admin/archive/features`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => FeaturesSchemaFromJSON(jsonValue)); + } + + /** + */ + async apiAdminArchiveFeaturesGet(initOverrides?: RequestInit): Promise { + const response = await this.apiAdminArchiveFeaturesGetRaw(initOverrides); + return await response.value(); + } + + /** + */ + async apiAdminFeaturesGetRaw(initOverrides?: RequestInit): Promise> { + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Authorization"] = this.configuration.apiKey("Authorization"); // apiKey authentication + } + + const response = await this.request({ + path: `/api/admin/features/`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => FeaturesSchemaFromJSON(jsonValue)); + } + + /** + */ + async apiAdminFeaturesGet(initOverrides?: RequestInit): Promise { + const response = await this.apiAdminFeaturesGetRaw(initOverrides); + return await response.value(); + } + + /** + */ + async apiAdminProjectsProjectIdFeaturesFeatureNameChangeProjectPostRaw(requestParameters: ApiAdminProjectsProjectIdFeaturesFeatureNameChangeProjectPostRequest, initOverrides?: RequestInit): Promise> { + if (requestParameters.projectId === null || requestParameters.projectId === undefined) { + throw new runtime.RequiredError('projectId','Required parameter requestParameters.projectId was null or undefined when calling apiAdminProjectsProjectIdFeaturesFeatureNameChangeProjectPost.'); + } + + if (requestParameters.featureName === null || requestParameters.featureName === undefined) { + throw new runtime.RequiredError('featureName','Required parameter requestParameters.featureName was null or undefined when calling apiAdminProjectsProjectIdFeaturesFeatureNameChangeProjectPost.'); + } + + if (requestParameters.changeProjectSchema === null || requestParameters.changeProjectSchema === undefined) { + throw new runtime.RequiredError('changeProjectSchema','Required parameter requestParameters.changeProjectSchema was null or undefined when calling apiAdminProjectsProjectIdFeaturesFeatureNameChangeProjectPost.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Authorization"] = this.configuration.apiKey("Authorization"); // apiKey authentication + } + + const response = await this.request({ + path: `/api/admin/projects/{projectId}/features/{featureName}/changeProject`.replace(`{${"projectId"}}`, encodeURIComponent(String(requestParameters.projectId))).replace(`{${"featureName"}}`, encodeURIComponent(String(requestParameters.featureName))), + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: ChangeProjectSchemaToJSON(requestParameters.changeProjectSchema), + }, initOverrides); + + return new runtime.VoidApiResponse(response); + } + + /** + */ + async apiAdminProjectsProjectIdFeaturesFeatureNameChangeProjectPost(requestParameters: ApiAdminProjectsProjectIdFeaturesFeatureNameChangeProjectPostRequest, initOverrides?: RequestInit): Promise { + await this.apiAdminProjectsProjectIdFeaturesFeatureNameChangeProjectPostRaw(requestParameters, initOverrides); + } + + /** + */ + async apiAdminProjectsProjectIdFeaturesFeatureNameGetRaw(requestParameters: ApiAdminProjectsProjectIdFeaturesFeatureNameGetRequest, initOverrides?: RequestInit): Promise> { + if (requestParameters.projectId === null || requestParameters.projectId === undefined) { + throw new runtime.RequiredError('projectId','Required parameter requestParameters.projectId was null or undefined when calling apiAdminProjectsProjectIdFeaturesFeatureNameGet.'); + } + + if (requestParameters.featureName === null || requestParameters.featureName === undefined) { + throw new runtime.RequiredError('featureName','Required parameter requestParameters.featureName was null or undefined when calling apiAdminProjectsProjectIdFeaturesFeatureNameGet.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Authorization"] = this.configuration.apiKey("Authorization"); // apiKey authentication + } + + const response = await this.request({ + path: `/api/admin/projects/{projectId}/features/{featureName}`.replace(`{${"projectId"}}`, encodeURIComponent(String(requestParameters.projectId))).replace(`{${"featureName"}}`, encodeURIComponent(String(requestParameters.featureName))), + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => FeatureSchemaFromJSON(jsonValue)); + } + + /** + */ + async apiAdminProjectsProjectIdFeaturesFeatureNameGet(requestParameters: ApiAdminProjectsProjectIdFeaturesFeatureNameGetRequest, initOverrides?: RequestInit): Promise { + const response = await this.apiAdminProjectsProjectIdFeaturesFeatureNameGetRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + */ + async apiAdminProjectsProjectIdFeaturesGetRaw(requestParameters: ApiAdminProjectsProjectIdFeaturesGetRequest, initOverrides?: RequestInit): Promise> { + if (requestParameters.projectId === null || requestParameters.projectId === undefined) { + throw new runtime.RequiredError('projectId','Required parameter requestParameters.projectId was null or undefined when calling apiAdminProjectsProjectIdFeaturesGet.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Authorization"] = this.configuration.apiKey("Authorization"); // apiKey authentication + } + + const response = await this.request({ + path: `/api/admin/projects/{projectId}/features`.replace(`{${"projectId"}}`, encodeURIComponent(String(requestParameters.projectId))), + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => FeaturesSchemaFromJSON(jsonValue)); + } + + /** + */ + async apiAdminProjectsProjectIdFeaturesGet(requestParameters: ApiAdminProjectsProjectIdFeaturesGetRequest, initOverrides?: RequestInit): Promise { + const response = await this.apiAdminProjectsProjectIdFeaturesGetRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + */ + async apiAdminProjectsProjectIdFeaturesPostRaw(requestParameters: ApiAdminProjectsProjectIdFeaturesPostRequest, initOverrides?: RequestInit): Promise> { + if (requestParameters.projectId === null || requestParameters.projectId === undefined) { + throw new runtime.RequiredError('projectId','Required parameter requestParameters.projectId was null or undefined when calling apiAdminProjectsProjectIdFeaturesPost.'); + } + + if (requestParameters.createFeatureSchema === null || requestParameters.createFeatureSchema === undefined) { + throw new runtime.RequiredError('createFeatureSchema','Required parameter requestParameters.createFeatureSchema was null or undefined when calling apiAdminProjectsProjectIdFeaturesPost.'); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.apiKey) { + headerParameters["Authorization"] = this.configuration.apiKey("Authorization"); // apiKey authentication + } + + const response = await this.request({ + path: `/api/admin/projects/{projectId}/features`.replace(`{${"projectId"}}`, encodeURIComponent(String(requestParameters.projectId))), + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: CreateFeatureSchemaToJSON(requestParameters.createFeatureSchema), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => FeatureSchemaFromJSON(jsonValue)); + } + + /** + */ + async apiAdminProjectsProjectIdFeaturesPost(requestParameters: ApiAdminProjectsProjectIdFeaturesPostRequest, initOverrides?: RequestInit): Promise { + const response = await this.apiAdminProjectsProjectIdFeaturesPostRaw(requestParameters, initOverrides); + return await response.value(); + } + +} diff --git a/frontend/src/openapi/apis/index.ts b/frontend/src/openapi/apis/index.ts new file mode 100644 index 0000000000..e16840a95b --- /dev/null +++ b/frontend/src/openapi/apis/index.ts @@ -0,0 +1,3 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './AdminApi'; diff --git a/frontend/src/openapi/index.ts b/frontend/src/openapi/index.ts new file mode 100644 index 0000000000..be9d1edeef --- /dev/null +++ b/frontend/src/openapi/index.ts @@ -0,0 +1,5 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './runtime'; +export * from './apis'; +export * from './models'; diff --git a/frontend/src/openapi/models/ChangeProjectSchema.ts b/frontend/src/openapi/models/ChangeProjectSchema.ts new file mode 100644 index 0000000000..13a543e5b3 --- /dev/null +++ b/frontend/src/openapi/models/ChangeProjectSchema.ts @@ -0,0 +1,56 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Unleash API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 4.10.0-beta.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * + * @export + * @interface ChangeProjectSchema + */ +export interface ChangeProjectSchema { + /** + * + * @type {string} + * @memberof ChangeProjectSchema + */ + newProjectId: string; +} + +export function ChangeProjectSchemaFromJSON(json: any): ChangeProjectSchema { + return ChangeProjectSchemaFromJSONTyped(json, false); +} + +export function ChangeProjectSchemaFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChangeProjectSchema { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'newProjectId': json['newProjectId'], + }; +} + +export function ChangeProjectSchemaToJSON(value?: ChangeProjectSchema | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'newProjectId': value.newProjectId, + }; +} + diff --git a/frontend/src/openapi/models/ConstraintSchema.ts b/frontend/src/openapi/models/ConstraintSchema.ts new file mode 100644 index 0000000000..0cfe1d1360 --- /dev/null +++ b/frontend/src/openapi/models/ConstraintSchema.ts @@ -0,0 +1,72 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Unleash API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 4.10.0-beta.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * + * @export + * @interface ConstraintSchema + */ +export interface ConstraintSchema { + /** + * + * @type {string} + * @memberof ConstraintSchema + */ + contextName: string; + /** + * + * @type {string} + * @memberof ConstraintSchema + */ + operator: string; + /** + * + * @type {Array} + * @memberof ConstraintSchema + */ + values?: Array; +} + +export function ConstraintSchemaFromJSON(json: any): ConstraintSchema { + return ConstraintSchemaFromJSONTyped(json, false); +} + +export function ConstraintSchemaFromJSONTyped(json: any, ignoreDiscriminator: boolean): ConstraintSchema { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'contextName': json['contextName'], + 'operator': json['operator'], + 'values': !exists(json, 'values') ? undefined : json['values'], + }; +} + +export function ConstraintSchemaToJSON(value?: ConstraintSchema | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'contextName': value.contextName, + 'operator': value.operator, + 'values': value.values, + }; +} + diff --git a/frontend/src/openapi/models/CreateFeatureSchema.ts b/frontend/src/openapi/models/CreateFeatureSchema.ts new file mode 100644 index 0000000000..744a6e3d9a --- /dev/null +++ b/frontend/src/openapi/models/CreateFeatureSchema.ts @@ -0,0 +1,80 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Unleash API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 4.10.0-beta.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * + * @export + * @interface CreateFeatureSchema + */ +export interface CreateFeatureSchema { + /** + * + * @type {string} + * @memberof CreateFeatureSchema + */ + name: string; + /** + * + * @type {string} + * @memberof CreateFeatureSchema + */ + type?: string; + /** + * + * @type {string} + * @memberof CreateFeatureSchema + */ + description?: string; + /** + * + * @type {boolean} + * @memberof CreateFeatureSchema + */ + impressionData?: boolean; +} + +export function CreateFeatureSchemaFromJSON(json: any): CreateFeatureSchema { + return CreateFeatureSchemaFromJSONTyped(json, false); +} + +export function CreateFeatureSchemaFromJSONTyped(json: any, ignoreDiscriminator: boolean): CreateFeatureSchema { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'name': json['name'], + 'type': !exists(json, 'type') ? undefined : json['type'], + 'description': !exists(json, 'description') ? undefined : json['description'], + 'impressionData': !exists(json, 'impressionData') ? undefined : json['impressionData'], + }; +} + +export function CreateFeatureSchemaToJSON(value?: CreateFeatureSchema | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'name': value.name, + 'type': value.type, + 'description': value.description, + 'impressionData': value.impressionData, + }; +} + diff --git a/frontend/src/openapi/models/FeatureSchema.ts b/frontend/src/openapi/models/FeatureSchema.ts new file mode 100644 index 0000000000..99b6ced8bb --- /dev/null +++ b/frontend/src/openapi/models/FeatureSchema.ts @@ -0,0 +1,149 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Unleash API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 4.10.0-beta.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import { + StrategySchema, + StrategySchemaFromJSON, + StrategySchemaFromJSONTyped, + StrategySchemaToJSON, +} from './StrategySchema'; +import { + VariantSchema, + VariantSchemaFromJSON, + VariantSchemaFromJSONTyped, + VariantSchemaToJSON, +} from './VariantSchema'; + +/** + * + * @export + * @interface FeatureSchema + */ +export interface FeatureSchema { + /** + * + * @type {string} + * @memberof FeatureSchema + */ + name: string; + /** + * + * @type {string} + * @memberof FeatureSchema + */ + type?: string; + /** + * + * @type {string} + * @memberof FeatureSchema + */ + description?: string; + /** + * + * @type {string} + * @memberof FeatureSchema + */ + project: string; + /** + * + * @type {boolean} + * @memberof FeatureSchema + */ + enabled?: boolean; + /** + * + * @type {boolean} + * @memberof FeatureSchema + */ + stale?: boolean; + /** + * + * @type {boolean} + * @memberof FeatureSchema + */ + impressionData?: boolean; + /** + * + * @type {Date} + * @memberof FeatureSchema + */ + createdAt?: Date | null; + /** + * + * @type {Date} + * @memberof FeatureSchema + */ + lastSeenAt?: Date | null; + /** + * + * @type {Array} + * @memberof FeatureSchema + */ + strategies?: Array; + /** + * + * @type {Array} + * @memberof FeatureSchema + */ + variants?: Array; +} + +export function FeatureSchemaFromJSON(json: any): FeatureSchema { + return FeatureSchemaFromJSONTyped(json, false); +} + +export function FeatureSchemaFromJSONTyped(json: any, ignoreDiscriminator: boolean): FeatureSchema { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'name': json['name'], + 'type': !exists(json, 'type') ? undefined : json['type'], + 'description': !exists(json, 'description') ? undefined : json['description'], + 'project': json['project'], + 'enabled': !exists(json, 'enabled') ? undefined : json['enabled'], + 'stale': !exists(json, 'stale') ? undefined : json['stale'], + 'impressionData': !exists(json, 'impressionData') ? undefined : json['impressionData'], + 'createdAt': !exists(json, 'createdAt') ? undefined : (json['createdAt'] === null ? null : new Date(json['createdAt'])), + 'lastSeenAt': !exists(json, 'lastSeenAt') ? undefined : (json['lastSeenAt'] === null ? null : new Date(json['lastSeenAt'])), + 'strategies': !exists(json, 'strategies') ? undefined : ((json['strategies'] as Array).map(StrategySchemaFromJSON)), + 'variants': !exists(json, 'variants') ? undefined : ((json['variants'] as Array).map(VariantSchemaFromJSON)), + }; +} + +export function FeatureSchemaToJSON(value?: FeatureSchema | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'name': value.name, + 'type': value.type, + 'description': value.description, + 'project': value.project, + 'enabled': value.enabled, + 'stale': value.stale, + 'impressionData': value.impressionData, + 'createdAt': value.createdAt === undefined ? undefined : (value.createdAt === null ? null : value.createdAt.toISOString().substr(0,10)), + 'lastSeenAt': value.lastSeenAt === undefined ? undefined : (value.lastSeenAt === null ? null : value.lastSeenAt.toISOString().substr(0,10)), + 'strategies': value.strategies === undefined ? undefined : ((value.strategies as Array).map(StrategySchemaToJSON)), + 'variants': value.variants === undefined ? undefined : ((value.variants as Array).map(VariantSchemaToJSON)), + }; +} + diff --git a/frontend/src/openapi/models/FeaturesSchema.ts b/frontend/src/openapi/models/FeaturesSchema.ts new file mode 100644 index 0000000000..e9486e9549 --- /dev/null +++ b/frontend/src/openapi/models/FeaturesSchema.ts @@ -0,0 +1,71 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Unleash API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 4.10.0-beta.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import { + FeatureSchema, + FeatureSchemaFromJSON, + FeatureSchemaFromJSONTyped, + FeatureSchemaToJSON, +} from './FeatureSchema'; + +/** + * + * @export + * @interface FeaturesSchema + */ +export interface FeaturesSchema { + /** + * + * @type {number} + * @memberof FeaturesSchema + */ + version: number; + /** + * + * @type {Array} + * @memberof FeaturesSchema + */ + features: Array; +} + +export function FeaturesSchemaFromJSON(json: any): FeaturesSchema { + return FeaturesSchemaFromJSONTyped(json, false); +} + +export function FeaturesSchemaFromJSONTyped(json: any, ignoreDiscriminator: boolean): FeaturesSchema { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'version': json['version'], + 'features': ((json['features'] as Array).map(FeatureSchemaFromJSON)), + }; +} + +export function FeaturesSchemaToJSON(value?: FeaturesSchema | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'version': value.version, + 'features': ((value.features as Array).map(FeatureSchemaToJSON)), + }; +} + diff --git a/frontend/src/openapi/models/OverrideSchema.ts b/frontend/src/openapi/models/OverrideSchema.ts new file mode 100644 index 0000000000..c9f61a8dad --- /dev/null +++ b/frontend/src/openapi/models/OverrideSchema.ts @@ -0,0 +1,64 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Unleash API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 4.10.0-beta.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * + * @export + * @interface OverrideSchema + */ +export interface OverrideSchema { + /** + * + * @type {string} + * @memberof OverrideSchema + */ + contextName: string; + /** + * + * @type {Array} + * @memberof OverrideSchema + */ + values: Array; +} + +export function OverrideSchemaFromJSON(json: any): OverrideSchema { + return OverrideSchemaFromJSONTyped(json, false); +} + +export function OverrideSchemaFromJSONTyped(json: any, ignoreDiscriminator: boolean): OverrideSchema { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'contextName': json['contextName'], + 'values': json['values'], + }; +} + +export function OverrideSchemaToJSON(value?: OverrideSchema | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'contextName': value.contextName, + 'values': value.values, + }; +} + diff --git a/frontend/src/openapi/models/StrategySchema.ts b/frontend/src/openapi/models/StrategySchema.ts new file mode 100644 index 0000000000..1b08568f05 --- /dev/null +++ b/frontend/src/openapi/models/StrategySchema.ts @@ -0,0 +1,87 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Unleash API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 4.10.0-beta.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import { + ConstraintSchema, + ConstraintSchemaFromJSON, + ConstraintSchemaFromJSONTyped, + ConstraintSchemaToJSON, +} from './ConstraintSchema'; + +/** + * + * @export + * @interface StrategySchema + */ +export interface StrategySchema { + /** + * + * @type {string} + * @memberof StrategySchema + */ + id: string; + /** + * + * @type {string} + * @memberof StrategySchema + */ + name: string; + /** + * + * @type {Array} + * @memberof StrategySchema + */ + constraints: Array; + /** + * + * @type {object} + * @memberof StrategySchema + */ + parameters: object; +} + +export function StrategySchemaFromJSON(json: any): StrategySchema { + return StrategySchemaFromJSONTyped(json, false); +} + +export function StrategySchemaFromJSONTyped(json: any, ignoreDiscriminator: boolean): StrategySchema { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'id': json['id'], + 'name': json['name'], + 'constraints': ((json['constraints'] as Array).map(ConstraintSchemaFromJSON)), + 'parameters': json['parameters'], + }; +} + +export function StrategySchemaToJSON(value?: StrategySchema | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'id': value.id, + 'name': value.name, + 'constraints': ((value.constraints as Array).map(ConstraintSchemaToJSON)), + 'parameters': value.parameters, + }; +} + diff --git a/frontend/src/openapi/models/VariantSchema.ts b/frontend/src/openapi/models/VariantSchema.ts new file mode 100644 index 0000000000..57063b14dc --- /dev/null +++ b/frontend/src/openapi/models/VariantSchema.ts @@ -0,0 +1,103 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Unleash API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 4.10.0-beta.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +import { + OverrideSchema, + OverrideSchemaFromJSON, + OverrideSchemaFromJSONTyped, + OverrideSchemaToJSON, +} from './OverrideSchema'; + +/** + * + * @export + * @interface VariantSchema + */ +export interface VariantSchema { + /** + * + * @type {string} + * @memberof VariantSchema + */ + name: string; + /** + * + * @type {number} + * @memberof VariantSchema + */ + weight: number; + /** + * + * @type {string} + * @memberof VariantSchema + */ + weightType: string; + /** + * + * @type {string} + * @memberof VariantSchema + */ + stickiness: string; + /** + * + * @type {object} + * @memberof VariantSchema + */ + payload?: object; + /** + * + * @type {Array} + * @memberof VariantSchema + */ + overrides: Array; +} + +export function VariantSchemaFromJSON(json: any): VariantSchema { + return VariantSchemaFromJSONTyped(json, false); +} + +export function VariantSchemaFromJSONTyped(json: any, ignoreDiscriminator: boolean): VariantSchema { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'name': json['name'], + 'weight': json['weight'], + 'weightType': json['weightType'], + 'stickiness': json['stickiness'], + 'payload': !exists(json, 'payload') ? undefined : json['payload'], + 'overrides': ((json['overrides'] as Array).map(OverrideSchemaFromJSON)), + }; +} + +export function VariantSchemaToJSON(value?: VariantSchema | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'name': value.name, + 'weight': value.weight, + 'weightType': value.weightType, + 'stickiness': value.stickiness, + 'payload': value.payload, + 'overrides': ((value.overrides as Array).map(OverrideSchemaToJSON)), + }; +} + diff --git a/frontend/src/openapi/models/index.ts b/frontend/src/openapi/models/index.ts new file mode 100644 index 0000000000..079750bed3 --- /dev/null +++ b/frontend/src/openapi/models/index.ts @@ -0,0 +1,10 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './ChangeProjectSchema'; +export * from './ConstraintSchema'; +export * from './CreateFeatureSchema'; +export * from './FeatureSchema'; +export * from './FeaturesSchema'; +export * from './OverrideSchema'; +export * from './StrategySchema'; +export * from './VariantSchema'; diff --git a/frontend/src/openapi/runtime.ts b/frontend/src/openapi/runtime.ts new file mode 100644 index 0000000000..a72c6462fb --- /dev/null +++ b/frontend/src/openapi/runtime.ts @@ -0,0 +1,320 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Unleash API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 4.10.0-beta.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export const BASE_PATH = "http://localhost:4242".replace(/\/+$/, ""); + +const isBlob = (value: any) => typeof Blob !== 'undefined' && value instanceof Blob; + +/** + * This is the base class for all generated API classes. + */ +export class BaseAPI { + + private middleware: Middleware[]; + + constructor(protected configuration = new Configuration()) { + this.middleware = configuration.middleware; + } + + withMiddleware(this: T, ...middlewares: Middleware[]) { + const next = this.clone(); + next.middleware = next.middleware.concat(...middlewares); + return next; + } + + withPreMiddleware(this: T, ...preMiddlewares: Array) { + const middlewares = preMiddlewares.map((pre) => ({ pre })); + return this.withMiddleware(...middlewares); + } + + withPostMiddleware(this: T, ...postMiddlewares: Array) { + const middlewares = postMiddlewares.map((post) => ({ post })); + return this.withMiddleware(...middlewares); + } + + protected async request(context: RequestOpts, initOverrides?: RequestInit): Promise { + const { url, init } = this.createFetchParams(context, initOverrides); + const response = await this.fetchApi(url, init); + if (response.status >= 200 && response.status < 300) { + return response; + } + throw response; + } + + private createFetchParams(context: RequestOpts, initOverrides?: RequestInit) { + let url = this.configuration.basePath + context.path; + if (context.query !== undefined && Object.keys(context.query).length !== 0) { + // only add the querystring to the URL if there are query parameters. + // this is done to avoid urls ending with a "?" character which buggy webservers + // do not handle correctly sometimes. + url += '?' + this.configuration.queryParamsStringify(context.query); + } + const body = ((typeof FormData !== "undefined" && context.body instanceof FormData) || context.body instanceof URLSearchParams || isBlob(context.body)) + ? context.body + : JSON.stringify(context.body); + + const headers = Object.assign({}, this.configuration.headers, context.headers); + const init = { + method: context.method, + headers: headers, + body, + credentials: this.configuration.credentials, + ...initOverrides + }; + return { url, init }; + } + + private fetchApi = async (url: string, init: RequestInit) => { + let fetchParams = { url, init }; + for (const middleware of this.middleware) { + if (middleware.pre) { + fetchParams = await middleware.pre({ + fetch: this.fetchApi, + ...fetchParams, + }) || fetchParams; + } + } + let response = await (this.configuration.fetchApi || fetch)(fetchParams.url, fetchParams.init); + for (const middleware of this.middleware) { + if (middleware.post) { + response = await middleware.post({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + response: response.clone(), + }) || response; + } + } + return response; + } + + /** + * Create a shallow clone of `this` by constructing a new instance + * and then shallow cloning data members. + */ + private clone(this: T): T { + const constructor = this.constructor as any; + const next = new constructor(this.configuration); + next.middleware = this.middleware.slice(); + return next; + } +}; + +export class RequiredError extends Error { + name: "RequiredError" = "RequiredError"; + constructor(public field: string, msg?: string) { + super(msg); + } +} + +export const COLLECTION_FORMATS = { + csv: ",", + ssv: " ", + tsv: "\t", + pipes: "|", +}; + +export type FetchAPI = GlobalFetch['fetch']; + +export interface ConfigurationParameters { + basePath?: string; // override base path + fetchApi?: FetchAPI; // override for fetch implementation + middleware?: Middleware[]; // middleware to apply before/after fetch requests + queryParamsStringify?: (params: HTTPQuery) => string; // stringify function for query strings + username?: string; // parameter for basic security + password?: string; // parameter for basic security + apiKey?: string | ((name: string) => string); // parameter for apiKey security + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string | Promise); // parameter for oauth2 security + headers?: HTTPHeaders; //header params we want to use on every request + credentials?: RequestCredentials; //value for the credentials param we want to use on each request +} + +export class Configuration { + constructor(private configuration: ConfigurationParameters = {}) {} + + get basePath(): string { + return this.configuration.basePath != null ? this.configuration.basePath : BASE_PATH; + } + + get fetchApi(): FetchAPI | undefined { + return this.configuration.fetchApi; + } + + get middleware(): Middleware[] { + return this.configuration.middleware || []; + } + + get queryParamsStringify(): (params: HTTPQuery) => string { + return this.configuration.queryParamsStringify || querystring; + } + + get username(): string | undefined { + return this.configuration.username; + } + + get password(): string | undefined { + return this.configuration.password; + } + + get apiKey(): ((name: string) => string) | undefined { + const apiKey = this.configuration.apiKey; + if (apiKey) { + return typeof apiKey === 'function' ? apiKey : () => apiKey; + } + return undefined; + } + + get accessToken(): ((name?: string, scopes?: string[]) => string | Promise) | undefined { + const accessToken = this.configuration.accessToken; + if (accessToken) { + return typeof accessToken === 'function' ? accessToken : async () => accessToken; + } + return undefined; + } + + get headers(): HTTPHeaders | undefined { + return this.configuration.headers; + } + + get credentials(): RequestCredentials | undefined { + return this.configuration.credentials; + } +} + +export type Json = any; +export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD'; +export type HTTPHeaders = { [key: string]: string }; +export type HTTPQuery = { [key: string]: string | number | null | boolean | Array | HTTPQuery }; +export type HTTPBody = Json | FormData | URLSearchParams; +export type ModelPropertyNaming = 'camelCase' | 'snake_case' | 'PascalCase' | 'original'; + +export interface FetchParams { + url: string; + init: RequestInit; +} + +export interface RequestOpts { + path: string; + method: HTTPMethod; + headers: HTTPHeaders; + query?: HTTPQuery; + body?: HTTPBody; +} + +export function exists(json: any, key: string) { + const value = json[key]; + return value !== null && value !== undefined; +} + +export function querystring(params: HTTPQuery, prefix: string = ''): string { + return Object.keys(params) + .map((key) => { + const fullKey = prefix + (prefix.length ? `[${key}]` : key); + const value = params[key]; + if (value instanceof Array) { + const multiValue = value.map(singleValue => encodeURIComponent(String(singleValue))) + .join(`&${encodeURIComponent(fullKey)}=`); + return `${encodeURIComponent(fullKey)}=${multiValue}`; + } + if (value instanceof Date) { + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(value.toISOString())}`; + } + if (value instanceof Object) { + return querystring(value as HTTPQuery, fullKey); + } + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`; + }) + .filter(part => part.length > 0) + .join('&'); +} + +export function mapValues(data: any, fn: (item: any) => any) { + return Object.keys(data).reduce( + (acc, key) => ({ ...acc, [key]: fn(data[key]) }), + {} + ); +} + +export function canConsumeForm(consumes: Consume[]): boolean { + for (const consume of consumes) { + if ('multipart/form-data' === consume.contentType) { + return true; + } + } + return false; +} + +export interface Consume { + contentType: string +} + +export interface RequestContext { + fetch: FetchAPI; + url: string; + init: RequestInit; +} + +export interface ResponseContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + response: Response; +} + +export interface Middleware { + pre?(context: RequestContext): Promise; + post?(context: ResponseContext): Promise; +} + +export interface ApiResponse { + raw: Response; + value(): Promise; +} + +export interface ResponseTransformer { + (json: any): T; +} + +export class JSONApiResponse { + constructor(public raw: Response, private transformer: ResponseTransformer = (jsonValue: any) => jsonValue) {} + + async value(): Promise { + return this.transformer(await this.raw.json()); + } +} + +export class VoidApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return undefined; + } +} + +export class BlobApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return await this.raw.blob(); + }; +} + +export class TextApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return await this.raw.text(); + }; +} diff --git a/frontend/src/types/global-fetch.d.ts b/frontend/src/types/global-fetch.d.ts new file mode 100644 index 0000000000..6e552fe53a --- /dev/null +++ b/frontend/src/types/global-fetch.d.ts @@ -0,0 +1,3 @@ +// Add missing GlobalFetch declaration for the OpenAPI bindings. +// https://github.com/apollographql/apollo-link/issues/1131#issuecomment-526109609 +declare type GlobalFetch = WindowOrWorkerGlobalScope; diff --git a/frontend/src/utils/openapiClient.ts b/frontend/src/utils/openapiClient.ts new file mode 100644 index 0000000000..9351122831 --- /dev/null +++ b/frontend/src/utils/openapiClient.ts @@ -0,0 +1,12 @@ +import { Configuration, AdminApi } from 'openapi'; +import { getBasePath } from 'utils/formatPath'; + +const createAdminApi = (): AdminApi => { + return new AdminApi( + new Configuration({ + basePath: getBasePath(), + }) + ); +}; + +export const openApiAdmin = createAdminApi(); diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 3f1a7d46d3..dcedc67bdb 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1502,6 +1502,29 @@ outvariant "^1.2.1" strict-event-emitter "^0.2.0" +"@nestjs/common@8.2.6": + version "8.2.6" + resolved "https://registry.yarnpkg.com/@nestjs/common/-/common-8.2.6.tgz#34cd5cc44082d3525c56c95db42ca0e5277b7d85" + integrity sha512-flLYSXunxcKyjbYddrhwbc49uE705MxBt85rS3mHyhDbAIPSGGeZEqME44YyAzCg1NTfJSNe7ztmOce5kNkb9A== + dependencies: + axios "0.24.0" + iterare "1.2.1" + tslib "2.3.1" + uuid "8.3.2" + +"@nestjs/core@8.2.6": + version "8.2.6" + resolved "https://registry.yarnpkg.com/@nestjs/core/-/core-8.2.6.tgz#08eb38203fb01a828227ea25972d38bfef5c818f" + integrity sha512-NwPcEIMmCsucs3QaDlQvkoU1FlFM2wm/WjaqLQhkSoIEmAR1gNtBo88f5io5cpMwCo1k5xYhqGlaSl6TfngwWQ== + dependencies: + "@nuxtjs/opencollective" "0.3.2" + fast-safe-stringify "2.1.1" + iterare "1.2.1" + object-hash "2.2.0" + path-to-regexp "3.2.0" + tslib "2.3.1" + uuid "8.3.2" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1523,11 +1546,41 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@nuxtjs/opencollective@0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz#620ce1044f7ac77185e825e1936115bb38e2681c" + integrity sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA== + dependencies: + chalk "^4.1.0" + consola "^2.15.0" + node-fetch "^2.6.1" + "@open-draft/until@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca" integrity sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q== +"@openapitools/openapi-generator-cli@^2.4.26": + version "2.4.26" + resolved "https://registry.yarnpkg.com/@openapitools/openapi-generator-cli/-/openapi-generator-cli-2.4.26.tgz#67622fc41c258aeae3ff074cd92772978e03484f" + integrity sha512-O42H9q1HWGoIpcpMaUu318b6bmOgcjP3MieHwOrFdoG3KyttceBGlbLf9Kbf7WM91WSNCDXum7cnEKASuoGjAg== + dependencies: + "@nestjs/common" "8.2.6" + "@nestjs/core" "8.2.6" + "@nuxtjs/opencollective" "0.3.2" + chalk "4.1.2" + commander "8.3.0" + compare-versions "3.6.0" + concurrently "6.5.1" + console.table "0.10.0" + fs-extra "10.0.0" + glob "7.1.6" + inquirer "8.2.0" + lodash "4.17.21" + reflect-metadata "0.1.13" + rxjs "7.5.2" + tslib "2.0.3" + "@pmmmwh/react-refresh-webpack-plugin@^0.5.3": version "0.5.5" resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.5.tgz#e77aac783bd079f548daa0a7f080ab5b5a9741ca" @@ -2807,6 +2860,13 @@ axe-core@^4.3.5: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.1.tgz#7dbdc25989298f9ad006645cd396782443757413" integrity sha512-gd1kmb21kwNuWr6BQz8fv6GNECPBnUasepcoLbekws23NVBLODdsClRZ+bQ8+9Uomf3Sm3+Vwn0oYG9NvwnJCw== +axios@0.24.0: + version "0.24.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" + integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== + dependencies: + follow-redirects "^1.14.4" + axobject-query@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be" @@ -3192,6 +3252,14 @@ chalk@4.1.1: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@4.1.2, chalk@^4.0.2, chalk@^4.1.1, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@^2.0.0, chalk@^2.4.1: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -3209,10 +3277,10 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" @@ -3422,6 +3490,11 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" +commander@8.3.0, commander@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -3437,11 +3510,6 @@ commander@^7.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== -commander@^8.3.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" - integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== - common-path-prefix@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz#7d007a7e07c58c4b4d5f433131a19141b29f11e0" @@ -3457,6 +3525,11 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= +compare-versions@3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" + integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== + compressible@~2.0.16: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" @@ -3482,6 +3555,20 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +concurrently@6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-6.5.1.tgz#4518c67f7ac680cf5c34d5adf399a2a2047edc8c" + integrity sha512-FlSwNpGjWQfRwPLXvJ/OgysbBxPkWpiVjy1042b0U7on7S7qwwMIILRj7WTN1mTgqa582bG6NFuScOoh6Zgdag== + dependencies: + chalk "^4.1.0" + date-fns "^2.16.1" + lodash "^4.17.21" + rxjs "^6.6.3" + spawn-command "^0.0.2-1" + supports-color "^8.1.0" + tree-kill "^1.2.2" + yargs "^16.2.0" + confusing-browser-globals@^1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81" @@ -3492,6 +3579,18 @@ connect-history-api-fallback@^1.6.0: resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== +consola@^2.15.0: + version "2.15.3" + resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550" + integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== + +console.table@0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/console.table/-/console.table-0.10.0.tgz#0917025588875befd70cf2eff4bef2c6e2d75d04" + integrity sha1-CRcCVYiHW+/XDPLv9L7yxuLXXQQ= + dependencies: + easy-table "1.1.0" + content-disposition@0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" @@ -3878,7 +3977,7 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" -date-fns@2.28.0: +date-fns@2.28.0, date-fns@^2.16.1: version "2.28.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2" integrity sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw== @@ -4175,6 +4274,13 @@ duplexer@^0.1.2: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== +easy-table@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/easy-table/-/easy-table-1.1.0.tgz#86f9ab4c102f0371b7297b92a651d5824bc8cb73" + integrity sha1-hvmrTBAvA3G3KXuSplHVgkvIy3M= + optionalDependencies: + wcwidth ">=1.0.1" + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -4770,6 +4876,11 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fast-safe-stringify@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" + integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== + fastq@^1.6.0: version "1.13.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" @@ -4904,7 +5015,7 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== -follow-redirects@^1.0.0: +follow-redirects@^1.0.0, follow-redirects@^1.14.4: version "1.14.9" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== @@ -4966,6 +5077,15 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= +fs-extra@10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1" + integrity sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@^10.0.0: version "10.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.1.tgz#27de43b4320e833f6867cc044bfce29fdf0ef3b8" @@ -5092,10 +5212,10 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== +glob@7.1.6, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: + version "7.1.6" + resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -5263,11 +5383,16 @@ html-encoding-sniffer@^2.0.1: dependencies: whatwg-encoding "^1.0.5" -html-entities@^2.1.0, html-entities@^2.3.2: +html-entities@^2.1.0: version "2.3.3" resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.3.tgz#117d7626bece327fc8baace8868fa6f5ef856e46" integrity sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA== +html-entities@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.2.tgz#760b404685cb1d794e4f4b744332e3b00dcfe488" + integrity sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ== + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" @@ -5504,6 +5629,26 @@ ini@^1.3.5: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== +inquirer@8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.0.tgz#f44f008dd344bbfc4b30031f45d984e034a3ac3a" + integrity sha512-0crLweprevJ02tTuA6ThpoAERAGyVILC4sS74uib58Xf/zSr1/ZWtmm7D5CI+bSQEaA04f0K7idaHpQbSWgiVQ== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.1" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.21" + mute-stream "0.0.8" + ora "^5.4.1" + run-async "^2.4.0" + rxjs "^7.2.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + inquirer@^8.2.0: version "8.2.2" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.2.tgz#1310517a87a0814d25336c78a20b44c3d9b7629d" @@ -5818,6 +5963,11 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +iterare@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/iterare/-/iterare-1.2.1.tgz#139c400ff7363690e33abffa33cbba8920f00042" + integrity sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q== + jake@^10.6.1: version "10.8.4" resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.4.tgz#f6a8b7bf90c6306f768aa82bb7b98bf4ca15e84a" @@ -6638,7 +6788,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: +lodash@4.17.21, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -6910,7 +7060,7 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" -node-fetch@^2.6.7: +node-fetch@^2.6.1, node-fetch@^2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== @@ -6978,7 +7128,7 @@ object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= -object-hash@^2.2.0: +object-hash@2.2.0, object-hash@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== @@ -7288,6 +7438,11 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= +path-to-regexp@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-3.2.0.tgz#fa7877ecbc495c601907562222453c43cc204a5f" + integrity sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA== + path-to-regexp@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" @@ -8342,6 +8497,11 @@ redux@^4.1.2: dependencies: "@babel/runtime" "^7.9.2" +reflect-metadata@0.1.13: + version "0.1.13" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" + integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== + regenerate-unicode-properties@^10.0.1: version "10.0.1" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56" @@ -8560,6 +8720,27 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +rxjs@7.5.2: + version "7.5.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.2.tgz#11e4a3a1dfad85dbf7fb6e33cbba17668497490b" + integrity sha512-PwDt186XaL3QN5qXj/H9DGyHhP3/RYYgZZwqBv9Tv8rsAaiwFH1IsJJlcgD37J7UW5a6O67qX0KWKS3/pu0m4w== + dependencies: + tslib "^2.1.0" + +rxjs@^6.6.3: + version "6.6.7" + resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + +rxjs@^7.2.0: + version "7.5.4" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.4.tgz#3d6bd407e6b7ce9a123e76b1e770dc5761aa368d" + integrity sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ== + dependencies: + tslib "^2.1.0" + rxjs@^7.5.1, rxjs@^7.5.5: version "7.5.5" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f" @@ -8896,6 +9077,11 @@ sourcemap-codec@^1.4.8: resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== +spawn-command@^0.0.2-1: + version "0.0.2-1" + resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" + integrity sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A= + spdy-transport@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" @@ -9129,7 +9315,7 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -supports-color@^8.0.0, supports-color@^8.1.1: +supports-color@^8.0.0, supports-color@^8.1.0, supports-color@^8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== @@ -9398,6 +9584,11 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= +tree-kill@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== + tryer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" @@ -9413,16 +9604,21 @@ tsconfig-paths@^3.14.1: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^1.8.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" + integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== -tslib@^2.0.3, tslib@^2.1.0: +tslib@2.3.1, tslib@^2.0.3, tslib@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== +tslib@^1.8.1, tslib@^1.9.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -9603,9 +9799,9 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= -uuid@^8.3.2: +uuid@8.3.2, uuid@^8.3.2: version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== v8-compile-cache@^2.0.3: @@ -9677,7 +9873,7 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" -wcwidth@^1.0.1: +wcwidth@>=1.0.1, wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=