1
0
Эх сурвалжийг харах

[Connectors API] Fix: Handle nullable fields correctly in the ConnectorSyncJob parser (#103183)

Declare nullable fields correctly in the ConnectorSyncJob parser.
Tim Grein 1 жил өмнө
parent
commit
784c49b2b8

+ 6 - 0
docs/changelog/103183.yaml

@@ -0,0 +1,6 @@
+pr: 103183
+summary: "[Connectors API] Handle nullable fields correctly in the `ConnectorSyncJob`\
+  \ parser"
+area: Application
+type: bug
+issues: []

+ 25 - 6
x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/connector/syncjob/ConnectorSyncJob.java

@@ -263,12 +263,22 @@ public class ConnectorSyncJob implements Writeable, ToXContentObject {
     static {
         PARSER.declareField(
             optionalConstructorArg(),
-            (p, c) -> Instant.parse(p.text()),
+            (p, c) -> parseNullableInstant(p),
             CANCELATION_REQUESTED_AT_FIELD,
             ObjectParser.ValueType.STRING_OR_NULL
         );
-        PARSER.declareField(optionalConstructorArg(), (p, c) -> Instant.parse(p.text()), CANCELED_AT_FIELD, ObjectParser.ValueType.STRING);
-        PARSER.declareField(optionalConstructorArg(), (p, c) -> Instant.parse(p.text()), COMPLETED_AT_FIELD, ObjectParser.ValueType.STRING);
+        PARSER.declareField(
+            optionalConstructorArg(),
+            (p, c) -> parseNullableInstant(p),
+            CANCELED_AT_FIELD,
+            ObjectParser.ValueType.STRING_OR_NULL
+        );
+        PARSER.declareField(
+            optionalConstructorArg(),
+            (p, c) -> parseNullableInstant(p),
+            COMPLETED_AT_FIELD,
+            ObjectParser.ValueType.STRING_OR_NULL
+        );
         PARSER.declareField(
             constructorArg(),
             (p, c) -> ConnectorSyncJob.syncJobConnectorFromXContent(p),
@@ -287,9 +297,14 @@ public class ConnectorSyncJob implements Writeable, ToXContentObject {
             JOB_TYPE_FIELD,
             ObjectParser.ValueType.STRING
         );
-        PARSER.declareField(constructorArg(), (p, c) -> Instant.parse(p.text()), LAST_SEEN_FIELD, ObjectParser.ValueType.STRING);
+        PARSER.declareField(constructorArg(), (p, c) -> parseNullableInstant(p), LAST_SEEN_FIELD, ObjectParser.ValueType.STRING_OR_NULL);
         PARSER.declareField(constructorArg(), (p, c) -> p.map(), METADATA_FIELD, ObjectParser.ValueType.OBJECT);
-        PARSER.declareField(optionalConstructorArg(), (p, c) -> Instant.parse(p.text()), STARTED_AT_FIELD, ObjectParser.ValueType.STRING);
+        PARSER.declareField(
+            optionalConstructorArg(),
+            (p, c) -> parseNullableInstant(p),
+            STARTED_AT_FIELD,
+            ObjectParser.ValueType.STRING_OR_NULL
+        );
         PARSER.declareField(
             constructorArg(),
             (p, c) -> ConnectorSyncStatus.fromString(p.text()),
@@ -303,7 +318,11 @@ public class ConnectorSyncJob implements Writeable, ToXContentObject {
             TRIGGER_METHOD_FIELD,
             ObjectParser.ValueType.STRING
         );
-        PARSER.declareString(optionalConstructorArg(), WORKER_HOSTNAME_FIELD);
+        PARSER.declareStringOrNull(optionalConstructorArg(), WORKER_HOSTNAME_FIELD);
+    }
+
+    private static Instant parseNullableInstant(XContentParser p) throws IOException {
+        return p.currentToken() == XContentParser.Token.VALUE_NULL ? null : Instant.parse(p.text());
     }
 
     @SuppressWarnings("unchecked")

+ 91 - 1
x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/connector/syncjob/ConnectorSyncJobTests.java

@@ -158,7 +158,7 @@ public class ConnectorSyncJobTests extends ESTestCase {
         assertThat(syncJob.getWorkerHostname(), equalTo("worker-hostname"));
     }
 
-    public void testFromXContent_WithAllNonOptionalFieldsSet_DoesNotThrow() throws IOException {
+    public void testFromXContent_WithOnlyNonNullableFieldsSet_DoesNotThrow() throws IOException {
         String content = XContentHelper.stripWhitespace("""
             {
                                 "connector": {
@@ -242,6 +242,96 @@ public class ConnectorSyncJobTests extends ESTestCase {
         ConnectorSyncJob.fromXContentBytes(new BytesArray(content), XContentType.JSON);
     }
 
+    public void testFromXContent_WithAllNullableFieldsSetToNull_DoesNotThrow() throws IOException {
+        String content = XContentHelper.stripWhitespace("""
+            {
+            "cancelation_requested_at": null,
+                                "canceled_at": null,
+                                "completed_at": null,
+                                "connector": {
+                                    "id": "connector-id",
+                                    "filtering": [
+                                        {
+                                            "active": {
+                                                "advanced_snippet": {
+                                                    "created_at": "2023-12-01T14:18:37.397819Z",
+                                                    "updated_at": "2023-12-01T14:18:37.397819Z",
+                                                    "value": {}
+                                                },
+                                                "rules": [
+                                                    {
+                                                        "created_at": "2023-12-01T14:18:37.397819Z",
+                                                        "field": "_",
+                                                        "id": "DEFAULT",
+                                                        "order": 0,
+                                                        "policy": "include",
+                                                        "rule": "regex",
+                                                        "updated_at": "2023-12-01T14:18:37.397819Z",
+                                                        "value": ".*"
+                                                    }
+                                                ],
+                                                "validation": {
+                                                    "errors": [],
+                                                    "state": "valid"
+                                                }
+                                            },
+                                            "domain": "DEFAULT",
+                                            "draft": {
+                                                "advanced_snippet": {
+                                                    "created_at": "2023-12-01T14:18:37.397819Z",
+                                                    "updated_at": "2023-12-01T14:18:37.397819Z",
+                                                    "value": {}
+                                                },
+                                                "rules": [
+                                                    {
+                                                        "created_at": "2023-12-01T14:18:37.397819Z",
+                                                        "field": "_",
+                                                        "id": "DEFAULT",
+                                                        "order": 0,
+                                                        "policy": "include",
+                                                        "rule": "regex",
+                                                        "updated_at": "2023-12-01T14:18:37.397819Z",
+                                                        "value": ".*"
+                                                    }
+                                                ],
+                                                "validation": {
+                                                    "errors": [],
+                                                    "state": "valid"
+                                                }
+                                            }
+                                        }
+                                    ],
+                                    "index_name": "search-connector",
+                                    "language": "english",
+                                    "pipeline": {
+                                        "extract_binary_content": true,
+                                        "name": "ent-search-generic-ingestion",
+                                        "reduce_whitespace": true,
+                                        "run_ml_inference": false
+                                    },
+                                    "service_type": "service type",
+                                    "configuration": {}
+                                },
+                                "created_at": "2023-12-01T14:18:43.07693Z",
+                                "deleted_document_count": 10,
+                                "error": null,
+                                "id": "HIC-JYwB9RqKhB7x_hIE",
+                                "indexed_document_count": 10,
+                                "indexed_document_volume": 10,
+                                "job_type": "full",
+                                "last_seen": null,
+                                "metadata": {},
+                                "started_at": null,
+                                "status": "canceling",
+                                "total_document_count": 0,
+                                "trigger_method": "scheduled",
+                                "worker_hostname": null
+                                }
+            """);
+
+        ConnectorSyncJob.fromXContentBytes(new BytesArray(content), XContentType.JSON);
+    }
+
     private void assertTransportSerialization(ConnectorSyncJob testInstance) throws IOException {
         ConnectorSyncJob deserializedInstance = copyInstance(testInstance);
         assertNotSame(testInstance, deserializedInstance);