#1 2025-08-08 11:52:53

Luwo
Member
Registered: 2025-08-08
Posts: 10

TOpenApiParser fails on this OpenApi-Schema...

Bonjour Dear Arnaud, dear Community

I try to generate Rest-Classes out of this OpenApi-Json-Schema ("1.1-OnPrem"): OpenAPI description v1.1


1) fetched the OpenApi-Definition

mget https://developer.jtl-software.com/_spec/products/erpapi/@1.1-onprem/openapi.json?download  /o openapi_v1.1.json

2) This is the Code

  ApiParser := TOpenApiParser.Create('JTL');
  try

    ApiParser.Options := [];
    ApiParser.ParseFile('openapi_v1.1.json');
    ApiParser.ExportToDirectory( 'c:\temp' );

  except  
    on E : Exception do writeln('Exception: '+E.Message);
  end;

...but it failes with this exception

 TOpenApiParser.ParseRecordDefinition: JTL.Data.Contracts.Keys.ArtikelKey is integer, not object 

what I already did:

  • Validated this schema and it seems to be correct

  • The former schema ("1.0-OnPrem") runs fine  - OpenAPI description v1.0 (maybe of less references? idk)


I could not figure out what the problem causes. Could sombody help here?


Many Thanks in advance
Greetings Luwo

Offline

#2 2025-08-08 18:50:31

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 15,181
Website

Re: TOpenApiParser fails on this OpenApi-Schema...

So we first have an integer:

      "JTL.Data.Contracts.Keys.ArtikelKey": {
        "type": "integer",
        "additionalProperties": false,
        "format": "int32"
      },

which is then referenced as an object:

      "JTL.Shared.Patterns.Maybe.MayOfJTL.Data.Contracts.Keys.ArtikelKey": {
        "type": "object",
        "allOf": [
          {
            "$ref": "#/components/schemas/JTL.Data.Contracts.Keys.ArtikelKey"
          }
        ],
        "additionalProperties": false
      },

Then as an array:

      "JTL.Shared.Patterns.Maybe.MayOfSystem.Collections.Generic.ListOfJTL.Data.Contracts.Keys.ArtikelKey": {
        "type": "array",
        "items": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/JTL.Data.Contracts.Keys.ArtikelKey"
          }
        },
        "additionalProperties": false
      },

I understand the concept to map an integer as an array (i.e. mapped into an array of integer) - but I don't know how to change an integer as an object...
Or perhaps just use the original integer type (since in DotNet an integer is also an object)?

The worse is that those "JTL.Shared.Patterns.Maybe.*" types are in fact NOT used anywhere in the schema.
The framework generating this OpenAPI specs sounds a bit overlooking its output.

Have you any idea?
I don't know what to do with this.

Offline

#3 2025-08-09 07:39:53

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 15,181
Website

Re: TOpenApiParser fails on this OpenApi-Schema...

So I will just replace this definition with a "variant" type in our code: which may be whatever is needed from JSON (integer/array/object/string...).
https://github.com/synopse/mORMot2/commit/e226f4417
Since the "JTL.Shared.Patterns.Maybe.*" types are not used, it won't hurt.
From my side, it did the trick, and the units are generated and do compile.

I have added this OpenAPI specs to the reference set run during regression tests:
https://github.com/synopse/mORMot2/commit/0a60cd101

Offline

#4 2025-08-11 11:08:19

Luwo
Member
Registered: 2025-08-08
Posts: 10

Re: TOpenApiParser fails on this OpenApi-Schema...

Salut Arnaud!

Tu es mon héros officiel du jour! :-)
you are my officially "hero of the day"! :-)

great! generation runs smile
-------------------

If you don´t mind, I have another issue with this schema:

