Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New endpoints #4

Draft
wants to merge 16 commits into
base: formsflow
Choose a base branch
from
Draft
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Converting Spliff to microfront end
ratheesh-aot committed Jul 17, 2024
commit 678cc4fcd13ec83de264717a28ba980786a7dc4b
61 changes: 61 additions & 0 deletions spiffworkflow-frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions spiffworkflow-frontend/package.json
Original file line number Diff line number Diff line change
@@ -71,12 +71,13 @@
},
"scripts": {
"build": "vite build",
"build-micro": "vite build --base=/spliff --config vite.micro.config.ts --mode production",
"eslint": "./node_modules/.bin/eslint src --ext .js,.jsx,.ts,.tsx",
"format": "prettier --write src/**/*.[tj]s{,x}",
"lint": "npm run eslint && npm run typecheck",
"lint:fix": "./node_modules/.bin/eslint --fix src --ext .js,.jsx,.ts,.tsx",
"serve": "vite preview",
"start": "VITE_VERSION_INFO='{\"version\":\"local\"}' vite",
"start": "vite",
"test": "vitest run --coverage",
"typecheck": "./node_modules/.bin/tsc --noEmit"
},
@@ -114,6 +115,7 @@
"cypress-slow-down": "^1.3.1",
"cypress-vite": "^1.5.0",
"eslint": "^8.56.0",
"eslint_d": "^12.2.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-cypress": "^3.3.0",
@@ -124,14 +126,15 @@
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-sonarjs": "^1.0.3",
"eslint-plugin-unused-imports": "^3.2.0",
"eslint_d": "^12.2.0",
"inherits-browser": "^0.0.1",
"jsdom": "^24.0.0",
"nice-select2": "^2.1.0",
"prettier": "^3.3.2",
"safe-regex": "^2.1.1",
"single-spa-react": "^6.0.1",
"tiny-svg": "^2.2.3",
"ts-migrate": "^0.1.30",
"vite-plugin-single-spa": "^0.7.0",
"vitest": "^1.5.0"
}
}
55 changes: 55 additions & 0 deletions spiffworkflow-frontend/src/AppSpa.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { defineAbility } from '@casl/ability';

import { createBrowserRouter, Outlet, RouterProvider } from 'react-router-dom';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { AbilityContext } from './contexts/Can';
import APIErrorProvider from './contexts/APIErrorContext';
import ContainerForExtensionsMicro from './ContainerForExtensionsMicro';
import { BASENAME_URL } from './config';

const queryClient = new QueryClient();

export default function AppSpa() {
const ability = defineAbility(() => {});
const routeComponents = () => {
return [
{
path: '*',
element: <ContainerForExtensionsMicro />,
},
];
};

/**
* Note that QueryClientProvider and ReactQueryDevTools
* are React Qery, now branded under the Tanstack packages.
* https://tanstack.com/query/latest
*/
const layout = () => {
return (
<div className="cds--white">
<QueryClientProvider client={queryClient}>
<APIErrorProvider>
<AbilityContext.Provider value={ability}>
<Outlet />
<ReactQueryDevtools initialIsOpen={false} />
</AbilityContext.Provider>
</APIErrorProvider>
</QueryClientProvider>
</div>
);
};
const router = createBrowserRouter([
{
path: '*',
Component: layout,
children: routeComponents(),
},
],
{
basename: BASENAME_URL
}
);
return <RouterProvider router={router} />;
}
161 changes: 161 additions & 0 deletions spiffworkflow-frontend/src/ContainerForExtensionsMicro.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import { Content } from '@carbon/react';
import { Routes, Route, useLocation } from 'react-router-dom';
import React, { useEffect, useState } from 'react';
import { ErrorBoundary } from 'react-error-boundary';

import ScrollToTop from './components/ScrollToTop';
import EditorRoutes from './routes/EditorRoutes';
import Extension from './routes/Extension';
import { useUriListForPermissions } from './hooks/UriListForPermissions';
import { PermissionsToCheck, ProcessFile, ProcessModel } from './interfaces';
import { usePermissionFetcher } from './hooks/PermissionService';
import {
ExtensionUiSchema,
UiSchemaUxElement,
} from './extension_ui_schema_interfaces';
import HttpService from './services/HttpService';
import { ErrorBoundaryFallback } from './ErrorBoundaryFallack';
import BaseRoutes from './routes/BaseRoutes';
import BaseRoutesMicro from './routes/BaseRoutesMicro';
import BackendIsDown from './routes/BackendIsDown';
import Login from './routes/Login';
import NavigationBar from './components/NavigationBar';
import useAPIError from './hooks/UseApiError';

