diff --git a/controller/components/Layout/index.tsx b/controller/components/Layout/index.tsx index de830eb5..ae617727 100644 --- a/controller/components/Layout/index.tsx +++ b/controller/components/Layout/index.tsx @@ -4,8 +4,7 @@ import { PAGE_ADMIN, PAGE_CALENDAR, PAGE_START_TEST, - PAGE_TEST_HISTORY, - PAGE_YAML_WRITER + PAGE_TEST_HISTORY } from "../../types"; import { Button, LinkButton, defaultButtonTheme } from "../LinkButton"; import React, { useEffect } from "react"; @@ -20,6 +19,8 @@ import getConfig from "next/config"; const publicRuntimeConfig: any = getConfig() && getConfig().publicRuntimeConfig ? getConfig().publicRuntimeConfig : process.env; const HIDE_ENVIRONMENT: unknown = publicRuntimeConfig.HIDE_ENVIRONMENT; +export const PAGE_YAML_WRITER: string = "https://familysearch.github.io/pewpew/results-viewer-react/yaml.html"; + export type OtherControllers = RecordCalendar - Yaml Writer + Yaml Writer {authPermission === AuthPermission.Admin && diff --git a/controller/components/LinkButton/index.tsx b/controller/components/LinkButton/index.tsx index 9d2b0381..dd0716dd 100644 --- a/controller/components/LinkButton/index.tsx +++ b/controller/components/LinkButton/index.tsx @@ -24,6 +24,7 @@ export const defaultButtonTheme: LinkButtonTheme = { buttonFontSize: ".8rem", bu export interface LinkButtonProps { name?: string; href: string; + target?: string; title?: string; onClick?: (event: React.MouseEvent) => void; theme?: LinkButtonTheme; @@ -33,6 +34,7 @@ export interface LinkButtonProps { export const LinkButton = ({ name, href, + target, title, onClick, theme, @@ -41,8 +43,8 @@ export const LinkButton = ({ return ( {/* https://nextjs.org/docs/messages/invalid-new-link-with-extra-anchor */} - - + + diff --git a/controller/components/Modal/index.tsx b/controller/components/Modal/index.tsx deleted file mode 100644 index 31738caa..00000000 --- a/controller/components/Modal/index.tsx +++ /dev/null @@ -1,214 +0,0 @@ -import { LogLevel, log } from "../../pages/api/util/log"; -import React, { - Ref, - forwardRef, - useEffect, - useImperativeHandle, - useState -} from "react"; -import styled from "styled-components"; - -const ModalDiv = styled.div` - width: 70vw; - position: fixed; - background: white; - border: 2px solid rgb(97, 97, 97); - transition: 1.1s ease-out; - box-shadow: -2rem 2rem 2rem rgba(0, 0, 0, 0.2); - filter: blur(0); - transform: scale(1); - opacity: 1; - visibility: visible; - left: 0; - right: 0; - margin: 0 auto; - top: 15%; - z-index: 10; - background-color: rgb(61, 64, 67); -`; -const ModalTitle = styled.h2` - border-bottom: 1px solid #ccc; - padding: 1rem; - margin: 0; -`; -const ModalContent = styled.div` - padding: 1rem; - max-height: 450px; - overflow: auto; -`; -const ModalActions = styled.div` - border-top: 1px solid #ccc; - padding: 0.5rem 1rem; -`; - -const ModalStyle = styled.div` - #modal.off { - opacity: 0; - visibility: hidden; - filter: blur(8px); - transform: scale(0.33); - box-shadow: 1rem 0 0 rgba(0, 0, 0, 0.2); - } - @supports (offset-rotation: 0deg) and - (offset-rotation: 0deg) and - (offset-path: path("M 250,100 S -300,500 -700,-200")) { - #modal.off { - offset-distance: 100%; - } - } - @media (prefers-reduced-motion) { - #modal { - offset-path: none; - } - } - #submitBtn, #closeBtn { - border-radius: 5px; - padding: 0.5rem 1rem; - font-size: 0.8rem; - line-height: 1; - cursor: pointer; - margin-right: 5px; - } - #submitBtn:disabled { - cursor: default; - color: black; - border: 2px solid gray; - background: none; - } - #closeBtn { - background: none; - border: 2px solid gray; - } - #centered-toggle-button { - position: absolute; - } -`; - -interface ModalProps { - title?: string; - onClose?: (event?: React.MouseEvent) => void; - closeText?: string; - onSubmit?: (event?: React.MouseEvent) => Promise; - submitText?: string; - children?: React.ReactNode; - isReady?: boolean; -} - -export interface ModalObject { - isOpen: () => boolean; - openModal: () => void; - closeModal: () => void; - submitModal: () => void; -} - -/** - * Creates a useEffect eventListener to catch the Escape key to close the modal - * @param modalRef {React.MutableRefObject} reference returned by a `useRef` call - * @param logLevel {LogLevel} Optional: logging level when logging the event listener. Used for testing - */ - export const useEffectModal = ( - modalRef: React.MutableRefObject, - logLevel: LogLevel = LogLevel.DEBUG -) => { - useEffect(() => { - const close = (e: KeyboardEvent) => { - if (modalRef.current?.isOpen() && e.key === "Escape") { - log("keyDown: " + e.key, logLevel, { e }); - modalRef.current?.closeModal(); - } - }; - log("addEventListener keyDown:", logLevel); - window.addEventListener("keydown", close); - return () => { - log("removeEventListener keyDown:", logLevel); - window.removeEventListener("keydown", close); - }; - }, []); -}; - -// Modal component for usage -export const Modal = forwardRef(({ - title, - onClose, - closeText = "close", - onSubmit, - submitText = "submit", - children, - isReady -}: ModalProps, ref: Ref) => { - const [display, setDisplay] = useState(false); - let windowOffset: number = 0; - - useImperativeHandle(ref, () => { - return { - isOpen: () => display, - openModal: () => open(), - closeModal: () => close(), - submitModal: () => submit() - }; - }); - - const open = () => { - windowOffset = window.scrollY; - const root = document.getElementById("root"); - if (root) { - root.style.position = "fixed"; - root.style.top = `-${windowOffset}px`; - } else { - log("Cannot find element #root", LogLevel.DEBUG); - } - setDisplay(true); - }; - - const close = (event?: React.MouseEvent) => { - const root = document.getElementById("root"); - if (root) { - const scrollY = root.style.top; - root.style.position = ""; - root.style.top = ""; - window.scrollTo(0, parseInt(scrollY || "0", 10) * -1); - } else { - log("Cannot find element #root", LogLevel.DEBUG); - } - if (onClose) { - onClose(event); - } - setDisplay(false); - }; - - const submit = (event?: React.MouseEvent) => { - const scrollY = document.getElementById("root")!.style.top; - document.getElementById("root")!.style.position = ""; - document.getElementById("root")!.style.top = ""; - window.scrollTo(0, parseInt(scrollY || "0", 10) * -1); - if (onSubmit) { - onSubmit(event).finally(() => setDisplay(false)); - } else { - setDisplay(false); - } - }; - - if (!display) { - return null; - } - return ( - - - {title && {title}} - {children}{/* Any elements that are children of modal will be rendered here */} - - {onSubmit && - - } - - - - - ); -}); - -export default Modal; diff --git a/controller/components/Modal/story.tsx b/controller/components/Modal/story.tsx deleted file mode 100644 index e36a9386..00000000 --- a/controller/components/Modal/story.tsx +++ /dev/null @@ -1,210 +0,0 @@ -import { LogLevel, log } from "../../pages/api/util/log"; -import { Modal, ModalObject, useEffectModal } from "."; -import React, { useRef, useState } from "react"; -import { DisplayDivMain } from "../YamlWriterForm"; -import DropFile from "../DropFile"; -import { GlobalStyle } from "../Layout"; -import { HeaderMain } from "../YamlWriterUpload"; -import { Label } from "../YamlStyles"; -import LoggerModal from "../YamlLoggers/LoggerModal"; -import { ProviderListEntry } from "../../types/yamlwriter"; - -/** - * Developing and visually testing components in isolation before composing them in your app is useful. - * This file shows an example of that for the Modal component. - * Source: https://storybook.js.org - */ - -const ModalUploadHarDemo = () => { - const modalRef = useRef(null); - useEffectModal(modalRef, LogLevel.INFO); - - const defaultFiles: File[] = []; - const [files, setFiles] = useState(defaultFiles); - const [isReady, setIsReady] = useState(false); - - const submitEvent = () => { - log("submitting event and creating yaml file", LogLevel.INFO); - return Promise.resolve(); - }; - const handleFileInput = (fileList: File[]) => { - log("handling file input", LogLevel.INFO, fileList); - setIsReady(files.length > 0 || fileList.length > 0); - setFiles((oldFiles: File[]) => [...oldFiles, ...fileList]); - }; - const onRemoveFile = (index: number) => { - log("handling file remove: " + index, LogLevel.INFO, index); - setIsReady(files.length > 1); - setFiles((oldFiles: File[]) => { - oldFiles.splice(index); - return [...oldFiles]; - }); - }; - - return ( -
- - - - -
- Drag file to drop zone or click to select file to load. Currently will only load one - file at a time. -
-
- -
-
- {files.map((file, index) => ( -
- - {file.name} -
- ))} -
-
-
-
- ); -}; - -const ModalListDemo = () => { - const modalRef = useRef(null); - useEffectModal(modalRef, LogLevel.INFO); - - const list = [{ id: "50", name: "list", value: "10" }]; - const onChange = () => { - // eslint-disable-next-line no-console - console.log("changing value"); - }; - return ( -
- - - - -
- Add values to your List provider   - - -
- - - - - - - - - {list.map((item: ProviderListEntry, index: number) => { - return ( - - - - - ); - })} - -
List
- - {item.value}
-
-
-
- ); -}; - -const ModalLoggerDemo = () => { - const modalRef = useRef(null); - useEffectModal(modalRef, LogLevel.INFO); - - const logger = { - id: "0", - name: "", - select: [], - where: "", - to: "", - pretty: false, - limit: "", - kill: false, - ready: false - }; - const changeLoggerSelect = () => { - // eslint-disable-next-line no-console - console.log("changing logger select"); - }; - return ( -
- - - - - -
- ); -}; - -const ModalCreateYamlDemo = () => { - const modalRef = useRef(null); - useEffectModal(modalRef, LogLevel.INFO); - - const changeFileName = () => { - // eslint-disable-next-line no-console - console.log("changing logger select"); - }; - const submitEvent = () => { - // eslint-disable-next-line no-console - console.log("submitting event and creating yaml file"); - return Promise.resolve(); - }; - return ( -
- - - - - - - -
- ); -}; - -export default { - title: "Modal" -}; - -export const UploadHarModal = () => ; - -export const ListModal = () => ; - -export const _LoggerModal = () => ; - -export const CreateYamlModal = () => ; diff --git a/controller/components/StartTestForm/index.tsx b/controller/components/StartTestForm/index.tsx index 982aa300..0ead2170 100644 --- a/controller/components/StartTestForm/index.tsx +++ b/controller/components/StartTestForm/index.tsx @@ -65,7 +65,6 @@ const notAuthorizedMessageOpenId = (username?: string | null): JSX.Element => Please request 'Pewpew - User' permission if you need to be able to run tests.

- DO NOT request 'Non Prod' Permissions. Those are for internal authentication testing only. ; diff --git a/controller/components/YamlEndpoints/index.tsx b/controller/components/YamlEndpoints/index.tsx deleted file mode 100644 index fd0e2a6b..00000000 --- a/controller/components/YamlEndpoints/index.tsx +++ /dev/null @@ -1,193 +0,0 @@ -import { CSSTransition, TransitionGroup } from "react-transition-group"; -import { Checkbox, InputsDiv, Label, NonFlexSpan } from "../YamlStyles"; -import { - HIT_RATE_REGEX, - UrlProps, - Urls, - getAuthorizationHeader, - getDefaultHeaders, - getHitRateStyle, - getHitRateTitle -} from "../YamlUrls"; -import { - HarEndpoint, - HarHeader, - InputEvent, - PewPewAPI, - PewPewHeader -} from "../../types/yamlwriter"; -import { LogLevel, log } from "../../pages/api/util/log"; -import React, { useEffect, useRef, useState } from "react"; -import { QuestionBubble } from "../YamlQuestionBubble"; -import styled from "styled-components"; -import { uniqueId } from "../../pages/api/util/clientutil"; - -const defaultHeaders = "defaultHeaders"; - -export const DisplayDivMain = styled.div` - display: flex; - flex-direction: column; - flex-wrap: wrap; - padding-left: 2%; - font: 16px "Century Gothic", Futura, sans-serif; - text-align: left; - align-items: last baseline; -`; -export const DisplayDivBody = styled.div` -display: flex; -flex-direction: column; -#createYaml { - cursor: pointer; - color: rgb(200, 200, 200); - } -#createYaml:disabled { - cursor: default; - color: black; -} -`; -export const UrlsDiv = styled.div` - border-right: 2px solid black; - border-bottom: 2px solid black; - margin-right: 40px; - padding-right: 40px; - padding-bottom: 40px; - margin-bottom: 10px; -`; -const HitratesDiv = styled.div` - margin-top: 10px; - margin-bottom: 10px; -`; - -export interface EndpointsProps extends Pick { - addUrl: (pewpewUrl: PewPewAPI) => void; - clearAllUrls: () => void; - defaultYaml: boolean; - /** State of Authenticated checkbox */ - authenticated: boolean - urls: PewPewAPI[], -} - -export interface EndpointsState { - hitRate: string; - defaultHeaders: boolean; -} - -export const newUrl = (deaultHeaders: boolean, authenticated: boolean, point?: HarEndpoint): PewPewAPI => { - const pointHeaders: PewPewHeader[] = point?.headers.map(({ name, value }: HarHeader): PewPewHeader => ({ id: uniqueId(), name, value })) || []; - const pewpewHeaders: PewPewHeader[] = deaultHeaders - ? getDefaultHeaders(authenticated) - : (authenticated ? [getAuthorizationHeader()] : []); - return { - id: uniqueId(), - url: point?.url || "", - hitRate: "1hpm", - headers: [...pewpewHeaders, ...pointHeaders], - method: point?.method || "GET", - authorization: null - }; -}; - -export const Endpoints = ({ urls, ...props }: EndpointsProps) => { - const defaultState: EndpointsState = { - hitRate: "", - defaultHeaders: props.defaultYaml - }; - /** Map to keep id's unique */ - const urlsMap = new Map(urls.map((url) => ([url.id, url]))); - log("Endpoints", LogLevel.DEBUG, { urls, map: Array.from(urlsMap.values()) }); - - const [state, setState] = useState(defaultState); - const updateState = (newState: Partial) => setState((oldState: EndpointsState) => ({ ...oldState, ...newState})); - - useEffect(() => { - // If the props is changed, update the local one to match, but the user can still toggle to see the difference - updateState({ defaultHeaders: props.defaultYaml }); - }, [props.defaultYaml]); - - const handleClickDefault = (event: React.ChangeEvent) => { - updateState({ defaultHeaders: event.target.checked }); - // URLs will update via the props passed in - }; - - // Adds endpoint to array - // Called from clicking add button, or when endpoints are sent from App.js through refs.child.updatePoints - const addUrl = () => { - props.addUrl(newUrl(state.defaultHeaders, props.authenticated)); - }; - - // Updates the hit rate for each endpoint when "update" button is pressed - const updateAllUrl = (_event: InputEvent) => { - const hitRate = state.hitRate; - log("Updating all endpoints hitRate to " + hitRate, LogLevel.DEBUG); - for (const url of urls) { - if (url.hitRate !== hitRate) { - props.changeUrl({ ...url, hitRate }); - } - } - updateState({ hitRate: "" }); - }; - - // Updates the value of hit rate to be changed in all urls when update button is pressed or enter key is pressed - const updateHitRate = (event: React.ChangeEvent) => { - setState((prevState) => ({...prevState, hitRate: event.target.value })); - }; - - // Handles the changing of the "Change All Hitrates" when enter key is pressed - const handleKeyUp = (event: React.KeyboardEvent) => { - if (event.key === "Enter" && !invalidHitRate) { - updateAllUrl(event); - } - }; - - const invalidHitRate = !HIT_RATE_REGEX.test(state.hitRate); - const hitRateStyle: React.CSSProperties = getHitRateStyle(invalidHitRate); - const hitRateTitle: string | undefined = state.hitRate === "" ? "Please enter a Hit Rate" : (getHitRateTitle(invalidHitRate) || "Update all hit rates"); - - // https://github.com/reactjs/react-transition-group/issues/904 - // http://reactcommunity.org/react-transition-group/transition#Transition-prop-nodeRef - const nodeRef = useRef(null); - return ( - - -    - -    - - - - - - - - - - - - - - - {Array.from(urlsMap.values()).map((url) => ( - - - - ))} - - - ); -}; - -export default Endpoints; diff --git a/controller/components/YamlEndpoints/story.tsx b/controller/components/YamlEndpoints/story.tsx deleted file mode 100644 index d3c085b1..00000000 --- a/controller/components/YamlEndpoints/story.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { Endpoints, EndpointsProps } from "."; -import { PewPewAPI, PewPewHeader } from "../../types"; -import { GlobalStyle } from "../Layout"; -import React from "react"; -import { getDefaultHeaders } from "../YamlUrls"; - -/** - * Developing and visually testing components in isolation before composing them in your app is useful. - * This file shows an example of that for the Modal component. - * Source: https://storybook.js.org - */ - -const props: EndpointsProps = { - addUrl: (pewpewUrl: PewPewAPI) => { - // eslint-disable-next-line no-console - console.log("Adding endpoint", pewpewUrl); - }, - clearAllUrls: () => { - // eslint-disable-next-line no-console - console.log("Removing all endpoints"); - }, - deleteUrl: (urlId: string) => { - // eslint-disable-next-line no-console - console.log("deleting endpoint " + urlId); - }, - changeUrl: (pewpewUrl: PewPewAPI) => { - // eslint-disable-next-line no-console - console.log("changing endpoint " + pewpewUrl.id, pewpewUrl); - }, - addHeaders: (urlId: string, newHeaders: PewPewHeader[]) => { - // eslint-disable-next-line no-console - console.log("adding headers to " + urlId, newHeaders); - }, - deleteHeader: (urlId: string, headerId: string) => { - // eslint-disable-next-line no-console - console.log(`removing header ${headerId} from url ${urlId}`); - }, - defaultYaml: false, - urls: [], - authenticated: false -}; - -const propsLoaded: EndpointsProps = { - ...props, - defaultYaml: true, - authenticated: true, - urls: [ - { - id: "0", - url: "https://www.pewpew.org/", - headers: [...getDefaultHeaders(true), { id: "0", name: "header 0", value: "value 0" }], - method: "", - hitRate: "5hps", - authorization: null - }, - { id: "1", url: "badUrl", headers: [], method: "", hitRate: "10hps", authorization: null } - ] -}; - -export default { - title: "YamlEndpoints" -}; - -export const Default = () => ( - - - - -); - -export const Loaded = () => ( - - - - -); diff --git a/controller/components/YamlLoadPatterns/index.tsx b/controller/components/YamlLoadPatterns/index.tsx deleted file mode 100644 index a5bb2db1..00000000 --- a/controller/components/YamlLoadPatterns/index.tsx +++ /dev/null @@ -1,165 +0,0 @@ -import { CSSTransition, TransitionGroup } from "react-transition-group"; -import { Checkbox, Div, InputsDiv, Label, Span} from "../YamlStyles"; -import React, { useEffect, useRef, useState } from "react"; -import { PewPewLoadPattern } from "../../types/yamlwriter"; -import QuestionBubble from "../YamlQuestionBubble"; -import { uniqueId } from "../../pages/api/util/clientutil"; - -export type PewPewLoadPatternStringType = "from" | "to" | "over"; - -export interface LoadPatternProps { - addPattern: (pewpewPattern: PewPewLoadPattern) => void; - clearAllPatterns: () => void; - deletePattern: (id: string) => void; - changePattern: (pewpewPattern: PewPewLoadPattern) => void; - defaultYaml: boolean; - patterns: PewPewLoadPattern[]; -} - -interface LoadPatternState { - defaultPatterns: boolean; -} - -export const OVER_REGEX = new RegExp("^((((\\d+)\\s?(h|hr|hrs|hour|hours))\\s?)?(((\\d+)\\s?(m|min|mins|minute|minutes))\\s?)?(((\\d+)\\s?(s|sec|secs|second|seconds)))?)$"); -export const NUMBER_REGEX = new RegExp("^[+]?([0-9]+(?:[\\.][0-9]*)?|\\.[0-9]+)$"); -export const PATTERNS = "patterns"; -export const RAMP_PATTERN = "rampPattern"; -export const LOAD_PATTERN = "loadPattern"; - -export const newLoadPattern = (patternId: string = uniqueId()): PewPewLoadPattern => ({ id: patternId, from: "", to: "", over: "" }); -export const newRampLoadPattern = (): PewPewLoadPattern => ({ id: RAMP_PATTERN, from: "10", to: "100", over: "15m" }); -export const newLoadLoadPattern = (): PewPewLoadPattern => ({ id: LOAD_PATTERN, from: "100", to: "100", over: "15m" }); - - -const errorColor: React.CSSProperties = { color: "red" }; - -export function LoadPatterns ({ defaultYaml, patterns, ...props }: LoadPatternProps) { - const defaultState: LoadPatternState = { - defaultPatterns: defaultYaml - }; - /** Map to keep id's unique */ - const loadPatternsMap = new Map(patterns.map((pewpewPattern) => ([pewpewPattern.id, pewpewPattern]))); - - const [state, setState] = useState(defaultState); - const updateState = (newState: Partial) => setState((oldState): LoadPatternState => ({ ...oldState, ...newState })); - - useEffect(() => { - switchDefault(defaultYaml); - }, [defaultYaml]); - - const handleClickDefault = (event: React.ChangeEvent) => { - switchDefault(event.target.checked); - }; - - const switchDefault = (newChecked: boolean) => { - if (newChecked && !loadPatternsMap.has(RAMP_PATTERN)) { - // Add it (will update the map when it comes back in via props) - props.addPattern(newRampLoadPattern()); - } else if (!newChecked && loadPatternsMap.has(RAMP_PATTERN)) { - // Remove it (will update the map when it comes back in via props) - props.deletePattern(RAMP_PATTERN); - } - if (newChecked && !loadPatternsMap.has(LOAD_PATTERN)) { - // Add it (will update the map when it comes back in via props) - props.addPattern(newLoadLoadPattern()); - } else if (!newChecked && loadPatternsMap.has(LOAD_PATTERN)) { - // Remove it (will update the map when it comes back in via props) - props.deletePattern(LOAD_PATTERN); - } - updateState({ defaultPatterns: newChecked }); - }; - - const changePattern = (pewpewPattern: PewPewLoadPattern, type: PewPewLoadPatternStringType, value: string) => { - pewpewPattern[type] = value; - props.changePattern(pewpewPattern); - }; - - const deletePattern = (patternId: string) => { - if (patternId === RAMP_PATTERN || patternId === LOAD_PATTERN) { - updateState({ defaultPatterns: false }); - } - props.deletePattern(patternId); - }; - - const clearAllPatterns = () => { - updateState({ defaultPatterns: false }); - props.clearAllPatterns(); - }; - - // https://github.com/reactjs/react-transition-group/issues/904 - // http://reactcommunity.org/react-transition-group/transition#Transition-prop-nodeRef - const nodeRef = useRef(null); - return ( - - -    - -    - - - - - - - {Array.from(loadPatternsMap.values()).map((pewpewPattern: PewPewLoadPattern) => { - // TODO: Do we want to check if they're greater than 0? - const validFrom: boolean = !pewpewPattern.from || NUMBER_REGEX.test(pewpewPattern.from); - const validTo: boolean = NUMBER_REGEX.test(pewpewPattern.to); - const validOver: boolean = OVER_REGEX.test(pewpewPattern.over); - return -
- - - - changePattern(pewpewPattern, "from", event.target.value)} - name={pewpewPattern.id} - id="from" - value={pewpewPattern.from} - title={validFrom ? undefined : "Invalid From"} - /> - - - - - - changePattern(pewpewPattern, "to", event.target.value)} - name={pewpewPattern.id} - id="to" - value={pewpewPattern.to} - title={validTo ? undefined : "Invalid To"} - /> - - - - - - changePattern(pewpewPattern, "over", event.target.value)} - name={pewpewPattern.id} - id="over" - value={pewpewPattern.over} - title={validOver ? undefined : "Invalid Over"} - /> - - -
-
; - })} -
-
- ); -} - -export default LoadPatterns; diff --git a/controller/components/YamlLoadPatterns/story.tsx b/controller/components/YamlLoadPatterns/story.tsx deleted file mode 100644 index a35d9bbc..00000000 --- a/controller/components/YamlLoadPatterns/story.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { DisplayDivBody, DisplayDivMain } from "../YamlWriterForm"; -import { LOAD_PATTERN, LoadPatternProps, LoadPatterns, RAMP_PATTERN } from "."; -import { GlobalStyle } from "../Layout"; -import { PewPewLoadPattern } from "../../types"; -import React from "react"; - -const props: LoadPatternProps = { - addPattern: (pewpewPattern: PewPewLoadPattern) => { - // eslint-disable-next-line no-console - console.log("Adding new LoadPattern", pewpewPattern); - }, - deletePattern: (id: string) => { - // eslint-disable-next-line no-console - console.log("Removing LoadPattern " + id); - }, - clearAllPatterns: () => { - // eslint-disable-next-line no-console - console.log("Removing all LoadPatterns"); - }, - changePattern: (pewpewPattern: PewPewLoadPattern) => { - // eslint-disable-next-line no-console - console.log("changing LoadPattern " + pewpewPattern.id, pewpewPattern); - }, - defaultYaml: false, - patterns: [] -}; - -const propsDefault: LoadPatternProps = { - ...props, - defaultYaml: true, - patterns: [ - { id: RAMP_PATTERN, from: "10", to: "100", over: "15m" }, - { id: LOAD_PATTERN, from: "100", to: "100", over: "15m" } - ] -}; - -const propsLoaded: LoadPatternProps = { - ...props, - defaultYaml: false, - patterns: [ - { id: "0", from: "10", to: "100", over: "15m" }, - { id: "1", from: "100", to: "100", over: "15m" }, - { id: "2", from: "", to: "", over: "" }, - { id: "3", from: "", to: "", over: "15m" }, - { id: "4", from: "10", to: "", over: "" }, - { id: "5", from: "", to: "100", over: "" }, - { id: "6", from: "", to: "100", over: "15m" }, - { id: "7", from: "10", to: "100", over: "5m" } - ] -}; - -export default { - title: "YamlLoadPatterns" -}; - -export const Default = () => ( - - - - - - - - -); - -export const Empty = () => ( - - - - - - - - -); - -export const Loaded = () => ( - - - - - - - - -); diff --git a/controller/components/YamlLoggers/LoggerModal.tsx b/controller/components/YamlLoggers/LoggerModal.tsx deleted file mode 100644 index 8d1b5eaa..00000000 --- a/controller/components/YamlLoggers/LoggerModal.tsx +++ /dev/null @@ -1,411 +0,0 @@ -import { Label, Span } from "../YamlStyles"; -import { LogLevel, log } from "../../pages/api/util/log"; -import { LoggerSelectEntry, PewPewLogger } from "../../types/yamlwriter"; -import { Modal, ModalObject } from "../Modal"; -import React, { Ref, forwardRef, useEffect, useState } from "react"; -import styled from "styled-components"; -import { uniqueId } from "../../pages/api/util/clientutil"; - -const ColumnBlockDiv = styled.div` - display:inline-block; - vertical-align: top; -`; -const LabelBody = styled.label` - margin-right: 40px; - display: flex; - flex-direction: row; -`; -const LabelHeader = styled.label` - margin-right: 40px; - display: flex; - flex-direction: row; - font-size: 18px; - font-weight: 20px; - border-bottom: solid black; - margin-bottom: 5px; -`; -const TextDiv = styled.div` - padding-top: 15px; - padding-bottom: 10px; -`; - -interface LoggerModalProps { - onClose?: (event?: React.MouseEvent) => void; - changeLogger: (parameter: LoggerSelectEntry[]) => void; - data: PewPewLogger; -} - -const LOGGER_SELECT = "loggerSelect"; -const REQUEST = "request"; -const RESPONSE = "response"; -const TIMING = "timing"; - -type HeaderLoggerType = "request" | "response" | "timing"; -type RequestLoggerName = "request" | "method" | "url" | "requestHeaders" | "requestHeadersAll" | "requestBody"; -type ResponseLoggerName = "response" | "status" | "responseHeaders" | "responseHeadersAll" | "responseBody"; -type TimingLoggerName = "timestamp" | "rtt"; -type LoggerName = RequestLoggerName | ResponseLoggerName | TimingLoggerName; - -type LoggerSelectEntryDisplay = Omit & { display: string } -type RequestLogger = Record; -type ResponseLogger = Record; -type TimingLogger = Record; - -const requestLoggers: RequestLogger = { - "request": { display: "start-line", name: "request", value: "request[\"start-line\"]" }, - "method": { display: "method", name: "method", value: "request.method" }, - "url": { display: "url", name: "url", value: "request.url" }, - "requestHeaders": { display: "headers", name: "requestHeaders", value: "request.headers" }, - "requestHeadersAll": { display: "headers_all", name: "requestHeadersAll", value: "request.headers_all" }, - "requestBody": { display: "body", name: "requestBody", value: "request.body" } -}; -const responseLoggers: ResponseLogger = { - "response": { display: "start-line", name: "response", value: "response[\"start-line\"]" }, - "status": { display: "status", name: "status", value: "response.status" }, - "responseHeaders": { display: "headers", name: "responseHeaders", value: "response.headers" }, - "responseHeadersAll": { display: "headers_all", name: "responseHeadersAll", value: "response.headers_all" }, - "responseBody": { display: "body", name: "responseBody", value: "response.body" } -}; -const timingLoggers: TimingLogger = { - "timestamp": { display: "timestamp", name: "timestamp", value: "epoch(\"ms\")" }, - "rtt": { display: "stats.rtt", name: "rtt", value: "stats.rtt" } -}; - -const getLoggerSelectEntryByName = (loggerName: LoggerName): LoggerSelectEntry => { - let displayLogger: LoggerSelectEntryDisplay | undefined; - if (loggerName in requestLoggers) { - displayLogger = requestLoggers[loggerName as RequestLoggerName]; - } else if (loggerName in responseLoggers) { - displayLogger = responseLoggers[loggerName as ResponseLoggerName]; - } else if (loggerName in timingLoggers) { - displayLogger = timingLoggers[loggerName as TimingLoggerName]; - } - if (displayLogger) { - const { name, value } = displayLogger; - const loggerEntry: LoggerSelectEntry = { id: name, name, value }; - log("getLoggerSelectEntryByName", LogLevel.DEBUG, { name, loggerEntry }); - return loggerEntry; - } else { - log("Unknown LoggerSelectEntry name " + loggerName, LogLevel.ERROR); - throw new Error ("Unknown LoggerSelectEntry name " + loggerName); - } -}; - -export const defaultLoggers: { - request: RequestLogger, - response: ResponseLogger, - timing: TimingLogger -} = { - request: requestLoggers, - response: responseLoggers, - timing: timingLoggers -}; - -export const loggerOptions: { - type: HeaderLoggerType; - stateVariable: string; - returnTypeArray: LoggerSelectEntryDisplay[]; -}[] = [ - // Here is all of the Request information (loggerOptions[0]) - { - type: REQUEST, - stateVariable: "defaultRequest", - returnTypeArray: Object.values(requestLoggers) - }, - // Here is all of the Response information (loggerOptions[1]) - { - type: RESPONSE, - stateVariable: "defaultResponse", - returnTypeArray: Object.values(responseLoggers) - }, - // Here is all of the Timing information (loggerOptions[2]) - { - type: TIMING, - stateVariable: "defaultTiming", - returnTypeArray: Object.values(timingLoggers) - } -]; - -export const debugLoggerSelect: LoggerSelectEntry[] = [ - getLoggerSelectEntryByName("timestamp"), - getLoggerSelectEntryByName("rtt"), - getLoggerSelectEntryByName("request"), - getLoggerSelectEntryByName("method"), - getLoggerSelectEntryByName("requestHeaders"), - getLoggerSelectEntryByName("requestBody"), - getLoggerSelectEntryByName("response"), - getLoggerSelectEntryByName("status"), - getLoggerSelectEntryByName("responseHeaders"), - getLoggerSelectEntryByName("responseBody") -]; -export const errorLoggerSelect: LoggerSelectEntry[] = [ - getLoggerSelectEntryByName("timestamp"), - getLoggerSelectEntryByName("rtt"), - getLoggerSelectEntryByName("request"), - getLoggerSelectEntryByName("requestHeaders"), - getLoggerSelectEntryByName("requestBody"), - getLoggerSelectEntryByName("response"), - getLoggerSelectEntryByName("status"), - getLoggerSelectEntryByName("responseHeaders") -]; -export const killLoggerSelect = [ - getLoggerSelectEntryByName("timestamp"), - getLoggerSelectEntryByName("status"), - getLoggerSelectEntryByName("request"), - getLoggerSelectEntryByName("response") -]; - -interface RequestState { - request: boolean; - method: boolean; - url: boolean; - requestHeaders: boolean; - requestHeadersAll: boolean; - requestBody: boolean; -} -interface ResponseState { - response: boolean; - status: boolean; - responseHeaders: boolean; - responseHeadersAll: boolean; - responseBody: boolean; -} - -interface TimingState { - timestamp: boolean; - rtt: boolean; -} - -interface LoggerModalState { - name: string; - value: string; - defaultRequest: boolean; - defaultResponse: boolean; - defaultTiming: boolean; - request: RequestState; - response: ResponseState; - timing: TimingState; -} - -const defaultRequest: RequestState = { request: false, method: false, url: false, requestHeaders: false, requestHeadersAll: false, requestBody: false}; -const defaultResponse: ResponseState = { response: false, status: false, responseHeaders: false, responseHeadersAll: false, responseBody: false}; -const defaultTiming: TimingState = { timestamp: false, rtt: false }; - -const getLoggerModalStateData = (loggerSelects: LoggerSelectEntry[]): Pick => { - // test - const request: RequestState = { ...defaultRequest }; - const response: ResponseState = { ...defaultResponse }; - const timing: TimingState = { ...defaultTiming }; - for (const loggerSelect of loggerSelects) { - log("getLoggerModalStateData", LogLevel.DEBUG, { loggerSelect }); - // Check if it's one of them - if (loggerSelect.id in requestLoggers) { - request[loggerSelect.id as keyof RequestState] = true; - log("getLoggerModalStateData requestState", LogLevel.DEBUG, { id: loggerSelect.id, requestState: request }); - } else if (loggerSelect.id in responseLoggers) { - response[loggerSelect.id as keyof ResponseState] = true; - log("getLoggerModalStateData responseState", LogLevel.DEBUG, { id: loggerSelect.id, responseState: response }); - } else if (loggerSelect.id in timingLoggers) { - timing[loggerSelect.id as keyof TimingState] = true; - log("getLoggerModalStateData timingState", LogLevel.DEBUG, { id: loggerSelect.id, timingState: timing }); - } else { - // Something else - log("getLoggerModalStateData other", LogLevel.DEBUG, { id: loggerSelect.id }); - } - } - return { request, response, timing }; -}; - -export const LoggerModal = forwardRef(({ onClose, changeLogger, data }: LoggerModalProps, ref: Ref) => { - getLoggerModalStateData(data.select); - const defaultState: LoggerModalState = { - name: "", - value: "", - defaultRequest: false, - defaultResponse: false, - defaultTiming: false, - ...getLoggerModalStateData(data.select) - }; - - const [state, setState] = useState(defaultState); - const updateState = (newState: Partial) => - setState((oldState: LoggerModalState): LoggerModalState => ({ ...oldState, ...newState })); - - useEffect(() => { - const loggerState = getLoggerModalStateData(data.select); - updateState(loggerState); - }, [data.select]); - - const addInput = (id: string, name: string, value: string) => { - const list = data.select; - if (id === "timestamp") { - list.unshift({ id, name, value }); - } else { - list.push({ id, name, value }); - } - changeLogger(list); - }; - - const addInputUser = () => { - addInput(uniqueId(), state.name, state.value); - updateState({ name: "", value: ""}); - }; - - const handleChangeName = (event: React.ChangeEvent) => { - updateState({ name: event.target.value }); - }; - - const handleChangeValue = (event: React.ChangeEvent) => { - updateState({ value: event.target.value }); - }; - - const onKeyUp = (event: React.KeyboardEvent) => { - if (state.name && state.value) { - if (event.key === "Enter") { - addInputUser(); - } - } - }; - - const handleClickDeleteItem = (itemId: string, name: string) => { - deleteItem([itemId]); - // Check if it's a checkbox? - log("handleClickDeleteItem", LogLevel.DEBUG, { itemId, name }); - if (itemId in state.request && typeof state.request[itemId as keyof RequestState] === "boolean") { - setState(({ request, ...oldState }) => ({ ...oldState, request: { ...request, [itemId]: false } })); - } - if (itemId in state.response && typeof state.response[itemId as keyof ResponseState] === "boolean") { - setState(({ response, ...oldState }) => ({ ...oldState, response: { ...response, [itemId]: false } })); - } - if (itemId in state.timing && typeof state.timing[itemId as keyof TimingState] === "boolean") { - setState(({ timing, ...oldState }) => ({ ...oldState, timing: { ...timing, [itemId]: false } })); - } - }; - - const deleteItem = (itemIds: string[]) => { - const list = data.select = data.select.filter((item: LoggerSelectEntry) => !itemIds.includes(item.id)); - changeLogger(list); - }; - - // This function changes the state of all check boxes under a certain header and adds/removes all of the values - const headerClick = (dataType: HeaderLoggerType, newChecked: boolean) => { - let loggerArray: { - type: HeaderLoggerType; - stateVariable: string; - returnTypeArray: LoggerSelectEntryDisplay[]; - } | undefined; - for (const loggerOption of loggerOptions) { - if (loggerOption.type === dataType) { loggerArray = loggerOption; } - } - log("headerClick", LogLevel.DEBUG, { dataType, loggerArray, newChecked }); - if (!loggerArray) { - log("Uknown loggerOptions: " + dataType, LogLevel.ERROR); - return; - } - updateState({ [loggerArray.stateVariable]: newChecked }); - for (const logger of loggerArray.returnTypeArray) { - log("headerClick listClick", LogLevel.DEBUG, { dataType, logger, newChecked }); - // BUG: Add works via listClick. delete only does 1, delete below - listClick(dataType, logger.name as LoggerName, logger.value, newChecked, true); - } - if (!newChecked) { - // BUG: Add works via listClick. delete only does 1 - deleteItem(loggerArray.returnTypeArray.map((logger) => logger.name)); - } - }; - - // When any checkbox button is clicked, flip the state variable of that checkbox and add/remove that input - const listClick = (headerType: HeaderLoggerType, name: LoggerName, value: string, newChecked: boolean, dontDelete?: boolean) => { - let stateType: RequestState | ResponseState | TimingState | undefined; - let stateValue: boolean | undefined; - if (headerType === REQUEST) { stateType = state.request; stateValue = state.request[name as RequestLoggerName]; } - if (headerType === RESPONSE) { stateType = state.response; stateValue = state.response[name as ResponseLoggerName]; } - if (headerType === TIMING) { stateType = state.timing; stateValue = state.timing[name as TimingLoggerName]; } - if (stateValue === undefined || stateValue === newChecked) { - // Nothing needed - log("listClick no change", LogLevel.DEBUG, { headerType, name, value, newChecked, stateValue, current: stateType }); - return; - } - - setState((prevState) => ({...prevState, [headerType]: {...prevState[headerType], [name]: newChecked }})); - log("listClick", LogLevel.DEBUG, { headerType, name, newChecked }); - if (newChecked) { - addInput(name, name, value); - } else if (dontDelete !== true) { - deleteItem([name]); - } - }; - - return ( - - {loggerOptions.map((header, optionsIndex: number) => { - const stateVariable = header.type === REQUEST ? state.defaultRequest : header.type === RESPONSE ? state.defaultResponse : state.defaultTiming; - const itemType = header.type === REQUEST ? state.request : header.type === RESPONSE ? state.response : state.timing; - return ( - - - {header.type} - headerClick(header.type, event.target.checked)} - checked={stateVariable} - /> - - {header.returnTypeArray.map((item, returnTypeIndex: number) => { - return ( - - {item.display}     - listClick(header.type, item.name as LoggerName, item.value, (event.target as HTMLInputElement).checked)} - checked={(itemType as any)[item.name]} - /> - - ); - })} - - ); - })} - - Add any other items you want to be logged: - - - -    - -    - - - - - {data.select.map((item: LoggerSelectEntry, index: number) => { - return ( - - - - - - ); - })} - -
{item.name}:{item.value}
-
- ); -}); - -export default LoggerModal; diff --git a/controller/components/YamlLoggers/index.tsx b/controller/components/YamlLoggers/index.tsx deleted file mode 100644 index 63002b38..00000000 --- a/controller/components/YamlLoggers/index.tsx +++ /dev/null @@ -1,309 +0,0 @@ -import { CSSTransition, TransitionGroup } from "react-transition-group"; -import { Checkbox, Div, InputsDiv, Label, NonFlexSpan, Span } from "../YamlStyles"; -import { LogLevel, log } from "../../pages/api/util/log"; -import { LoggerModal, debugLoggerSelect, errorLoggerSelect, killLoggerSelect } from "./LoggerModal"; -import { LoggerSelectEntry, PewPewLogger } from "../../types"; -import { ModalObject, useEffectModal } from "../Modal"; -import React, { useEffect, useRef, useState } from "react"; -import QuestionBubble from "../YamlQuestionBubble"; -import styled from "styled-components"; -import { uniqueId } from "../../pages/api/util/clientutil"; - -const BorderDiv = styled.div` - border-bottom: 2px dotted rgb(206, 199, 199); - padding-top: 12px; - border-top: 2px dotted rgb(206, 199, 199); - margin-top: 5px; -`; -const EditListButton = styled.button` - width: 100px; -`; - -export const LOGGERS = "loggers"; -const DEBUG_LOGGER = "debugLogger"; -const ERROR_LOGGER = "errorLogger"; -const KILL_LOGGER = "killLogger"; -const DEFAULT_LOGGERS = "defaultLoggers"; - -export type DefaultLoggerTypeAll = DefaultVariablesType | "defaultLoggers"; -export type PewPewLoggerBooleanType = "kill" | "pretty"; -export type PewPewLoggerStringType = "name" | "where" | "to" | "limit"; -interface DefaultVariables { - debugLogger: boolean; - errorLogger: boolean; - killLogger: boolean; -} - -type DefaultVariablesType = keyof DefaultVariables; - -// This is the default state of all checkboxes and drop downs when the UI is initially loaded -const defaultUI: DefaultVariables = { - debugLogger: false, - errorLogger: true, - killLogger: true -}; - -export const newLogger = (loggerId: string = uniqueId()): PewPewLogger => ({ - id: loggerId, - name: "", - where: "", - to: "", - select: [], - limit: "", - pretty: false, - kill: false -}); - -export const debugLoggerVar = (): PewPewLogger => ({ - id: DEBUG_LOGGER, - name: "httpAll", - where: "", - to: "stdout", - select: JSON.parse(JSON.stringify(debugLoggerSelect)), - limit: "", - pretty: false, - kill: false -}); -export const errorLoggerVar = (): PewPewLogger => ({ - id: ERROR_LOGGER, - name: "httpErrors", - where: "response.status >= 400", - to: "stderr", - select: JSON.parse(JSON.stringify(errorLoggerSelect)), - limit: 200, - pretty: false, - kill: false -}); -export const killLoggerVar = (): PewPewLogger => ({ - id: KILL_LOGGER, - name: "testEnd", - where: "response.status >= 500", - to: "stderr", - select: JSON.parse(JSON.stringify(killLoggerSelect)), - limit: 50, - pretty: false, - kill: true -}); - -function getDefaultLogger (loggerName: DefaultVariablesType): PewPewLogger { - switch (loggerName) { - case DEBUG_LOGGER: - return debugLoggerVar(); - case ERROR_LOGGER: - return errorLoggerVar(); - case KILL_LOGGER: - return killLoggerVar(); - default: - throw new Error("getDefaultLogger Invalid loggerName: " + loggerName); - } -} - -export function getDefaultLoggers (defaultVars: DefaultVariables = defaultUI): PewPewLogger[] { - const pewpewVars: PewPewLogger[] = []; - for (const [varName, isEnabled] of Object.entries(defaultVars)) { - if (isEnabled) { - pewpewVars.push(getDefaultLogger(varName as DefaultVariablesType)); - } - } - - return pewpewVars; -} - - -export interface LoggerProps { - addLogger: (pewpewLogger: PewPewLogger) => void; - clearAllLoggers: () => void; - deleteLogger: (loggerId: string) => void; - changeLogger: (pewpewLogger: PewPewLogger) => void; - defaultYaml: boolean; - loggers: PewPewLogger[]; -} - -interface LoggerState extends DefaultVariables { - /** State of the "Default Loggers" checkbox */ - defaultLoggers: boolean; - /** This should be a PewPewLogger.id */ - currentLogger: PewPewLogger; -} - -export function Loggers ({ defaultYaml, ...props }: LoggerProps) { - const defaultState: LoggerState = { - defaultLoggers: defaultYaml, - currentLogger: newLogger(), - ...defaultUI - }; - /** Map to keep id's unique */ - const loggerMap = new Map(props.loggers.map((logger) => ([logger.id, logger]))); - - const [state, setState] = useState(defaultState); - const updateState = ((newState: Partial) => setState((oldState: LoggerState) => ({ ...oldState, ...newState }))); - const modalRef = useRef(null); - useEffectModal(modalRef); - - // Prepopulate the loggers based on the defaultYaml - useEffect(() => { - switchAllDefaults(defaultYaml); - }, [defaultYaml]); - - const handleClickDefault = (event: React.ChangeEvent) => { - const target = event.target as HTMLInputElement; - const id = target.id as DefaultLoggerTypeAll; - const checked = target.checked; - log("handleClickDefault", LogLevel.DEBUG, { id, checked }); - if (id === DEFAULT_LOGGERS) { - switchAllDefaults(checked); - } else { - switchDefault(id, checked); - } - }; - - const switchAllDefaults = (newChecked: boolean) => { - log("switchAllDefaults", LogLevel.DEBUG, { newChecked }); - switchDefault("errorLogger", newChecked); - switchDefault("killLogger", newChecked); - updateState({ defaultLoggers: newChecked }); - }; - - const switchDefault = (loggerType: DefaultVariablesType, newChecked: boolean) => { - log("switchDefault", LogLevel.DEBUG, { loggerType, newChecked }); - updateState({ [loggerType]: newChecked }); - // Add/delete from varsMap/vars - if (newChecked && !loggerMap.has(loggerType)) { - // Add it (will update the map when it comes back in via props) - const newVar = getDefaultLogger(loggerType); - props.addLogger(newVar); - } else if (!newChecked && loggerMap.has(loggerType)) { - // Remove it (will update the map when it comes back in via props) - props.deleteLogger(loggerType); - } - }; - - const handleClickLogger = (logger: PewPewLogger, loggerType: PewPewLoggerBooleanType, newChecked: boolean) => { - log("handleClickLogger", LogLevel.DEBUG, { newChecked, loggerType, logger }); - logger[loggerType] = newChecked; - props.changeLogger(logger); - }; - - const changeLogger = (logger: PewPewLogger, loggerType: PewPewLoggerStringType, value: string) => { - log("changeLogger", LogLevel.DEBUG, { loggerType, value, logger }); - if (loggerType === "limit" && `${parseInt(value)}` === value) { - logger.limit = parseInt(value); - } else { - logger[loggerType] = value; - } - props.changeLogger(logger); - }; - - const changeLoggerSelect = (parameter: LoggerSelectEntry[]) => { - setState(({ currentLogger, ...oldState }) => ({ ...oldState, currentLogger: { ...currentLogger, select: parameter } })); - }; - - const changeLoggerSelectOnClose = () => { - // Save it off so when we modify it - const currentLogger = state.currentLogger; - log("closeModal", LogLevel.DEBUG, currentLogger); - props.changeLogger(currentLogger); - }; - - const deleteLogger = (loggerId: string) => { - if (loggerId in defaultUI) { - // This will do the props.deleteVar() - switchDefault(loggerId as DefaultVariablesType, false); - } else { - props.deleteLogger(loggerId); - } - }; - - const openModal = (logger: PewPewLogger) => { - log("openModal", LogLevel.DEBUG, logger); - updateState({ currentLogger: logger }); - modalRef.current?.openModal(); - }; - - // https://github.com/reactjs/react-transition-group/issues/904 - // http://reactcommunity.org/react-transition-group/transition#Transition-prop-nodeRef - const nodeRef = useRef(null); - return ( - - -    - -    - - - -
- - - - - - - - - - - -
- - {Array.from(loggerMap.values()).map((logger: PewPewLogger) => ( - - - - - - changeLogger(logger, "name", event.target.value)} value={logger.name} /> - - - - openModal(logger)}> - Edit List - - - - - - changeLogger(logger, "where", event.target.value)} value={logger.where} /> - - - - changeLogger(logger, "limit", event.target.value)} value={logger.limit} /> - - - - - - - changeLogger(logger, "to", event.target.value)} value={logger.to} /> -
- - - - handleClickLogger(logger, "kill", event.target.checked)} checked={logger.kill} /> - - - - - handleClickLogger(logger, "pretty", event.target.checked)} checked={logger.pretty} /> - -
-
-
-
- ))} - -
-
- ); -} - -export default Loggers; diff --git a/controller/components/YamlLoggers/story.tsx b/controller/components/YamlLoggers/story.tsx deleted file mode 100644 index 1472f015..00000000 --- a/controller/components/YamlLoggers/story.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { DisplayDivBody, DisplayDivMain } from "../YamlWriterForm"; -import { LoggerProps, Loggers, getDefaultLoggers } from "."; -import { GlobalStyle } from "../Layout"; -import { PewPewLogger } from "../../types"; -import React from "react"; - -const props: LoggerProps = { - addLogger: (newLogger: PewPewLogger) => { - // eslint-disable-next-line no-console - console.log("Adding logger", newLogger); - }, - clearAllLoggers: () => { - // eslint-disable-next-line no-console - console.log("Removing all loggers"); - }, - deleteLogger: (loggerId: string) => { - // eslint-disable-next-line no-console - console.log("deleting logger " + loggerId); - }, - changeLogger: (pewPewLogger: PewPewLogger) => { - // eslint-disable-next-line no-console - console.log("changing logger " + pewPewLogger.id, pewPewLogger); - }, - defaultYaml: true, - loggers: [] -}; - -const propsEmpty: LoggerProps = { ...props }; - -const propsLoaded: LoggerProps = { - ...props, - loggers: [ - { id: "0", name: "", select: [], where: "", to: "", pretty: false, limit: "", kill: false }, - ...getDefaultLoggers() - ] -}; - -export default { - title: "YamlLoggers" -}; - -export const Default = () => ( - - - - - - - - -); - -export const Loaded = () => ( - - - - - - - - -); diff --git a/controller/components/YamlProviders/File.tsx b/controller/components/YamlProviders/File.tsx deleted file mode 100644 index f916113a..00000000 --- a/controller/components/YamlProviders/File.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { Div, Label, Span } from "../YamlStyles"; -import { PewPewFileProvider, PewPewProvidersBooleanType, PewPewProvidersStringType, ProviderProps} from "./ProviderTypes"; -import QuestionBubble from "../YamlQuestionBubble"; -import React from "react"; - -interface FileProviderProps extends ProviderProps { - data: PewPewFileProvider; -} - -export function FileProvider ({ data, ...props }: FileProviderProps) { - - const handleClick = (type: PewPewProvidersBooleanType, newChecked: boolean) => { - props.changeProvider({ ...data, [type]: newChecked }); - }; - - const changeProvider = (type: PewPewProvidersStringType, value: string) => { - props.changeProvider({ ...data, [type]: value }); - }; - - const deleteSelf = () => { - props.deleteProvider(data.id); - }; - - return ( -
- - - - changeProvider("name", event.target.value)} value={data.name}/> - - - - - changeProvider("file", event.target.value)} value={data.file} /> - - - - - handleClick("repeat", event.target.checked)} checked={data.repeat}/> - - - - - handleClick("random", event.target.checked)} checked={data.random}/> - - -
- ); -} - -export default FileProvider; diff --git a/controller/components/YamlProviders/List.tsx b/controller/components/YamlProviders/List.tsx deleted file mode 100644 index d60da7d2..00000000 --- a/controller/components/YamlProviders/List.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import { Div, Label, Span } from "../YamlStyles"; -import { Modal, ModalObject, useEffectModal } from "../Modal"; -import { PewPewListProvider, PewPewProvidersBooleanType, PewPewProvidersStringType, ProviderProps } from "./ProviderTypes"; -import React, { useRef, useState } from "react"; -import { ProviderListEntry } from "../../types/yamlwriter"; -import QuestionBubble from "../YamlQuestionBubble"; -import { uniqueId } from "../../pages/api/util/clientutil"; - -interface ListProviderProps extends ProviderProps { - data: PewPewListProvider; -} - -interface ListProviderState { - value: string; -} - -export function ListProvider ({ data, ...props }: ListProviderProps) { - const defaultState: ListProviderState = { - value: "" - }; - - const [state, setState] = useState(defaultState); - const updateState = (newState: Partial) => setState((oldState): ListProviderState => ({ ...oldState, ...newState })); - - const modalRef = useRef(null); - useEffectModal(modalRef); - - const addListItem = () => { - const list = [...data.list, { id: uniqueId(), value: (state.value) }]; - props.changeProvider({ ...data, list }); - updateState({ value: "" }); - }; - - const handleChangeModalValue = (event: React.ChangeEvent) => { - setState((prevState: ListProviderState) => ({...prevState, value: event.target.value})); - }; - - const deleteListItem = (event: React.MouseEvent) => { - const element = event.target as HTMLInputElement; - const list = (data.list).filter((item: ProviderListEntry) => item.id !== element.value); - props.changeProvider({ ...data, list }); - }; - - const handleClick = (type: PewPewProvidersBooleanType, newChecked: boolean) => { - props.changeProvider({ ...data, [type]: newChecked }); - }; - - const onKeyUp = (event: React.KeyboardEvent) => { - if (event.key === "Enter") { - addListItem(); - } - }; - - const changeProvider = (type: PewPewProvidersStringType, value: string) => { - props.changeProvider({ ...data, [type]: value }); - }; - - const deleteProvider = () => { - props.deleteProvider(data.id); - }; - - return ( -
- - - - changeProvider("name", event.target.value)} name={data.id} value={data.name} /> - - - - - - - -
- Add values to your List provider   - - -
- - - - - - - - - {(data.list).map((item: ProviderListEntry) => { - return ( - - - - ); - })} - -
List
{item.value}
-
- - - - handleClick("repeat", event.target.checked)} checked={data.repeat}/> - - - - - handleClick("random", event.target.checked)} checked={data.random}/> - - -
- ); -} - -export default ListProvider; diff --git a/controller/components/YamlProviders/ProviderTypes.tsx b/controller/components/YamlProviders/ProviderTypes.tsx deleted file mode 100644 index fe405a0c..00000000 --- a/controller/components/YamlProviders/ProviderTypes.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { PewPewProvider, ProviderListEntry } from "../../types/yamlwriter"; -import FileProvider from "./File"; -import ListProvider from "./List"; -import RangeProvider from "./Range"; -import React from "react"; -import ResponseProvider from "./Response"; - -export interface ProviderTypesProps { - deleteProvider: (id: string) => void; - changeProvider: (pewpewProvider: PewPewProvider) => void; - data: PewPewProvider; -} - -export interface ProviderProps { - deleteProvider: (id: string) => void; - changeProvider: (pewpewProvider: PewPewProvider) => void; - data: PewPewListProvider | PewPewRangeProvider | PewPewResponseProvider | PewPewFileProvider; -} - -export interface PewPewListProvider extends PewPewProvider { - list: ProviderListEntry[]; -} - -export interface PewPewRangeProvider extends PewPewProvider { - start: number; - end: number | string; - step: number | string; -} - -export interface PewPewResponseProvider extends PewPewProvider { - response: Record; -} - -export interface PewPewFileProvider extends PewPewProvider { - file: string; -} - -export function ProviderTypes ({deleteProvider: deleteSelf, changeProvider, data}: ProviderTypesProps) { - return ( -
- {(data.type === "file") && - } - {(data.type === "response") && - } - {(data.type === "range") && - } - {(data.type === "list") && - } -
- ); -} - -export enum ProviderType { - "file" = "file", - "response" = "response", - "range" = "range", - "list" = "list" -} -export type PewPewProvidersStringType = "name" | "file"; -export type PewPewProvidersNumberType = "start" | "end" | "step"; -export type PewPewProvidersBooleanType = "repeat" | "random"; diff --git a/controller/components/YamlProviders/Range.tsx b/controller/components/YamlProviders/Range.tsx deleted file mode 100644 index c2a86927..00000000 --- a/controller/components/YamlProviders/Range.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { Div, Label, Span} from "../YamlStyles"; -import { PewPewProvidersNumberType, PewPewRangeProvider, ProviderProps } from "./ProviderTypes"; -import QuestionBubble from "../YamlQuestionBubble"; -import React from "react"; - -interface RangeProviderProps extends ProviderProps { - data: PewPewRangeProvider; -} - -export function RangeProvider ({ data, ...props }: RangeProviderProps) { - const handleClick = (newChecked: boolean) => { - props.changeProvider({ ...data, repeat: newChecked }); - }; - - const changeProviderName = (value: string) => { - props.changeProvider({ ...data, name: value }); - }; - - const changeProviderNumber = (type: PewPewProvidersNumberType, value: string) => { - props.changeProvider({ ...data, [type]: parseInt(value) }); - }; - - const deleteProvider = () => { - props.deleteProvider(data.id); - }; - - return ( -
- - - - changeProviderName(event.target.value)} value={data.name} /> - - - - - changeProviderNumber("start", event.target.value)} value={data.start} /> - - - - - changeProviderNumber("end", event.target.value)} value={data.end} /> - - - - - changeProviderNumber("step", event.target.value)} value={data.step} /> - - - - - handleClick(event.target.checked)} checked={data.repeat}/> - - -
- ); -} - -export default RangeProvider; diff --git a/controller/components/YamlProviders/Response.tsx b/controller/components/YamlProviders/Response.tsx deleted file mode 100644 index e14f32ef..00000000 --- a/controller/components/YamlProviders/Response.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import { Div, Label, Span } from "../YamlStyles"; -import { LogLevel, log } from "../../pages/api/util/log"; -import { PewPewResponseProvider, ProviderProps} from "./ProviderTypes"; -import React, { useState } from "react"; -import QuestionBubble from "../YamlQuestionBubble"; - -interface ResponseProviderProps extends ProviderProps { - data: PewPewResponseProvider; -} - -interface ResponseProviderState { - responseProviderType: ResponseProviderType; - autoReturnType: AutoReturnType; - bufferSize: string; -} - -enum ResponseProviderType { - Empty = "empty", - Auto_Return = "auto_return", - Buffer = "buffer" -} - -enum AutoReturnType { - If_not_full = "if_not_full", - Block = "block", - Force = "force" -} - -export function ResponseProvider ({ data, ...props }: ResponseProviderProps) { - const defaultState: ResponseProviderState = { - responseProviderType: ResponseProviderType.Empty, - autoReturnType: AutoReturnType.If_not_full, - bufferSize: "" - }; - - const [state, setState] = useState(defaultState); - const updateState = (newState: Partial) => setState((oldState): ResponseProviderState => ({ ...oldState, ...newState })); - - const changeProviderName = (value: string) => { - props.changeProvider({ ...data, name: value }); - }; - - const handleClick = (responseProviderType: ResponseProviderType) => { - updateState({ responseProviderType }); - let response: Record = {}; - switch (responseProviderType) { - case ResponseProviderType.Empty: - response = {}; - break; - case ResponseProviderType.Auto_Return: - // eslint-disable-next-line camelcase - response = { auto_return: state.autoReturnType }; - break; - case ResponseProviderType.Buffer: - response = { buffer: state.bufferSize === "" ? "auto" : state.bufferSize }; - break; - default:{ - const errorMessage = "Unknown ResponseProviderType " + responseProviderType; - log(errorMessage, LogLevel.ERROR, responseProviderType); - throw new Error(errorMessage); - } - } - props.changeProvider({ ...data, response }); - }; - - const changeAutoReturnType = (autoReturnType: AutoReturnType) => { - updateState({ autoReturnType }); - // eslint-disable-next-line camelcase - props.changeProvider({ ...data, response: { auto_return: autoReturnType } }); - }; - - const changeBufferSize = (bufferSize: string) => { - updateState({ bufferSize }); - props.changeProvider({ ...data, response: { buffer: bufferSize === "" ? "auto" : bufferSize } }); - }; - - const deleteProvider = () => { - props.deleteProvider(data.id); - }; - - return ( -
- - - - changeProviderName(event.target.value)} value={data.name} /> - - - - { state.responseProviderType === ResponseProviderType.Empty && -
- - -
- } - { state.responseProviderType === ResponseProviderType.Auto_Return && -
- - -
- } - { state.responseProviderType === ResponseProviderType.Buffer && -
- changeBufferSize(event.target.value)} value={state.bufferSize} placeholder={"auto"} /> - -
- } -
- - - handleClick(event.target.value as ResponseProviderType)} defaultChecked/> - - - - handleClick(event.target.value as ResponseProviderType)}/> - - - - handleClick(event.target.value as ResponseProviderType)} /> - - -
- ); -} - -export default ResponseProvider; diff --git a/controller/components/YamlProviders/index.tsx b/controller/components/YamlProviders/index.tsx deleted file mode 100644 index cbd5f3c8..00000000 --- a/controller/components/YamlProviders/index.tsx +++ /dev/null @@ -1,162 +0,0 @@ -import { CSSTransition, TransitionGroup } from "react-transition-group"; -import { Div, InputsDiv} from "../YamlStyles"; -import { ProviderType, ProviderTypes } from "./ProviderTypes"; -import React, { useRef, useState } from "react"; -import { PewPewProvider } from "../../types/yamlwriter"; -import QuestionBubble from "../YamlQuestionBubble"; -import styled from "styled-components"; -import { uniqueId } from "../../pages/api/util/clientutil"; - -const ProviderInputsDiv = styled.div` - margin-right: 40px; - padding-right: 40px; - padding-bottom: 0px; - padding-top: 5px; -`; - -export interface ProviderMainProps { - addProvider: (pewpewProvider: PewPewProvider) => void; - clearAllProviders: () => void; - deleteProvider: (id: string) => void; - changeProvider: (pewpewProvider: PewPewProvider) => void; - providers: PewPewProvider[]; -} - -export const PROVIDERS = "providers"; -export const PROVIDER_FILE = "providerFile"; -export const PROVIDER_RESPONSE = "providerResponse"; -export const PROVIDER_RANGE = "providerRange"; -export const PROVIDER_LIST = "providerList"; - -export const newProviderFile = (providerId: string = uniqueId()): PewPewProvider => ({ id: providerId, type: "file", name: "", file: "", repeat: true, random: false }); -export const newProviderResponse = (providerId: string = uniqueId()): PewPewProvider => ({ id: providerId, type: "response", name: "", response: {} }); -export const newProviderRange = (providerId: string = uniqueId()): PewPewProvider => ({ id: providerId, type: "range", name: "", start: 0, end: "", step: "", repeat: false }); -export const newProviderList = (providerId: string = uniqueId()): PewPewProvider => ({ id: providerId, type: "list", name: "", list: [] , repeat: true, random: false }); -export const newProvider = (providerType: ProviderType) => { - switch (providerType) { - case ProviderType.file: - return newProviderFile(); - case ProviderType.response: - return newProviderResponse(); - case ProviderType.range: - return newProviderRange(); - case ProviderType.list: - return newProviderList(); - default: - throw new Error("Unknown ProviderType: " + PROVIDER_FILE); - } -}; - -export function Providers ({ providers, ...props }: ProviderMainProps) { - const providersMap = new Map(providers.map((pewpewPattern) => ([pewpewPattern.id, pewpewPattern]))); - - const [display, setDisplay] = useState(false); - - const addProviderDropDown = () => { - setDisplay(true); - }; - - const clearProviders = () => { - setDisplay(false); - props.clearAllProviders(); - }; - - // // Changes information about the provider - // const changeProvider = (id: string, event: InputEvent, type: string, parameter?: ProviderListEntry[]) => { - // const element = event.target as HTMLInputElement; - // if (element.value.length < 0) { - // return; - // } - // const index = state.providers.findIndex((provider) => { - // return (provider.id === id); - // }); - // const provider = Object.assign({}, state.providers[index]); - // switch (type) { - // case "providerName": - // provider.name = element.value; - // break; - // case "providerFile": - // provider.file = convertInput(element.value); - // break; - // case "providerStart": - // provider.start = convertInput(element.value); - // break; - // case "providerEnd": - // provider.end = convertInput(element.value); - // break; - // case "providerStep": - // provider.step = convertInput(element.value); - // break; - // case "providerList": - // provider.list = convertInput(parameter); - // break; - // case "providerResponseEmpty": - // provider.response = {}; - // break; - // case "providerResponseAuto": - // // eslint-disable-next-line camelcase - // provider.response = {auto_return: convertInput(element.value)}; - // break; - // case "providerResponseBuffer": - // provider.response = {buffer: (element.value === "" ) ? "auto" : convertInput(element.value)}; - // break; - // case "providerRepeat": - // provider.repeat = element.checked; - // break; - // case "providerRandom": - // provider.random = element.checked; - // break; - // } - // const providers = state.providers; - // providers[index] = provider; - // setState((prevState) => ({...prevState, providers })); - // }; - - // https://github.com/reactjs/react-transition-group/issues/904 - // http://reactcommunity.org/react-transition-group/transition#Transition-prop-nodeRef - const nodeRef = useRef(null); - return ( - - -    - -    -
- {display && - - - - -    - - - } -
- - {Array.from(providersMap.values()).map((provider: PewPewProvider) => ( - - - - ))} - -
- ); -} - -export default Providers; diff --git a/controller/components/YamlProviders/story.tsx b/controller/components/YamlProviders/story.tsx deleted file mode 100644 index 6c904fd8..00000000 --- a/controller/components/YamlProviders/story.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { DisplayDivBody, DisplayDivMain } from "../YamlWriterForm"; -import Providers, { ProviderMainProps } from "."; -import { GlobalStyle } from "../Layout"; -import { PewPewProvider } from "../../types"; -import React from "react"; - -const props: ProviderMainProps = { - addProvider: (pewpewProvider: PewPewProvider) => { - // eslint-disable-next-line no-console - console.log("Adding provider", pewpewProvider); - }, - clearAllProviders: () => { - // eslint-disable-next-line no-console - console.log("Removing all providers"); - }, - deleteProvider: (id: string) => { - // eslint-disable-next-line no-console - console.log("deleting provider " + id); - }, - changeProvider: (pewpewProvider: PewPewProvider) => { - // eslint-disable-next-line no-console - console.log("changing provider " + pewpewProvider.id, pewpewProvider); - }, - providers: [] -}; - -const propsEmpty: ProviderMainProps = { ...props, providers: [] }; - -const propsLoaded: ProviderMainProps = { - ...props, - providers: [ - { id: "0", type: "file", name: "cisids", file: "cisids.csv", repeat: true, random: false }, - { id: "1", type: "response", name: "sessionId", response: {} }, - { id: "2", type: "range", name: "length", start: 0, end: "", step: "", repeat: false }, - { - id: "3", - type: "list", - name: "urls", - list: [ - { id: "0", value: "element 0" }, - { id: "1", value: "element 1" }, - { id: "2", value: "element 2" }, - { id: "3", value: "element 3" } - ], - repeat: true, - random: false - } - ] -}; - -export default { - title: "YamlProviders" -}; - -export const Default = () => ( - - - - - - - - -); - -export const Loaded = () => ( - - - - - - - - -); diff --git a/controller/components/YamlQuestionBubble/index.tsx b/controller/components/YamlQuestionBubble/index.tsx deleted file mode 100644 index 4204edb8..00000000 --- a/controller/components/YamlQuestionBubble/index.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from "react"; -import styled from "styled-components"; - -export interface QuestionBubbleProps { - text: string; - href?: string; -} - -const QuestionBubbleStyling = styled.span` - .questionHover .questionHoverText { - visibility: hidden; - width: 120px; - bottom: 100%; - left: 50%; - margin-left: -60px; - background-color: black; - color: #fff; - text-align: center; - border-radius: 6px; - padding: 5px 0; - - /* Position the tooltip */ - position: absolute; - z-index: 1; - } - .questionHover:hover .questionHoverText { - visibility: visible; - } - .questionHover { - position: relative; - display: inline-block; - } -`; - -export function QuestionBubble ({ text, href }: QuestionBubbleProps) { - return ( - - {href && ?{text}} - {!href && ?{text}} - - ); -} - -export default QuestionBubble; \ No newline at end of file diff --git a/controller/components/YamlQuestionBubble/story.tsx b/controller/components/YamlQuestionBubble/story.tsx deleted file mode 100644 index b1e59ff0..00000000 --- a/controller/components/YamlQuestionBubble/story.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import QuestionBubble, { QuestionBubbleProps } from "."; -import { DisplayDivBody } from "../YamlWriterForm"; -import { GlobalStyle } from "../Layout"; -import React from "react"; - -const props: QuestionBubbleProps = { - text: "This is what the question bubble looks like", - href: "" -}; - -const linkProps: QuestionBubbleProps = { - text: "This question bubble links to the pew pew documentation", - href: "https://familysearch.github.io/pewpew/" -}; - -export default { - title: "YamlQuestionBubble" -}; - -export const Default = () => ( - - - -

- -
-
-); - -export const LinkedQuestionMark = () => ( - - - -

- -
-
-); diff --git a/controller/components/YamlStyles/index.tsx b/controller/components/YamlStyles/index.tsx deleted file mode 100644 index 532dd2b3..00000000 --- a/controller/components/YamlStyles/index.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import React from "react"; -import styled from "styled-components"; - -export const Div = styled.div` - margin-top: 15px; - display: flex; - flex-direction: row; -`; -export const Label = styled.label` - margin-right: 8px; - display: flex; - flex-direction: row; -`; -export const Span = styled.span` - display: flex; - flex-direction: row; - flex-wrap: wrap; - align-items: center; - margin-right: 15px; -`; -export const NonFlexSpan = styled.span` - display: flex; - flex-direction: row; - flex-wrap: no-wrap; - align-items: center; - margin-right: 15px; -`; -export const Checkbox = styled.input` -margin-right: 15px; -`; -export const InputsDiv = styled.div` - display: inline-block; - border-right: 2px solid black; - border-bottom: 2px solid black; - margin-right: 40px; - padding-right: 40px; - padding-bottom: 40px; - padding-top: 5px; - margin-bottom: 10px; -`; - -export interface InputPropsText { - style?: React.CSSProperties; - id?: string; - type: "text" | "number"; - name?: string; - value?: string | number; - onChange: (event: React.ChangeEvent, type: string) => void; - dataType: string; - defaultValue?: number | string; - min?: string; - max?: string; - defaultChecked?: boolean; - title?: string; -} - -export const Input = (props: InputPropsText) => { - - const changeInput = (event: React.ChangeEvent, type: string) => { - props.onChange(event, type); - }; - - return ( - ) => changeInput(event, props.dataType)} - defaultValue={props.defaultValue} - defaultChecked={props.defaultChecked} - title={props.title} - /> - ); -}; - -export default Input; \ No newline at end of file diff --git a/controller/components/YamlStyles/story.tsx b/controller/components/YamlStyles/story.tsx deleted file mode 100644 index 0312f16d..00000000 --- a/controller/components/YamlStyles/story.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import Input, { Checkbox, Div, InputPropsText, InputsDiv, Label, NonFlexSpan, Span } from "."; -import { DisplayDivMain } from "../YamlWriterForm"; -import { GlobalStyle } from "../Layout"; -import React from "react"; - -/** - * Developing and visually testing components in isolation before composing them in your app is useful. - * This file shows an example of that for the Layout component. - * Source: https://storybook.js.org - */ - -const onChange = () => { - // eslint-disable-next-line no-console - console.log("Change value of input"); -}; - -const inputProps: InputPropsText = { - type: "text", - onChange, - dataType: "this is a data type value" -}; - -export default { - title: "YamlStyles" -}; - -export const Default = () => ( - - - - -
- - - - - - -
-
-
-
-); - -export const _NonFlexSpan = () => ( - - - - -
- - - - - - -
-
-
-
-); - -export const FilledAndChecked = { - render: () => ( - - - - -
- - - - - - -
-
-
-
- ), - - name: "Filled and Checked" -}; diff --git a/controller/components/YamlUrls/index.tsx b/controller/components/YamlUrls/index.tsx deleted file mode 100644 index 9bb08b49..00000000 --- a/controller/components/YamlUrls/index.tsx +++ /dev/null @@ -1,311 +0,0 @@ -import { Div, Label, Span } from "../YamlStyles"; -import { LogLevel, log } from "../../pages/api/util/log"; -import { Modal, ModalObject, useEffectModal } from "../Modal"; -import { PewPewAPI, PewPewHeader } from "../../types/yamlwriter"; -import React, { useEffect, useRef, useState } from "react"; -import QuestionBubble from "../YamlQuestionBubble"; -import styled from "styled-components"; -import { uniqueId } from "../../pages/api/util/clientutil"; - -// import axios from "axios"; - -const ModalEndpointInput = styled.div` - display: flex; - flex-direction: row; - margin-bottom: 10px; -`; -const ModalHitMethodInput = styled.div` - display: flex; - flex-direction: row; - margin: 25px 0; -`; -const EndpointDisplay = styled.span` - margin-right: 5px; - max-width: 450px; - width: 450px; - white-space: nowrap; - overflow: hidden; -`; - -export interface UrlProps { - deleteUrl: (id: string) => void; - changeUrl: (pewpewUrl: PewPewAPI) => void; - addHeaders: (urlId: string, newHeaders: PewPewHeader[]) => void; - deleteHeader: (urlId: string, headerId: string) => void; - data: PewPewAPI; - /** State of Authenticated checkbox */ - authenticated: boolean - /** State of Default Headers checkbox */ - defaultHeaders: boolean, -} - -export interface UrlState { - passed: boolean; -} - -export const HIT_RATE_REGEX: RegExp = new RegExp("^(\\d+)hp(h|m|s)$"); -export const URLS = "urls"; -const EMPTY_HEADER = "emptyHeader"; -const DEFAULT_HEADERS = "defaultHeaders"; -export const AUTHENTICATED = "authenticated"; -const ACCEPT_LANGUAGE = "acceptLanguage"; -const CONTENT_TYPE = "contentType"; -type HeaderType = "defaultHeaders" | "authenticated" | "acceptLanguage" | "contentType"; -type PewPewApiStringType = "url" | "method" | "hitRate"; -type PewPewHeaderStringType = "name" | "value"; - -export const newHeader = () => ({ id: uniqueId(), name: "", value: "" }); -export const getAuthorizationHeader = (): PewPewHeader => ({ id: AUTHENTICATED, name: "Authorization", value: "Bearer ${sessionId}" }); -const getAcceptLanguageHeader = (): PewPewHeader => ({ id: ACCEPT_LANGUAGE, name: "Accept-Language", value: "en-us"}); -const getContentTypedHeader = (): PewPewHeader => ({ id: CONTENT_TYPE, name: "Content-Type", value: "application/json"}); -export const getDefaultHeaders = (authenticated?: boolean): PewPewHeader[] => [ - ...(authenticated ? [getAuthorizationHeader()] : []), - getAcceptLanguageHeader(), - getContentTypedHeader() -]; - -function getHeader (headerType: HeaderType | "emptyHeader" = EMPTY_HEADER): PewPewHeader { - switch (headerType) { - case AUTHENTICATED: - return getAuthorizationHeader(); - case ACCEPT_LANGUAGE: - return getAcceptLanguageHeader(); - case CONTENT_TYPE: - return getContentTypedHeader(); - case EMPTY_HEADER: - return newHeader(); - default: - throw new Error("getHeader Invalid headerType: " + headerType); - } -} - - -export const getUrlStyle = (invalidUrl: boolean): React.CSSProperties => ({ color: invalidUrl ? "red" : undefined }); -export const getUrlTitle = (invalidUrl: boolean): string | undefined => invalidUrl ? "Endpoint URL is not valid" : undefined; -export const getHitRateStyle = (invalidHitRate: boolean): React.CSSProperties => ({ color: invalidHitRate ? "red" : undefined }); -export const getHitRateTitle = (invalidHitRate: boolean): string | undefined => invalidHitRate ? "Hit Rate is not valid" : undefined; - -export function isValidUrl (url: string): boolean { - try { - new URL(url); - return true; - } catch { - return false; - } -} - -export function Urls ({ data: { headers, ...data }, ...props }: UrlProps) { - const defaultState: UrlState = { - passed: false - }; - // /** Map to keep id's unique */ - const headersMap = new Map(headers.map((header) => ([header.id, header]))); - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [state, setState] = useState(defaultState); - const modalRef = useRef(null); - useEffectModal(modalRef); - - // Changes the state of authenticated button when default is checked or unchecked - useEffect(() => { - // Add/delete from headersMap/headers - if (props.authenticated && !headersMap.has(AUTHENTICATED)) { - // Add it (will update the map when it comes back in via props) - addHeader(AUTHENTICATED); - } else if (!props.authenticated && headersMap.has(AUTHENTICATED)) { - // Remove it (will update the map when it comes back in via props) - removeHeader(AUTHENTICATED); - } - }, [props.authenticated]); - - useEffect(() => { - // Add/delete from headersMap/headers - const hasAcceptLanguage = headersMap.has(ACCEPT_LANGUAGE); - const hasContentType = headersMap.has(CONTENT_TYPE); - // If we need to change both, we need to group them into a single "defaultHeaders" transaction - if (props.defaultHeaders && (!hasAcceptLanguage || !hasContentType)) { - // Add it (will update the map when it comes back in via props) - const headerToAdd: HeaderType = !hasAcceptLanguage && !hasContentType - ? DEFAULT_HEADERS - : !hasAcceptLanguage ? ACCEPT_LANGUAGE : CONTENT_TYPE; - addHeader(headerToAdd); - } else if (!props.defaultHeaders && (hasAcceptLanguage || hasAcceptLanguage)) { - // Remove it (will update the map when it comes back in via props) - const headerToRemove: HeaderType = hasAcceptLanguage && hasContentType - ? DEFAULT_HEADERS - : hasAcceptLanguage ? ACCEPT_LANGUAGE : CONTENT_TYPE; - removeHeader(headerToRemove); - } - }, [props.defaultHeaders]); - - // Currently unoperational. Needs a server to access websites correctly. - // Would recommend simply using Postman instead of getting this function to work. - // Postman is already used by a lot of the FamilySearch org and does what this function below would do and more. - // Users can also simply create the testing script, test endpoints locally with pew pew, and edit the endpoints as needed. - const checkUrl = () => { - alert("Currently not working. Sorry!"); - // console.log(data.url); - // const myUrl = `https://cors-escape-git-master.shalvah.now.sh/${data.url}`; - // console.log(myUrl); - /* let xhr = new XMLHttpRequest(); - xhr.onreadystatechange = function () { - if (readyState === 4 && status === 200) { - let response = responseText; - console.log(response); - } - } - xhr.open("GET", myUrl); - xhr.setRequestHeader("Accept", "application/json"); - xhr.send(); */ - - /* fetch(myUrl, { - method: "POST", - headers: { - "Accept-Language": "en-us", - "Accept": "application/json", - "Content-Type": "application/json", - }, - redirect: "follow", - }) - .then(response => { - console.log(response); - if (response.ok) { - response.text().then(text => { - console.log(text); - //setState((prevState: UrlState): UrlState => ({...prevState, passed: true})); - }) - } - }) - .catch(error => console.error(error)); - } */ - /* axios.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded"; - axios.get(myUrl) - .then((response) => { - console.log(response); - if (response.statusText === "OK") { - setState((prevState: UrlState): UrlState => ({...prevState, passed: true })); - } else { - setState((prevState: UrlState): UrlState => ({...prevState, passed: false })); - } - }) - .catch((error) => { - console.error(error); - setState((prevState: UrlState): UrlState => ({...prevState, passed: false })); - }) - .finally(() => { - props.updateReady(state.urlReady && state.hitReady); - }); - */ - }; - - // Adds header to array in given url - // Url found by id - const addHeader = (headerType?: HeaderType) => { - log("addHeader " + headerType, LogLevel.DEBUG); - // Adding header when Authenticated option is checked - if (headerType === DEFAULT_HEADERS) { - // add all - props.addHeaders(data.id, getDefaultHeaders()); - } else { - // put at end - props.addHeaders(data.id, [getHeader(headerType)]); - } - }; - - // Removes header from array in given url - // Url found by id and type of header - const removeHeader = (headerId: string) => { - // Removing header when X button is pressed in urls modal - log("removeHeader " + headerId, LogLevel.DEBUG); - if (headerId === DEFAULT_HEADERS) { - // Remove both! - props.deleteHeader(data.id, ACCEPT_LANGUAGE); - props.deleteHeader(data.id, CONTENT_TYPE); - } else { - props.deleteHeader(data.id, headerId); - } - }; - - const changeUrl = (type: PewPewApiStringType, value: string) => { - data[type] = value; - props.changeUrl({ ...data, headers }); - }; - - const changeHeader = (headerIndex: number, type: PewPewHeaderStringType, value: string) => { - // const header: PewPewHeader = headers[headerIndex]; - // // type = "name" or "value" - // header[type] = value; - // Typechecking above ^ for next line - headers[headerIndex][type] = value; - props.changeUrl({ ...data, headers }); - }; - - const checked = state.passed ? " Passed" : ""; - const invalidUrl: boolean = !isValidUrl(data.url); - const urlStyle: React.CSSProperties = getUrlStyle(invalidUrl); - const urlTitle: string | undefined = getUrlTitle(invalidUrl); - const invalidHitRate = !HIT_RATE_REGEX.test(data.hitRate); - return ( -
- - {data.url ? data.url : "Url"} - - - - - changeUrl("url", event.target.value)} title={urlTitle} name={data.id} value={data.url} id="urlUrl" /> - -

Endpoint must be in the form "https://www.(url)" or "http://www.(url)"

-
- - - - - changeUrl("hitRate", event.target.value)} name={data.id} value={data.hitRate} id="urlHitrate" title={getHitRateTitle(invalidHitRate)} /> - - - - - - - -
- - - - - - - - - - {headers.map((header: PewPewHeader, index: number) => { - // This maps out all of the headers uploaded from har file - return ( - - - -
NameValue
changeHeader(index, "name", event.target.value)} />