IDK why, but the "securityScheme" seems to appear nowhere

    "securitySchemes": {
      "Wawi": {
        "type": "apiKey",
        "description": "Fill Wawi Token here",
        "name": "Authorization",
        "in": "header"
      }

It is just a header-field "Authorization" with the value "Wawi <guid-string>" as described in the schema-comment at the beginning.


Would it be a good Idea to define a kind of "default-header" in the Client-Class?
i.e. in case of

  • the needed header isn´t specified that way that the generator could find it

  • the format of the value itself seems to be a bit "uncommon" (string + GUID)

  • to simplify the calls (I don´t have to add this header in each function, though it is needed in every function and is always the same)


Maybe something like this:

procedure TForm1.CreateApiClient;
begin
 FJsonClient := TJsonClient.Create(FBaseUrl); 
 FClient := TJtlClient.Create(FJsonClient) ;
 
 // also because of the format of the value, what is "Wawi " + GUID-String 
 FClient.AddDefaultHeader('Authorization', 'Wawi '+GetApiKey);
end;

What do you think?


greetings

Offline

#5 2025-08-11 12:06:27

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 15,181
Website

Re: TOpenApiParser fails on this OpenApi-Schema...

AFAICT you already have IJsonClient.SetBearer() for this purpose.

Offline

#6 2025-08-11 15:08:44

Luwo
Member
Registered: 2025-08-08
Posts: 10

Re: TOpenApiParser fails on this OpenApi-Schema...

Yes Arnaud, you're absolutely right.

That's the standard that 99.99% of API developers probably adhere to smile

Unfortunately, in this case, the bearer isn't called "Bearer" but "Wawi"

In other words, this code here (from "mormot.net.client.pas")

    case Scheme of
      wraBasic:
       ...
      wraBearer:
        SockSendLine(['Authorization: Bearer ', Token]);

It should look like this, but that's nonsense.

    case Scheme of
      wraBasic:
       ...
      wraBearer:
        SockSendLine(['Authorization: Wawi ', Token]);

I have no idea why the name was chosen that way. But I'm sure there's a reason for it...   ...but I just don't know it :-)


greetings

Offline

#7 2025-08-11 18:28:12

danielkuettner
Member
From: Germany
Registered: 2014-08-06
Posts: 401

Re: TOpenApiParser fails on this OpenApi-Schema...

Luwo wrote:

I have no idea why the name was chosen that way. But I'm sure there's a reason for it...   ...but I just don't know it :-)

Wawi is a short for Warenwirtschaft.
JTL-Software-GmbH are Germans. For such Wawi is a fundamental word in their business language (just like beer for English -> beer -> bearer)

Offline

#8 2025-08-12 11:22:42

Luwo
Member
Registered: 2025-08-08
Posts: 10

Re: TOpenApiParser fails on this OpenApi-Schema...

Hallo Daniel smile

deutsch
Danke für deinen Hinweis. Und ja, natürlich hast Du Recht. Mir ist schon klar, dass "Wawi" für "Warenwirtschaft" steht. (Und jetzt auch nochmal für alle Nicht-German-Native-Mitleser erklärt smile )
Mir ging es um einen anderen Punkt, WARUM sollte man einen Quasi-Standard derart verbiegen. Wo ist da der Sinn? Wird vielleicht später eine weitere Securtiy-Schicht drübergestülpt? IDK.
Ich werde das mal direkt beim Hersteller in Erfahrung bringen und dann hier auch was dazu schreiben.

english
Thanks for your comment. And yes, of course you're right. I'm well aware that "Wawi" stands for "warehouse management." (And now also explained for all non-German readers smile )
I was talking about a different point: WHY would you bend a quasi-standard like that? What's the point? Will another security layer be added later? IDK.
I'll find out directly from the manufacturer and then write something about it here.


PS: "beer" -> "bearer" - you made my day smile

Offline

#9 2025-08-13 10:10:23

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 15,181
Website

Re: TOpenApiParser fails on this OpenApi-Schema...

To beer or not to beer, that is no question, and I have added the method to IJsonClient:
https://github.com/synopse/mORMot2/commit/4bc699044

Offline

#10 2025-08-22 17:46:22

Luwo
Member
Registered: 2025-08-08
Posts: 10

Re: TOpenApiParser fails on this OpenApi-Schema...

Dear Arnaud, dear community,

I need further help, I could not figure out whats wrong here.

The most types are named perfect, but there are a few "anonymous" types, that seems to be left unresolved.

procedure RegisterRtti;
begin
  TRttiJson.RegisterCustomEnumValues([
    TypeInfo(TInt32), nil, @_TInt32]);
  Rtti.RegisterFromText([
    TypeInfo(TJTLEntitiesDboTableFeldTypEntity), _TJTLEntitiesDboTableFeldTypEntity,
    TypeInfo(TJTLEntitiesDboTableAttributSpracheEntity), _TJTLEntitiesDboTableAttributSpracheEntity,
    TypeInfo(TJTLEntitiesDboTableAttributEntity), _TJTLEntitiesDboTableAttributEntity,
    TypeInfo(TDtoJtl9), _TDtoJtl9,
    TypeInfo(TJTLWawiRestContractsModelsV1CommonCreateAddress), _TJTLWawiRestContractsModelsV1CommonCreateAddress,
    TypeInfo(TDtoJtl10), _TDtoJtl10,
    TypeInfo(TJTLWawiRestContractsModelsV1CommonCreateDimensions), _TJTLWawiRestContractsModelsV1CommonCreateDimensions,
    TypeInfo(TDtoJtl11), _TDtoJtl11,
    TypeInfo(TJTLWawiRestContractsModelsV1CommonUpdateAddress), _TJTLWawiRestContractsModelsV1CommonUpdateAddress,
    TypeInfo(TDtoJtl12), _TDtoJtl12,
    TypeInfo(TJTLWawiRestContractsModelsV1CommonUpdateDimensions), _TJTLWawiRestContractsModelsV1CommonUpdateDimensions,
 ...

...the generator seems to skip some declarations and leave them as an number (counter?).

The code

  // from #/components/schemas/JTL.Wawi.Rest.Contracts.Models.V1.SalesOrder.UpdateSalesOrderPaymentDetail
  TDtoJtl39 = packed record
    PaymentMethodId: integer;
    CurrencyIso: RawUtf8;
    CurrencyFactor: double;
    PaymentTarget: integer;
    CashDiscount: double;
    CashDiscountDays: integer;
  end;
  PDtoJtl39 = ^TDtoJtl39;

...is expected to look like this:

  // from #/components/schemas/JTL.Wawi.Rest.Contracts.Models.V1.SalesOrder.UpdateSalesOrderPaymentDetail
  TJTLWawiRestContractsModelsV1SalesOrderUpdateSalesOrderPaymentDetail = packed record
    PaymentMethodId: integer;
    CurrencyIso: RawUtf8;
    CurrencyFactor: double;
    PaymentTarget: integer;
    CashDiscount: double;
    CashDiscountDays: integer;
  end;
  PJTLWawiRestContractsModelsV1SalesOrderUpdateSalesOrderPaymentDetail = ^TJTLWawiRestContractsModelsV1SalesOrderUpdateSalesOrderPaymentDetail;

...anyone any idea whats going wrong here?


Thanks in advance

Offline

#11 2025-08-23 14:18:05

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 15,181
Website

Re: TOpenApiParser fails on this OpenApi-Schema...

It is not a bug/wrong, it is a feature. wink

The huge names are just replaced with a number to please the compiler.
The original name - I so much hate the DotNet naming spaces ! - is still in comments.

Offline

#12 2025-08-25 13:21:17

Luwo
Member
Registered: 2025-08-08
Posts: 10

Re: TOpenApiParser fails on this OpenApi-Schema...

Dear Arnaud,

ok, now i see. :-)

My compiler is D12.2 and i just made a lot of tests how long the name of an record could be.

...made successfully tests with length up to 1500 chars if just declared. But if it comes to compile a program, that really USES this long-named-record, the compiler sometimes(!) failes with L902-internal error.
After a new "build-all" (not only hit F9) it compiles - looks like a compiler-cache-problem.

// recordname with 1000 chars - multi-line because of 1024-max-line-length ;)
type 
T1000_012345678901234567890123456789012345678901234567890123456798012345678901234567890123456789012345678901234567890123456789123456789012345678901234567890123456789012345678901234567980123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234123456789123457891235648979879879817987198765716871681768716154617671321654654321321654654985643216565465465465465465498798798798798732132132132165465465465465465465_SKYNETISNEAR_9876543210000EOM
= packed record
 ask:string;
end;


procedure doSearch;
var x1000:T1000_012345678901234567890123456789012345678901234567890123456798012345678901234567890123456789012345678901234567890123456789123456789012345678901234567890123456789012345678901234567980123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234567801234123456789123457891235648979879879817987198765716871681768716154617671321654654321321654654985643216565465465465465465498798798798798732132132132165465465465465465465_SKYNETISNEAR_9876543210000EOM;
begin
  x1000.ask:='Where is Sarah Connor?';
end;


Further tests shows, that a length about 100-150 chars works fine.

What do you think about a patch like this in the create-method in mormo.net.openapi.pas?

constructor TPascalCustomType.Create(aParser: TOpenApiParser);
var
 iMaxLen: Integer;
begin

 // newer Compilers could handle Recordnames longer than 70
 if (opoGenerateOldDelphiCompatible in aParser.fOptions) then iMaxLen:= 70
                                                         else iMaxLen:= 120;

  // inheriting constructor should have set fName
  if fName = '' then
    EOpenApi.RaiseUtf8('%.Create(name?)', [self]);
  fParser := aParser;
  if (fName[1] = '#') or // ensure type name is not too long
     (length(fName) > iMaxLen) or // Arnaud.orig: (length(fName) > 70) or
     // DotNet generates e.g. /schemas/System.Tupple`2[[...,...]]`
     (PosExChar('`', fName) > 0) then
  begin
    inc(fParser.fDtoCounter); // TDto### is simple and convenient
    Make(['TDto', fParser.Name, fParser.fDtoCounter], fPascalName);
  end
  else
    fPascalName := 'T' + SanitizePascalName(fName, {keywordcheck:}false);
end;

...would this be a suitable way?


Thanks in advance
Luwo


PS:
i am curious:  where do you have the length of 70 from? i found no relable information about how long a recordname could/should be.

Offline

#13 2025-08-25 21:54:48

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 15,181
Website

Re: TOpenApiParser fails on this OpenApi-Schema...

For readability, I try to keep the source code lines around 80 chars maximum.
So 70 chars sounds like something very generous.
I usually expect type names to be no more than 20 chars long.

Offline

#14 2025-09-09 13:27:05

Luwo
Member
Registered: 2025-08-08
Posts: 10

Re: TOpenApiParser fails on this OpenApi-Schema...

Dear Arnaud, dear community smile

So, here I am again with a new little request. Just a small suggestion to improve the openapi-generator.  smile

In my case (with the jtl-schema) the endpoint-methods are incredibly bloated because of the 5 default-headers.

  • api-version

  • x-appid

  • x-appversion

  • x-runas

  • X-SessionId


ALL header-parameters are passed to EVERY endpoint-method, which makes the calls confusing and unnecessarily bloated.
Wouldn't it be nice if we could define which header-parameters should be ignored for the method calls?

To make it more clear what I mean:

a) In the generator, a list of Header-parameters could be defined so that they do NOT have to be specified for every endpoint

 ApiParser := TOpenApiParser.Create('JTL');
  try

    ApiParser.Options := [];

    // all the (to-be-ignored) header-parameters
    ApiParser.DefineDefaultHeaderParameters([ 'api-version', 'x-appid', 'x-appversion', 'x-runas', 'x-sessionID' ]); 

    ApiParser.ParseFile('openapi_v1.1.json');
    ApiParser.ExportToDirectory( 'c:\temp' );

  except  
    on E : Exception do writeln('Exception: '+E.Message);
  end;

b) And in the client, the default header(s) only needs to be set ONCE

  procedure TForm1.CreateApiClient;
  begin
	 FJsonClient := TJsonClient.Create(FBaseUrl); 
	 FClient := TJtlClient.Create(FJsonClient) ;
	 
	 // also because of the format of the value, what is "Wawi " + GUID-String 
	 FClient.AddDefaultHeader('Authorization', 'Wawi '+GetApiKey);

	 FClient.AddDefaultHeader('api-version', '<value>');
	 FClient.AddDefaultHeader('x-appid', '<value>');
	 FClient.AddDefaultHeader('x-appversion', '<value>');
	 FClient.AddDefaultHeader('x-runas', '<value>');
	 FClient.AddDefaultHeader('x-sessionID', '<value>');
  end;

