Skip to content

Commit

Permalink
PoC for online demo
Browse files Browse the repository at this point in the history
- Compile with AGAMA_DEMO=<demo_data> to build a static site with the recorded data
  • Loading branch information
lslezak committed Nov 20, 2024
1 parent 72170de commit e2ec16e
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 22 deletions.
31 changes: 30 additions & 1 deletion web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
* find current contact information at www.suse.com.
*/

import React from "react";
import React, { useState } from "react";
import { Alert, AlertActionCloseButton } from "@patternfly/react-core";
import { Navigate, Outlet, useLocation } from "react-router-dom";
import { ServerError } from "~/components/core";
import { Loading, PlainLayout } from "~/components/layout";
Expand All @@ -35,6 +36,8 @@ import { useDeprecatedChanges } from "~/queries/storage";
import { ROOT, PRODUCT } from "~/routes/paths";
import { InstallationPhase } from "~/types/status";

import { _ } from "~/i18n";

/**
* Main application component.
*/
Expand All @@ -44,6 +47,7 @@ function App() {
const { connected, error } = useInstallerClientStatus();
const { selectedProduct, products } = useProduct();
const { language } = useInstallerL10n();
const [isOpen, setIsOpen] = useState(true);
useL10nConfigChanges();
useProductChanges();
useIssuesChanges();
Expand Down Expand Up @@ -89,8 +93,33 @@ function App() {

if (!language) return null;

// TRANSLATORS: the text in square brackets [] is a clickable link
const [msgStart, msgLink, msgEnd] = _(
"The demo runs in read-only mode, you cannot change any values and the installation cannot \
be started. To find more details about Agama check the [home page].",
).split(/[[\]]/);

const alert =
process.env.AGAMA_DEMO && isOpen ? (
<Alert
variant="warning"
title={_("This is Agama installer online demo.")}
timeout={12000}
actionClose={<AlertActionCloseButton onClose={() => setIsOpen(false)} />}
>
<p>
{msgStart}
<a href="https://agama-project.github.io/" rel="noreferrer" target="_blank">
{msgLink}
</a>
{msgEnd}
</p>
</Alert>
) : null;

return (
<>
{alert}
<Content />
<Questions />
</>
Expand Down
42 changes: 37 additions & 5 deletions web/src/api/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,48 +22,80 @@

import axios from "axios";

let demo_data;
if (process.env.AGAMA_DEMO) {
demo_data = await import(process.env.AGAMA_DEMO);
}

const http = axios.create({
responseType: "json",
});

function mock_response(method: string, url: string) {
console.info("Demo mode, ignoring request", method, url);

return Promise.resolve({
data: {},
status: 200,
statusText: "OK",
headers: {},
config: {
headers: {},
},
});
}

/**
* Retrieves the object from given URL
*
* @param url - HTTP URL
* @return data from the response body
*/
const get = (url: string) => http.get(url).then(({ data }) => data);
const get = (url: string) => {
if (process.env.AGAMA_DEMO) {
if (!(url in demo_data)) {
console.error("Missing demo data for REST API path", url);
}
return Promise.resolve(demo_data[url]);
} else {
return http.get(url).then(({ data }) => data);
}
};

/**
* Performs a PATCH request with the given URL and data
*
* @param url - endpoint URL
* @param data - Request payload
*/
const patch = (url: string, data?: object) => http.patch(url, data);
const patch = (url: string, data?: object) =>
process.env.AGAMA_DEMO ? mock_response("PATCH", url) : http.patch(url, data);

/**
* Performs a PUT request with the given URL and data
*
* @param url - endpoint URL
* @param data - request payload
*/
const put = (url: string, data: object) => http.put(url, data);
const put = (url: string, data: object) =>
process.env.AGAMA_DEMO ? mock_response("PUT", url) : http.put(url, data);

/**
* Performs a POST request with the given URL and data
*
* @param url - endpoint URL
* @param data - request payload
*/
const post = (url: string, data?: object) => http.post(url, data);
const post = (url: string, data?: object) =>
process.env.AGAMA_DEMO ? mock_response("POST", url) : http.post(url, data);

/**
* Performs a DELETE request on the given URL
*
* @param url - endpoint URL
* @param data - request payload
*/
const del = (url: string) => http.delete(url);
const del = (url: string) =>
process.env.AGAMA_DEMO ? mock_response("DELETE", url) : http.delete(url);

export { get, patch, post, put, del };
3 changes: 3 additions & 0 deletions web/src/client/ws.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,13 @@ class WSClient {
}

isConnected() {
if (process.env.AGAMA_DEMO) return true;
return this.wsState() === SocketStates.CONNECTED;
}

buildClient() {
if (process.env.AGAMA_DEMO) return null;

const client = new WebSocket(this.url);
client.onopen = () => {
console.log("Websocket connected");
Expand Down
28 changes: 15 additions & 13 deletions web/src/context/auth.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const AuthErrors = Object.freeze({
* @param {React.ReactNode} [props.children] - content to display within the provider
*/
function AuthProvider({ children }) {
const [isLoggedIn, setIsLoggedIn] = useState(undefined);
const [isLoggedIn, setIsLoggedIn] = useState(process.env.AGAMA_DEMO ? true : undefined);
const [error, setError] = useState(null);

const login = useCallback(async (password) => {
Expand Down Expand Up @@ -79,19 +79,21 @@ function AuthProvider({ children }) {
}, []);

useEffect(() => {
fetch("/api/auth", {
headers: { "Content-Type": "application/json" },
})
.then((response) => {
setIsLoggedIn(response.status === 200);
if (response.status >= 500 && response.status < 600) {
setError(AuthErrors.SERVER);
}
if (response.status >= 400 && response.status < 500) {
setError(AuthErrors.AUTH);
}
if (!process.env.AGAMA_DEMO) {
fetch("/api/auth", {
headers: { "Content-Type": "application/json" },
})
.catch(() => setIsLoggedIn(false));
.then((response) => {
setIsLoggedIn(response.status === 200);
if (response.status >= 500 && response.status < 600) {
setError(AuthErrors.SERVER);
}
if (response.status >= 400 && response.status < 500) {
setError(AuthErrors.AUTH);
}
})
.catch(() => setIsLoggedIn(false));
}
}, []);

return (
Expand Down
2 changes: 1 addition & 1 deletion web/src/context/installer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ function useInstallerClientStatus() {
*/
function InstallerClientProvider({ children, client = null }) {
const [value, setValue] = useState(client);
const [connected, setConnected] = useState(false);
const [connected, setConnected] = useState(process.env.AGAMA_DEMO !== undefined);
const [error, setError] = useState(false);

useEffect(() => {
Expand Down
2 changes: 1 addition & 1 deletion web/src/context/installerL10n.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ function reload(newLanguage: string) {
*/
function InstallerL10nProvider({ children }: { children?: React.ReactNode }) {
const { connected } = useInstallerClientStatus();
const [language, setLanguage] = useState(undefined);
const [language, setLanguage] = useState(process.env.AGAMA_DEMO ? "en-us" : undefined);
const [keymap, setKeymap] = useState(undefined);

const syncBackendLanguage = useCallback(async () => {
Expand Down
2 changes: 1 addition & 1 deletion web/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const plugins = [
// the current value of the environment variable, that variable is set to
// "true" when running the development server ("npm run server")
// https://webpack.js.org/plugins/environment-plugin/
new webpack.EnvironmentPlugin({ WEBPACK_SERVE: null, LOCAL_CONNECTION: null }),
new webpack.EnvironmentPlugin({ WEBPACK_SERVE: null, LOCAL_CONNECTION: null, AGAMA_DEMO: null }),
].filter(Boolean);

if (eslint) {
Expand Down

0 comments on commit e2ec16e

Please sign in to comment.