export default function ContainerForExtensionsMicro() {
const [backendIsUp, setBackendIsUp] = useState<boolean | null>(null);
const [extensionUxElements, setExtensionUxElements] = useState<
UiSchemaUxElement[] | null
>(null);

let contentClassName = 'main-site-body-centered';
if (window.location.pathname.startsWith('/editor/')) {
contentClassName = 'no-center-stuff';
}
const { targetUris } = useUriListForPermissions();
const permissionRequestData: PermissionsToCheck = {
[targetUris.extensionListPath]: ['GET'],
};
const { ability, permissionsLoaded } = usePermissionFetcher(
permissionRequestData,
);

const { removeError } = useAPIError();

const location = useLocation();

// never carry an error message across to a different path
useEffect(() => {
removeError();
// if we include the removeError function to the dependency array of this useEffect, it causes
// an infinite loop where the page with the error adds the error,
// then this runs and it removes the error, etc. it is ok not to include it here, i think, because it never changes.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [location.pathname]);

// eslint-disable-next-line sonarjs/cognitive-complexity
useEffect(() => {
const processExtensionResult = (processModels: ProcessModel[]) => {
const eni: UiSchemaUxElement[] = processModels
.map((processModel: ProcessModel) => {
const extensionUiSchemaFile = processModel.files.find(
(file: ProcessFile) => file.name === 'extension_uischema.json',
);
if (extensionUiSchemaFile && extensionUiSchemaFile.file_contents) {
try {
const extensionUiSchema: ExtensionUiSchema = JSON.parse(
extensionUiSchemaFile.file_contents,
);
if (
extensionUiSchema &&
extensionUiSchema.ux_elements &&
!extensionUiSchema.disabled
) {
return extensionUiSchema.ux_elements;
}
} catch (jsonParseError: any) {
console.error(
`Unable to get navigation items for ${processModel.id}`,
);
}
}
return [] as UiSchemaUxElement[];
})
.flat();
if (eni) {
setExtensionUxElements(eni);
}
};

const getExtensions = () => {
setBackendIsUp(true);
if (!permissionsLoaded) {
return;
}
if (ability.can('GET', targetUris.extensionListPath)) {
HttpService.makeCallToBackend({
path: targetUris.extensionListPath,
successCallback: processExtensionResult,
});
} else {
// set to an empty array so we know that it loaded
setExtensionUxElements([]);
}
};

HttpService.makeCallToBackend({
path: targetUris.statusPath,
successCallback: getExtensions,
failureCallback: () => setBackendIsUp(false),
});
}, [
targetUris.extensionListPath,
targetUris.statusPath,
permissionsLoaded,
ability,
]);

const routeComponents = () => {
return (
<Routes>
<Route
path="*"
element={<BaseRoutesMicro extensionUxElements={extensionUxElements} />}
/>
<Route path="editor/*" element={<EditorRoutes />} />
<Route path="extensions/:page_identifier" element={<Extension />} />
<Route path="login" element={<Login />} />
</Routes>
);
};

const backendIsDownPage = () => {
return [<BackendIsDown />];
};

const innerComponents = () => {
if (backendIsUp === null) {
return [];
}
if (backendIsUp) {
return routeComponents();
}
return backendIsDownPage();
};

return (
<>
{/* TODO : remove this NavigationBar when we have a new navigation system */}
<div className="hidden">
<NavigationBar extensionUxElements={extensionUxElements} />
</div>

<Content className={contentClassName}>
<ScrollToTop />
<ErrorBoundary FallbackComponent={ErrorBoundaryFallback}>
{innerComponents()}
</ErrorBoundary>
</Content>
</>
);
}
3 changes: 2 additions & 1 deletion spiffworkflow-frontend/src/components/LoginHandler.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import UserService from '../services/UserService';
import { BASENAME_URL } from '../config';

export default function LoginHandler() {
const navigate = useNavigate();
useEffect(() => {
if (!UserService.isLoggedIn()) {
navigate(`/login?original_url=${UserService.getCurrentLocation()}`);
navigate(BASENAME_URL+ `/login?original_url=${UserService.getCurrentLocation()}`);
}
}, [navigate]);
return null;
Loading