What do you think?


Greetings Luwo

Offline

#15 2025-09-09 19:33:47

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 15,181
Website

Re: TOpenApiParser fails on this OpenApi-Schema...

Do you have some schema to look at?

Offline

#16 2025-09-10 09:27:06

Luwo
Member
Registered: 2025-08-08
Posts: 10

Re: TOpenApiParser fails on this OpenApi-Schema...

of course, it is the schema v1.1 from the first post in this thread.

mget https://developer.jtl-software.com/_spec/products/erpapi/@1.1-onprem/openapi.json?download  /o openapi_v1.1.json

unfortunately the current schema-version v1.2 does not work

mget https://developer.jtl-software.com/_spec/products/erpapi/@1.2-onprem/openapi.json?download   /o openapi_v1.2.json

Exception: TPascalOperation.Body: missing {customerId} in [/customers({customerId})/bankaccounts]

Offline

#17 2025-09-10 18:17:09

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 15,181
Website

Re: TOpenApiParser fails on this OpenApi-Schema...

The definition of /customers({customerId})/bankaccounts is weird/inconsistent/incorrect/buggy/wrong.
It is a path parameter, but it is located as "in": "query" - it should be "in": "path".

So IMHO this schema is incorrect.

Grok wrote:

Does the OpenAPI Standard Allow This?
No, the OpenAPI standard does not allow this mismatch. The OpenAPI Specification (version 3.0 and later) explicitly states that the in field must correspond to where the parameter is used in the request (e.g., path, query, header, or cookie). Defining a path parameter like {customerId} as "in": "query" is invalid and violates the specification. Tools that strictly adhere to the OpenAPI standard (e.g., validators like Swagger Parser or client generators like OpenAPI Generator) will likely flag this as an error or fail to process it correctly.
The OpenAPI Specification (OAS 3.0.3, for example) states: The in property indicates the location of the parameter. Possible values are "query", "header", "path", or "cookie".

