unit XData.OpenApi.Service.Internal;

interface

uses
  System.Classes, System.SysUtils, Generics.Collections,
  XData.OpenApi.Builder,
  XData.Service.Common;

type
  [URIPathSegment('openapi')]
  [SwaggerExclude]
  ICustomOpenApiService = interface(IInvokable)
  ['{E2337352-5139-45CB-8567-770CC41B73BE}']
    [HttpGet, URIPathSegment('swagger.json')]
    function SwaggerDocument(
      [XDefault(False)] ExcludeEntities: Boolean = False;
      [XDefault(False)] ExcludeOperations: Boolean = False): TStream;
  end;

  TCustomOpenApiService = class(TInterfacedObject, ICustomOpenApiService)
  public
    function SwaggerDocument(
      ExcludeEntities: Boolean = False;
      ExcludeOperations: Boolean = False): TStream;
  end;

implementation

uses
  Bcl.Json.Writer,
  Sparkle.Utils,
  OpenAPI.Document,
  OpenAPI.Json.Serializer,
  XData.Consts,
  XData.Aurelius.Model,
  XData.Server.Module;

{ TCustomOpenApiService }

function TCustomOpenApiService.SwaggerDocument(
  ExcludeEntities: Boolean = False;
  ExcludeOperations: Boolean = False): TStream;

  function GetQueryParam(const Query, Param: string; var Value: string): Boolean;
  var
    QueryItem: TPair<string, string>;
  begin
    for QueryItem in TSparkleUtils.GetQueryParams(Query) do
      if SameText(Param, QueryItem.Key) then
      begin
        Value := QueryItem.Value;
        Exit(True);
      end;
    Result := False;
  end;

var
  Doc: TOpenApiDocument;
  Builder: TOpenApiBuilder;
  Writer: TJsonWriter;
  Serializer: TSerializer;
  XModel: TXDataAureliusModel;
  Query: string;
  OptValue: string;
  Module: TXDataServerModule;
begin
  Doc := nil;
  Writer := nil;
  Serializer := nil;
  XModel := TXDataOperationContext.Current.Handler.XModel;
  Module := TXDataOperationContext.Current.Handler.XModule;
  Builder := TOpenApiBuilder.Create(Module);
  try
    Query := TXDataOperationContext.Current.Request.Uri.OriginalQuery;

    // Set build parameters
    Builder.ExcludeOperations := ExcludeOperations;
    Builder.ExcludeEntities := ExcludeEntities;
    Builder.JwtSecurity := Module.SwaggerOptions.AuthMode = TSwaggerAuthMode.Jwt;
    if GetQueryParam(Query, 'authmode', OptValue) then
    begin
      OptValue := Lowercase(OptValue);
      if OptValue = 'jwt' then
        Builder.JwtSecurity := True
      else
      if OptValue = 'none' then
        Builder.JwtSecurity := False;
    end;
    Builder.EnableKeyAsSegment := TXDataOperationContext.Current.Handler.XModule.EnableEntityKeyAsSegment;
    Builder.EnableKeyInParenthesis := not Builder.EnableKeyAsSegment;
    if GetQueryParam(Query, 'entityurl', OptValue) then
    begin
      OptValue := Lowercase(OptValue);
      if (OptValue = 'default') or (OptValue = 'odata') or (OptValue = 'parenthesis') then
      begin
        Builder.EnableKeyAsSegment := False;
        Builder.EnableKeyInParenthesis := True;
      end
      else
      if ((OptValue = 'path') or (OptValue = 'segment')) and Builder.EnableKeyAsSegment then
        Builder.EnableKeyInParenthesis := False
      else
      if (OptValue = 'all') then
        Builder.EnableKeyInParenthesis := True;
    end;

    // Now build
    Doc := Builder.GetDocument;

    Doc.Info.Title := XModel.Title;
    Doc.Info.Version := XModel.Version;
    Doc.Info.Description := XModel.Description;

    Doc.Host := TXDataOperationContext.Current.Request.Uri.Authority;
    Doc.BasePath := TXDataOperationContext.Current.Handler.XModule.BaseUri.Path;
    Doc.Paths.Sort;

    TXDataOperationContext.Current.Response.ContentType := JsonMediaType;

    Result := TMemoryStream.Create;
    try
      Writer := TJsonWriter.Create(Result);
      Serializer := TSerializer.Create;
      Serializer.Write(Doc, Writer);
    except
      Result.Free;
      raise;
    end;
  finally
    Serializer.Free;
    Writer.Free;
    Doc.Free;
    Builder.Free;
  end;
end;

end.
