2019-07-29 15:50:09 +02:00
|
|
|
unit MVCFramework.Controllers.Swagger;
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
|
|
|
Classes
|
|
|
|
, MVCFramework
|
|
|
|
, MVCFramework.Commons
|
|
|
|
, System.Generics.Collections
|
|
|
|
, Swag.Doc
|
|
|
|
, Swag.Doc.Path
|
|
|
|
, Swag.Doc.Path.Operation
|
|
|
|
, Swag.Doc.Definition
|
|
|
|
, Swag.Common.Types
|
|
|
|
, Swag.Doc.Path.Operation.Response
|
|
|
|
, Swag.Doc.Path.Operation.RequestParameter
|
|
|
|
, Json.Schema
|
|
|
|
, Json.Schema.Field
|
|
|
|
, Json.Schema.Field.Strings
|
|
|
|
, Json.Schema.Field.Arrays
|
|
|
|
, Json.Schema.Field.Objects
|
2019-08-05 13:37:42 +02:00
|
|
|
, Json.Schema.Field.Numbers
|
2019-07-29 15:50:09 +02:00
|
|
|
;
|
|
|
|
|
|
|
|
type
|
|
|
|
TMVCStatusResponses = class
|
|
|
|
strict private
|
|
|
|
FStatusCode : Integer;
|
|
|
|
FStatusDescription : String;
|
|
|
|
FReturnType : TClass;
|
2019-08-05 13:37:42 +02:00
|
|
|
FKind: TTypeKind;
|
2019-07-29 15:50:09 +02:00
|
|
|
public
|
|
|
|
property StatusCode: Integer read FStatusCode write FStatusCode;
|
|
|
|
property StatusDescription : string read FStatusDescription write FStatusDescription;
|
|
|
|
property ReturnType: TClass read FReturnType write FReturnType;
|
2019-08-05 13:37:42 +02:00
|
|
|
property Kind: TTypeKind read FKind write FKind;
|
2019-07-29 15:50:09 +02:00
|
|
|
function GetReturnTypeName:string;
|
|
|
|
end;
|
|
|
|
|
|
|
|
TMVCEndPoint = class
|
|
|
|
strict private
|
|
|
|
FDoc : string;
|
|
|
|
FMethod : TMVCHTTPMethods;
|
|
|
|
FOperationId: string;
|
|
|
|
FProduces : string;
|
|
|
|
FConsumes : string;
|
|
|
|
FPath : string;
|
|
|
|
public
|
2019-08-05 13:37:42 +02:00
|
|
|
Statuses : TObjectList<TMVCStatusResponses>;
|
|
|
|
Params : TStringList;
|
2019-07-29 15:50:09 +02:00
|
|
|
property Documentation : string read FDoc write FDoc;
|
|
|
|
property Method : TMVCHTTPMethods read FMethod write FMethod;
|
|
|
|
property OperationId : string read FOperationId write FOperationId;
|
|
|
|
property Produces : string read FProduces write FProduces;
|
|
|
|
property Consumes : string read FConsumes write FConsumes;
|
|
|
|
property Path : string read FPath write FPath;
|
|
|
|
public
|
|
|
|
constructor Create;
|
|
|
|
destructor Destroy; override;
|
|
|
|
end;
|
|
|
|
|
|
|
|
type
|
|
|
|
|
|
|
|
[MVCPath('/')]
|
|
|
|
TMVCSwaggerController = class(TMVCController)
|
|
|
|
private
|
|
|
|
fEndpoints : TObjectList<TMVCEndPoint>;
|
|
|
|
fSwagDoc: TSwagDoc;
|
|
|
|
fDefinitions : TList<TClass>;
|
2019-08-05 13:37:42 +02:00
|
|
|
procedure ConvertFieldAttributesToSwagger(AField: TJsonField; AAttributes: TArray<TCustomAttribute>);
|
2019-07-29 15:50:09 +02:00
|
|
|
private
|
|
|
|
procedure ProcessControllerMethods(aClass: TClass);
|
2019-08-05 13:37:42 +02:00
|
|
|
procedure ProcessObject(schema: TJSONSchema; aClass:TClass);
|
|
|
|
procedure ProcessObjectForDefinition(aClass:TClass);
|
2019-07-29 15:50:09 +02:00
|
|
|
public
|
2019-07-30 08:29:31 +02:00
|
|
|
class function ProcessAndRewriteURL(params: TStringList; const rootPath:string; const path:string): string;
|
|
|
|
class function MVCMethodToSwaggerOperation(inMethod:TMVCHTTPMethodType): TSwagPathTypeOperation;
|
2019-07-29 15:50:09 +02:00
|
|
|
[MVCDoc('This is some documentation')]
|
|
|
|
[MVCPath('/swagger')]
|
|
|
|
[MVCHTTPMethod([httpGET])]
|
|
|
|
procedure Swagger;
|
|
|
|
public
|
|
|
|
constructor Create; override;
|
|
|
|
destructor Destroy; override;
|
|
|
|
end;
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
|
|
|
uses
|
|
|
|
RTTI
|
|
|
|
, windows
|
|
|
|
, System.SysUtils
|
|
|
|
, System.RegularExpressions
|
|
|
|
, System.RegularExpressionsCore
|
|
|
|
;
|
|
|
|
|
|
|
|
{ TMVCSwaggerController }
|
|
|
|
|
|
|
|
constructor TMVCSwaggerController.Create;
|
|
|
|
begin
|
|
|
|
inherited;
|
|
|
|
fEndpoints := TObjectList<TMVCEndPoint>.Create;
|
|
|
|
fDefinitions := TList<TClass>.Create;
|
|
|
|
fSwagDoc := TSwagDoc.Create;
|
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TMVCSwaggerController.Destroy;
|
|
|
|
begin
|
|
|
|
FreeAndNil(fEndpoints);
|
|
|
|
FreeAndNil(fSwagDoc);
|
|
|
|
FreeAndNil(fDefinitions);
|
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
|
2019-08-05 13:37:42 +02:00
|
|
|
procedure TMVCSwaggerController.ConvertFieldAttributesToSwagger(AField: TJsonField; AAttributes: TArray<TCustomAttribute>);
|
|
|
|
var
|
|
|
|
LAttribute: TCustomAttribute;
|
|
|
|
begin
|
|
|
|
for LAttribute in AAttributes do
|
|
|
|
begin
|
|
|
|
if LAttribute is MVCDocAttribute then
|
|
|
|
begin
|
|
|
|
AField.Description := MVCDocAttribute(LAttribute).Value;
|
|
|
|
end
|
|
|
|
else if LAttribute is MVCPatternAttribute then
|
|
|
|
begin
|
|
|
|
(AField as TJSONFieldString).Pattern := MVCPatternAttribute(LAttribute).Value;
|
|
|
|
end
|
|
|
|
else if LAttribute is MVCMaxLengthAttribute then
|
|
|
|
begin
|
|
|
|
(AField as TJSONFieldString).MaxLength := MVCMaxLengthAttribute(LAttribute).Value;
|
|
|
|
end
|
|
|
|
else if LAttribute is MVCMinimumAttribute then
|
|
|
|
begin
|
|
|
|
if AField is TJsonFieldInt64 then
|
|
|
|
(AField as TJsonFieldInt64).MinValue := MVCMinimumAttribute(LAttribute).Value
|
|
|
|
else if AField is TJsonFieldInteger then
|
|
|
|
(AField as TJsonFieldInteger).MinValue := MVCMinimumAttribute(LAttribute).Value
|
|
|
|
else if AField is TJsonFieldNumber then
|
|
|
|
(AField as TJsonFieldNumber).MinValue := MVCMinimumAttribute(LAttribute).Value
|
|
|
|
else
|
|
|
|
raise Exception.Create('Minimum not valid on ' + AField.ClassName);
|
|
|
|
end
|
|
|
|
else if LAttribute is MVCMaximumAttribute then
|
|
|
|
begin
|
|
|
|
if AField is TJsonFieldInt64 then
|
|
|
|
(AField as TJsonFieldInt64).MaxValue := MVCMaximumAttribute(LAttribute).Value
|
|
|
|
else if AField is TJsonFieldInteger then
|
|
|
|
(AField as TJsonFieldInteger).MaxValue := MVCMaximumAttribute(LAttribute).Value
|
|
|
|
else if AField is TJsonFieldNumber then
|
|
|
|
(AField as TJsonFieldNumber).MaxValue := MVCMaximumAttribute(LAttribute).Value
|
|
|
|
else
|
|
|
|
raise Exception.Create('Maximum not valid on ' + AField.ClassName);
|
|
|
|
end
|
|
|
|
else if LAttribute is MVCFormatAttribute then
|
|
|
|
begin
|
|
|
|
if AField is TJsonFieldString then
|
|
|
|
(AField as TJsonFieldString).Format := MVCFormatAttribute(LAttribute).Value
|
|
|
|
else if AField is TJsonFieldString then
|
|
|
|
(AField as TJsonFieldInteger).Format := MVCFormatAttribute(LAttribute).Value
|
|
|
|
else if AField is TJsonFieldInt64 then
|
|
|
|
(AField as TJsonFieldInt64).Format := MVCFormatAttribute(LAttribute).Value
|
|
|
|
else if AField is TJsonFieldNumber then
|
|
|
|
(AField as TJsonFieldNumber).Format := MVCFormatAttribute(LAttribute).Value
|
|
|
|
else
|
|
|
|
raise Exception.Create('Format not valid on ' + AField.ClassName);
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2019-07-30 08:29:31 +02:00
|
|
|
class function TMVCSwaggerController.MVCMethodToSwaggerOperation(inMethod:TMVCHTTPMethodType): TSwagPathTypeOperation;
|
|
|
|
begin
|
2019-08-05 13:37:42 +02:00
|
|
|
case inMethod of
|
|
|
|
httpGET: Result := TSwagPathTypeOperation.ohvGet;
|
|
|
|
httpPOST: Result := TSwagPathTypeOperation.ohvPost;
|
|
|
|
httpPUT: Result := TSwagPathTypeOperation.ohvPut;
|
|
|
|
httpDELETE: Result := TSwagPathTypeOperation.ohvDelete;
|
|
|
|
httpHEAD: Result := TSwagPathTypeOperation.ohvHead;
|
|
|
|
httpOPTIONS: Result := TSwagPathTypeOperation.ohvOptions;
|
|
|
|
httpPATCH: Result := TSwagPathTypeOperation.ohvPatch;
|
|
|
|
httpTRACE: Result := TSwagPathTypeOperation.ohvTrace;
|
2019-07-30 08:29:31 +02:00
|
|
|
else
|
|
|
|
Result := TSwagPathTypeOperation.ohvNotDefined;
|
2019-08-05 13:37:42 +02:00
|
|
|
end;
|
2019-07-30 08:29:31 +02:00
|
|
|
end;
|
|
|
|
|
|
|
|
class function TMVCSwaggerController.ProcessAndRewriteURL(params: TStringList; const rootPath:string; const path:string):string;
|
2019-07-29 15:50:09 +02:00
|
|
|
var
|
|
|
|
LRegEx : TRegEx;
|
|
|
|
LMatches: TMatchCollection;
|
|
|
|
i: Integer;
|
|
|
|
begin
|
|
|
|
if (rootPath = '/') and path.StartsWith('/') then
|
|
|
|
Result := path
|
|
|
|
else
|
|
|
|
Result := rootPath + Path;
|
|
|
|
|
|
|
|
LRegEx := TRegEx.Create('\(\$\w+\)');
|
|
|
|
LMatches := LRegEx.Matches(Result);
|
|
|
|
for i := LMatches.Count - 1 downto 0 do
|
|
|
|
begin
|
|
|
|
params.Add(Copy(LMatches.Item[i].Value,3,LMatches.Item[i].Value.length-3));
|
|
|
|
Result[LMatches.Item[i].Index + LMatches.Item[i].Length - 1] := '}';
|
|
|
|
Result[LMatches.Item[i].Index] := '{';
|
|
|
|
Delete(Result, LMatches.Item[i].Index + 1,1);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TMVCSwaggerController.ProcessControllerMethods(aClass: TClass);
|
|
|
|
var
|
|
|
|
LRttiContext: TRttiContext;
|
|
|
|
LRttiType: TRttiType;
|
|
|
|
LAttribute: TCustomAttribute;
|
|
|
|
LMethods : TArray<TRttiMethod>;
|
|
|
|
LMethod: TRttiMethod;
|
|
|
|
LRootPath : string;
|
|
|
|
LEndpoint : TMVCEndPoint;
|
|
|
|
LStatus : TMVCStatusResponses;
|
|
|
|
begin
|
|
|
|
try
|
|
|
|
LRttiContext := TRttiContext.Create;
|
|
|
|
LRttiType := LRttiContext.GetType(aClass);
|
|
|
|
for LAttribute in LRttiType.GetAttributes do
|
|
|
|
begin
|
|
|
|
if LAttribute is MVCPathAttribute then
|
|
|
|
begin
|
|
|
|
LRootPath := MVCPathAttribute(LAttribute).Path;
|
|
|
|
end
|
|
|
|
end;
|
|
|
|
|
|
|
|
LMethods := LRttiType.GetMethods;
|
|
|
|
for LMethod in LMethods do
|
|
|
|
begin
|
|
|
|
LEndpoint := TMVCEndPoint.Create;
|
|
|
|
for LAttribute in LMethod.GetAttributes do
|
|
|
|
begin
|
|
|
|
if LAttribute is MVCPathAttribute then
|
|
|
|
begin
|
2019-08-05 13:37:42 +02:00
|
|
|
LEndpoint.Path := TMVCSwaggerController.ProcessAndRewriteURL(LEndpoint.params, LRootPath, MVCPathAttribute(LAttribute).Path);
|
2019-07-29 15:50:09 +02:00
|
|
|
end
|
|
|
|
else if LAttribute is MVCHTTPMethodAttribute then
|
|
|
|
begin
|
2019-08-05 13:37:42 +02:00
|
|
|
LEndpoint.Method := MVCHTTPMethodAttribute(LAttribute).MVCHTTPMethods;
|
2019-07-29 15:50:09 +02:00
|
|
|
end
|
|
|
|
else if LAttribute is MVCDocAttribute then
|
|
|
|
begin
|
2019-08-05 13:37:42 +02:00
|
|
|
LEndpoint.Documentation := MVCDocAttribute(LAttribute).Value;
|
2019-07-29 15:50:09 +02:00
|
|
|
end
|
|
|
|
else if LAttribute is MVCProducesAttribute then
|
|
|
|
begin
|
2019-08-05 13:37:42 +02:00
|
|
|
LEndpoint.Produces := MVCProducesAttribute(LAttribute).Value;
|
2019-07-29 15:50:09 +02:00
|
|
|
end
|
|
|
|
else if LAttribute is MVCConsumesAttribute then
|
|
|
|
begin
|
2019-08-05 13:37:42 +02:00
|
|
|
LEndpoint.Consumes := MVCConsumesAttribute(LAttribute).Value;
|
2019-07-29 15:50:09 +02:00
|
|
|
end
|
|
|
|
else if LAttribute is MVCResponseAttribute then
|
|
|
|
begin
|
|
|
|
LStatus := TMVCStatusResponses.Create;
|
|
|
|
LStatus.statusCode := MVCResponseAttribute(LAttribute).StatusCode;
|
|
|
|
LStatus.statusDescription := MVCResponseAttribute(LAttribute).Description;
|
|
|
|
LStatus.ReturnType := MVCResponseAttribute(LAttribute).ResponseClass;
|
2019-08-05 13:37:42 +02:00
|
|
|
LStatus.Kind := tkClass;
|
2019-07-29 15:50:09 +02:00
|
|
|
LEndpoint.statuses.Add(LStatus);
|
|
|
|
end
|
2019-07-30 08:29:31 +02:00
|
|
|
else if LAttribute is MVCResponseListAttribute then
|
|
|
|
begin
|
|
|
|
LStatus := TMVCStatusResponses.Create;
|
|
|
|
LStatus.statusCode := MVCResponseListAttribute(LAttribute).StatusCode;
|
|
|
|
LStatus.statusDescription := MVCResponseListAttribute(LAttribute).Description;
|
|
|
|
LStatus.ReturnType := MVCResponseListAttribute(LAttribute).ResponseClass;
|
2019-08-05 13:37:42 +02:00
|
|
|
LStatus.Kind := tkArray;
|
2019-07-30 08:29:31 +02:00
|
|
|
LEndpoint.statuses.Add(LStatus);
|
|
|
|
end
|
2019-07-29 15:50:09 +02:00
|
|
|
end;
|
|
|
|
|
2019-07-30 08:29:31 +02:00
|
|
|
LEndpoint.OperationId := LMethod.Name;
|
2019-07-29 15:50:09 +02:00
|
|
|
fEndpoints.Add(LEndpoint);
|
|
|
|
end;
|
|
|
|
finally
|
|
|
|
LRttiContext.Free;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2019-08-05 13:37:42 +02:00
|
|
|
procedure TMVCSwaggerController.ProcessObjectForDefinition(aClass:TClass);
|
2019-07-29 15:50:09 +02:00
|
|
|
var
|
|
|
|
LRttiContext: TRttiContext;
|
|
|
|
LRttiType: TRttiType;
|
|
|
|
LField : TJsonField;
|
|
|
|
LChildschema : TJsonSchema;
|
|
|
|
LDefinition : TSwagDefinition;
|
|
|
|
LSchema : TJsonSchema;
|
|
|
|
LIndexedProperty: TRttiIndexedProperty;
|
2019-08-05 13:37:42 +02:00
|
|
|
LProperty : TRttiProperty;
|
2019-07-29 15:50:09 +02:00
|
|
|
LChildObject : TJSONFieldObject;
|
2019-08-05 13:37:42 +02:00
|
|
|
LTypeKind : TTypeKind;
|
|
|
|
LPropertyName : string;
|
|
|
|
LAttributes : TArray<TCustomAttribute>;
|
2019-07-29 15:50:09 +02:00
|
|
|
begin
|
|
|
|
LSchema := TJsonSchema.Create;
|
|
|
|
LRttiContext := TRttiContext.Create;
|
|
|
|
try
|
|
|
|
LRttiType := LRttiContext.GetType(aClass);
|
|
|
|
for LIndexedProperty in LRttiType.GetIndexedProperties do
|
|
|
|
begin
|
2019-08-05 13:37:42 +02:00
|
|
|
LTypeKind := LIndexedProperty.PropertyType.TypeKind;
|
|
|
|
LPropertyName := LIndexedProperty.Name;
|
|
|
|
case LTypeKind of
|
|
|
|
tkClassRef, tkPointer, tkProcedure, tkMRecord, tkInterface,
|
|
|
|
tkEnumeration, tkMethod, tkVariant, tkSet, tkRecord: ;
|
|
|
|
tkWChar, tkLString, tkWString, tkString, tkUString, tkChar:
|
|
|
|
LField := LSchema.AddField<String>(LPropertyName);
|
|
|
|
tkUnknown:
|
|
|
|
LField := LSchema.AddField<String>(LPropertyName);
|
|
|
|
tkInteger:
|
|
|
|
LField := LSchema.AddField<Integer>(LPropertyName);
|
|
|
|
tkInt64:
|
|
|
|
LField := LSchema.AddField<Int64>(LPropertyName);
|
|
|
|
tkFloat:
|
|
|
|
LField := LSchema.AddField<Double>(LPropertyName);
|
|
|
|
tkClass:
|
|
|
|
begin
|
|
|
|
LField := LSchema.AddField<TJsonFieldArray>(LPropertyName);
|
|
|
|
LChildObject := TJsonFieldObject.Create;
|
|
|
|
LChildObject.Ref := '#/definitions/' + TRttiInstanceType(LIndexedProperty.PropertyType).MetaclassType.ClassName;
|
|
|
|
OutputDebugString(PChar(LIndexedProperty.PropertyType.Name));
|
2019-07-29 15:50:09 +02:00
|
|
|
|
2019-08-05 13:37:42 +02:00
|
|
|
(LField as TJsonFieldArray).ItemFieldType := LChildObject;
|
|
|
|
OutputDebugString(PChar(LIndexedProperty.PropertyType.Name));
|
|
|
|
end;
|
|
|
|
tkArray:
|
|
|
|
OutputDebugString(PChar(LIndexedProperty.PropertyType.Name));
|
|
|
|
tkDynArray:
|
|
|
|
OutputDebugString(PChar(LIndexedProperty.PropertyType.Name));
|
|
|
|
end;
|
|
|
|
LAttributes := LIndexedProperty.GetAttributes;
|
|
|
|
ConvertFieldAttributesToSwagger(LField, LAttributes);
|
2019-07-29 15:50:09 +02:00
|
|
|
end;
|
|
|
|
|
|
|
|
for LProperty in LRttiType.GetProperties do
|
|
|
|
begin
|
2019-08-05 13:37:42 +02:00
|
|
|
LPropertyName := LProperty.Name;
|
2019-07-29 15:50:09 +02:00
|
|
|
if (LProperty.PropertyType.TypeKind = tkInteger) then
|
|
|
|
begin
|
2019-08-05 13:37:42 +02:00
|
|
|
LField := LSchema.AddField<Integer>(LPropertyName);
|
2019-07-29 15:50:09 +02:00
|
|
|
end
|
|
|
|
else if (LProperty.PropertyType.TypeKind = tkInt64) then
|
|
|
|
begin
|
2019-08-05 13:37:42 +02:00
|
|
|
LField := LSchema.AddField<Int64>(LPropertyName);
|
2019-07-29 15:50:09 +02:00
|
|
|
end
|
|
|
|
else if (LProperty.PropertyType.TypeKind = tkFloat) then
|
|
|
|
begin
|
2019-08-05 13:37:42 +02:00
|
|
|
LField := LSchema.AddField<Double>(LPropertyName);
|
|
|
|
end
|
|
|
|
else if (LProperty.PropertyType.TypeKind = tkWString) then
|
|
|
|
begin
|
|
|
|
LField := LSchema.AddField<WideString>(LPropertyName);
|
2019-07-29 15:50:09 +02:00
|
|
|
end
|
|
|
|
else if (LProperty.PropertyType.TypeKind = tkClass) then
|
|
|
|
begin
|
|
|
|
if LProperty.PropertyType.Name='TRttiInstanceType' then continue;
|
|
|
|
if LProperty.PropertyType.Name.StartsWith('TRtti') then continue;
|
|
|
|
LChildschema := TJsonSchema.Create;
|
|
|
|
LChildschema.Ref := '#/definitions/' + TRttiInstanceType(LProperty.PropertyType).MetaclassType.ClassName;
|
|
|
|
OutputDebugString(PChar(LProperty.PropertyType.Name));
|
|
|
|
LField := LSchema.AddField(LChildschema);
|
2019-08-05 13:37:42 +02:00
|
|
|
LField.Name := LPropertyName;
|
2019-07-29 15:50:09 +02:00
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
2019-08-05 13:37:42 +02:00
|
|
|
LField := LSchema.AddField<string>(LPropertyName);
|
2019-07-29 15:50:09 +02:00
|
|
|
end;
|
|
|
|
|
2019-08-05 13:37:42 +02:00
|
|
|
LAttributes := LProperty.GetAttributes;
|
|
|
|
ConvertFieldAttributesToSwagger(LField, LAttributes);
|
2019-07-29 15:50:09 +02:00
|
|
|
end;
|
|
|
|
LDefinition := TSwagDefinition.Create;
|
|
|
|
LDefinition.SetJsonSchema(aClass.ClassName, LSchema);
|
|
|
|
fSwagDoc.Definitions.Add(LDefinition);
|
|
|
|
finally
|
|
|
|
LRttiContext.Free;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-08-05 13:37:42 +02:00
|
|
|
procedure TMVCSwaggerController.ProcessObject(schema: TJSONSchema; aClass:TClass);
|
2019-07-29 15:50:09 +02:00
|
|
|
var
|
|
|
|
LRttiContext: TRttiContext;
|
|
|
|
LRttiType: TRttiType;
|
|
|
|
LProperty : TRttiProperty;
|
|
|
|
LAttribute : TCustomAttribute;
|
|
|
|
LField : TJsonField;
|
|
|
|
LChildschema : TJsonSchema;
|
|
|
|
begin
|
|
|
|
LRttiContext := TRttiContext.Create;
|
2019-08-05 13:37:42 +02:00
|
|
|
LField := nil;
|
2019-07-29 15:50:09 +02:00
|
|
|
try
|
|
|
|
LRttiType := LRttiContext.GetType(aClass);
|
|
|
|
for LProperty in LRttiType.GetProperties do
|
|
|
|
begin
|
|
|
|
if (LProperty.PropertyType.TypeKind = tkInteger) then
|
|
|
|
begin
|
2019-08-05 13:37:42 +02:00
|
|
|
LField := schema.AddField<Integer>(LProperty.Name);
|
2019-07-29 15:50:09 +02:00
|
|
|
end
|
|
|
|
else if (LProperty.PropertyType.TypeKind = tkInt64) then
|
|
|
|
begin
|
2019-08-05 13:37:42 +02:00
|
|
|
LField := schema.AddField<Int64>(LProperty.Name);
|
2019-07-29 15:50:09 +02:00
|
|
|
end
|
|
|
|
else if (LProperty.PropertyType.TypeKind = tkFloat) then
|
|
|
|
begin
|
2019-08-05 13:37:42 +02:00
|
|
|
LField := schema.AddField<Double>(LProperty.Name);
|
2019-07-29 15:50:09 +02:00
|
|
|
end
|
|
|
|
else if (LProperty.PropertyType.TypeKind = tkArray) then
|
|
|
|
begin
|
|
|
|
OutputDebugString(PChar(LProperty.PropertyType.Name));
|
|
|
|
end
|
|
|
|
else if (LProperty.PropertyType.TypeKind = tkDynArray) then
|
|
|
|
begin
|
|
|
|
OutputDebugString(PChar(LProperty.PropertyType.Name));
|
|
|
|
end
|
|
|
|
else if (LProperty.PropertyType.TypeKind = tkClass) then
|
|
|
|
begin
|
|
|
|
if LProperty.PropertyType.Name='TRttiInstanceType' then
|
|
|
|
continue;
|
|
|
|
if LProperty.PropertyType.Name.StartsWith('TRtti') then
|
|
|
|
continue;
|
|
|
|
LChildschema := TJsonSchema.Create;
|
|
|
|
OutputDebugString(PChar(LProperty.PropertyType.Name));
|
2019-08-05 13:37:42 +02:00
|
|
|
ProcessObject(LChildschema, TRttiInstanceType(LProperty.PropertyType).MetaclassType);
|
2019-07-29 15:50:09 +02:00
|
|
|
fDefinitions.Add(TRttiInstanceType(LProperty.PropertyType).MetaclassType);
|
|
|
|
LField := schema.AddField(LChildschema);
|
|
|
|
LField.Name := LProperty.Name;
|
|
|
|
end
|
|
|
|
else if (LProperty.PropertyType.TypeKind = tkWString) then
|
|
|
|
begin
|
2019-08-05 13:37:42 +02:00
|
|
|
LField := schema.AddField<WideString>(LProperty.Name);
|
2019-07-29 15:50:09 +02:00
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
2019-08-05 13:37:42 +02:00
|
|
|
LField := schema.AddField<string>(LProperty.Name);
|
2019-07-29 15:50:09 +02:00
|
|
|
end;
|
|
|
|
for LAttribute in LProperty.GetAttributes do
|
|
|
|
begin
|
|
|
|
if LAttribute is MVCDocAttribute then
|
|
|
|
begin
|
|
|
|
LField.Description := MVCDocAttribute(LAttribute).Value;
|
|
|
|
end
|
|
|
|
else if LAttribute is MVCPatternAttribute then
|
|
|
|
begin
|
|
|
|
// Property doesnt currently exist on TJSONField
|
|
|
|
if LField is TJsonFieldString then
|
|
|
|
(LField as TJsonFieldString).Pattern := MVCPatternAttribute(LAttribute).Value;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
finally
|
|
|
|
LRttiContext.Free;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TMVCSwaggerController.Swagger;
|
|
|
|
var
|
|
|
|
i, p, j: Integer;
|
|
|
|
k: Integer;
|
|
|
|
LPath : TSwagPath;
|
|
|
|
LPathOperation : TSwagPathOperation;
|
|
|
|
LSwagResponse : TSwagResponse;
|
|
|
|
LHttpMethod : TMVCHTTPMethodType;
|
|
|
|
LParam : TSwagRequestParameter;
|
|
|
|
LSchema : TJsonSchema;
|
2019-08-05 13:37:42 +02:00
|
|
|
LController : TMVCControllerDelegate;
|
|
|
|
LDefinition : TClass;
|
2019-07-29 15:50:09 +02:00
|
|
|
begin
|
2019-08-05 13:37:42 +02:00
|
|
|
for LController in Engine.Controllers do
|
2019-07-29 15:50:09 +02:00
|
|
|
begin
|
2019-08-05 13:37:42 +02:00
|
|
|
ProcessControllerMethods(LController.Clazz);
|
2019-07-29 15:50:09 +02:00
|
|
|
end;
|
2019-08-05 13:37:42 +02:00
|
|
|
|
2019-07-29 15:50:09 +02:00
|
|
|
Context.Response.ContentType := 'application/json';
|
|
|
|
|
|
|
|
for i := 0 to fEndpoints.Count - 1 do
|
|
|
|
begin
|
|
|
|
LPath := TSwagPath.Create;
|
|
|
|
LPath.Uri := fEndpoints[i].path;
|
|
|
|
for LHttpMethod in fEndpoints[i].method do
|
|
|
|
begin
|
|
|
|
LPathOperation := TSwagPathOperation.Create;
|
2019-07-30 08:29:31 +02:00
|
|
|
LPathOperation.Operation := TMVCSwaggerController.MVCMethodToSwaggerOperation(LHttpMethod);
|
2019-07-29 15:50:09 +02:00
|
|
|
LPathOperation.Description := fEndpoints[i].Documentation;
|
|
|
|
|
|
|
|
for p := fEndpoints[i].params.Count - 1 downto 0 do
|
|
|
|
begin
|
|
|
|
LParam := TSwagRequestParameter.Create;
|
|
|
|
LParam.Name := fEndpoints[i].params[p];
|
|
|
|
LParam.InLocation := rpiPath;
|
|
|
|
LPathOperation.Parameters.Add(LParam);
|
|
|
|
end;
|
|
|
|
|
2019-08-05 13:37:42 +02:00
|
|
|
if fEndpoints[i].Produces.length > 0 then
|
2019-07-29 15:50:09 +02:00
|
|
|
LPathOperation.Produces.Add(fEndpoints[i].produces);
|
|
|
|
|
2019-08-05 13:37:42 +02:00
|
|
|
if fEndpoints[i].Consumes.length > 0 then
|
2019-07-29 15:50:09 +02:00
|
|
|
LPathOperation.Consumes.Add(fEndpoints[i].consumes);
|
|
|
|
|
|
|
|
for j := 0 to fEndpoints[i].statuses.Count - 1 do
|
|
|
|
begin
|
|
|
|
LSwagResponse := TSwagResponse.Create;
|
2019-08-05 13:37:42 +02:00
|
|
|
LSwagResponse.StatusCode := fEndpoints[i].Statuses[j].statusCode.ToString;
|
|
|
|
LSwagResponse.Description := fEndpoints[i].Statuses[j].statusDescription;
|
2019-07-29 15:50:09 +02:00
|
|
|
LSchema := TJsonSchema.Create;
|
2019-08-05 13:37:42 +02:00
|
|
|
if Assigned(fEndpoints[i].Statuses[j].ReturnType) then
|
2019-07-29 15:50:09 +02:00
|
|
|
begin
|
2019-08-05 13:37:42 +02:00
|
|
|
ProcessObjectForDefinition(fEndpoints[i].Statuses[j].ReturnType);
|
|
|
|
ProcessObject(LSchema, fEndpoints[i].Statuses[j].ReturnType);
|
|
|
|
LSwagResponse.Schema.SetJsonSchema(fEndpoints[i].Statuses[j].ReturnType.ClassName, LSchema);
|
2019-07-29 15:50:09 +02:00
|
|
|
end;
|
2019-08-05 13:37:42 +02:00
|
|
|
LPathOperation.Responses.Add(fEndpoints[i].Statuses[j].StatusCode.ToString, LSwagResponse);
|
2019-07-29 15:50:09 +02:00
|
|
|
end;
|
|
|
|
OutputDebugString(PChar(TRttiEnumerationType.GetName(LPathOperation.Operation) + ' ' + LPath.Uri ));
|
2019-08-05 13:37:42 +02:00
|
|
|
LPathOperation.OperationId := fEndpoints[i].OperationId;
|
2019-07-29 15:50:09 +02:00
|
|
|
LPath.Operations.Add(LPathOperation);
|
|
|
|
end;
|
|
|
|
fSwagDoc.Paths.Add(LPath);
|
|
|
|
end;
|
|
|
|
|
2019-08-05 13:37:42 +02:00
|
|
|
for LDefinition in fDefinitions do
|
2019-07-29 15:50:09 +02:00
|
|
|
begin
|
2019-08-05 13:37:42 +02:00
|
|
|
ProcessObjectForDefinition(LDefinition);
|
2019-07-29 15:50:09 +02:00
|
|
|
end;
|
|
|
|
|
|
|
|
fSwagDoc.GenerateSwaggerJson;
|
|
|
|
Context.Response.Content := fSwagDoc.SwaggerJson.ToJSON;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ TMVCEndPoint }
|
|
|
|
|
|
|
|
constructor TMVCEndPoint.Create;
|
|
|
|
begin
|
|
|
|
statuses := TObjectList<TMVCStatusResponses>.Create;
|
|
|
|
params := TStringList.Create;
|
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TMVCEndPoint.Destroy;
|
|
|
|
begin
|
|
|
|
FreeAndNil(statuses);
|
|
|
|
FreeAndNil(params);
|
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ TStatusResponses }
|
|
|
|
|
|
|
|
// Possibly use this method to strip the Default 'T' off the start of an object name
|
|
|
|
function TMVCStatusResponses.GetReturnTypeName: string;
|
|
|
|
begin
|
|
|
|
Result := ReturnType.ClassName;
|
|
|
|
end;
|
|
|
|
|
|
|
|
end.
|