Unless the client should be somewhat more tolerant that the standard... but if we start into this direction, where will it end, as it risks generating incorrect or unreliable code?
What do you think?

I have added the opoRelaxedSchema option, which should fix this problem:
https://github.com/synopse/mORMot2/commit/da702d97e
(not tested yet, so your feedback is welcome)

Offline

#18 2025-09-10 18:26:49

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 15,181
Website

Re: TOpenApiParser fails on this OpenApi-Schema...

About the default headers trick, I am concerned to use a single place where the same headers are added for every requests...
The schema defines header per-URI, so it is perfectly possible to have very diverse set of headers, even some with the same name but not the same meaning and expected value...

Perhaps the more elegant way to solve this may be to define a record with each used set of headers, then use it as a single mandatory parameter to the query?
Then the client could prepare the values ahead of time, then pass it to each call, safe and sound.
My guess is that there should be only a few types of records needed, since the headers are likely to be shared among URI.
AFAICT in your schema they are almost always the same, but during the authentication/login phase when some "challenge" headers appear, and when retrieving the API feature set. So 3 or 4 records to use only.

Of course, it could be more work on my side, but it could be an elegant way to fix this.
- only that I don't have time yet for that.... wink

What do you think?

Offline

#19 2025-09-11 13:38:45

Luwo
Member
Registered: 2025-08-08
Posts: 10

