From ce60a3ddd7082fe4d5a2c72f630a292b1da92074 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Wed, 19 Apr 2023 08:40:49 -0500 Subject: [PATCH 1/8] add apikey to redux --- frontend/src/actions/user.js | 88 ++++++--- frontend/src/reducers/user.ts | 47 +++-- frontend/src/types/action.ts | 335 ++++++++++++++++++---------------- frontend/src/types/data.ts | 149 ++++++++------- 4 files changed, 352 insertions(+), 267 deletions(-) diff --git a/frontend/src/actions/user.js b/frontend/src/actions/user.js index 25eb094bc..5d050b298 100644 --- a/frontend/src/actions/user.js +++ b/frontend/src/actions/user.js @@ -1,42 +1,57 @@ -import {V2} from "../openapi"; +import { V2 } from "../openapi"; import Cookies from "universal-cookie"; import config from "../app.config"; -import {handleErrors} from "./common"; -import {ADD_GROUP_MEMBER} from "./group"; const cookies = new Cookies(); export const userActions = { login, - logout + logout, }; // TODO need to clean up this file with all the mixed login/logout methods -export async function loginHelper(email, password, first_name=null, last_name=null, register = false) { - const data = {"email": email, "password": password}; +export async function loginHelper( + email, + password, + first_name = null, + last_name = null, + register = false +) { + const data = { email: email, password: password }; if (register) { - return V2.LoginService.saveUserApiV2UsersPost({...data, "first_name":first_name, "last_name": last_name}) - .then(user => {return user;}) - .catch(reason => { - // logout(); - return {"errorMsg": `Failed to register a user! ${reason}`}; + return V2.LoginService.saveUserApiV2UsersPost({ + ...data, + first_name: first_name, + last_name: last_name, + }) + .then((user) => { + return user; + }) + .catch((reason) => { + // logout(); + return { errorMsg: `Failed to register a user! ${reason}` }; }); } else { return V2.LoginService.loginApiV2LoginPost(data) - .then(user => {return user;}) - .catch(reason => { - // logout(); - return {"errorMsg": `Failed to login a user! ${reason}`}; + .then((user) => { + return user; + }) + .catch((reason) => { + // logout(); + return { errorMsg: `Failed to login a user! ${reason}` }; }); } } -export async function logoutHelper(){ - const headers = {"Authorization": cookies.get("Authorization")}; +export async function logoutHelper() { + const headers = { Authorization: cookies.get("Authorization") }; V2.OpenAPI.TOKEN = undefined; cookies.remove("Authorization", { path: "/" }); - return await fetch(config.KeycloakLogout, { method: "GET", headers: headers}); + return await fetch(config.KeycloakLogout, { + method: "GET", + headers: headers, + }); } export const LOGIN_ERROR = "LOGIN_ERROR"; @@ -61,7 +76,10 @@ export function login(email, password) { } else { return dispatch({ type: LOGIN_ERROR, - errorMsg: json["errorMsg"] !== undefined && json["errorMsg"] !== "" ? json["errorMsg"]: "Email/Password incorrect!" + errorMsg: + json["errorMsg"] !== undefined && json["errorMsg"] !== "" + ? json["errorMsg"] + : "Email/Password incorrect!", }); } }; @@ -77,7 +95,10 @@ export function register(email, password, firstname, lastname) { } else { return dispatch({ type: REGISTER_ERROR, - errorMsg: json["errorMsg"] !== undefined && json["errorMsg"] !== "" ? json["errorMsg"]: "Fail to register!" + errorMsg: + json["errorMsg"] !== undefined && json["errorMsg"] !== "" + ? json["errorMsg"] + : "Fail to register!", }); } }; @@ -93,18 +114,37 @@ export function logout() { } export const LIST_USERS = "LIST_USERS"; -export function fetchAllUsers(skip=0, limit=101){ + +export function fetchAllUsers(skip = 0, limit = 101) { return (dispatch) => { return V2.UsersService.getUsersApiV2UsersGet(skip, limit) - .then(json => { + .then((json) => { dispatch({ type: LIST_USERS, users: json, receivedAt: Date.now(), }); }) - .catch(reason => { - dispatch(fetchAllUsers(skip=0, limit=21)); + .catch((reason) => { + dispatch(fetchAllUsers((skip = 0), (limit = 21))); + }); + }; +} + +export const GENERATE_API_KEY = "GENERATE_API_KEY"; + +export function generateApiKey(minutes = 30) { + return (dispatch) => { + return V2.UsersService.generateUserApiKeyApiV2UsersKeysPost(minutes) + .then((json) => { + dispatch({ + type: GENERATE_API_KEY, + apiKey: json, + receivedAt: Date.now(), + }); + }) + .catch((reason) => { + dispatch(generateApiKey((minutes = 30))); }); }; } diff --git a/frontend/src/reducers/user.ts b/frontend/src/reducers/user.ts index 4333f3251..a17f8dbfb 100644 --- a/frontend/src/reducers/user.ts +++ b/frontend/src/reducers/user.ts @@ -1,26 +1,45 @@ -import {SET_USER, LOGIN_ERROR, REGISTER_ERROR, REGISTER_USER} from "../actions/user"; -import {UserState} from "../types/data"; -import {DataAction} from "../types/action"; +import { + GENERATE_API_KEY, + LOGIN_ERROR, + REGISTER_ERROR, + REGISTER_USER, + SET_USER, +} from "../actions/user"; +import { UserState } from "../types/data"; +import { DataAction } from "../types/action"; const defaultState: UserState = { Authorization: null, loginError: false, registerSucceeded: false, errorMsg: "", + apiKey: "", }; const user = (state = defaultState, action: DataAction) => { - switch(action.type) { - case SET_USER: - return Object.assign({}, state, {Authorization: action.Authorization, loginError: false}); - case LOGIN_ERROR: - return Object.assign({}, state, {Authorization: null, loginError: true, errorMsg: action.errorMsg}); - case REGISTER_USER: - return Object.assign({}, state, {registerSucceeded: true}); - case REGISTER_ERROR: - return Object.assign({}, state, {registerSucceeded: false, errorMsg: action.errorMsg}); - default: - return state; + switch (action.type) { + case SET_USER: + return Object.assign({}, state, { + Authorization: action.Authorization, + loginError: false, + }); + case LOGIN_ERROR: + return Object.assign({}, state, { + Authorization: null, + loginError: true, + errorMsg: action.errorMsg, + }); + case REGISTER_USER: + return Object.assign({}, state, { registerSucceeded: true }); + case REGISTER_ERROR: + return Object.assign({}, state, { + registerSucceeded: false, + errorMsg: action.errorMsg, + }); + case GENERATE_API_KEY: + return Object.assign({}, state, { apiKey: action.apiKey }); + default: + return state; } }; diff --git a/frontend/src/types/action.ts b/frontend/src/types/action.ts index fd87b8b7d..149242354 100644 --- a/frontend/src/types/action.ts +++ b/frontend/src/types/action.ts @@ -1,16 +1,24 @@ -import {Dataset, ExtractedMetadata, MetadataJsonld, FilePreview, Folder} from "./data"; import { - GroupOut as Group, - MetadataOut as Metadata, + Dataset, + ExtractedMetadata, + FilePreview, + Folder, + MetadataJsonld, +} from "./data"; +import { + AuthorizationBase, FileOut as FileSummary, FileVersion, - AuthorizationBase, - RoleType, UserOut + GroupAndRole, + GroupOut as Group, + MetadataDefinitionOut as MetadataDefinition, + MetadataOut as Metadata, + RoleType, + UserAndRole, + UserOut, } from "../openapi/v2"; -import {MetadataDefinitionOut as MetadataDefinition} from "../openapi/v2"; -import {GroupAndRole, UserAndRole} from "../openapi/v2"; -import {LIST_USERS} from "../actions/user"; -import {DELETE_GROUP} from "../actions/group"; +import { LIST_USERS } from "../actions/user"; +import { DELETE_GROUP } from "../actions/group"; interface RECEIVE_FILES_IN_DATASET { type: "RECEIVE_FILES_IN_DATASET"; @@ -22,295 +30,300 @@ interface DELETE_FILE { file: FileSummary; } -interface RECEIVE_DATASET_ABOUT{ +interface RECEIVE_DATASET_ABOUT { type: "RECEIVE_DATASET_ABOUT"; about: Dataset; } -interface RECEIVE_DATASET_ROLE{ +interface RECEIVE_DATASET_ROLE { role: AuthorizationBase; type: "RECEIVE_DATASET_ROLE"; } -interface RECEIVE_DATASET_GROUPS_AND_ROLES{ - groupsAndRoles : GroupAndRole[]; +interface RECEIVE_DATASET_GROUPS_AND_ROLES { + groupsAndRoles: GroupAndRole[]; type: "RECEIVE_DATASET_GROUPS_AND_ROLES"; } -interface RECEIVE_DATASET_USERS_AND_ROLES{ - usersAndRoles : UserAndRole[]; +interface RECEIVE_DATASET_USERS_AND_ROLES { + usersAndRoles: UserAndRole[]; type: "RECEIVE_DATASET_USERS_AND_ROLES"; } -interface RECEIVE_FILE_ROLE{ +interface RECEIVE_FILE_ROLE { role: AuthorizationBase; type: "RECEIVE_FILE_ROLE"; } - -interface RECEIVE_DATASETS{ +interface RECEIVE_DATASETS { type: "RECEIVE_DATASETS"; datasets: Dataset[]; } -interface DELETE_DATASET{ +interface DELETE_DATASET { type: "DELETE_DATASET"; dataset: Dataset; } -interface RECEIVE_FILE_EXTRACTED_METADATA{ +interface RECEIVE_FILE_EXTRACTED_METADATA { type: "RECEIVE_FILE_EXTRACTED_METADATA"; extractedMetadata: ExtractedMetadata; } -interface RECEIVE_FILE_METADATA_JSONLD{ - type:"RECEIVE_FILE_METADATA_JSONLD"; +interface RECEIVE_FILE_METADATA_JSONLD { + type: "RECEIVE_FILE_METADATA_JSONLD"; metadataJsonld: MetadataJsonld[]; } -interface RECEIVE_PREVIEWS{ - type:"RECEIVE_PREVIEWS"; +interface RECEIVE_PREVIEWS { + type: "RECEIVE_PREVIEWS"; previews: FilePreview[]; } -interface RECEIVE_VERSIONS{ +interface RECEIVE_VERSIONS { type: "RECEIVE_VERSIONS"; fileVersions: FileVersion[]; } -interface SET_USER{ - type: "SET_USER", - Authorization: string, +interface SET_USER { + type: "SET_USER"; + Authorization: string; } -interface LOGIN_ERROR{ - errorMsg: string, - type: "LOGIN_ERROR", +interface LOGIN_ERROR { + errorMsg: string; + type: "LOGIN_ERROR"; } -interface LOGOUT{ - type: "LOGOUT", - loggedOut: boolean, +interface LOGOUT { + type: "LOGOUT"; + loggedOut: boolean; } -interface RESET_LOGOUT{ - type: "RESET_LOGOUT", - loggedOut: boolean +interface RESET_LOGOUT { + type: "RESET_LOGOUT"; + loggedOut: boolean; } -interface REGISTER_ERROR{ - errorMsg: string, - type: "REGISTER_ERROR" +interface REGISTER_ERROR { + errorMsg: string; + type: "REGISTER_ERROR"; } -interface REGISTER_USER{ - type: "REGISTER_USER" +interface REGISTER_USER { + type: "REGISTER_USER"; } -interface CREATE_DATASET{ - type: "CREATE_DATASET", - dataset: Dataset +interface GENERATE_API_KEY { + type: "GENERATE_API_KEY"; + apiKey: string; } -interface RESET_CREATE_DATASET{ - type: "RESET_CREATE_DATASET", - newDataset: Dataset + +interface CREATE_DATASET { + type: "CREATE_DATASET"; + dataset: Dataset; +} + +interface RESET_CREATE_DATASET { + type: "RESET_CREATE_DATASET"; + newDataset: Dataset; } -interface CREATE_FILE{ - type: "CREATE_FILE", - newFile: File +interface CREATE_FILE { + type: "CREATE_FILE"; + newFile: File; } -interface RESET_CREATE_FILE{ - type: "RESET_CREATE_FILE", - newFile: File - file: FileSummary +interface RESET_CREATE_FILE { + type: "RESET_CREATE_FILE"; + newFile: File; + file: FileSummary; } -interface FAILED{ +interface FAILED { stack: "string"; - type: "FAILED", - reason: string + type: "FAILED"; + reason: string; } interface NOT_FOUND { stack: "string"; - type: "NOT_FOUND", - reason: string + type: "NOT_FOUND"; + reason: string; } -interface RESET_FAILED{ - type: "RESET_FAILED", - reason: string +interface RESET_FAILED { + type: "RESET_FAILED"; + reason: string; } -interface FOLDER_ADDED{ - type: "FOLDER_ADDED", - folder: Folder +interface FOLDER_ADDED { + type: "FOLDER_ADDED"; + folder: Folder; } -interface RECEIVE_FILE_SUMMARY{ - type: "RECEIVE_FILE_SUMMARY", - fileSummary: FileSummary +interface RECEIVE_FILE_SUMMARY { + type: "RECEIVE_FILE_SUMMARY"; + fileSummary: FileSummary; } -interface RECEIVE_DATASET_METADATA{ - type: "RECEIVE_DATASET_METADATA", - metadataList: Metadata[] +interface RECEIVE_DATASET_METADATA { + type: "RECEIVE_DATASET_METADATA"; + metadataList: Metadata[]; } -interface RECEIVE_FILE_METADATA{ - type:"RECEIVE_FILE_METADATA"; +interface RECEIVE_FILE_METADATA { + type: "RECEIVE_FILE_METADATA"; metadataList: Metadata[]; } -interface RECEIVE_FOLDERS_IN_DATASET{ - type: "RECEIVE_FOLDERS_IN_DATASET", - folders: Folder[] +interface RECEIVE_FOLDERS_IN_DATASET { + type: "RECEIVE_FOLDERS_IN_DATASET"; + folders: Folder[]; } -interface UPDATE_DATASET_METADATA{ - type:"UPDATE_DATASET_METADATA", - metadata: Metadata +interface UPDATE_DATASET_METADATA { + type: "UPDATE_DATASET_METADATA"; + metadata: Metadata; } -interface UPDATE_FILE_METADATA{ - type:"UPDATE_FILE_METADATA", - metadata: Metadata +interface UPDATE_FILE_METADATA { + type: "UPDATE_FILE_METADATA"; + metadata: Metadata; } -interface POST_DATASET_METADATA{ - type:"POST_DATASET_METADATA", - metadata: Metadata +interface POST_DATASET_METADATA { + type: "POST_DATASET_METADATA"; + metadata: Metadata; } -interface POST_FILE_METADATA{ - type:"POST_FILE_METADATA", - metadata: Metadata +interface POST_FILE_METADATA { + type: "POST_FILE_METADATA"; + metadata: Metadata; } -interface RECEIVE_METADATA_DEFINITIONS{ - type:"RECEIVE_METADATA_DEFINITIONS", - metadataDefinitionList: MetadataDefinition[] +interface RECEIVE_METADATA_DEFINITIONS { + type: "RECEIVE_METADATA_DEFINITIONS"; + metadataDefinitionList: MetadataDefinition[]; } -interface SAVE_METADATA_DEFINITIONS{ - type:"SAVE_METADATA_DEFINITIONS", - metadataDefinitionList: MetadataDefinition[] +interface SAVE_METADATA_DEFINITIONS { + type: "SAVE_METADATA_DEFINITIONS"; + metadataDefinitionList: MetadataDefinition[]; } -interface DOWNLOAD_FILE{ - type:"DOWNLOAD_FILE" +interface DOWNLOAD_FILE { + type: "DOWNLOAD_FILE"; } -interface DELETE_DATASET_METADATA{ - type: "DELETE_DATASET_METADATA" - metadata: Metadata +interface DELETE_DATASET_METADATA { + type: "DELETE_DATASET_METADATA"; + metadata: Metadata; } -interface DELETE_FILE_METADATA{ - type: "DELETE_FILE_METADATA" - metadata: Metadata +interface DELETE_FILE_METADATA { + type: "DELETE_FILE_METADATA"; + metadata: Metadata; } -interface FOLDER_DELETED{ - type: "FOLDER_DELETED" - folder: Folder +interface FOLDER_DELETED { + type: "FOLDER_DELETED"; + folder: Folder; } -interface GET_FOLDER_PATH{ - type: "GET_FOLDER_PATH" - folderPath: String[] +interface GET_FOLDER_PATH { + type: "GET_FOLDER_PATH"; + folderPath: String[]; } -interface RECEIVE_LISTENERS{ - type: "RECEIVE_LISTENERS" - listeners: [] +interface RECEIVE_LISTENERS { + type: "RECEIVE_LISTENERS"; + listeners: []; } -interface SEARCH_LISTENERS{ - type: "SEARCH_LISTENERS" - listeners: [] +interface SEARCH_LISTENERS { + type: "SEARCH_LISTENERS"; + listeners: []; } -interface RECEIVE_LISTENER_CATEGORIES{ - type: "RECEIVE_LISTENER_CATEGORIES" - categories: [] +interface RECEIVE_LISTENER_CATEGORIES { + type: "RECEIVE_LISTENER_CATEGORIES"; + categories: []; } -interface RECEIVE_LISTENER_LABELS{ - type: "RECEIVE_LISTENER_LABELS" - labels: [] +interface RECEIVE_LISTENER_LABELS { + type: "RECEIVE_LISTENER_LABELS"; + labels: []; } -interface RECEIVE_LISTENER_JOBS{ - type: "RECEIVE_LISTENER_JOBS" - jobs: [] +interface RECEIVE_LISTENER_JOBS { + type: "RECEIVE_LISTENER_JOBS"; + jobs: []; } -interface SUBMIT_FILE_EXTRACTION{ - type: "SUBMIT_FILE_EXTRACTION", - job_id: String +interface SUBMIT_FILE_EXTRACTION { + type: "SUBMIT_FILE_EXTRACTION"; + job_id: String; } -interface SUBMIT_DATASET_EXTRACTION{ - type: "SUBMIT_DATASET_EXTRACTION", - job_id: String +interface SUBMIT_DATASET_EXTRACTION { + type: "SUBMIT_DATASET_EXTRACTION"; + job_id: String; } - -interface FETCH_JOB_SUMMARY{ - type: "FETCH_JOB_SUMMARY" - currJobSummary: []; +interface FETCH_JOB_SUMMARY { + type: "FETCH_JOB_SUMMARY"; + currJobSummary: []; } -interface FETCH_JOB_UPDATES{ - type: "FETCH_JOB_UPDATES" - currJobUpdates: []; +interface FETCH_JOB_UPDATES { + type: "FETCH_JOB_UPDATES"; + currJobUpdates: []; } -interface RECEIVE_GROUPS{ - type: "RECEIVE_GROUPS" +interface RECEIVE_GROUPS { + type: "RECEIVE_GROUPS"; groups: Group[]; } -interface SEARCH_GROUPS{ - type: "SEARCH_GROUPS" +interface SEARCH_GROUPS { + type: "SEARCH_GROUPS"; groups: Group[]; } -interface DELETE_GROUP{ - type: "DELETE_GROUP" +interface DELETE_GROUP { + type: "DELETE_GROUP"; about: Group; } -interface RECEIVE_GROUP_ABOUT{ - type: "RECEIVE_GROUP_ABOUT" +interface RECEIVE_GROUP_ABOUT { + type: "RECEIVE_GROUP_ABOUT"; about: Group; } -interface RECEIVE_GROUP_ROLE{ - type: "RECEIVE_GROUP_ROLE" +interface RECEIVE_GROUP_ROLE { + type: "RECEIVE_GROUP_ROLE"; role: RoleType; } -interface DELETE_GROUP_MEMBER{ +interface DELETE_GROUP_MEMBER { about: Group; type: "DELETE_GROUP_MEMBER"; } -interface ADD_GROUP_MEMBER{ +interface ADD_GROUP_MEMBER { about: Group; type: "ADD_GROUP_MEMBER"; } -interface LIST_USERS{ - type: "LIST_USERS" - users: UserOut[] +interface LIST_USERS { + type: "LIST_USERS"; + users: UserOut[]; } -interface ASSIGN_GROUP_MEMBER_ROLE{ - type: "ASSIGN_GROUP_MEMBER_ROLE" - about: Group + +interface ASSIGN_GROUP_MEMBER_ROLE { + type: "ASSIGN_GROUP_MEMBER_ROLE"; + about: Group; } export type DataAction = @@ -332,6 +345,7 @@ export type DataAction = | LOGOUT | REGISTER_ERROR | REGISTER_USER + | GENERATE_API_KEY | CREATE_DATASET | RESET_CREATE_DATASET | CREATE_FILE @@ -359,10 +373,10 @@ export type DataAction = | RECEIVE_LISTENER_CATEGORIES | RECEIVE_LISTENER_LABELS | RECEIVE_LISTENER_JOBS - | SUBMIT_FILE_EXTRACTION - | SUBMIT_DATASET_EXTRACTION - | FETCH_JOB_SUMMARY - | FETCH_JOB_UPDATES + | SUBMIT_FILE_EXTRACTION + | SUBMIT_DATASET_EXTRACTION + | FETCH_JOB_SUMMARY + | FETCH_JOB_UPDATES | RECEIVE_GROUPS | SEARCH_GROUPS | DELETE_GROUP @@ -373,5 +387,4 @@ export type DataAction = | ASSIGN_GROUP_MEMBER_ROLE | LIST_USERS | RECEIVE_DATASET_GROUPS_AND_ROLES - | RECEIVE_DATASET_USERS_AND_ROLES - ; + | RECEIVE_DATASET_USERS_AND_ROLES; diff --git a/frontend/src/types/data.ts b/frontend/src/types/data.ts index 9d3041d80..6ccecb314 100644 --- a/frontend/src/types/data.ts +++ b/frontend/src/types/data.ts @@ -1,15 +1,22 @@ import { + AuthorizationBase, + EventListenerJob, + FileOut as FileSummary, + FileVersion, + FolderOut, + GroupAndRole, + GroupOut, MetadataDefinitionOut, MetadataOut as Metadata, - FileOut as FileSummary, - FileVersion, FolderOut, EventListenerJob, AuthorizationBase, - GroupOut, RoleType, GroupAndRole, UserAndRole, UserOut + RoleType, + UserAndRole, + UserOut, } from "../openapi/v2"; export interface Dataset { name: string; description: string; - id:string; + id: string; author: Author; created: string | Date; modified: string | Date; @@ -26,6 +33,7 @@ export interface Extractor { id: string; parameters: any; } + export interface Listener { name: string; description: string; @@ -36,168 +44,173 @@ export interface Listener { export interface Author { id: string; email: string; - "first_name": string|null; - "last_name": string|null; + first_name: string | null; + last_name: string | null; } export interface Folder { id: string; name: string; author: Author; - "parent_folder": string|null; + parent_folder: string | null; } export interface FileMetadata { id: string; "content-type": string; - size:number; + size: number; created: string | Date; name: string; creator: Author; status: string; filedescription: string; - thumbnail:string; - downloads:number; - views:number; + thumbnail: string; + downloads: number; + views: number; version: string; } -export interface FileMetadataList{ +export interface FileMetadataList { id: string; metadata: FileMetadata; } -export interface Preview{ - "p_id": string; - "pv_route": string; - "pv_id": string; - "p_path": string; - "pv_contenttype": string; +export interface Preview { + p_id: string; + pv_route: string; + pv_id: string; + p_path: string; + pv_contenttype: string; } -export interface FilePreview{ - "file_id": string; +export interface FilePreview { + file_id: string; previews: Preview[]; } -export interface PreviewConfiguration{ +export interface PreviewConfiguration { previewType: string; - url:string; - fileid:string; - previewer:string; - fileType:string; - resource:string | null; + url: string; + fileid: string; + previewer: string; + fileType: string; + resource: string | null; } -export interface Path{ +export interface Path { name: string; id: string; - type:string + type: string; } -export interface ExtractedMetadata{ - filename:string; +export interface ExtractedMetadata { + filename: string; } -export interface MetadataJsonld{ - "id":string; - "@context": (Context|string)[]; - agent:Agent; - "attached_to": AttatchTo; +export interface MetadataJsonld { + id: string; + "@context": (Context | string)[]; + agent: Agent; + attached_to: AttatchTo; content: any; - "created_at": string | Date; + created_at: string | Date; } -interface Context{ - database:string; - scan:string; +interface Context { + database: string; + scan: string; } -interface Agent{ +interface Agent { "@type": string; - "extractor_id": string; - name: string + extractor_id: string; + name: string; } -interface AttatchTo{ - "resource_type": string; +interface AttatchTo { + resource_type: string; url: string; } -export interface Thumbnail{ +export interface Thumbnail { id: string; thumbnail: string; } -export interface DatasetState{ +export interface DatasetState { files: FileSummary[]; datasets: Dataset[]; newDataset: Dataset; newFile: FileSummary; about: Dataset; - datasetRole: AuthorizationBase + datasetRole: AuthorizationBase; groupsAndRoles: GroupAndRole[]; usersAndRoles: UserAndRole[]; } -export interface ListenerState{ + +export interface ListenerState { listeners: Listener[]; categories: string[]; labels: string[]; jobs: EventListenerJob[]; - currJobUpdates: EventListenerJob[]; - currJobSummary: JobSummary[]; - currJobId: string; + currJobUpdates: EventListenerJob[]; + currJobSummary: JobSummary[]; + currJobId: string; } -export interface GroupState{ + +export interface GroupState { groups: GroupOut[]; about: GroupOut; role: RoleType; users: UserOut[]; } -export interface MetadataState{ - metadataDefinitionList: MetadataDefinitionOut[], - datasetMetadataList: Metadata[], - fileMetadataList: Metadata[], + +export interface MetadataState { + metadataDefinitionList: MetadataDefinitionOut[]; + datasetMetadataList: Metadata[]; + fileMetadataList: Metadata[]; } -export interface FileState{ + +export interface FileState { fileSummary: FileSummary; extractedMetadata: ExtractedMetadata; metadataJsonld: MetadataJsonld[]; previews: FilePreview[]; fileVersions: FileVersion[]; - fileRole: AuthorizationBase + fileRole: AuthorizationBase; } -export interface UserState{ +export interface UserState { Authorization: string | null; loginError: boolean; registerSucceeded: boolean; errorMsg: string; + apiKey: string; } -export interface ErrorState{ +export interface ErrorState { stack: string; reason: string; loggedOut: boolean; } -export interface FolderState{ +export interface FolderState { folders: FolderOut[]; folderPath: String[]; } -export interface JobSummary{ - id?: string; - job_id: string; - status: string; - timestamp: string; +export interface JobSummary { + id?: string; + job_id: string; + status: string; + timestamp: string; } export interface RootState { metadata: MetadataState; error: ErrorState; - file:FileState; - dataset:DatasetState; + file: FileState; + dataset: DatasetState; listener: ListenerState; group: GroupState; user: UserState; From d90ca645c135e160a44afb6f213654ea8bb95694 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Wed, 19 Apr 2023 09:25:57 -0500 Subject: [PATCH 2/8] API key modal --- frontend/src/components/users/ApiKeyModal.tsx | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 frontend/src/components/users/ApiKeyModal.tsx diff --git a/frontend/src/components/users/ApiKeyModal.tsx b/frontend/src/components/users/ApiKeyModal.tsx new file mode 100644 index 000000000..eca184109 --- /dev/null +++ b/frontend/src/components/users/ApiKeyModal.tsx @@ -0,0 +1,81 @@ +import React, { useState } from "react"; +import { + Button, + Container, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + MenuItem, + Select, +} from "@mui/material"; +import { generateApiKey as generateApiKeyAction } from "../../actions/user"; +import { useDispatch, useSelector } from "react-redux"; +import { RootState } from "../../types/data"; +import { ClowderButton } from "../styledComponents/ClowderButton"; +import { ClowderMetadataTextField } from "../styledComponents/ClowderMetadataTextField"; + +export default function ApiKey() { + const dispatch = useDispatch(); + const generateApiKey = (minutes: number) => + dispatch(generateApiKeyAction(minutes)); + const apiKey = useSelector((state: RootState) => state.user.apiKey); + + const [open, setOpen] = useState(false); + const [minutes, setMinutes] = useState(30); + + const handleClose = () => { + setOpen(false); + }; + + const handleGenerate = () => { + generateApiKey(minutes); + }; + + const handleCopy = () => {}; + + return ( + + + Your API Key + + {apiKey ? ( + <> + + {apiKey} + + + Generate API Key + + + ) : ( + <> + + + Generate API Key + + + )} + + + + + + + ); +} From dbc414104e27c425d89448fe2afc26532af30019 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Wed, 19 Apr 2023 10:26:14 -0500 Subject: [PATCH 3/8] topbar icons --- frontend/src/components/Layout.tsx | 197 ++++++++++++++++++++--------- frontend/src/utils/common.js | 14 ++ 2 files changed, 150 insertions(+), 61 deletions(-) diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index 789f81082..580dd7244 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -1,10 +1,10 @@ import * as React from "react"; -import {useEffect} from "react"; -import {styled, useTheme} from "@mui/material/styles"; +import { useEffect } from "react"; +import { styled, useTheme } from "@mui/material/styles"; import Box from "@mui/material/Box"; import Drawer from "@mui/material/Drawer"; import CssBaseline from "@mui/material/CssBaseline"; -import MuiAppBar, {AppBarProps as MuiAppBarProps} from "@mui/material/AppBar"; +import MuiAppBar, { AppBarProps as MuiAppBarProps } from "@mui/material/AppBar"; import Toolbar from "@mui/material/Toolbar"; import List from "@mui/material/List"; import Divider from "@mui/material/Divider"; @@ -17,27 +17,31 @@ import ListItem from "@mui/material/ListItem"; import ListItemButton from "@mui/material/ListItemButton"; import ListItemIcon from "@mui/material/ListItemIcon"; import ListItemText from "@mui/material/ListItemText"; -import {Link} from "@mui/material"; -import {Link as RouterLink, useLocation} from "react-router-dom"; -import {useSelector} from "react-redux"; -import {RootState} from "../types/data"; -import {AddBox, Create, Explore, Group} from "@material-ui/icons"; -import {EmbeddedSearch} from "./search/EmbeddedSearch"; -import {searchTheme} from "../theme"; -import {ReactiveBase} from "@appbaseio/reactivesearch"; +import { Link, Menu, MenuItem, MenuList } from "@mui/material"; +import { Link as RouterLink, useLocation } from "react-router-dom"; +import { useSelector } from "react-redux"; +import { RootState } from "../types/data"; +import { AddBox, Create, Explore } from "@material-ui/icons"; +import { searchTheme } from "../theme"; +import { ReactiveBase } from "@appbaseio/reactivesearch"; import Cookies from "universal-cookie"; import config from "../app.config"; import HistoryIcon from "@mui/icons-material/History"; import { InputSearchBox } from "./search/InputSearchBox"; import GroupIcon from "@mui/icons-material/Group"; +import Gravatar from "react-gravatar"; +import PersonIcon from "@mui/icons-material/Person"; +import { getCurrEmail } from "../utils/common"; +import VpnKeyIcon from "@mui/icons-material/VpnKey"; +import LogoutIcon from "@mui/icons-material/Logout"; const cookies = new Cookies(); const drawerWidth = 240; -const Main = styled("main", {shouldForwardProp: (prop) => prop !== "open"})<{ +const Main = styled("main", { shouldForwardProp: (prop) => prop !== "open" })<{ open?: boolean; -}>(({theme, open}) => ({ +}>(({ theme, open }) => ({ flexGrow: 1, padding: theme.spacing(3), transition: theme.transitions.create("margin", { @@ -54,11 +58,10 @@ const Main = styled("main", {shouldForwardProp: (prop) => prop !== "open"})<{ }), })); - -const SearchDiv = styled("div")(({theme}) => ({ +const SearchDiv = styled("div")(({ theme }) => ({ position: "relative", marginLeft: theme.spacing(3), - marginBottom: "-5px", // to compoensate the tags div + marginBottom: "-5px", // to compoensate the tags div width: "50%", })); @@ -68,7 +71,7 @@ interface AppBarProps extends MuiAppBarProps { const AppBar = styled(MuiAppBar, { shouldForwardProp: (prop) => prop !== "open", -})(({theme, open}) => ({ +})(({ theme, open }) => ({ transition: theme.transitions.create(["margin", "width"], { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.leavingScreen, @@ -83,7 +86,7 @@ const AppBar = styled(MuiAppBar, { }), })); -const DrawerHeader = styled("div")(({theme}) => ({ +const DrawerHeader = styled("div")(({ theme }) => ({ display: "flex", alignItems: "center", padding: theme.spacing(0, 1), @@ -99,13 +102,15 @@ const link = { m: 2, }; -const headers = {"Authorization": cookies.get("Authorization")}; +const headers = { Authorization: cookies.get("Authorization") }; export default function PersistentDrawerLeft(props) { - const {children} = props; + const { children } = props; const theme = useTheme(); const [open, setOpen] = React.useState(false); const [embeddedSearchHidden, setEmbeddedSearchHidden] = React.useState(false); + const [anchorEl, setAnchorEl] = React.useState(null); + const isMenuOpen = Boolean(anchorEl); const handleDrawerOpen = () => { setOpen(true); @@ -115,6 +120,14 @@ export default function PersistentDrawerLeft(props) { setOpen(false); }; + const handleProfileMenuOpen = (event) => { + setAnchorEl(event.currentTarget); + }; + + const handleProfileMenuClose = () => { + setAnchorEl(null); + }; + const location = useLocation(); useEffect(() => { @@ -123,11 +136,10 @@ export default function PersistentDrawerLeft(props) { } else { setEmbeddedSearchHidden(false); } - }, [location]) + }, [location]); const loggedOut = useSelector((state: RootState) => state.error.loggedOut); - // @ts-ignore return ( // Wrap reactive search base on the most outside component @@ -137,8 +149,8 @@ export default function PersistentDrawerLeft(props) { headers={headers} theme={searchTheme} > - - + + - + - + {/*for searching*/} - - - { - loggedOut ? - <> - Register - Login - - : - Logout - } + + + {loggedOut ? ( + <> + + Register + + + Login + + + ) : ( + + {getCurrEmail() !== undefined ? ( + + ) : ( + + )} + + )} + {/*Profile menu*/} + + + + + + + API Key + + + + + + + Log Out + + + + {/*side drawer*/} - {theme.direction === "ltr" ? : } + {theme.direction === "ltr" ? ( + + ) : ( + + )} - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - +
- + {children}
diff --git a/frontend/src/utils/common.js b/frontend/src/utils/common.js index 0423b5ca6..80d44ac1b 100644 --- a/frontend/src/utils/common.js +++ b/frontend/src/utils/common.js @@ -1,6 +1,7 @@ import Cookies from "universal-cookie"; import { V2 } from "../openapi"; import { format } from "date-fns"; +import jwt_decode from "jwt-decode"; const cookies = new Cookies(); @@ -81,6 +82,19 @@ export function parseDate(dateString) { export const capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1).toLowerCase(); +// Get Current Email from JWT +export const getCurrEmail = () => { + const authorization = cookies.get("Authorization") || "Bearer none"; + if ( + authorization && + authorization !== "" && + authorization.split(" ").length > 0 + ) { + let userInfo = jwt_decode(authorization.split(" ")[1]); + return userInfo["email"]; + } +}; + // get current username // export function getCurrUsername(){ // if (process.env.DEPLOY_ENV === "local"){ From f5cc01a921c2dd4f0472c6bf82216a7250875b38 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Wed, 19 Apr 2023 10:38:19 -0500 Subject: [PATCH 4/8] add connection to popup --- frontend/src/components/Layout.tsx | 395 +++++++++--------- frontend/src/components/users/ApiKeyModal.tsx | 16 +- 2 files changed, 213 insertions(+), 198 deletions(-) diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index 580dd7244..d3175c92c 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -22,10 +22,7 @@ import { Link as RouterLink, useLocation } from "react-router-dom"; import { useSelector } from "react-redux"; import { RootState } from "../types/data"; import { AddBox, Create, Explore } from "@material-ui/icons"; -import { searchTheme } from "../theme"; -import { ReactiveBase } from "@appbaseio/reactivesearch"; import Cookies from "universal-cookie"; -import config from "../app.config"; import HistoryIcon from "@mui/icons-material/History"; import { InputSearchBox } from "./search/InputSearchBox"; import GroupIcon from "@mui/icons-material/Group"; @@ -34,6 +31,7 @@ import PersonIcon from "@mui/icons-material/Person"; import { getCurrEmail } from "../utils/common"; import VpnKeyIcon from "@mui/icons-material/VpnKey"; import LogoutIcon from "@mui/icons-material/Logout"; +import { ApiKeyModal } from "./users/ApiKeyModal"; const cookies = new Cookies(); @@ -110,6 +108,7 @@ export default function PersistentDrawerLeft(props) { const [open, setOpen] = React.useState(false); const [embeddedSearchHidden, setEmbeddedSearchHidden] = React.useState(false); const [anchorEl, setAnchorEl] = React.useState(null); + const [apiKeyModalOpen, setApiKeyModalOpen] = React.useState(false); const isMenuOpen = Boolean(anchorEl); const handleDrawerOpen = () => { @@ -143,201 +142,211 @@ export default function PersistentDrawerLeft(props) { // @ts-ignore return ( // Wrap reactive search base on the most outside component - - - - - - - - - - - + // + + + + + + + + + + - {/*for searching*/} - - - - {loggedOut ? ( - <> - - Register - - - Login - - - ) : ( - - {getCurrEmail() !== undefined ? ( - - ) : ( - - )} - - )} - - - - {/*Profile menu*/} - - - - {/*side drawer*/} - - - - {theme.direction === "ltr" ? ( - - ) : ( - - )} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - {children} -
-
-
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + {children} + + {/*modals*/} + +
+
+ //
); } diff --git a/frontend/src/components/users/ApiKeyModal.tsx b/frontend/src/components/users/ApiKeyModal.tsx index eca184109..a53412329 100644 --- a/frontend/src/components/users/ApiKeyModal.tsx +++ b/frontend/src/components/users/ApiKeyModal.tsx @@ -15,17 +15,23 @@ import { RootState } from "../../types/data"; import { ClowderButton } from "../styledComponents/ClowderButton"; import { ClowderMetadataTextField } from "../styledComponents/ClowderMetadataTextField"; -export default function ApiKey() { +type ApiKeyModalProps = { + apiKeyModalOpen: boolean; + setApiKeyModalOpen: any; +}; + +export const ApiKeyModal = (props: ApiKeyModalProps) => { + const { apiKeyModalOpen, setApiKeyModalOpen } = props; + const dispatch = useDispatch(); const generateApiKey = (minutes: number) => dispatch(generateApiKeyAction(minutes)); const apiKey = useSelector((state: RootState) => state.user.apiKey); - const [open, setOpen] = useState(false); const [minutes, setMinutes] = useState(30); const handleClose = () => { - setOpen(false); + setApiKeyModalOpen(false); }; const handleGenerate = () => { @@ -36,7 +42,7 @@ export default function ApiKey() { return ( - + Your API Key {apiKey ? ( @@ -78,4 +84,4 @@ export default function ApiKey() { ); -} +}; From 09557041ebd8f19070db663092f8aa10574d48f6 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Wed, 19 Apr 2023 10:53:49 -0500 Subject: [PATCH 5/8] temp save --- frontend/src/components/users/ApiKeyModal.tsx | 82 +++++++++++-------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/frontend/src/components/users/ApiKeyModal.tsx b/frontend/src/components/users/ApiKeyModal.tsx index a53412329..50ce32801 100644 --- a/frontend/src/components/users/ApiKeyModal.tsx +++ b/frontend/src/components/users/ApiKeyModal.tsx @@ -6,13 +6,14 @@ import { DialogActions, DialogContent, DialogTitle, + FormControl, + InputLabel, MenuItem, Select, } from "@mui/material"; import { generateApiKey as generateApiKeyAction } from "../../actions/user"; import { useDispatch, useSelector } from "react-redux"; import { RootState } from "../../types/data"; -import { ClowderButton } from "../styledComponents/ClowderButton"; import { ClowderMetadataTextField } from "../styledComponents/ClowderMetadataTextField"; type ApiKeyModalProps = { @@ -44,43 +45,54 @@ export const ApiKeyModal = (props: ApiKeyModalProps) => { Your API Key - - {apiKey ? ( - <> + + {apiKey ? ( + <> + {apiKey} - - Generate API Key - - - ) : ( - <> - - - Generate API Key - - - )} - - - - + + + + + + + ) : ( + <> + + + + Expire after + + + + + + + + + + )} ); From e011eb09147ac213989fe39570f48bbb8c4403da Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Wed, 19 Apr 2023 11:58:40 -0500 Subject: [PATCH 6/8] styling and add copy to clipboard --- frontend/src/components/users/ApiKeyModal.tsx | 112 +++++++++--------- frontend/src/reducers/user.ts | 2 +- 2 files changed, 59 insertions(+), 55 deletions(-) diff --git a/frontend/src/components/users/ApiKeyModal.tsx b/frontend/src/components/users/ApiKeyModal.tsx index 50ce32801..dc6537f9b 100644 --- a/frontend/src/components/users/ApiKeyModal.tsx +++ b/frontend/src/components/users/ApiKeyModal.tsx @@ -1,7 +1,6 @@ import React, { useState } from "react"; import { Button, - Container, Dialog, DialogActions, DialogContent, @@ -15,6 +14,8 @@ import { generateApiKey as generateApiKeyAction } from "../../actions/user"; import { useDispatch, useSelector } from "react-redux"; import { RootState } from "../../types/data"; import { ClowderMetadataTextField } from "../styledComponents/ClowderMetadataTextField"; +import { ClowderFootnote } from "../styledComponents/ClowderFootnote"; +// import { CopyToClipboard } from "react-copy-to-clipboard"; type ApiKeyModalProps = { apiKeyModalOpen: boolean; @@ -41,59 +42,62 @@ export const ApiKeyModal = (props: ApiKeyModalProps) => { const handleCopy = () => {}; - return ( - - - Your API Key + const handleExpirationChange = (e) => { + setMinutes(e.target.value); + }; - {apiKey ? ( - <> - - - {apiKey} - - - - - - - - ) : ( - <> - - - - Expire after - - - - - - - - - - )} - - + return ( + + Your API Key + {apiKey ? ( + <> + + + Make sure you copy your API key now as you will not be able to see + this again. + + + + + {/**/} + + {/**/} + + + + ) : ( + <> + + Your API key will expire + + After + + + + + + + + + )} + ); }; diff --git a/frontend/src/reducers/user.ts b/frontend/src/reducers/user.ts index a17f8dbfb..7f32f5683 100644 --- a/frontend/src/reducers/user.ts +++ b/frontend/src/reducers/user.ts @@ -13,7 +13,7 @@ const defaultState: UserState = { loginError: false, registerSucceeded: false, errorMsg: "", - apiKey: "", + apiKey: "e54b85cd-3e8e-4497-9a8b-4032ada7e632", }; const user = (state = defaultState, action: DataAction) => { From 301538eec712d5f256c7dd5040d108c7635241b8 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Wed, 19 Apr 2023 13:51:56 -0500 Subject: [PATCH 7/8] add copy to clipboard capability --- frontend/package-lock.json | 26 +++++++++++++++++++ frontend/package.json | 1 + frontend/src/components/users/ApiKeyModal.tsx | 8 +++--- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index fa6da5729..79a351b07 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -31,6 +31,7 @@ "pretty-bytes": "^6.0.0", "prop-types": "^15.7.2", "react": "^17.0.2", + "react-copy-to-clipboard": "^5.1.0", "react-dom": "^17.0.2", "react-gravatar": "^2.6.3", "react-loading-overlay-ts": "^1.0.4", @@ -7584,6 +7585,14 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "dev": true }, + "node_modules/copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, "node_modules/core-js": { "version": "3.29.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.29.1.tgz", @@ -16856,6 +16865,18 @@ "react-dom": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0" } }, + "node_modules/react-copy-to-clipboard": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz", + "integrity": "sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==", + "dependencies": { + "copy-to-clipboard": "^3.3.1", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": "^15.3.0 || 16 || 17 || 18" + } + }, "node_modules/react-day-picker": { "version": "7.4.10", "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-7.4.10.tgz", @@ -19577,6 +19598,11 @@ "node": ">=8.0" } }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index e344d77cf..467f67b20 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -54,6 +54,7 @@ "pretty-bytes": "^6.0.0", "prop-types": "^15.7.2", "react": "^17.0.2", + "react-copy-to-clipboard": "^5.1.0", "react-dom": "^17.0.2", "react-gravatar": "^2.6.3", "react-loading-overlay-ts": "^1.0.4", diff --git a/frontend/src/components/users/ApiKeyModal.tsx b/frontend/src/components/users/ApiKeyModal.tsx index dc6537f9b..e870bab1f 100644 --- a/frontend/src/components/users/ApiKeyModal.tsx +++ b/frontend/src/components/users/ApiKeyModal.tsx @@ -15,7 +15,7 @@ import { useDispatch, useSelector } from "react-redux"; import { RootState } from "../../types/data"; import { ClowderMetadataTextField } from "../styledComponents/ClowderMetadataTextField"; import { ClowderFootnote } from "../styledComponents/ClowderFootnote"; -// import { CopyToClipboard } from "react-copy-to-clipboard"; +import { CopyToClipboard } from "react-copy-to-clipboard"; type ApiKeyModalProps = { apiKeyModalOpen: boolean; @@ -63,9 +63,9 @@ export const ApiKeyModal = (props: ApiKeyModalProps) => { /> - {/**/} - - {/**/} + + + From 2cd70b6b66fe8c5c3cc6be763ee35c306d0850ae Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Wed, 19 Apr 2023 13:56:29 -0500 Subject: [PATCH 8/8] reset after creation --- frontend/src/actions/user.js | 11 +++++++++++ frontend/src/components/users/ApiKeyModal.tsx | 9 ++++++--- frontend/src/reducers/user.ts | 5 ++++- frontend/src/types/action.ts | 6 ++++++ 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/frontend/src/actions/user.js b/frontend/src/actions/user.js index 5d050b298..2841f3879 100644 --- a/frontend/src/actions/user.js +++ b/frontend/src/actions/user.js @@ -148,3 +148,14 @@ export function generateApiKey(minutes = 30) { }); }; } + +export const RESET_API_KEY = "RESET_API_KEY"; + +export function resetApiKey() { + return (dispatch) => { + dispatch({ + type: RESET_API_KEY, + receivedAt: Date.now(), + }); + }; +} diff --git a/frontend/src/components/users/ApiKeyModal.tsx b/frontend/src/components/users/ApiKeyModal.tsx index e870bab1f..b03ffb5fc 100644 --- a/frontend/src/components/users/ApiKeyModal.tsx +++ b/frontend/src/components/users/ApiKeyModal.tsx @@ -10,7 +10,10 @@ import { MenuItem, Select, } from "@mui/material"; -import { generateApiKey as generateApiKeyAction } from "../../actions/user"; +import { + generateApiKey as generateApiKeyAction, + resetApiKey as resetApiKeyAction, +} from "../../actions/user"; import { useDispatch, useSelector } from "react-redux"; import { RootState } from "../../types/data"; import { ClowderMetadataTextField } from "../styledComponents/ClowderMetadataTextField"; @@ -28,11 +31,13 @@ export const ApiKeyModal = (props: ApiKeyModalProps) => { const dispatch = useDispatch(); const generateApiKey = (minutes: number) => dispatch(generateApiKeyAction(minutes)); + const resetApiKey = () => dispatch(resetApiKeyAction()); const apiKey = useSelector((state: RootState) => state.user.apiKey); const [minutes, setMinutes] = useState(30); const handleClose = () => { + resetApiKey(); setApiKeyModalOpen(false); }; @@ -40,8 +45,6 @@ export const ApiKeyModal = (props: ApiKeyModalProps) => { generateApiKey(minutes); }; - const handleCopy = () => {}; - const handleExpirationChange = (e) => { setMinutes(e.target.value); }; diff --git a/frontend/src/reducers/user.ts b/frontend/src/reducers/user.ts index 7f32f5683..a0f2a76c0 100644 --- a/frontend/src/reducers/user.ts +++ b/frontend/src/reducers/user.ts @@ -3,6 +3,7 @@ import { LOGIN_ERROR, REGISTER_ERROR, REGISTER_USER, + RESET_API_KEY, SET_USER, } from "../actions/user"; import { UserState } from "../types/data"; @@ -13,7 +14,7 @@ const defaultState: UserState = { loginError: false, registerSucceeded: false, errorMsg: "", - apiKey: "e54b85cd-3e8e-4497-9a8b-4032ada7e632", + apiKey: "", }; const user = (state = defaultState, action: DataAction) => { @@ -38,6 +39,8 @@ const user = (state = defaultState, action: DataAction) => { }); case GENERATE_API_KEY: return Object.assign({}, state, { apiKey: action.apiKey }); + case RESET_API_KEY: + return Object.assign({}, state, { apiKey: "" }); default: return state; } diff --git a/frontend/src/types/action.ts b/frontend/src/types/action.ts index 149242354..0505fc6ed 100644 --- a/frontend/src/types/action.ts +++ b/frontend/src/types/action.ts @@ -119,6 +119,11 @@ interface GENERATE_API_KEY { apiKey: string; } +interface RESET_API_KEY { + type: "RESET_API_KEY"; + apiKey: string; +} + interface CREATE_DATASET { type: "CREATE_DATASET"; dataset: Dataset; @@ -346,6 +351,7 @@ export type DataAction = | REGISTER_ERROR | REGISTER_USER | GENERATE_API_KEY + | RESET_API_KEY | CREATE_DATASET | RESET_CREATE_DATASET | CREATE_FILE