API client bouwen/genereren, wat met optionele velden?

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • Chris_147
  • Registratie: Juni 2005
  • Laatst online: 25-07 15:43
Beetje vervolg van dit topic: Swagger generated Java client objecten bevatten niet alles

Het gaat dus om deze API: https://testersuite.stopl...854ecfbc9-testersuite-api

Via OpenAPI-Generator heb ik daarvoor een Java API client gegenereerd.
Ik wil bvb de Producten ophalen. Nu kan men custom velden definiëren voor producten.
Een product heeft volgens de API volgende attributes: businessId, shortDescription, longDescription en customFields.

YAML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    Product:
      type: object
      title: Product
      properties:
        id:
          description: the id
          example: '1'
          type: string
          readOnly: true
        type:
          type: string
          enum:
            - product
        attributes:
          type: object
          properties:
            businessId:
              example: PRD1
              type: string
              readOnly: true
            shortDescription:
              example: A short description
              type: string
            longDescription:
              example: A long description
              type: string
            customFields:
              type: object
              deprecated: true
              description: |-
                Contains custom fields for field types: stringSmall, stringLarge and multiOption.
                DEPRECATION NOTICE: please use the customField_* fields directly defined in attributes for new situations instead of this field.


In de json die ik terugkrijg, zijn de customfields echter ook toegevoegd:
Java:
1
{"businessId":"PRD2","shortDescription":"Feature A","longDescription":"","customFields":{"jiraLink":null},"customField_17":null}

In de ene omgeving is dit customField_17 in een andere _18 etc. Soms zijn er geen.

De gegenereerde API client heeft code om de elementen van de json te valideren:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  public static void validateJsonElement(JsonElement jsonElement) throws IOException {
      if (jsonElement == null) {
        if (!ProductAttributes.openapiRequiredFields.isEmpty()) { // has required fields but JSON element is null
          throw new IllegalArgumentException(String.format("The required field(s) %s in ProductAttributes is not found in the empty JSON string", ProductAttributes.openapiRequiredFields.toString()));
        }
      }

      Set<Map.Entry<String, JsonElement>> entries = jsonElement.getAsJsonObject().entrySet();
      // check to see if the JSON string contains additional fields
      for (Map.Entry<String, JsonElement> entry : entries) {
        if (!ProductAttributes.openapiFields.contains(entry.getKey())) {
          throw new IllegalArgumentException(String.format("The field `%s` in the JSON string is not defined in the `ProductAttributes` properties. JSON: %s", entry.getKey(), jsonElement.toString()));
        }
      }
        JsonObject jsonObj = jsonElement.getAsJsonObject();
      if ((jsonObj.get("businessId") != null && !jsonObj.get("businessId").isJsonNull()) && !jsonObj.get("businessId").isJsonPrimitive()) {
        throw new IllegalArgumentException(String.format("Expected the field `businessId` to be a primitive type in the JSON string but got `%s`", jsonObj.get("businessId").toString()));
      }
      if ((jsonObj.get("shortDescription") != null && !jsonObj.get("shortDescription").isJsonNull()) && !jsonObj.get("shortDescription").isJsonPrimitive()) {
        throw new IllegalArgumentException(String.format("Expected the field `shortDescription` to be a primitive type in the JSON string but got `%s`", jsonObj.get("shortDescription").toString()));
      }
      if ((jsonObj.get("longDescription") != null && !jsonObj.get("longDescription").isJsonNull()) && !jsonObj.get("longDescription").isJsonPrimitive()) {
        throw new IllegalArgumentException(String.format("Expected the field `longDescription` to be a primitive type in the JSON string but got `%s`", jsonObj.get("longDescription").toString()));
      }
  }

Daar gooit hij natuurlijk de exception op regel 12, want customfield_17 is onbekend.

Wat moet ik doen?

1) Is de beschrijving correct of moet ik de leverancier vragen om dit aan te passen? En zo ja, hoe?
2) Als ik het aan mijn kant moet aanpassen, hoe?
Kan ik openapi-generator via een commandline argument zeggen om hier mee rekening te houden, of moet ik handmatig voor ieder object als Product aanpassen? En wederom: hoe moet ik het dan juist aanpassen?

Beste antwoord (via Chris_147 op 03-05-2024 10:48)


  • urk_forever
  • Registratie: Juni 2001
  • Laatst online: 15:54
Hier lees ik dat je die exceptie op onbekende velden kan uitzetten.

Hail to the king baby!

Alle reacties


Acties:
  • 0 Henk 'm!

  • SPee
  • Registratie: Oktober 2001
  • Laatst online: 15:48
Chris_147 schreef op donderdag 2 mei 2024 @ 15:33:

In de json die ik terugkrijg, zijn de customfields echter ook toegevoegd:
Java:
1
{"businessId":"PRD2","shortDescription":"Feature A","longDescription":"","customFields":{"jiraLink":null},"customField_17":null}