Re: TOpenApiParser fails on this OpenApi-Schema...

ab wrote:

The definition of /customers({customerId})/bankaccounts is weird/inconsistent/incorrect/buggy/wrong.
It is a path parameter, but it is located as "in": "query" - it should be "in": "path"./quote]
Unless the client should be somewhat more tolerant that the standard... but if we start into this direction, where will it end, as it risks generating incorrect or unreliable code?
What do you think?

Yes, I understand, it's definitely a schema error. "Path" vs. "Query." Technically similar, but logically very different.
I've already reported this to the manufacturer.

ab wrote:

I have added the opoRelaxedSchema option, which should fix this problem:
https://github.com/synopse/mORMot2/commit/da702d97e
(not tested yet, so your feedback is welcome)

The option opoRelaxedSchema works like a charm :-)
But nevertheless I use the fixed schema now.

Offline

#20 2025-09-11 14:19:50

Luwo
Member
Registered: 2025-08-08
Posts: 10

Re: TOpenApiParser fails on this OpenApi-Schema...

ab wrote:

Perhaps the more elegant way to solve this may be to define a record with each used set of headers, then use it as a single mandatory parameter to the query?

Yes, this is a great idea! It would be supercompact and also very elegantly solved :-)

greetings Luwo

Offline

Board footer

Powered by FluxBB