landgraf-dev / openapi-delphi-generator Goto Github PK
View Code? Open in Web Editor NEWGenerate Delphi client SDKs for any REST API defined with the OpenAPI specification.
License: Other
Generate Delphi client SDKs for any REST API defined with the OpenAPI specification.
License: Other
The importer worked fine!
When i tried do call a basic function of the imported api the request rises a certificate error ; when i use an tidhttp component on the form and i call the function it works.
i don't know much about certificate managment, how cani fix the problem with the irestrequest calling?
Using a local file as input I get:
Exception: Cannot retrieve OpenApi version - Value expected but invalid character found at $ (EJsonExpectedValue)
The file was saved in Windows, and does not contain leading UTF-8 characters
If I change the file and insert 3 blanks at the start, it works fine.
Debugging in Delphi, in OpenApiGen.Main, function GetOpenApiVersion, I see the value of "Content" being
wagger":"2.0"...
proposed change to unit OpenApiGen.Main
function LoadContent(const Source: string; Options: TBuilderOptions): string;
var
Uri: IUri;
begin
if StartsText('http://', Source) or StartsText('https://', Source) then
begin
Result := LoadHttpContent(Source);
Uri := TUri.Create(Source);
if Options.DocumentUrl = '' then
Options.DocumentUrl := Uri.Scheme + '://' + Uri.Authority;
end
else
begin
Result := trim(TFile.ReadAllText(Source, TEncoding.UTF8));
if (Result <> '') and (Result[1] <> '{') then
Result := trim(TFile.ReadAllText(Source));
end;
end;
When called attached schema it raises exception EJSchemaReaderException: Missing JSON schema type at $.paths./messages/{folder}.get.parameters[0].schema (all versions)
I'm trying to use the generator on a large swagger-file 154 kB, swagger version 2.0 and I get
OpenApi Client Generator for Delphi version 1.1.1
Copyright (c) Landgraf.dev - all rights reserved.
EStackOverflow: Stack overflow
At the moment following code is generated:
Request := CreateRequest(...);
Response := Request.Execute;
CheckError(Response);
I think it would be better, if the following code would be generated:
Request := CreateRequest(...);
Response := ExecuteRequest(Request);
With following default implementation of ExecuteRequest
:
TCustomRestService.ExecuteRequest(ARequest: IRestRequest): IRestResponse;
begin
Result := Request.Execute;
CheckError(Result);
end;
WHY?
I think it is cleaner to avoid the code duplication. But the most important reason is, that this kind of implementation allows for better error handling, eg:
TMyRestService.ExecuteRequest(ARequest: IRestRequest): IRestResponse;
begin
try
Result := inherited;
except
on E: Exception do
if IsTokenExpiredException(E) then
begin
//Reset Client to refresh token
Reset;
//try to execute again
Result := inherited;
end
else
raise;
end;
end;
There should exist an lightweight option to add logging for requests and responses.
I would like to be able to add a logging class like this to do my logging for debugging purposes:
type
TIOpenApiRestLogger = class(TInterfacedObject, IRestLogger)
private
FLogDir: string;
function GetLogFileName(AMethod, AUrl: string; ALogId: string; APrefix: string = ''): string;
function GetContentInfo(AMethod, AUrl: string): string;
protected //access via interface
function LogRequest(AMethod: string; AUrl: string; ARequestBody: TStringStream): string;
procedure LogResponse(AMethod: string; AUrl: string; ALogId: string; AResponse: IRestResponse);
public
constructor Create(ALogDir: string);
end;
constructor TIOpenApiRestLogger.Create(ALogDir: string);
begin
if ALogDir <> '' then
begin
ForceDirectories(ALogDir);
FLogDir := MakePathStr(ALogDir);
end;
end;
/// LogId is used to match request to response
function TIOpenApiRestLogger.GetLogFileName(AMethod, AUrl: string; ALogId: string; APrefix: string = ''): string;
var
Path: string;
begin
Path := ExtractUrlPath(AUrl);
Result := FLogDir + APrefix + AMethod + '.'
+ StrToFileName(Path, REPLACE_SPACES_YES) + '.' + ALogId + '.txt';
end;
function TIOpenApiRestLogger.GetContentInfo(AMethod, AUrl: string): string;
begin
Result := '---' + #13#10; //YAML frontmatter format
Result := Result + 'Method: ' + AMethod + #13#10;
Result := Result + 'Url: ' + AUrl + #13#10;
end;
function TIOpenApiRestLogger.LogRequest(AMethod, AUrl: string; ARequestBody: TStringStream): string;
var
ToLog: string;
begin
Result := GuidToBase64(GenerateTGuid());
if FLogDir <> '' then
begin
ToLog := GetContentInfo(AMethod, AUrl);
ToLog := ToLog + '---' + #13#10;
if Assigned(ARequestBody) then
ToLog := ToLog + ARequestBody.DataString;
StrToFile(GetLogFileName(AMethod, AUrl, Result, 'REQ.'), ToLog, TEncoding.UTF8);
end;
end;
procedure TIOpenApiRestLogger.LogResponse(AMethod, AUrl, ALogId: string; AResponse: IRestResponse);
var
ToLog: string;
function HeaderInfo(AHeaderName: string): string;
begin
Result := ' ' + AHeaderName + ': ' + AResponse.GetHeader(AHeaderName) + #13#10;
end;
begin
Assert(Assigned(AResponse));
//---
if FLogDir <> '' then
begin
ToLog := GetContentInfo(AMethod, AUrl);
ToLog := ToLog + 'StatusCode: ' + IntToStr(AResponse.StatusCode) + #13#10;
ToLog := ToLog + 'Headers: ' + #13#10;
ToLog := ToLog + HeaderInfo('Date');
ToLog := ToLog + HeaderInfo('Content-Type');
ToLog := ToLog + HeaderInfo('Content-Length');
ToLog := ToLog + HeaderInfo('Connection');
ToLog := ToLog + HeaderInfo('Vary');
ToLog := ToLog + HeaderInfo('Allow');
ToLog := ToLog + '---' + #13#10;
ToLog := ToLog + AResponse.ContentAsString;
StrToFile(GetLogFileName(AMethod, AUrl, ALogId, 'RESP.'), ToLog, TEncoding.UTF8);
end;
end;
We have a server that delivers the following JSON (for TClient
):
{
"id": 6718,
"first_name": "Hans",
"last_name": "Bauer",
"telephone": null,
"email": null,
"gender": null
}
If I read this into a TClient
variable and then send this to the server again, this is translated to
{
"id": 6718,
"first_name": "Hans",
"last_name": "Bauer",
"telephone": "",
"email": "",
"gender": 0
}
which the server does not like: "0 is not a valid value for gender".
There are two solutions to this problem:
null
valuesnull
as not ...HasValue
While I think 1 is the correct solution, 2 can be implemented more easily by modifying OpenApiJson.pas
, for example for USEDBX
:
function TJsonWrapper.ObjContains(JObj: TJSONValue; const Name: string; out Value: TJSONValue): Boolean;
var
Pair: TJSONPair;
begin
Pair := TJSONObject(JObj).Get(Name);
if Assigned(Pair) then
begin
Value := Pair.JsonValue;
if Value.Null and FOptionTreatNullAsNotHasValue then
Value := nil;
end
else
Value := nil;
end;
Solution 2 would result in the following JSON, which the server happily processes:
{
"id": 6718,
"first_name": "Hans",
"last_name": "Bauer"
}
@wlandgraf What do you think?
I'd like to add a simple FMX GUI for calling the command, do you think it could be interesting as a PULL REQUEST (with a "source-gui" folder) in the project or as a new repository ?
Bcl.Code.MetaClasses missing
Bcl.Logging,
Bcl.Code.MetaClasses,
Bcl.Code.DelphiGenerator,
OpenAPI.Classes,
OpenAPI.Classes.Path,
OpenAPI.Classes.Operation,
OpenAPI.Classes.Parameter,
OpenAPI.Document,
OpenAPI.Types,
XData.JSchema.Classes,
Any more?
Hello,
I try to import the attached json-file with:
OpenApiDelphiGen.exe -i:"t:\AB-API.json" -o:"T:" -n:HSAuftrag
I get the following error:
EConvertError: '#/components/requestBodies/UserWithPassword' is not a valid floating point value
AB-API.zip
What is the problem here?
Thanks
Stefan
We spotted this data in the wild:
"created_at":"2023-09-29T07:55:53.123860Z"
This is not parsed, because TDateTimeFromJsonValue
handles at maximum 3 decimal places, but ISO allows unlimited decimal places, see https://en.wikipedia.org/wiki/ISO_8601:
There is no limit on the number of decimal places for the decimal fraction. However, the number of
decimal places needs to be agreed to by the communicating parties.
I got an access violation exception when I tried with an OpenAPi 3.0 Specification(File and URL).
After attempting to compile using a trial version of TMS, I discovered that the issue lies in the line where TAnalyzer.Analyze(Document) is called.
I'm not sure but it could be a bug in the TMS library.
Hello,
I tried to import the OpenApi schema from Shipcloud, a parcel shipping provider. I got an error message running the importer:
EJSchemaReaderException: Missing JSON schema type at $.paths./addresses.post.responses.200.content.application/json.schema
The schema is attached. Any ideas whats going wrong ?
Best regards,
When I try to use the generator on the attached api file, I get a stack overflow:
.\OpenApiDelphiGen -i:".\testapi.json" -o:"D:\Delphi\test" -n:testapi -m:MultipleClientsFromFirstTagAndOperationId
OpenApi Client Generator for Delphi version 1.1.1
Copyright (c) Landgraf.dev - all rights reserved.
EStackOverflow: Stack overflow
The api file works fine in swagger and other openapi tools.
Importing https://developer.ebay.com/api-docs/master/sell/feed/openapi/3/sell_feed_v1_oas3.json
Has huge comment. Maybe can try and break them up?
Delphi 11.3
/// The value of the <strong>limit</strong> parameter submitted in the request, which is the maximum number of schedule templates to return per page, from the result set. A result set is the complete set of schedule templates returned by the method. <p> <span class="tablenote"><strong>Note:</strong> Though this parameter is not required to be submitted in the request, the parameter defaults to 10 if omitted.</span></p><p> <span class="tablenote"><strong>Note:</strong> If this is the last or only page of the result set, the page may contain fewer tasks than the <strong>limit</strong> value. To determine the number of pages in a result set, divide the total value (total number of tasks matching input criteria) by this limit value, and then round up to the next integer. For example, if the <strong>total</strong> value was <code>120</code> (120 total tasks) and the <strong>limit</strong> value was <code>50</code> (show 50 tasks per page), the total number of pages in the result set is three, so the seller would have to make three separate <strong>getScheduleTemplates</strong> calls to view all tasks matching the input criteria.</span></p>
At the moment Delphi XE8 or better is required for the generated code to run.
With existing support for Indy it is not too difficult to support older Delphi versions as well.
Hi,
thank you for this awesome generator.
But if i generate the Pet Example with your exe i got different class implementations than in your example from
https://github.com/landgraf-dev/openapi-delphi-generator/tree/main/Tests/PetStore
i think the compiled exe File is old?!
When importing an OpenApi V3.0.1 json, the methods corresponding to the routes are not created as in the example available in the OpenApi 2.0 version
swagger.zip
the files openapi.v3.document and openapi.v3.json.serializer are missing. i have teh newest version of xdata installed.
where can i get them?
The JSON generated by TJsonWrapper.JsonValueToJson
is not always correctly escaped.
For example
{"path":"C:\Test\"}
is generated, instead of
{"path":"C:\\Test\\"}
At least if Data.DBXJSON
is used (XE4 support), this issue appears.
Given a OpenAPI parameter like this:
{
"name": "fee",
"in": "query",
"description": "",
"required": false,
"type": "number"
}
the generated code uses the Double data type for Fee
. This is than called in the following way:
Request.AddQueryParam('fee', Fee);
with
procedure AddQueryParam(const Name, Value: string);
This does not seem to be correct. The code should not rely on implicit conversion as this will use the system locale.
At least in XE4 this wont even compile.
A resolution would be to have an explicit conversion.
Hello,
I tried to import the OpenApi schema from qdrant, a vector database to be used on OpenAI embeddings. I got an error message running the importer:
EJSchemaReaderException: Missing JSON schema type at $.components.schemas.CollectionConfig.properties.quantization_config
Thanks for any help.
Endpoints with multiple results are ignored and appropriate code is not generated. No (reason/warning) message is showed, it's very confusing.
Nice solution is used by nswag. For response 200 generate correct result (function result type) and typed exception for others (any exception with json object is welcomed).
------------ sample: json multiple responses ----------------
"responses": {
"200": {
"description": "OK result - array of type 'Source' instances",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Sources"
}
}
}
},
"default": {
"description": "Processing error (unexpected)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Errors"
}
}
}
}
}
------------- sample: nswag implementation -----------------
ProcessResponse(client_, response_);
var status_ = (int)response_.StatusCode;
if (status_ == 200)
{
var objectResponse_ = await ReadObjectResponseAsync<System.Collections.Generic.ICollection<Source>>(response_, headers_, cancellationToken).ConfigureAwait(false);
if (objectResponse_.Object == null)
{
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
}
return objectResponse_.Object;
}
else
{
var objectResponse_ = await ReadObjectResponseAsync<System.Collections.Generic.ICollection<Error>>(response_, headers_, cancellationToken).ConfigureAwait(false);
if (objectResponse_.Object == null)
{
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
}
throw new ApiException<System.Collections.Generic.ICollection<Error>>("Processing error (unexpected)", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
}
---------- the end -----------------------
Please support oneOf, anyOf, allOf of OAS 3.0
To compile the code generated by this project the files in the Dist folder are necessary.
This project is licensed under the Apache 2.0 with Commons Clause License which forbids to sell the software generated by the code.
This means IMHO, that the generated code may not be use commercially.
Is this your intention?
If yes, you should place a clear statement on the project page to hint at this issue.
If no, you should license the files in the Dist folder under a more permissive license (eg. Apache 2.0 License).
Hi,
It seems I cant use the use the TClientCredentialsTokenProvider, are the plans so we can use it?
Hi, can I use your code to integrate your solution into such a project?
https://github.com/HemulGM/Delphi-JsonToDelphiClass
Hi, I encountered an error while compiling OpenApi250.bpl at the line FValues.AddPair(AName, AValue.Value);. The issue was resolved when I replaced it with FValues.AddPair(TJSONPair.Create(AName, AValue.Value.ToString));.
It should be possible to implement error handling on a per service basis. For this to be possible TCustomRestService.CheckError
in OpenApiRest
should be virtual
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.