FraunhoferIOSB / FAAAST-Service

FA³ST - Fraunhofer Advanced Asset Administration Shell Tools (for Digital Twins)
Other
58 stars 11 forks source link

Add validation of incoming data on write-operations #494

Closed arnoweiss closed 1 year ago

arnoweiss commented 1 year ago

Describe the bug Value-only serialization fails for a given Submodel. To Reproduce

  1. Start arbitrary FAAAST-Service v0.5.0
    
    public class SimpleAasServer {
    public static void main(String[] args) throws ConfigurationException, AssetConnectionException, MessageBusException, EndpointException {
        Service faaast = new Service(ServiceConfig.builder()
                .core(CoreConfig.builder()
                        .requestHandlerThreadPoolSize(2)
                        .build())
                .persistence(PersistenceInMemoryConfig.builder()
                        .initialModel(new DefaultAssetAdministrationShellEnvironment.Builder().build())
                        .build())
                .endpoint(HttpEndpointConfig.builder().build())
                .messageBus(MessageBusInternalConfig.builder().build())
                .build());
        faaast.start();
    }
    }
2. POST submodel to /submodels endpoint
```json
{
  "modelType": {
    "name": "Submodel"
  },
  "kind": "Instance",
  "semanticId": {
    "keys": [
      {
        "idType": "Custom",
        "type": "ConceptDescription",
        "value": "urn:bamm:io.catenax.single_level_bom_as_planned:1.1.0#SingleLevelBomAsPlanned"
      }
    ]
  },
  "identification": {
    "idType": "Iri",
    "id": "urn:uuid:9d6c1f03-3f3e-3d30-a33d-0895b5f176a7"
  },
  "idShort": "SingleLevelBomAsPlanned",
  "submodelElements": [
    {
      "modelType": {
        "name": "Property"
      },
      "kind": "Instance",
      "semanticId": {
        "keys": [
          {
            "idType": "Custom",
            "type": "ConceptDescription",
            "value": "urn:bamm:io.catenax.single_level_bom_as_planned:1.1.0#catenaXId"
          }
        ]
      },
      "value": "urn:uuid:0733946c-59c6-41ae-9570-cb43a6e4c79e",
      "valueType": "http://www.w3.org/2001/XMLSchema#string",
      "idShort": "catenaXId",
      "description": [
        {
          "language": "en",
          "text": "The Catena-X ID of the given part (e.g. the component), valid for the Catena-X dataspace."
        }
      ],
      "displayName": [
        {
          "language": "en",
          "text": "Catena-X Identifier"
        }
      ]
    },
    {
      "modelType": {
        "name": "SubmodelElementCollection"
      },
      "idShort": "childParts",
      "description": [
        {
          "language": "en",
          "text": "Set of child parts in As-Planned lifecycle phase, of which the given parent object is assembled by (one structural level down)."
        }
      ],
      "displayName": [
        {
          "language": "en",
          "text": "Child Parts"
        }
      ],
      "value": [
        {
          "modelType": {
            "name": "SubmodelElementCollection"
          },
          "idShort": "ChildData",
          "description": [
            {
              "language": "en",
              "text": "Catena-X ID and meta data of the child part."
            }
          ],
          "displayName": [
            {
              "language": "en",
              "text": "Child Data"
            }
          ],
          "value": [
            {
              "modelType": {
                "name": "Property"
              },
              "kind": "Instance",
              "semanticId": {
                "keys": [
                  {
                    "idType": "Custom",
                    "type": "ConceptDescription",
                    "value": "urn:bamm:io.catenax.single_level_bom_as_planned:1.1.0#createdOn"
                  }
                ]
              },
              "value": "2022-02-03",
              "valueType": "http://www.w3.org/2001/XMLSchema#dateTime",
              "idShort": "createdOn",
              "description": [
                {
                  "language": "en",
                  "text": "Timestamp when the relation between the parent part and the child part was created"
                }
              ],
              "displayName": [
                {
                  "language": "en",
                  "text": "Created on"
                }
              ]
            },
            {
              "modelType": {
                "name": "SubmodelElementCollection"
              },
              "idShort": "Quantity",
              "description": [
                {
                  "language": "en",
                  "text": "Comprises the number of objects and the unit of measurement for the respective child objects"
                }
              ],
              "displayName": [
                {
                  "language": "en",
                  "text": "Quantity"
                }
              ],
              "value": [
                {
                  "modelType": {
                    "name": "Property"
                  },
                  "kind": "Instance",
                  "semanticId": {
                    "keys": [
                      {
                        "idType": "Custom",
                        "type": "ConceptDescription",
                        "value": "urn:bamm:io.catenax.single_level_bom_as_planned:1.1.0#quantityNumber"
                      }
                    ]
                  },
                  "value": "1.0",
                  "valueType": "http://www.w3.org/2001/XMLSchema#double",
                  "idShort": "quantityNumber",
                  "description": [
                    {
                      "language": "en",
                      "text": "The number of objects related to the measurement unit"
                    }
                  ],
                  "displayName": [
                    {
                      "language": "en",
                      "text": "Quantity Number"
                    }
                  ]
                },
                {
                  "modelType": {
                    "name": "Property"
                  },
                  "kind": "Instance",
                  "semanticId": {
                    "keys": [
                      {
                        "idType": "Custom",
                        "type": "ConceptDescription",
                        "value": "urn:bamm:io.catenax.single_level_bom_as_planned:1.1.0#measurementUnit"
                      }
                    ]
                  },
                  "value": "null",
                  "valueType": "urn:bamm:io.openmanufacturing:meta-model:2.0.0#curie",
                  "idShort": "measurementUnit",
                  "description": [
                    {
                      "language": "en",
                      "text": "Unit of measurement for the quantity of objects.\n                                                                    If possible, use units from the aspect meta model unit catalog, which is based on the UNECE Recommendation No. 20 \"Codes for Units of Measure used in International Trade\"."
                    }
                  ],
                  "displayName": [
                    {
                      "language": "en",
                      "text": "Measurement Unit"
                    }
                  ]
                }
              ]
            },
            {
              "modelType": {
                "name": "Property"
              },
              "kind": "Instance",
              "semanticId": {
                "keys": [
                  {
                    "idType": "Custom",
                    "type": "ConceptDescription",
                    "value": "urn:bamm:io.catenax.single_level_bom_as_planned:1.1.0#lastModifiedOn"
                  }
                ]
              },
              "value": "2022-02-03",
              "valueType": "http://www.w3.org/2001/XMLSchema#dateTime",
              "idShort": "lastModifiedOn",
              "description": [
                {
                  "language": "en",
                  "text": "Timestamp when the relationship between parent part and child part was last modified."
                }
              ],
              "displayName": [
                {
                  "language": "en",
                  "text": "Last Modified on"
                }
              ]
            },
            {
              "modelType": {
                "name": "Property"
              },
              "kind": "Instance",
              "semanticId": {
                "keys": [
                  {
                    "idType": "Custom",
                    "type": "ConceptDescription",
                    "value": "urn:bamm:io.catenax.single_level_bom_as_planned:1.1.0#childCatenaXId"
                  }
                ]
              },
              "value": "urn:uuid:07cb071f-8716-45fe-89f1-f2f77a1ce93b",
              "valueType": "http://www.w3.org/2001/XMLSchema#string",
              "idShort": "childCatenaXId",
              "description": [
                {
                  "language": "en",
                  "text": "The Catena-X ID of the child object which is assembled into the given parent part."
                }
              ],
              "displayName": [
                {
                  "language": "en",
                  "text": "Catena-X Child ID"
                }
              ]
            }
          ]
        },
        {
          "modelType": {
            "name": "SubmodelElementCollection"
          },
          "idShort": "ChildData",
          "description": [
            {
              "language": "en",
              "text": "Catena-X ID and meta data of the child part."
            }
          ],
          "displayName": [
            {
              "language": "en",
              "text": "Child Data"
            }
          ],
          "value": [
            {
              "modelType": {
                "name": "Property"
              },
              "kind": "Instance",
              "semanticId": {
                "keys": [
                  {
                    "idType": "Custom",
                    "type": "ConceptDescription",
                    "value": "urn:bamm:io.catenax.single_level_bom_as_planned:1.1.0#createdOn"
                  }
                ]
              },
              "value": "2022-02-03",
              "valueType": "http://www.w3.org/2001/XMLSchema#dateTime",
              "idShort": "createdOn",
              "description": [
                {
                  "language": "en",
                  "text": "Timestamp when the relation between the parent part and the child part was created"
                }
              ],
              "displayName": [
                {
                  "language": "en",
                  "text": "Created on"
                }
              ]
            },
            {
              "modelType": {
                "name": "SubmodelElementCollection"
              },
              "idShort": "Quantity",
              "description": [
                {
                  "language": "en",
                  "text": "Comprises the number of objects and the unit of measurement for the respective child objects"
                }
              ],
              "displayName": [
                {
                  "language": "en",
                  "text": "Quantity"
                }
              ],
              "value": [
                {
                  "modelType": {
                    "name": "Property"
                  },
                  "kind": "Instance",
                  "semanticId": {
                    "keys": [
                      {
                        "idType": "Custom",
                        "type": "ConceptDescription",
                        "value": "urn:bamm:io.catenax.single_level_bom_as_planned:1.1.0#quantityNumber"
                      }
                    ]
                  },
                  "value": "1.0",
                  "valueType": "http://www.w3.org/2001/XMLSchema#double",
                  "idShort": "quantityNumber",
                  "description": [
                    {
                      "language": "en",
                      "text": "The number of objects related to the measurement unit"
                    }
                  ],
                  "displayName": [
                    {
                      "language": "en",
                      "text": "Quantity Number"
                    }
                  ]
                },
                {
                  "modelType": {
                    "name": "Property"
                  },
                  "kind": "Instance",
                  "semanticId": {
                    "keys": [
                      {
                        "idType": "Custom",
                        "type": "ConceptDescription",
                        "value": "urn:bamm:io.catenax.single_level_bom_as_planned:1.1.0#measurementUnit"
                      }
                    ]
                  },
                  "value": "null",
                  "valueType": "urn:bamm:io.openmanufacturing:meta-model:2.0.0#curie",
                  "idShort": "measurementUnit",
                  "description": [
                    {
                      "language": "en",
                      "text": "Unit of measurement for the quantity of objects.\n                                                                    If possible, use units from the aspect meta model unit catalog, which is based on the UNECE Recommendation No. 20 \"Codes for Units of Measure used in International Trade\"."
                    }
                  ],
                  "displayName": [
                    {
                      "language": "en",
                      "text": "Measurement Unit"
                    }
                  ]
                }
              ]
            },
            {
              "modelType": {
                "name": "Property"
              },
              "kind": "Instance",
              "semanticId": {
                "keys": [
                  {
                    "idType": "Custom",
                    "type": "ConceptDescription",
                    "value": "urn:bamm:io.catenax.single_level_bom_as_planned:1.1.0#lastModifiedOn"
                  }
                ]
              },
              "value": "2022-02-03",
              "valueType": "http://www.w3.org/2001/XMLSchema#dateTime",
              "idShort": "lastModifiedOn",
              "description": [
                {
                  "language": "en",
                  "text": "Timestamp when the relationship between parent part and child part was last modified."
                }
              ],
              "displayName": [
                {
                  "language": "en",
                  "text": "Last Modified on"
                }
              ]
            },
            {
              "modelType": {
                "name": "Property"
              },
              "kind": "Instance",
              "semanticId": {
                "keys": [
                  {
                    "idType": "Custom",
                    "type": "ConceptDescription",
                    "value": "urn:bamm:io.catenax.single_level_bom_as_planned:1.1.0#childCatenaXId"
                  }
                ]
              },
              "value": "urn:uuid:2c57b0e9-a653-411d-bdcd-64787e9fd3a7",
              "valueType": "http://www.w3.org/2001/XMLSchema#string",
              "idShort": "childCatenaXId",
              "description": [
                {
                  "language": "en",
                  "text": "The Catena-X ID of the child object which is assembled into the given parent part."
                }
              ],
              "displayName": [
                {
                  "language": "en",
                  "text": "Catena-X Child ID"
                }
              ]
            }
          ]
        },
        {
          "modelType": {
            "name": "SubmodelElementCollection"
          },
          "idShort": "ChildData",
          "description": [
            {
              "language": "en",
              "text": "Catena-X ID and meta data of the child part."
            }
          ],
          "displayName": [
            {
              "language": "en",
              "text": "Child Data"
            }
          ],
          "value": [
            {
              "modelType": {
                "name": "Property"
              },
              "kind": "Instance",
              "semanticId": {
                "keys": [
                  {
                    "idType": "Custom",
                    "type": "ConceptDescription",
                    "value": "urn:bamm:io.catenax.single_level_bom_as_planned:1.1.0#createdOn"
                  }
                ]
              },
              "value": "2022-02-03",
              "valueType": "http://www.w3.org/2001/XMLSchema#dateTime",
              "idShort": "createdOn",
              "description": [
                {
                  "language": "en",
                  "text": "Timestamp when the relation between the parent part and the child part was created"
                }
              ],
              "displayName": [
                {
                  "language": "en",
                  "text": "Created on"
                }
              ]
            },
            {
              "modelType": {
                "name": "SubmodelElementCollection"
              },
              "idShort": "Quantity",
              "description": [
                {
                  "language": "en",
                  "text": "Comprises the number of objects and the unit of measurement for the respective child objects"
                }
              ],
              "displayName": [
                {
                  "language": "en",
                  "text": "Quantity"
                }
              ],
              "value": [
                {
                  "modelType": {
                    "name": "Property"
                  },
                  "kind": "Instance",
                  "semanticId": {
                    "keys": [
                      {
                        "idType": "Custom",
                        "type": "ConceptDescription",
                        "value": "urn:bamm:io.catenax.single_level_bom_as_planned:1.1.0#quantityNumber"
                      }
                    ]
                  },
                  "value": "1.0",
                  "valueType": "http://www.w3.org/2001/XMLSchema#double",
                  "idShort": "quantityNumber",
                  "description": [
                    {
                      "language": "en",
                      "text": "The number of objects related to the measurement unit"
                    }
                  ],
                  "displayName": [
                    {
                      "language": "en",
                      "text": "Quantity Number"
                    }
                  ]
                },
                {
                  "modelType": {
                    "name": "Property"
                  },
                  "kind": "Instance",
                  "semanticId": {
                    "keys": [
                      {
                        "idType": "Custom",
                        "type": "ConceptDescription",
                        "value": "urn:bamm:io.catenax.single_level_bom_as_planned:1.1.0#measurementUnit"
                      }
                    ]
                  },
                  "value": "null",
                  "valueType": "urn:bamm:io.openmanufacturing:meta-model:2.0.0#curie",
                  "idShort": "measurementUnit",
                  "description": [
                    {
                      "language": "en",
                      "text": "Unit of measurement for the quantity of objects.\n                                                                    If possible, use units from the aspect meta model unit catalog, which is based on the UNECE Recommendation No. 20 \"Codes for Units of Measure used in International Trade\"."
                    }
                  ],
                  "displayName": [
                    {
                      "language": "en",
                      "text": "Measurement Unit"
                    }
                  ]
                }
              ]
            },
            {
              "modelType": {
                "name": "Property"
              },
              "kind": "Instance",
              "semanticId": {
                "keys": [
                  {
                    "idType": "Custom",
                    "type": "ConceptDescription",
                    "value": "urn:bamm:io.catenax.single_level_bom_as_planned:1.1.0#lastModifiedOn"
                  }
                ]
              },
              "value": "2022-02-03",
              "valueType": "http://www.w3.org/2001/XMLSchema#dateTime",
              "idShort": "lastModifiedOn",
              "description": [
                {
                  "language": "en",
                  "text": "Timestamp when the relationship between parent part and child part was last modified."
                }
              ],
              "displayName": [
                {
                  "language": "en",
                  "text": "Last Modified on"
                }
              ]
            },
            {
              "modelType": {
                "name": "Property"
              },
              "kind": "Instance",
              "semanticId": {
                "keys": [
                  {
                    "idType": "Custom",
                    "type": "ConceptDescription",
                    "value": "urn:bamm:io.catenax.single_level_bom_as_planned:1.1.0#childCatenaXId"
                  }
                ]
              },
              "value": "urn:uuid:aad27ddb-43aa-4e42-98c2-01e529ef127c",
              "valueType": "http://www.w3.org/2001/XMLSchema#string",
              "idShort": "childCatenaXId",
              "description": [
                {
                  "language": "en",
                  "text": "The Catena-X ID of the child object which is assembled into the given parent part."
                }
              ],
              "displayName": [
                {
                  "language": "en",
                  "text": "Catena-X Child ID"
                }
              ]
            }
          ]
        }
      ]
    }
  ],
  "description": [
    {
      "language": "en",
      "text": "The single-level Bill of Material represents one sub-level of an assembly and does not include any lower-level subassemblies. In As-Planned lifecycle state all variants are covered (\"120% BoM\").\n                    If multiple versions of child parts exist that can be assembled into the same parent part, all versions of the child part are included in the BoM.\n                    If there are multiple suppliers for the same child part, each supplier has an entry for their child part in the BoM."
    }
  ]
}
  1. GET {{baseUrl}}/submodels/dXJuOnV1aWQ6OWQ2YzFmMDMtM2YzZS0zZDMwLWEzM2QtMDg5NWI1ZjE3NmE3/submodel?content=value

Expected behavior

{
  "catenaXId": "urn:uuid:0733946c-59c6-41ae-9570-cb43a6e4c79e",
  "childParts": {
    "ChildData": {
      "createdOn": "2022-02-03",
      "Quantity": {
        "quantityNumber": 1.0,
        "measurementUnit": "null"
      },
      "lastModifiedOn":"2022-02-03",
      "childCatenaXId": "urn:uuid:07cb071f-8716-45fe-89f1-f2f77a1ce93b"
    },
    "ChildData": {
      "createdOn": "some other date",
      "Quantity": {
        "quantityNumber": 12432,
        "measurementUnit": "null"
      },
      "lastModifiedOn":"someotherdate",
      "childCatenaXId": "someotheruuid"
    }
  }
}

Output

{
  "success" : false,
  "messages" : [ {
    "messageType" : "Exception",
    "text" : "serialization failed",
    "code" : "",
    "timestamp" : "2023-06-29T16:18:52.790+00:00"
  } ]
}

Additional context I suspect that this is because the Submodel is invalid itself. Thevalue-only-serialization would produce invalid json due to the twice-bound key "ChildData". Is there any way to verify this?

mjacoby commented 1 year ago

As you already suspected, the reason for the exception is that the serialization fails because of the duplicate idShort which makes the AAS model invalid. Unfortunately, the currently is no check for uniqueness of identifiers- A similar issue has already been raised previously (see https://github.com/FraunhoferIOSB/FAAAST-Service/issues/326).

We have this in our backlog but are still hoping that validation like this will be part of aas4j. However, currently it does not seem like proper validation will be available in aa4j in the near future so we might rethink implementing this ourselves.

Another thing I noticed during debugging your example is that currently FA³ST support model validation only for the initial loading of a model but does not apply this when the model is updated via the API. This is definitely something that will go into our backlog.

Unfortunately, this means that your problem will currently remain as-is as adding this kind of validation will probably not be our top priority right now because it "only" happens when the model is invalid in the first place. For this reason I'm also changing the label from bug to enhancement but will keep the issue open.

mjacoby commented 1 year ago

I managed to address this issue quicker than anticipated and already published an update on validation with https://github.com/FraunhoferIOSB/FAAAST-Service/commit/9520c84f39fe5eea007a19b98f8b04b8f2711dc4

FA³ST Service now supports validation not only on loading the initial model at startup but also when creating or updating elements via the API. Additionally, new validation checks for uniqueness of idShort and identifiers have been added together with the capability to configure which validation is performed at which step in detail (see documentation https://faaast-service.readthedocs.io/en/latest/gettingstarted/configuration/).

Please close the issue if this solves your problem and if not please provide details what is missing or not working properly.

arnoweiss commented 1 year ago

Thanks, this works in the sense that I'm getting a more expressive error message now with all relevant validation errors. However, a failed validation is no HTTP 500 as I understand it - after all, the server is acting as expected. Shouldn't it rather be deemed a bad request?

If you want, I can close this ticket and open a separate one since the new feature is functional.

mjacoby commented 1 year ago

Yes, you are right, HTTP 500 seems not appropriate here. better suited would be 400 Bad Request or even 422 Unprocessable Entity meaning "The request was well-formed but was unable to be followed due to semantic errors". Any preferences from your side which code to return?

arnoweiss commented 1 year ago

No strong preference. I've never seen 422 in the wild so 400 with the validation errors attached should suffice. Feel free to close this ticket when either is implemented.

mjacoby commented 1 year ago

FA³ST Service now correctly returns 400 Bad Request when a request to create/update an element is made with invalid content. Details about the validation errors are provided in the response body.