In de ene omgeving is dit customField_17 in een andere _18 etc. Soms zijn er geen.

De gegenereerde API client heeft code om de elementen van de json te valideren:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  public static void validateJsonElement(JsonElement jsonElement) throws IOException {
      if (jsonElement == null) {
        if (!ProductAttributes.openapiRequiredFields.isEmpty()) { // has required fields but JSON element is null
          throw new IllegalArgumentException(String.format("The required field(s) %s in ProductAttributes is not found in the empty JSON string", ProductAttributes.openapiRequiredFields.toString()));
        }
      }

      Set<Map.Entry<String, JsonElement>> entries = jsonElement.getAsJsonObject().entrySet();
      // check to see if the JSON string contains additional fields
      for (Map.Entry<String, JsonElement> entry : entries) {
        if (!ProductAttributes.openapiFields.contains(entry.getKey())) {
          throw new IllegalArgumentException(String.format("The field `%s` in the JSON string is not defined in the `ProductAttributes` properties. JSON: %s", entry.getKey(), jsonElement.toString()));
        }
      }
        JsonObject jsonObj = jsonElement.getAsJsonObject();
      if ((jsonObj.get("businessId") != null && !jsonObj.get("businessId").isJsonNull()) && !jsonObj.get("businessId").isJsonPrimitive()) {
        throw new IllegalArgumentException(String.format("Expected the field `businessId` to be a primitive type in the JSON string but got `%s`", jsonObj.get("businessId").toString()));
      }
      if ((jsonObj.get("shortDescription") != null && !jsonObj.get("shortDescription").isJsonNull()) && !jsonObj.get("shortDescription").isJsonPrimitive()) {
        throw new IllegalArgumentException(String.format("Expected the field `shortDescription` to be a primitive type in the JSON string but got `%s`", jsonObj.get("shortDescription").toString()));
      }
      if ((jsonObj.get("longDescription") != null && !jsonObj.get("longDescription").isJsonNull()) && !jsonObj.get("longDescription").isJsonPrimitive()) {
        throw new IllegalArgumentException(String.format("Expected the field `longDescription` to be a primitive type in the JSON string but got `%s`", jsonObj.get("longDescription").toString()));
      }
  }

Daar gooit hij natuurlijk de exception op regel 12, want customfield_17 is onbekend.

Wat moet ik doen?

1) Is de beschrijving correct of moet ik de leverancier vragen om dit aan te passen? En zo ja, hoe?
2) Als ik het aan mijn kant moet aanpassen, hoe?
Kan ik openapi-generator via een commandline argument zeggen om hier mee rekening te houden, of moet ik handmatig voor ieder object als Product aanpassen? En wederom: hoe moet ik het dan juist aanpassen?
Ik denk niet dat je de leverancier zover krijgt dat hij die optionele velden niet meestuurt of expliciet opgeeft. Soms kan dat niet eens, bijvoorbeeld als een klant zelf velden kan invullen. Dan worden die extra meegestuurd, maar zitten niet in de stricte definitie. Daarom kiezen ze ook voor JSON, omdat het daar zo makkelijk kan. ;)

Je moet tegen de generator zeggen dat die niet zo strict moet zijn, maar de extra velden moet accepteren.
Ik zie dat het via die tool via de parameters
code:
1
--skip-validate-spec --strict-spec false
zou kunnen.
Maar dan krijg je die waardes niet in je object. Daarvoor moet je weer wat anders doen. Wat vaak wordt gedaan is dan een Map<String, ?> voor die extra properties in het Object waar die dan in komen. Geen idee hoe dat via die tool gedaan kan worden.

Dat is wat ik in een korte blik zo opmerk.
Zelf zou ik die tool maar 1 keer uitvoeren en in het vervolg handmatig aanpassen. Zo vaak zal die API niet wijzigen. En enkele Objecten iets aanpassen is dan makkelijk te doen. Met het juiste framework is dat geen probleem.

let the past be the past.


Acties:
  • Beste antwoord
  • 0 Henk 'm!

  • urk_forever
  • Registratie: Juni 2001
  • Laatst online: 15:54
Hier lees ik dat je die exceptie op onbekende velden kan uitzetten.

Hail to the king baby!


Acties:
  • 0 Henk 'm!

  • Chris_147
  • Registratie: Juni 2005
  • Laatst online: 25-07 15:43
@urk_forever disallowAdditionalPropertiesIfNotPresent was inderdaad de oplossing!
Bedankt.

@SPee ik heb inderdaad overwogen om slechts 1x de generator uit te voeren.
Ik hoop echter dat de API nog sterk aangepast gaat worden in de toekomst, want op dit moment vind ik hem bijna niet werkbaar. Lijkt gemaakt te zijn om als ontwikkelaar zo gemakkelijk mogelijk een API aan te bieden, zonder goed na te denken of het aanroepen gemakkelijk is voor de client.