This is an automated email from the ASF dual-hosted git repository.
uranusjr pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 563bc494ab Fix UI rendering when XCom is INT, FLOAT, BOOL or NULL
(#41516)
563bc494ab is described below
commit 563bc494ab5c610c46a60b2fe6beed742e3d588e
Author: Jens Scheffler <[email protected]>
AuthorDate: Tue Aug 20 05:56:03 2024 +0200
Fix UI rendering when XCom is INT, FLOAT, BOOL or NULL (#41516)
---
airflow/api_connexion/openapi/v1.yaml | 1 +
.../www/static/js/components/RenderedJsonField.tsx | 21 +-----
airflow/www/static/js/components/utils.test.ts | 84 ++++++++++++++++++++++
airflow/www/static/js/components/utils.ts | 38 ++++++++++
.../js/dag/details/taskInstance/Xcom/XcomEntry.tsx | 9 ++-
airflow/www/static/js/types/api-generated.ts | 2 +-
6 files changed, 134 insertions(+), 21 deletions(-)
diff --git a/airflow/api_connexion/openapi/v1.yaml
b/airflow/api_connexion/openapi/v1.yaml
index fbd9a64eac..d0f5db43db 100644
--- a/airflow/api_connexion/openapi/v1.yaml
+++ b/airflow/api_connexion/openapi/v1.yaml
@@ -4134,6 +4134,7 @@ components:
- type: array
items: {}
- type: object
+ nullable: true
description: The value(s),
# Python objects
diff --git a/airflow/www/static/js/components/RenderedJsonField.tsx
b/airflow/www/static/js/components/RenderedJsonField.tsx
index 7000dc17ad..a0356f0a3a 100644
--- a/airflow/www/static/js/components/RenderedJsonField.tsx
+++ b/airflow/www/static/js/components/RenderedJsonField.tsx
@@ -30,32 +30,15 @@ import {
useTheme,
FlexProps,
} from "@chakra-ui/react";
+import jsonParse from "./utils";
interface Props extends FlexProps {
content: string | object;
jsonProps?: Omit<ReactJsonViewProps, "src">;
}
-const JsonParse = (content: string | object) => {
- let contentJson = null;
- let contentFormatted = "";
- let isJson = false;
- try {
- if (typeof content === "string") {
- contentJson = JSON.parse(content);
- } else {
- contentJson = content;
- }
- contentFormatted = JSON.stringify(contentJson, null, 4);
- isJson = true;
- } catch (e) {
- // skip
- }
- return [isJson, contentJson, contentFormatted];
-};
-
const RenderedJsonField = ({ content, jsonProps, ...rest }: Props) => {
- const [isJson, contentJson, contentFormatted] = JsonParse(content);
+ const [isJson, contentJson, contentFormatted] = jsonParse(content);
const { onCopy, hasCopied } = useClipboard(contentFormatted);
const theme = useTheme();
diff --git a/airflow/www/static/js/components/utils.test.ts
b/airflow/www/static/js/components/utils.test.ts
new file mode 100644
index 0000000000..8df5938814
--- /dev/null
+++ b/airflow/www/static/js/components/utils.test.ts
@@ -0,0 +1,84 @@
+/*!
+ * 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 jsonParse from "./utils";
+
+/* global describe, test, expect */
+
+describe("JSON Parsing.", () => {
+ test.each([
+ {
+ testName: "null",
+ testContent: null,
+ expectedIsJson: false,
+ },
+ {
+ testName: "boolean",
+ testContent: true,
+ expectedIsJson: false,
+ },
+ {
+ testName: "int",
+ testContent: 42,
+ expectedIsJson: false,
+ },
+ {
+ testName: "float",
+ testContent: 3.1415,
+ expectedIsJson: false,
+ },
+ {
+ testName: "string",
+ testContent: "hello world",
+ expectedIsJson: false,
+ },
+ {
+ testName: "array",
+ testContent: ["hello world", 42, 3.1515],
+ expectedIsJson: true,
+ },
+ {
+ testName: "array as string",
+ testContent: JSON.stringify(["hello world", 42, 3.1515]),
+ expectedIsJson: true,
+ },
+ {
+ testName: "dict",
+ testContent: { key: 42 },
+ expectedIsJson: true,
+ },
+ {
+ testName: "dict as string",
+ testContent: JSON.stringify({ key: 42 }),
+ expectedIsJson: true,
+ },
+ ])(
+ "Input value is $testName",
+ ({ testName, testContent, expectedIsJson }) => {
+ const [isJson, contentJson, contentFormatted] = jsonParse(testContent);
+
+ expect(testName).not.toBeNull();
+ expect(isJson).toEqual(expectedIsJson);
+ if (expectedIsJson) {
+ expect(contentJson).not.toBeNull();
+ expect(contentFormatted.length).toBeGreaterThan(0);
+ }
+ }
+ );
+});
diff --git a/airflow/www/static/js/components/utils.ts
b/airflow/www/static/js/components/utils.ts
new file mode 100644
index 0000000000..7ba2ee0573
--- /dev/null
+++ b/airflow/www/static/js/components/utils.ts
@@ -0,0 +1,38 @@
+/*!
+ * 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.
+ */
+
+const jsonParse = (content: any) => {
+ let contentJson = null;
+ let contentFormatted = "";
+ let isJson = false;
+ try {
+ if (typeof content === "string") {
+ contentJson = JSON.parse(content);
+ } else {
+ contentJson = content;
+ }
+ contentFormatted = JSON.stringify(contentJson, null, 4);
+ isJson = contentJson != null && typeof contentJson === "object"; // ensure
numbers/bool are not treated as JSON
+ } catch (e) {
+ // skip
+ }
+ return [isJson, contentJson, contentFormatted];
+};
+
+export default jsonParse;
diff --git a/airflow/www/static/js/dag/details/taskInstance/Xcom/XcomEntry.tsx
b/airflow/www/static/js/dag/details/taskInstance/Xcom/XcomEntry.tsx
index 2e9ba769ae..806209bccb 100644
--- a/airflow/www/static/js/dag/details/taskInstance/Xcom/XcomEntry.tsx
+++ b/airflow/www/static/js/dag/details/taskInstance/Xcom/XcomEntry.tsx
@@ -60,13 +60,20 @@ const XcomEntry = ({
content = <Spinner />;
} else if (error) {
content = <ErrorAlert error={error} />;
- } else if (!xcom || !xcom.value) {
+ } else if (!xcom) {
content = (
<Alert status="info">
<AlertIcon />
No value found for XCom key
</Alert>
);
+ } else if (!xcom.value) {
+ content = (
+ <Alert status="info">
+ <AlertIcon />
+ Value is NULL
+ </Alert>
+ );
} else {
let xcomString = "";
if (typeof xcom.value !== "string") {
diff --git a/airflow/www/static/js/types/api-generated.ts
b/airflow/www/static/js/types/api-generated.ts
index 30948df332..d5c1e06b6e 100644
--- a/airflow/www/static/js/types/api-generated.ts
+++ b/airflow/www/static/js/types/api-generated.ts
@@ -1658,7 +1658,7 @@ export interface components {
Partial<number> &
Partial<boolean> &
Partial<unknown[]> &
- Partial<{ [key: string]: unknown }>;
+ Partial<{ [key: string]: unknown } | null>;
};
/**
* @description DAG details.