You are not logged in.
Pages: 1
Salut Arnaud,
just for your information - maybe the approach from https://diataxis.fr is of use for the new documentation.
Thank you for your great work!
Thanks Thomas for your hint!
I successfully used your approach with mORMotService.TSynDaemon - High CPU-Load is gone.
But i had to use ConsoleWaitForEnterKey() instead of readln() in procedure TSynDaemon.CommandLine, else the mainthread was blocked and we currently use some Timers and Services running in Mainthread.
Hi,
on Windows Server 2019 my console-application has constant 25% CPU load, conhost.exe another 25%.
On Windows 7, everything is just fine (< 1% during idle).
I'm using the function ConsoleWaitForEnterKey.
Can you give me a hint?
Thanks in advance!
Hi Folks,
you have to disable the QuickEdit Mode in the command-line on Windows 10 / Windows Server 2019.
Else your application hangs, waiting for key-input, as soon as you click inside the window.
Solution: https://stackoverflow.com/questions/338 … windows-10
Regards,
sevo
If I understand correctly, using TServiceCustomAnswer, I have to write my own wrapper to correctly generate the data to swagger?
As the typeinfo of TServiceCustomAnswer is missing in the /wrapper/context, the response model can not be generated.
Unfortunately, even aliasing the type, e.g. TMyServiceCustomAnswer = TServiceCustomAnswer, did not work. The type in wrapper/context gets resolved to TServiceCustomAnswer -> again no typeinfo.
The only way for now to document the response for TServiceCustomAnswer is to write a comment (/// + text/html) above the interface function definition.
AFAIK if you use mormot interface services, only calls via POST or GET are supported.
See https://synopse.info/files/html/Synopse … #TITLE_454
If you want to e.g. call via DELETE, you have to use method-based services - these are currently not supported by the swagger-template.
@ab it would be nice to have in the server-context
- a "fieldDescription" per record field
- the response-format, eg. ResultAsJSONObjectWithoutResult, ResultAsJSONObject, Default, XML... to generate the according response-schema for swagger.
Hi Esteban,
i can not confirm. In my case, all the descriptions are in the servercontext and also in the genereated swagger-json.
I think the description of the record / enum has to be in the same unit as the interface definition of the service.
Here another small update for serviceDescription:
...
"tags": [
{{#soa}} {{#services}}
{
"name": "{{uri}}",
"description": {{jsonQuote serviceDescription}} //<-- was "Everything about {{uri}}"
} {{^-last}},{{/-last}}
...
Regards,
sevo
Hi all,
i have some additions, marked as "//<-- .."
...
"info": {
"description": "Generiert durch mORMot {{mORMotVersion}} am {{time}}",
"title": "Services von {{protocol}}://{{host}}:{{port}}/{{root}}" //<-- add Server details
...
"definitions": {
{{! Records }}
{{#records}}
"{{name}}": {
"type": "object",
"description": {{jsonQuote recordDescription}}, //<-- add record description
"properties": {
...
{{#enumerates}}
"{{name}}": {
"type": "integer", //<-- change type to integer (enum-index)
"enum": [
{{#values}}
{{-index0}} {{^-last}},{{/-last}} //<-- use enum-index for enum-parameters
{{/values}}
],
"description": //<-- add enum-descrtiption
"{{enumDescription}}\n\n{{#values}}\n{{-index0}} = {{.}}{{^-last}},{{/-last}}\n{{/values}}", //<-- add enum-descrtiption
"required": true
},
{{/enumerates}}
...
Edit: correct use of jsonQuote, was double-quoted
Thx for your reply!
Instead of HTTP-Headers I should use URL-Parameters.
I managed to do so using UrlEncode:
Call.InBody := //binary data from picture
Call.Url := Client.Model.Root + '/' + 'UploadPicture' + UrlEncode(['Param', 123, 'Context', 456]);
Call.Verb := 'POST';
Client.URI(Call);
Why are in UrlEncode only a-zA-Z as parameter names allowed?
According to https://www.ietf.org/rfc/rfc3986.txt the query can consist of *( pchar / "/" / "?" )
Where pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
and unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
Edit: typo
Hi Folks,
i want to send binary data (a Picture) with additional HTTP-Header-Data from an Android-App using Firemonkey (FMX) to a method-based service.
But in unit SynCrossPlatformSpecific.pas procedure THttpClientHttpConnectionClass.URI(var Call: TSQLRestURIParams, ...) the HTTP-Headers in parameter Call.InHead are not used/sent.
IMHO the Call.InHead must be filled into a TNetHeader array and passed to the THttpClient.Post/.Put/.. functions.
Here is a working - but naive and memory-leaking function:
procedure TextToNetHeaders(Text : String; var Headers : TNetHeaders);
var
Pairs : Integer;
sl : TStringList;
i, Sep : Integer;
begin
sl := TStringList.Create;
try
sl.Text := Text;
SetLength(Headers, sl.Count);
for i := 0 to sl.Count - 1 do begin
Sep := Pos(':', sl[i]);
Headers[i] := TNetHeader.Create(Trim(Copy(sl[i], 1, Sep - 1)),
trim(Copy(sl[i], Sep + 1, MAXINT)));
end;
finally
sl.Free;
end;
end;
Can you please help cleaning up the function/solution and push upstream?
Edit: removed [BUG] from title
Try with lowercase 'f' in 'Function'.
Yes, you can generate links which point to a method based service.
https://synopse.info/files/html/Synopse … ml#TITL_95
Edit: Chapter "Returning file content" in the documentation.
I'm fine with the internal structure / modules!
Imho we need documentation of the url schema, like for example here: https://postgrest.com/en/latest/api.html.
And someone should finish the openapi/swagger integration.
Afaik this is not automagically possible.
In my case, client and server had to ignore the dummy fields.
i had this problem with Delphi 2010, too.
The rtti was missing for some records in an interface-based service.
Adding a dummy field of type string did the job.
We have javascript clients reading and writing enum-names as text.
With current trunk, enum-names, as well as enum-index, as request-paramter are still working, but in the response is the enum-index.
With mORMot-version "mORMot_and_Open_Source_friends_2014-07-02_074115_da7141e88e", Default was enum-name. Using Delphi 2010.
We use the swagger-api to develop the client against. With RawJSON as parameter type we will lose the type-info.
Interface-Based-Service, out-Records containing Enums.
How to respond as default with Enumname instead of the Enumindex?
I've found the option TTextWriterOption.twoEnumSetsAsTextInRecord but i don't know how to set this option as default.
I would try using Field-Aliases, e.g. "select table1.field1 as localfield ..." and calculate the field-values in OnCalcFields-Event of the existing client-dataset.
Maybe OnCalcFields is not trigged when loading with ToClientDataset.
Hi Arnaud,
i have a small update.
A patch for mORMotWrappers.pas to support some swagger-types in the server-context:
diff --git a/SQLite3/mORMotWrappers.pas b/SQLite3/mORMotWrappers.pas
index 7943ecf..492eedd 100644
--- a/SQLite3/mORMotWrappers.pas
+++ b/SQLite3/mORMotWrappers.pas
@@ -198,7 +198,7 @@ type
wObject, wSQLRecord, wInterface, wRecordVersion);
/// supported languages typesets
TWrapperLanguage = (
- lngDelphi, lngPascal, lngCS, lngJava, lngTypeScript);
+ lngDelphi, lngPascal, lngCS, lngJava, lngTypeScript, lngSwagger);
const
CROSSPLATFORM_KIND: array[TSQLFieldType] of TCrossPlatformSQLFieldKind = (
@@ -248,7 +248,27 @@ const
'mORMot.TModTime', 'mORMot.TCreateTime', 'number', 'number', 'number',
'mORMot.TDateTime', 'string', 'string', 'any', 'mORMot.TSQLRawBlob',
'mORMot.TGUID', 'mORMot.THttpBody', '', '', 'any', '', '', '',
- 'mORMot.TRecordVersion'));
+ 'mORMot.TRecordVersion'),
+ // lngSwagger
+ ('', // wUnknown,
+ '{"type": "boolean"}', // wBoolean
+ '', '', // wEnum, wSet,
+ '{"type":"integer"}', '{"type":"integer"}', // wByte, wWord,
+ '{"type":"integer"}', '{"type":"integer"}', // wInteger, wCardinal,
+ '{"type":"integer","format":"int64"}', // wInt64
+ '', '', '', '', '', //wID, wReference, wTimeLog, wModTime, wCreateTime,
+ '{"type":"number","format":"double"}', //wCurrency,
+ '{"type":"number","format":"float"}', // wSingle
+ '{"type":"number","format":"double"}', // wDouble
+ '{"type":"string","format":"date-time"}', //wDateTime
+ '{"type":"string"}', //wRawUTF8
+ '{"type":"string"}', //wString
+ '{"type":"object"}', //FIXME! //wRawJSON
+ '{"type":"string","format":"binary"}', //wBlob
+ '{"type":"string"}', //wGUID
+ '', '', '', '', //wCustomAnswer, wRecord, wArray, wVariant
+ '', '', '', '' //wObject, wSQLRecord, wInterface, wRecordVersion
+ ));
TYPES_ORM: array[TSQLFieldType] of TWrapperType =
(wUnknown, // sftUnknown
@@ -773,7 +793,7 @@ begin
'typeWrapper',typeWrapper^, 'typeSource',typName,
'typeDelphi',VarName(lngDelphi), 'typePascal',VarName(lngPascal),
'typeCS',VarName(lngCS), 'typeJava',VarName(lngJava),
- 'typeTS',VarName(lngTypeScript)]);
+ 'typeTS',VarName(lngTypeScript), 'typeSwagger',VarName(lngSwagger)]);
if self=nil then
exit; // no need to have full info if called e.g. from MVC
if typInfo<>nil then
And an updated template, using the swagger-types from the server-context and also generating valid json:
{{! WARNUNG: Benötigt gepatchtes mORMot }}
{{<schema-fuer-typ}}
{{#typeSwagger}}
{{!HACK: Enums, Records und Arrays, die separat definiert werden und referenziert werden müssen
haben den gleichen Swagger- wie Source-Typ. Alle integrierten Typen habe einen eigenen
Swagger-Typ}}
{{#Equals typeSwagger,typeSource}}
{ "$ref": "#/definitions/{{typeSwagger}}" }
{{/Equals}}
{{^Equals typeSwagger,typeSource}}
{{typeSwagger}}
{{/Equals}}
{{/typeSwagger}}
{{^typeSwagger}}
{{#nestedSimpleArray}}
{
"type": "array",
"items": {{>schema-fuer-typ}}
}
{{/nestedSimpleArray}}
{{/typeSwagger}}
{{/schema-fuer-typ}}
{
"swagger": "2.0",
"info": {
"description": "Generiert durch mORMot {{mORMotVersion}} am {{time}}",
"title": "mORMot -> Swagger"
},
"host": "{{host}}",
"basePath": "/{{root}}",
"tags": [
{{#soa}} {{#services}}
{
"name": "{{uri}}",
"description": "Everything about {{uri}}"
} {{^-last}},{{/-last}}
{{/services}} {{/soa}}
],
"definitions": {
{{! Records }}
{{#records}}
"{{name}}": {
"type": "object",
"properties": {
{{#fields}}
"{{propName}}": {{>schema-fuer-typ}} {{^-last}},{{/-last}}
{{/fields}}
}
},
{{/records}}
{{! Arrays }}
{{#arrays}}
"{{name}}": {
"type": "array",
"items": {{>schema-fuer-typ}}
},
{{/arrays}}
{{#enumerates}}
"{{name}}": {
"type": "string",
"enum": [
{{#values}}
"{{.}}" {{^-last}},{{/-last}}
{{/values}}
],
"required": true
},
{{/enumerates}}
{{!HACK: Sonst haben wir ein einzelnes Komma am Schluss }}
"__dummy__": {"type":"string"}
},
"paths": {
{{#soa}} {{#services}}
{{#methods}}
"/{{uri}}/{{methodName}}": {
"post": {
"tags": ["{{uri}}"],
"parameters": [{
"in": "body",
"name": "body",
"schema": {
"type": "object",
"properties": {
{{#args}}{{#dirInput}}
"{{argName}}": {{>schema-fuer-typ}}{{commaInSingle}}
{{/dirInput}}{{/args}}
}
}
}],
"responses": {
"200": {
"schema": {
"type": "object",
"properties": {
"result": {
"type": "array",
"items": [
{{#args}} {{#dirOutput}}
{{>schema-fuer-typ}}{{#commaOutResult}},{{/commaOutResult}}
{{/dirOutput}}{{/args}}
]
}
}
}
}
}
}
} {{^-last}},{{/-last}}
{{/methods}} {{^-last}},{{/-last}}
{{/services}} {{/soa}}
}
}
Now it is almost possible to use the client- and server-code generators from swagger with the resulting swagger.json.
There is an open issue with swagger, not allowing inline response-schemata in code-generation.
It would be nice to have the response-format in the server-context, eg. ResultAsJSONObjectWithoutResult, ResultAsJSONObject, Default, XML... to generate the according response-schema for swagger.
Thx!
sevo
In my case (ODBC-Connection), the changes are applied, but the inserted value is 'null'.
It is not possible to set an empty string or clear the field.
E.g. FieldByName('Field').AsString = '' -> inserts the string 'null'
and FieldByName('Field').Clear -> insterts the string 'null', too.
It ends up in TSQLDBStatement.BindVariant, Default (no match for TVarData(Data).VType) -> VariantToUTF8(undefined) -> 'null'
Is it possible to keep TDataSet-Compatibility here?
Update:
- Services are grouped in seperate sections, not in a single list of all functions anymore.
- Enums are resolved. They are passed as String, e.g. "cicUnknown" and all Enums are listet in the Data-Type -> Model - Section of each function.
HowTo:
- you have to provide the url of the generated .json.txt file to swagger. E.g. http://petstore.swagger.io/?url=http:// … t.json.txt
{{! FIXME: mORMot gibt bei nicht-Array-Typen keine "isArray" Eigenschaft aus,
und unterstützt die {{.Variable\}\} Notation auch nicht. Somit können verschachtelte Arrays
hier nicht sauber verarbeitet werden. Das Delphi-Template ist auf gleiche Weise limitiert. }}
{{<schema-fuer-typ}}{{#nestedSimpleArray}}
{
"type": "array",
"items": { "$ref": "#/definitions/{{typeSource}}" }
}
{{/nestedSimpleArray}}{{^nestedSimpleArray}}
{ "$ref": "#/definitions/{{typeSource}}" }
{{/nestedSimpleArray}}{{/schema-fuer-typ}}
{
"swagger": "2.0",
"info": {
"description": "Generiert durch mORMot {{mORMotVersion}} am {{time}}",
"title": "mORMot -> Swagger",
},
"host": "{{host}}",
"basePath": "/{{root}}",
"tags": [
{{#soa}} {{#services}}
{
"name": "{{uri}}",
"description": "Everything about {{uri}}",
},
{{/services}} {{/soa}}
],
"definitions": {
{{! Records }}
{{#records}}
"{{name}}": {
"type": "object",
"properties": {
{{#fields}}
"{{propName}}": {{>schema-fuer-typ}},
{{/fields}}
}
},
{{/records}}
{{! Arrays }}
{{#arrays}}
"{{name}}": {
"type": "array",
"items": {{>schema-fuer-typ}}
},
{{/arrays}}
{{#enumerates}}
"{{name}}": {
"type": "string",
"enum": [
{{#values}}
{{.}},
{{/values}}
],
"required": true
},
{{/enumerates}}
{{! HACK: Standard-Datentypen }}
"Integer": { "type": "integer" },
"Boolean": { "type": "boolean" },
"String": { "type": "string" },
"Double": { "type": "number", "format": "double" },
"Variant": { "type": "object" } {{! <-- FIXME }}
},
"paths": {
{{#soa}} {{#services}}
{{#methods}}
"/{{uri}}/{{methodName}}": {
"post": {
"tags": ["{{uri}}"],
"parameters": [{
"in": "body",
"name": "body",
"schema": {
"type": "object",
"properties": {
{{#args}}{{#dirInput}}
"{{argName}}": {{>schema-fuer-typ}},
{{/dirInput}}{{/args}}
}
}
}],
"responses": {
"200": {
"schema": {
"type": "object",
"properties": {
"result": {
"type": "array",
"items": [
{{#args}} {{#dirOutput}}
{{>schema-fuer-typ}},
{{/dirOutput}}{{/args}}
]
}
}
}
}
}
}
},
{{/methods}}
{{/services}} {{/soa}}
}
}
@sevo did you try to comile Zeos on WinCE?
no
I could build a client for WinCE/ARM using fpc 2.6.4 using the crossplatform-code generated by a mORMot server.
Server was interface-based/records-only.
Had some problems with UTF-8 conversion in TextToHttpBody and HttpBodyToText ('?' for umlauts) and changed the conversion to an implicit cast (if at all, i don't know)
//instead of Text := Utf8Decode(utf8)
Text := utf8;
Hope this helps!
Hello,
drop the following mustache-template in your Crossplatform/templates folder, open up http://petstore.swagger.io/, select the generated .json file and look at this beautiful, executable, interface-based-service documentation.
For now:
- authentication is not supported (basic-auth should work out of the box)
- only interfaced-based-services are supported
- swagger-types are mapped inside the template
- generated json is not valid, but ok for swagger-ui
Here is the template (swagger.json.mustache):
{{! FIXME: mORMot gibt bei nicht-Array-Typen keine "isArray" Eigenschaft aus,
und unterstützt die {{.Variable\}\} Notation auch nicht. Somit können verschachtelte Arrays
hier nicht sauber verarbeitet werden. Das Delphi-Template ist auf gleiche Weise limitiert. }}
{{<schema-fuer-typ}}{{#nestedSimpleArray}}
{
"type": "array",
"items": { "$ref": "#/definitions/{{typeSource}}" }
}
{{/nestedSimpleArray}}{{^nestedSimpleArray}}
{ "$ref": "#/definitions/{{typeSource}}" }
{{/nestedSimpleArray}}{{/schema-fuer-typ}}
{
"swagger": "2.0",
"info": {
"description": "Generiert durch mORMot {{mORMotVersion}} am {{time}}",
"title": "mORMot -> Swagger",
},
"host": "{{host}}",
"basePath": "/{{root}}",
"definitions": {
{{! Records }}
{{#records}}
"{{name}}": {
"type": "object",
"properties": {
{{#fields}}
"{{propName}}": {{>schema-fuer-typ}},
{{/fields}}
}
},
{{/records}}
{{! Arrays }}
{{#arrays}}
"{{name}}": {
"type": "array",
"items": {{>schema-fuer-typ}}
},
{{/arrays}}
{{! HACK: Standard-Datentypen }}
"Integer": { "type": "integer" },
"Boolean": { "type": "boolean" },
"String": { "type": "string" },
"Double": { "type": "number", "format": "double" },
"Variant": { "type": "object" } {{! <-- FIXME }}
},
"paths": {
{{#soa}} {{#services}}
{{#methods}}
"/{{uri}}/{{methodName}}": {
"post": {
"parameters": [{
"in": "body",
"name": "body",
"schema": {
"type": "object",
"properties": {
{{#args}}{{#dirInput}}
"{{argName}}": {{>schema-fuer-typ}},
{{/dirInput}}{{/args}}
}
}
}],
"responses": {
"200": {
"schema": {
"type": "object",
"properties": {
"result": {
"type": "array",
"items": [
{{#args}} {{#dirOutput}}
{{>schema-fuer-typ}},
{{/dirOutput}}{{/args}}
]
}
}
}
}
}
}
},
{{/methods}}
{{/services}} {{/soa}}
}
}
Pages: 1