From bb0d484dc89dd050225ca6a3288372ed0d8f6860 Mon Sep 17 00:00:00 2001 From: Jens Scheffler Date: Sun, 7 Jul 2024 22:02:49 +0200 Subject: [PATCH 1/3] Attempt to add ReactJSON view to rendered templates --- .../js/dag/details/taskInstance/Details.tsx | 60 ++++++++++++++++++- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/airflow/www/static/js/dag/details/taskInstance/Details.tsx b/airflow/www/static/js/dag/details/taskInstance/Details.tsx index 7432db840494b..67c2823095835 100644 --- a/airflow/www/static/js/dag/details/taskInstance/Details.tsx +++ b/airflow/www/static/js/dag/details/taskInstance/Details.tsx @@ -17,8 +17,22 @@ * under the License. */ +import ReactJson from "react-json-view"; + import React, { useEffect, useState } from "react"; -import { Text, Flex, Table, Tbody, Tr, Td, Code, Box } from "@chakra-ui/react"; +import { + Button, + Text, + Flex, + Spacer, + Table, + Tbody, + Tr, + Td, + Code, + Box, + useClipboard, +} from "@chakra-ui/react"; import { snakeCase } from "lodash"; import { useTIHistory } from "src/api"; @@ -130,6 +144,8 @@ const Details = ({ gridInstance, taskInstance, group }: Props) => { ["success", "failed", "upstream_failed", "skipped"].includes(state); const isOverall = (isMapped || isGroup) && "Overall "; + const { onCopy, setValue } = useClipboard(""); + return ( {!!taskInstance && ( @@ -319,7 +335,7 @@ const Details = ({ gridInstance, taskInstance, group }: Props) => { Rendered Templates - +
{Object.keys(instance.renderedFields).map((key) => { const renderedFields = instance.renderedFields as Record< @@ -335,11 +351,49 @@ const Details = ({ gridInstance, taskInstance, group }: Props) => { // skip } } + const fieldStr = field as string; + try { + const fieldJson = JSON.parse(fieldStr); + return ( + + + + + ); + } catch (e) { + // skip + console.error("oh noooooo -------------------------"); + console.error(e); + } return ( ); From 1d3cff6ff38281b1ad39a0ed225ed5448210283a Mon Sep 17 00:00:00 2001 From: Jens Scheffler Date: Mon, 8 Jul 2024 23:11:52 +0200 Subject: [PATCH 2/3] Add ReactJSON view to rendered templates, Review Feedback --- airflow/www/static/js/api/useGridData.test.ts | 1 - .../js/dag/details/RenderedJsonField.tsx | 70 +++++++++++++++++++ .../static/js/dag/details/dagRun/Details.tsx | 57 ++------------- .../js/dag/details/taskInstance/Details.tsx | 59 +--------------- .../static/js/dag/grid/dagRuns/index.test.tsx | 3 - airflow/www/static/js/dag/grid/index.test.tsx | 1 - airflow/www/static/js/types/index.ts | 1 - airflow/www/static/js/utils/index.test.ts | 1 - 8 files changed, 79 insertions(+), 114 deletions(-) create mode 100644 airflow/www/static/js/dag/details/RenderedJsonField.tsx diff --git a/airflow/www/static/js/api/useGridData.test.ts b/airflow/www/static/js/api/useGridData.test.ts index 929303efe1f5d..3e83e29ee56a2 100644 --- a/airflow/www/static/js/api/useGridData.test.ts +++ b/airflow/www/static/js/api/useGridData.test.ts @@ -34,7 +34,6 @@ const commonDagRunParams = { lastSchedulingDecision: null, externalTrigger: false, conf: null, - confIsJson: false, note: "", }; diff --git a/airflow/www/static/js/dag/details/RenderedJsonField.tsx b/airflow/www/static/js/dag/details/RenderedJsonField.tsx new file mode 100644 index 0000000000000..ddbae90d1b998 --- /dev/null +++ b/airflow/www/static/js/dag/details/RenderedJsonField.tsx @@ -0,0 +1,70 @@ +/*! + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from "react"; + +import ReactJson from "react-json-view"; + +import { Flex, Button, Code, Spacer, useClipboard } from "@chakra-ui/react"; + +interface Props { + content: string; +} + +const RenderedJsonField = ({ content }: Props) => { + let contentJson = null; + let contentFormatted = ""; + let isJson = false; + try { + contentJson = JSON.parse(content); + contentFormatted = JSON.stringify(contentJson, null, 4); + isJson = true; + } catch (e) { + // skip + } + + const { onCopy, hasCopied } = useClipboard(contentFormatted); + + let field = null; + if (isJson) { + field = ( + + + + + + ); + } else { + field = {content}; + } + return field; +}; + +export default RenderedJsonField; diff --git a/airflow/www/static/js/dag/details/dagRun/Details.tsx b/airflow/www/static/js/dag/details/dagRun/Details.tsx index c769da72d5c4f..10aab416d0bf3 100644 --- a/airflow/www/static/js/dag/details/dagRun/Details.tsx +++ b/airflow/www/static/js/dag/details/dagRun/Details.tsx @@ -16,21 +16,8 @@ * specific language governing permissions and limitations * under the License. */ -import React, { useEffect } from "react"; -import { - Flex, - Box, - Button, - Spacer, - Table, - Tbody, - Tr, - Td, - useClipboard, - Text, -} from "@chakra-ui/react"; - -import ReactJson from "react-json-view"; +import React from "react"; +import { Flex, Box, Table, Tbody, Tr, Td, Text } from "@chakra-ui/react"; import type { DagRun as DagRunType } from "src/types"; import { SimpleStatus } from "src/dag/StatusBox"; @@ -38,25 +25,13 @@ import { ClipboardText } from "src/components/Clipboard"; import { formatDuration, getDuration } from "src/datetime_utils"; import Time from "src/components/Time"; import RunTypeIcon from "src/components/RunTypeIcon"; +import RenderedJsonField from "../RenderedJsonField"; interface Props { run: DagRunType; } -const formatConf = (conf: string | null | undefined): string => { - if (!conf) { - return ""; - } - return JSON.stringify(JSON.parse(conf), null, 4); -}; - const DagRunDetails = ({ run }: Props) => { - const { onCopy, setValue, hasCopied } = useClipboard(formatConf(run?.conf)); - - useEffect(() => { - setValue(formatConf(run?.conf)); - }, [run, setValue]); - if (!run) return null; const { state, @@ -69,7 +44,6 @@ const DagRunDetails = ({ run }: Props) => { queuedAt, externalTrigger, conf, - confIsJson, } = run; return ( @@ -161,28 +135,9 @@ const DagRunDetails = ({ run }: Props) => { - {confIsJson ? ( - - ) : ( - - )} +
{key} + + + + + +
{key} - {field as string} + {fieldStr}
Run config - - - - - - {conf ?? "None"} + +
diff --git a/airflow/www/static/js/dag/details/taskInstance/Details.tsx b/airflow/www/static/js/dag/details/taskInstance/Details.tsx index 67c2823095835..37bfb4c60e652 100644 --- a/airflow/www/static/js/dag/details/taskInstance/Details.tsx +++ b/airflow/www/static/js/dag/details/taskInstance/Details.tsx @@ -17,22 +17,8 @@ * under the License. */ -import ReactJson from "react-json-view"; - import React, { useEffect, useState } from "react"; -import { - Button, - Text, - Flex, - Spacer, - Table, - Tbody, - Tr, - Td, - Code, - Box, - useClipboard, -} from "@chakra-ui/react"; +import { Text, Flex, Table, Tbody, Tr, Td, Code, Box } from "@chakra-ui/react"; import { snakeCase } from "lodash"; import { useTIHistory } from "src/api"; @@ -48,6 +34,7 @@ import type { TaskState, } from "src/types"; import TrySelector from "./TrySelector"; +import RenderedJsonField from "../RenderedJsonField"; interface Props { gridInstance?: GridTaskInstance; @@ -144,8 +131,6 @@ const Details = ({ gridInstance, taskInstance, group }: Props) => { ["success", "failed", "upstream_failed", "skipped"].includes(state); const isOverall = (isMapped || isGroup) && "Overall "; - const { onCopy, setValue } = useClipboard(""); - return ( {!!taskInstance && ( @@ -351,49 +336,11 @@ const Details = ({ gridInstance, taskInstance, group }: Props) => { // skip } } - const fieldStr = field as string; - try { - const fieldJson = JSON.parse(fieldStr); - return ( - - {key} - - - - - - - - - ); - } catch (e) { - // skip - console.error("oh noooooo -------------------------"); - console.error(e); - } return ( {key} - {fieldStr} + ); diff --git a/airflow/www/static/js/dag/grid/dagRuns/index.test.tsx b/airflow/www/static/js/dag/grid/dagRuns/index.test.tsx index a02df41975569..ebbb1f86e0cb4 100644 --- a/airflow/www/static/js/dag/grid/dagRuns/index.test.tsx +++ b/airflow/www/static/js/dag/grid/dagRuns/index.test.tsx @@ -43,7 +43,6 @@ const generateRuns = (length: number): DagRun[] => executionDate: datestring, externalTrigger: false, conf: null, - confIsJson: false, note: "someRandomValue", })); @@ -64,7 +63,6 @@ describe("Test DagRuns", () => { lastSchedulingDecision: datestring, externalTrigger: false, conf: null, - confIsJson: false, note: "someRandomValue", }, { @@ -80,7 +78,6 @@ describe("Test DagRuns", () => { lastSchedulingDecision: datestring, externalTrigger: false, conf: null, - confIsJson: false, note: "someRandomValue", }, ]; diff --git a/airflow/www/static/js/dag/grid/index.test.tsx b/airflow/www/static/js/dag/grid/index.test.tsx index f307fabedf158..ece090f7c890f 100644 --- a/airflow/www/static/js/dag/grid/index.test.tsx +++ b/airflow/www/static/js/dag/grid/index.test.tsx @@ -153,7 +153,6 @@ const mockGridData = { note: "myCoolDagRun", externalTrigger: false, conf: null, - confIsJson: false, }, ], ordering: ["dataIntervalStart"], diff --git a/airflow/www/static/js/types/index.ts b/airflow/www/static/js/types/index.ts index 573072b5ffada..1984a92c9e843 100644 --- a/airflow/www/static/js/types/index.ts +++ b/airflow/www/static/js/types/index.ts @@ -65,7 +65,6 @@ interface DagRun { lastSchedulingDecision: string | null; externalTrigger: boolean; conf: string | null; - confIsJson: boolean; note: string | null; } diff --git a/airflow/www/static/js/utils/index.test.ts b/airflow/www/static/js/utils/index.test.ts index 5d9c05cbb824c..aba3312bfb2e2 100644 --- a/airflow/www/static/js/utils/index.test.ts +++ b/airflow/www/static/js/utils/index.test.ts @@ -139,7 +139,6 @@ describe("Test getDagRunLabel", () => { lastSchedulingDecision: "2021-11-08T21:14:19.704433+00:00", externalTrigger: false, conf: null, - confIsJson: false, note: "someRandomValue", } as DagRun; From e5f6e8f6d0b501e315e393de4f3aecbc95c8bf35 Mon Sep 17 00:00:00 2001 From: Jens Scheffler Date: Mon, 8 Jul 2024 23:53:00 +0200 Subject: [PATCH 3/3] Add ReactJSON view to rendered templates, Review Feedback --- .../RenderedJsonField.tsx | 50 +++++++++---------- .../static/js/dag/details/dagRun/Details.tsx | 2 +- .../js/dag/details/taskInstance/Details.tsx | 2 +- 3 files changed, 27 insertions(+), 27 deletions(-) rename airflow/www/static/js/{dag/details => components}/RenderedJsonField.tsx (68%) diff --git a/airflow/www/static/js/dag/details/RenderedJsonField.tsx b/airflow/www/static/js/components/RenderedJsonField.tsx similarity index 68% rename from airflow/www/static/js/dag/details/RenderedJsonField.tsx rename to airflow/www/static/js/components/RenderedJsonField.tsx index ddbae90d1b998..518f6b9fd82df 100644 --- a/airflow/www/static/js/dag/details/RenderedJsonField.tsx +++ b/airflow/www/static/js/components/RenderedJsonField.tsx @@ -27,7 +27,7 @@ interface Props { content: string; } -const RenderedJsonField = ({ content }: Props) => { +const JsonParse = (content: string) => { let contentJson = null; let contentFormatted = ""; let isJson = false; @@ -38,33 +38,33 @@ const RenderedJsonField = ({ content }: Props) => { } catch (e) { // skip } + return [isJson, contentJson, contentFormatted]; +}; +const RenderedJsonField = ({ content }: Props) => { + const [isJson, contentJson, contentFormatted] = JsonParse(content); const { onCopy, hasCopied } = useClipboard(contentFormatted); - let field = null; - if (isJson) { - field = ( - - - - - - ); - } else { - field = {content}; - } - return field; + return isJson ? ( + + + + + + ) : ( + {content} + ); }; export default RenderedJsonField; diff --git a/airflow/www/static/js/dag/details/dagRun/Details.tsx b/airflow/www/static/js/dag/details/dagRun/Details.tsx index 10aab416d0bf3..317cbd99f2a32 100644 --- a/airflow/www/static/js/dag/details/dagRun/Details.tsx +++ b/airflow/www/static/js/dag/details/dagRun/Details.tsx @@ -25,7 +25,7 @@ import { ClipboardText } from "src/components/Clipboard"; import { formatDuration, getDuration } from "src/datetime_utils"; import Time from "src/components/Time"; import RunTypeIcon from "src/components/RunTypeIcon"; -import RenderedJsonField from "../RenderedJsonField"; +import RenderedJsonField from "src/components/RenderedJsonField"; interface Props { run: DagRunType; diff --git a/airflow/www/static/js/dag/details/taskInstance/Details.tsx b/airflow/www/static/js/dag/details/taskInstance/Details.tsx index 37bfb4c60e652..dfd2a921d35df 100644 --- a/airflow/www/static/js/dag/details/taskInstance/Details.tsx +++ b/airflow/www/static/js/dag/details/taskInstance/Details.tsx @@ -33,8 +33,8 @@ import type { TaskInstance as GridTaskInstance, TaskState, } from "src/types"; +import RenderedJsonField from "src/components/RenderedJsonField"; import TrySelector from "./TrySelector"; -import RenderedJsonField from "../RenderedJsonField"; interface Props { gridInstance?: GridTaskInstance;