Sfoglia il codice sorgente

Adding navigation handling

bodicsek 3 anni fa
parent
commit
91933b319c
6 ha cambiato i file con 118 aggiunte e 95 eliminazioni
  1. 3 2
      ojet.config.js
  2. 2 3
      package.json
  3. 1 0
      src/components/app.tsx
  4. 15 6
      src/components/header.tsx
  5. 91 84
      src/hooks/useLogin.tsx
  6. 6 0
      src/hooks/useQuery.ts

+ 3 - 2
ojet.config.js

@@ -11,7 +11,7 @@ const package = require("./package.json");
 const developmentConfig = {
   plugins: [
     new webpack.DefinePlugin({
-      API_URL: JSON.stringify(require("./package.json").config.devApiUrl)
+      API_URL: JSON.stringify(process.env.API_URL)
     })
   ],
   devServer: {
@@ -20,7 +20,8 @@ const developmentConfig = {
     allowedHosts: [
       "localhost",
       package.config.host
-    ]
+    ],
+    historyApiFallback: true
   }
 };
 

+ 2 - 3
package.json

@@ -1,11 +1,10 @@
 {
   "name": "occ-fw-ui",
-  "version": "1.0.11",
+  "version": "1.0.12",
   "description": "An Oracle JavaScript Extension Toolkit(JET) web app",
   "config": {
     "host": "customercentral-poc.oracle.com",
-    "registry": "iad.ocir.io/cesdev",
-    "devApiUrl": ""
+    "registry": "iad.ocir.io/cesdev"
   },
   "scripts": {
     "start": "DEBUG='express:*' ojet serve",

+ 1 - 0
src/components/app.tsx

@@ -31,6 +31,7 @@ const AppComponent: FunctionComponent<Props> = ({ appName = "OCC" }) => {
           login={login}
           logout={logout}
           navigations={navigations}
+          plugins={plugins}
         />
         <Content userLogin={userLogin} plugins={plugins} />
         <Footer />

+ 15 - 6
src/components/header.tsx

@@ -6,19 +6,19 @@ import { ojMenu } from "ojs/ojmenu";
 import { ojButton } from "ojs/ojbutton";
 import { FunctionComponent } from "preact";
 import { useRef, useState, useEffect } from "preact/hooks";
-import { NavigationItem } from "src/hooks/useQuery";
+import { isLinkItem, isPluginItem, NavigationItem, Plugin } from "../hooks/useQuery";
 
 type Props = Readonly<{
   appName: string,
   userLogin: string,
   login: () => void,
   logout: () => void,
-  navigations: NavigationItem[] | undefined
+  navigations: NavigationItem[] | undefined,
+  plugins: Plugin[] | undefined
 }>;
 
-export const Header: FunctionComponent<Props> = ({ appName, userLogin, login, logout, navigations }) => {
+export const Header: FunctionComponent<Props> = ({ appName, userLogin, login, logout, navigations, plugins }) => {
   const mediaQueryRef = useRef<MediaQueryList>(window.matchMedia(ResponsiveUtils.getFrameworkQuery("sm-only")));
-
   const [isSmallWidth, setIsSmallWidth] = useState(mediaQueryRef.current.matches);
 
   useEffect(() => {
@@ -50,6 +50,15 @@ export const Header: FunctionComponent<Props> = ({ appName, userLogin, login, lo
     }
   };
 
+  const navigationAction = (event: ojMenu.ojMenuAction) => {
+    const navItem: NavigationItem = event.detail.selectedValue;
+    if (isLinkItem(navItem)) {
+      window.open(navItem.path, navItem.openInNewTab ? "_blank" : "_self");
+    } else if (isPluginItem(navItem)) {
+      window.location.pathname = plugins.find(p => p.name === navItem.pluginName)?.path;
+    }
+  }
+
   return (
     <header role="banner" class="oj-web-applayout-header">
       <div class="oj-web-applayout-max-width oj-flex-bar oj-sm-align-items-center">
@@ -67,13 +76,13 @@ export const Header: FunctionComponent<Props> = ({ appName, userLogin, login, lo
           {navigations &&
             <oj-menu-button id="navMenuButton" display="icons" chroming="borderless">
               <span slot="endIcon" class="oj-ux-ico-leading-increase"></span>
-              <oj-menu id="navMenu" slot="menu" onojMenuAction={userMenuAction}>
+              <oj-menu id="navMenu" slot="menu" onojMenuAction={navigationAction}>
                 {navigations.map(nav => (
                   <oj-option value={nav.labelKey}>
                     {nav.labelKey}
                     {nav.children &&
                       <oj-menu>
-                        {nav.children.map(navChild => (<oj-option value={navChild.labelKey}>{navChild.labelKey}</oj-option>))}
+                        {nav.children.map(navChild => (<oj-option value={navChild}>{navChild.labelKey}</oj-option>))}
                       </oj-menu>
                     }
                   </oj-option>

+ 91 - 84
src/hooks/useLogin.tsx

@@ -1,93 +1,100 @@
 import { useEffect, useState } from "preact/hooks";
-import {apiUrl} from "../utils/environment";
+import { apiUrl } from "../utils/environment";
 
 const parseJwt = token => {
-    var base64Url = token.split('.')[1];
-    var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
-    var jsonPayload = decodeURIComponent(window.atob(base64).split('').map(
-      c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
-    ).join(''));
-    return JSON.parse(jsonPayload);
-  };
-  
-  // const idcsStripe = "https://idcs-25070016ce0c4eb8b6eea18f07fe170d.identity.oraclecloud.com";
-  const idcsStripe = "https://idcs-login-stage.identity.oraclecloud.com";
-  // const clientId = "4e728d65cf5b482ea81e56bf23a9ad8a";
-  const clientId = "754db2d1964d4f12ab312a2ab6f025ed";
-  
-  const login = () => {
-    const state = crypto.randomUUID();
-    const nonce = crypto.randomUUID();
-    sessionStorage.setItem("oauth", JSON.stringify({ state, nonce }));
-    window.location.href = `${idcsStripe}/oauth2/v1/authorize` +
-      `?client_id=${clientId}` +
-      "&response_type=code" +
-      `&redirect_uri=${encodeURIComponent(window.location.href)}` +
-      "&scope=openid" +
-      `&nonce=${nonce}` +
-      `&state=${state}`;
-  };
-  
-  const logout = (token: string) => () => {
-    // fetch(
-    //   `https://idcs-25070016ce0c4eb8b6eea18f07fe170d.identity.oraclecloud.com/oauth2/v1/userlogout?post_logout_redirect_uri=${encodeURIComponent(window.location.origin)}&id_token_hint=${token}`,
-    //   // `https://idcs-25070016ce0c4eb8b6eea18f07fe170d.identity.oraclecloud.com/sso/v1/user/logout?post_logout_redirect_uri=${encodeURIComponent(window.location.origin)}&id_token_hint=${token}`,
-    //   // `https://idcs-25070016ce0c4eb8b6eea18f07fe170d.identity.oraclecloud.com/oauth2/v1/userlogout?id_token_hint=${token}`,
-    //   // `${idcsStripe}/oauth2/v1/userlogout`,
-    //   {
-    //     headers: {
-    //       "Content-Type": "application/scim+json",
-    //       // Authorization: `Bearer ${accessToken}`
-    //       // Authorization: "Basic NGU3MjhkNjVjZjViNDgyZWE4MWU1NmJmMjNhOWFkOGE6ZGM1NDdkYmUtOGQ0Yi00MTU1LWEzNzgtZjNhMDNkNTZhNjU0"
-    //     }
-    //   }
-    // );
-    window.location.href = `${idcsStripe}/sso/v1/user/logout`;
-  }
+  var base64Url = token.split('.')[1];
+  var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
+  var jsonPayload = decodeURIComponent(window.atob(base64).split('').map(
+    c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
+  ).join(''));
+  return JSON.parse(jsonPayload);
+};
+
+// const idcsStripe = "https://idcs-25070016ce0c4eb8b6eea18f07fe170d.identity.oraclecloud.com";
+const idcsStripe = "https://idcs-login-stage.identity.oraclecloud.com";
+// const clientId = "4e728d65cf5b482ea81e56bf23a9ad8a";
+const clientId = "754db2d1964d4f12ab312a2ab6f025ed";
+
+const login = () => {
+  const state = crypto.randomUUID();
+  const nonce = crypto.randomUUID();
+  sessionStorage.setItem("oauth", JSON.stringify({ state, nonce }));
+  window.location.href = `${idcsStripe}/oauth2/v1/authorize` +
+    `?client_id=${clientId}` +
+    "&response_type=code" +
+    `&redirect_uri=${encodeURIComponent(window.location.href)}` +
+    "&scope=openid" +
+    `&nonce=${nonce}` +
+    `&state=${state}`;
+};
+
+const logout = (token: string) => () => {
+  // fetch(
+  //   `https://idcs-25070016ce0c4eb8b6eea18f07fe170d.identity.oraclecloud.com/oauth2/v1/userlogout?post_logout_redirect_uri=${encodeURIComponent(window.location.origin)}&id_token_hint=${token}`,
+  //   // `https://idcs-25070016ce0c4eb8b6eea18f07fe170d.identity.oraclecloud.com/sso/v1/user/logout?post_logout_redirect_uri=${encodeURIComponent(window.location.origin)}&id_token_hint=${token}`,
+  //   // `https://idcs-25070016ce0c4eb8b6eea18f07fe170d.identity.oraclecloud.com/oauth2/v1/userlogout?id_token_hint=${token}`,
+  //   // `${idcsStripe}/oauth2/v1/userlogout`,
+  //   {
+  //     headers: {
+  //       "Content-Type": "application/scim+json",
+  //       // Authorization: `Bearer ${accessToken}`
+  //       // Authorization: "Basic NGU3MjhkNjVjZjViNDgyZWE4MWU1NmJmMjNhOWFkOGE6ZGM1NDdkYmUtOGQ0Yi00MTU1LWEzNzgtZjNhMDNkNTZhNjU0"
+  //     }
+  //   }
+  // );
+  window.location.href = `${idcsStripe}/sso/v1/user/logout`;
+  sessionStorage.removeItem("oauth");
+}
 
 export const useLogin = () => {
-    const [userLogin, setUserLogin] = useState("");
-    const [accessToken, setAccessToken] = useState("");
-    const [idToken, setIdToken] = useState("");
+  const [userLogin, setUserLogin] = useState("");
+  const [accessToken, setAccessToken] = useState("");
+  const [idToken, setIdToken] = useState("");
 
-    useEffect(() => {
-        const searchParams = new URLSearchParams(window.location.search);
-      if (searchParams.has("code") && searchParams.has("state")) {
-        const { state, nonce } = JSON.parse(sessionStorage.getItem("oauth"));
-        const receivedState = searchParams.get("state");
-        if (receivedState !== state) {
-          return;
-        }
-        // Get the tokens using the received authorization code
-        const code = searchParams.get("code");
-        fetch(`${apiUrl}/auth/login`, {
-          method: "POST",
-          headers: new Headers({
-            "Content-Type": "application/json"
-          }),
-          body: JSON.stringify({ code, nonce })
-        })
-          .then(response => response.json())
-          .then(body => {
-            const idToken = parseJwt(body.idToken);
-            setUserLogin(idToken.user_displayname);
-            setIdToken(body.idToken);
-            setAccessToken(body.accessToken);
-            console.log("Tokens:", { idToken, accessToken: parseJwt(body.accessToken) })
-          })
-          .finally(() => sessionStorage.removeItem("oauth"));
-        // Clear the search parameter from the url
-        window.history.pushState({}, document.title, window.location.pathname);
-      } else {
-        sessionStorage.removeItem("oauth");
+  useEffect(() => {
+    const searchParams = new URLSearchParams(window.location.search);
+    const { state, nonce, at, it } = JSON.parse(sessionStorage.getItem("oauth") || "{}");
+    if (searchParams.has("code") && searchParams.has("state")) {
+      const receivedState = searchParams.get("state");
+      if (receivedState !== state) {
+        return;
       }
-    }, []);
+      // Get the tokens using the received authorization code
+      const code = searchParams.get("code");
+      fetch(`${apiUrl}/auth/login`, {
+        method: "POST",
+        headers: new Headers({
+          "Content-Type": "application/json"
+        }),
+        body: JSON.stringify({ code, nonce })
+      })
+        .then(response => response.json())
+        .then(body => {
+          const idToken = parseJwt(body.idToken);
+          setUserLogin(idToken.user_displayname);
+          setIdToken(body.idToken);
+          setAccessToken(body.accessToken);
+          sessionStorage.setItem("oauth", JSON.stringify({ at: body.accessToken, it: body.idToken }));
+          console.log("Tokens:", { idToken, accessToken: parseJwt(body.accessToken) })
+        })
+      // .finally(() => sessionStorage.removeItem("oauth"));
+      // Clear the search parameter from the url
+      window.history.pushState({}, document.title, window.location.pathname);
+    } else if (at && it) {
+      setAccessToken(at);
+      setIdToken(it);
+      const idToken = parseJwt(it);
+      setUserLogin(idToken.user_displayname);
+    } else {
+      sessionStorage.removeItem("oauth");
+    }
+  }, []);
 
-    return {
-        userLogin,
-        idToken,
-        accessToken,
-        login,
-        logout: logout(idToken)
-    };
+  return {
+    userLogin,
+    idToken,
+    accessToken,
+    login,
+    logout: logout(idToken)
+  };
 };

+ 6 - 0
src/hooks/useQuery.ts

@@ -25,6 +25,12 @@ export interface PluginItem extends BaseItem {
 
 export type NavigationItem = LinkItem | PluginItem;
 
+export const isLinkItem = (obj: any): obj is LinkItem => 
+    obj != null && obj.path !== undefined;
+
+export const isPluginItem = (obj: any): obj is LinkItem => 
+    obj != null && obj.pluginName !== undefined;
+
 export class HttpException extends Error {
     constructor(public path: string, public status: number) {
         super(`HTTP call to ${path} has been failed with ${status}`);