/// remote access to a mORMot server using SynCrossPlatform* units
{{#uri}}
// - retrieved from {{protocol}}://{{host}}/{{uri}}
// at {{time}} using "{{templateName}}" template
{{/uri}}
{{^uri}}
// - generated at {{time}}
{{/uri}}
unit {{fileName}};

{
  WARNING:
    This unit has been generated by a mORMot {{mORMotVersion}} server.
    Any manual modification of this file may be lost after regeneration.

  Synopse mORMot framework. Copyright (C) {{year}} Arnaud Bouchez
    Synopse Informatique - http://synopse.info

  This unit is released under a MPL/GPL/LGPL tri-license,
  and therefore may be freely included in any application.

  This unit would work on Delphi 6 and later, under all supported platforms
  (including MacOSX, and NextGen iPhone/iPad), and the Free Pascal Compiler.
}

interface

uses
  SynCrossPlatformJSON,
  SynCrossPlatformSpecific,
  SynCrossPlatformREST;
  
{{! recursive partials used to write records type definition}}
{{<writerec}}record
{{#fields}}
{{nestedIdentation}}    {{propName}}: {{#typePascal}}{{typePascal}};{{/typePascal}}{{#nestedRecord}}{{>writerec}}{{nestedIdentation}}    end;{{/nestedRecord}}{{#nestedSimpleArray}}array of {{typePascal}};{{/nestedSimpleArray}}{{#nestedRecordArray}}array of {{>writerec}}{{nestedIdentation}}    end;{{/nestedRecordArray}}
{{/fields}}{{/writerec}}
{{#withEnumerates}}
type // define some enumeration types, used below
{{#enumerates}}
  {{name}} = ({{#values}}{{.}}{{^-last}}, {{/-last}}{{/values}});
{{/enumerates}}

{{/withEnumerates}}
{{#withSets}}
type // define some set types, used below
{{#sets}}
  {{name}} = set of({{#values}}{{.}}{{^-last}}, {{/-last}}{{/values}});
{{/sets}}

{{/withSets}}
{{#withRecords}}
type // define some record types, used as properties below
{{#records}}
  {{name}} = {{>writerec}}  end;

{{/records}}
{{/withRecords}}
{{#withArrays}}
type // define some dynamic array types, used as properties below
{{#arrays}}
  {{name}} = array of {{typeSource}};
{{/arrays}}

{{/withArrays}}
{{<method}}{{methodName}}({{#args}}{{^dirResult}}{{dirName}} {{argName}}: {{typePascal}}{{commaArg}}{{/dirResult}}{{/args}}){{#args}}{{#dirResult}}: {{typePascal}}{{/dirResult}}{{/args}};{{/method}}
type
{{#orm}}
{{^isInMormotPas}}
  /// map "{{tableName}}" table
  {{className}} = class(TSQLRecord)
  protected
    {{#fields}}
    f{{name}}: {{typePascal}}; 
    {{/fields}}
  {{#hasRecords}}
  public
{{#fields}}
{{#isrecord}}
    property {{name}}: {{typePascal}} read f{{name}} write f{{name}}{{#unique}} stored AS_UNIQUE{{/unique}};
{{/isrecord}}
{{/fields}}
  {{/hasRecords}}
  published
{{#fields}}
{{^isrecord}}
{{#isSQLRecord}}    // defined as {{name}}: {{typeDelphi}} on the server
{{/isSQLRecord}}
    property {{name}}: {{typePascal}}{{#width}} index {{width}}{{/width}} read f{{name}} write f{{name}}{{#unique}} stored AS_UNIQUE{{/unique}};
{{/isrecord}}
{{/fields}}
  end;
  
{{/isInMormotPas}}
{{/orm}}
{{#soa.services}}
  /// service implemented by TService{{interfaceURI}}
  // - you can access this service as such:
  // !var a{{interfaceURI}}: I{{interfaceURI}};
  // !begin
  // !   a{{interfaceURI}} := TService{{interfaceURI}}.Create(aClient);
  // !   // now you can use a{{interfaceURI}} methods
  // !...
  I{{interfaceURI}} = interface(IServiceAbstract)
    ['{{GUID}}']
{{#methods}}
    {{verb}} {{>method}}
{{/methods}}
  end;

  /// implements I{{interfaceURI}} {{#uri}}from {{protocol}}://{{host}}/{{root}}/{{uri}}{{/uri}}
  // - this service will run in sic{{instanceCreationName}} mode
  TService{{interfaceURI}} = class(TServiceClientAbstract{{#isClientDriven}}ClientDriven{{/isClientDriven}},I{{interfaceURI}})
  public
    constructor Create(aClient: TSQLRestClientURI); override;
{{#methods}}
    {{verb}} {{>method}}
{{/methods}}
  end;

{{/soa.services}}
const
  /// the server port{{#uri}}, corresponding to {{protocol}}://{{host}}{{/uri}}
  SERVER_PORT = {{port}};
  /// the server model root name{{#uri}}, corresponding to {{protocol}}://{{host}}{{/uri}}
  SERVER_ROOT = '{{root}}';


/// return the database Model corresponding to this server
function GetModel(const aRoot: string=SERVER_ROOT): TSQLModel;

/// create a TSQLRestClientHTTP instance and connect to the server
// - it will use by default port {{port}} over root '{{root}}'{{#host}}, corresponding
// to {{protocol}}://{{host}}/{{root}}{{/host}}
{{#authClass}}
// - secure connection will be established via {{.}}
// with the supplied credentials - on connection or authentication error,
// this function will raise a corresponding exception
{{/authClass}}
function GetClient(const aServerAddress{{#authClass}}, aUserName,aPassword{{/authClass}}: string;
  aServerPort: integer=SERVER_PORT; const aServerRoot: string=SERVER_ROOT;
  aHttps: boolean={{#https}}true{{/https}}{{^https}}false{{/https}}): TSQLRestClientHTTP;

{{#withHelpers}}
// publish some low-level helpers for variant conversion
// - used internally: you should not need those functions in your end-user code
{{#enumerates}}
function Variant2{{name}}(const _variant: variant): {{name}};
{{/enumerates}}
{{#records}}
function Variant2{{name}}(_variant: variant): {{name}};
function {{name}}2Variant(const _record: {{name}}): variant;
{{/records}}
{{#arrays}}
function Variant2{{name}}(const _variant: variant): {{name}};
function {{name}}2Variant(const _array: {{name}}): variant;
{{/arrays}}

{{/withHelpers}}

implementation

{$HINTS OFF} // for H2164 hints of unused variables

{{#withEnumerates}}

{ Some helpers for enumerates types }

{{#enumerates}}
function Variant2{{name}}(const _variant: variant): {{name}};
begin
  result := {{name}}(VariantToEnum(_variant,[{{#values}}'{{.}}'{{^-last}},{{/-last}}{{/values}}]));
end;

{{/enumerates}}
{{/withEnumerates}}
{{#withRecords}}
{{<setrec}}{{#fields}}
{{#isSimple}}  result.{{fullPropName}} := _variant.{{fullPropName}};
{{/isSimple}}{{#nestedRecord}}{{>setrec}}{{/nestedRecord}}{{#fromVariant}}  result.{{fullPropName}} := {{fromVariant}}(_variant.{{fullPropName}});
{{/fromVariant}}{{#nestedSimpleArray}}  _arr := JSONVariantDataSafe(_variant.{{fullPropName}},jvArray);
  SetLength(result.{{fullPropName}},_arr^.Count);
  for _a := 0 to high(result.{{fullPropName}}) do
    result.{{fullPropName}}[_a] := {{#fromVariant}}{{fromVariant}}({{/fromVariant}}_arr^.Values[_a]{{#fromVariant}}){{/fromVariant}};
{{/nestedSimpleArray}}{{#nestedRecordArray}}  _arr := JSONVariantDataSafe(_variant.{{fullPropName}},jvArray);
  SetLength(result.{{fullPropName}},_arr^.Count);
  for _a := 0 to high(result.{{fullPropName}}) do
  with result.{{fullPropName}}[_a] do begin
{{#fields}}
    {{propName}} := {{#fromVariant}}{{fromVariant}}({{/fromVariant}}_arr^.Values[_a].{{propName}}{{#fromVariant}}){{/fromVariant}};
{{/fields}}
  end;
{{/nestedRecordArray}}{{/fields}}{{/setrec}}
{{<getrec}}{{#fields}}
{{#isSimple}}  res.SetPath('{{fullPropName}}',_record.{{fullPropName}});
{{/isSimple}}{{#nestedRecord}}{{>getrec}}{{/nestedRecord}}{{#toVariant}}  res.SetPath('{{fullPropName}}',{{toVariant}}(_record.{{fullPropName}}));
{{/toVariant}}{{#nestedSimpleArray}}  with res.EnsureData('{{fullPropName}}')^ do
    for i := 0 to high(_record.{{fullPropName}}) do
      AddValue({{#toVariant}}{{toVariant}}({{/toVariant}}_record.{{fullPropName}}[i]{{#toVariant}}){{/toVariant}});
{{/nestedSimpleArray}}{{#nestedRecordArray}}  with res.EnsureData('{{fullPropName}}')^ do
    for i := 0 to high(_record.{{fullPropName}}) do
    with AddItem^, _record.{{fullPropName}}[i] do begin
{{#fields}}
      AddNameValue('{{propName}}',{{#toVariant}}{{toVariant}}({{/toVariant}}{{propName}}{{#toVariant}}){{/toVariant}});
{{/fields}}
    end;
{{/nestedRecordArray}}{{/fields}}{{/getrec}}{ Some helpers for record types }
{{#records}}

function Variant2{{name}}(_variant: variant): {{name}};
var _a: integer;
    _arr: PJSONVariantData;
begin
{{>setrec}}
end;

function {{name}}2Variant(const _record: {{name}}): variant;
var i: integer;
    res: TJSONVariantData;
begin
  res.Init;
{{>getrec}}
  result := variant(res);
end;
{{/records}}

{{/withRecords}}
{{#withArrays}}

{ Some helpers for dynamic array types }

{{#arrays}}
function Variant2{{name}}(const _variant: variant): {{name}};
var i: integer;
    arr: PJSONVariantData;
begin
  arr := JSONVariantDataSafe(_variant,jvArray);
  SetLength(result,arr^.Count);
  for i := 0 to arr^.Count-1 do
    result[i] := {{#fromVariant}}{{fromVariant}}{{/fromVariant}}(arr^.Values[i]);
end;

function {{name}}2Variant(const _array: {{name}}): variant;
var i: integer;
    res: TJSONVariantData;
begin
  res.Init;
  for i := 0 to high(_array) do
    res.AddValue({{#toVariant}}{{toVariant}}{{/toVariant}}(_array[i]));
  result := variant(res);
end;

{{/arrays}}
{{/withArrays}}

{$HINTS ON} // for H2164 hints of unused variables

function GetModel(const aRoot: string): TSQLModel;
begin
  result := TSQLModel.Create([{{#orm}}{{className}}{{comma}}{{/orm}}],aRoot);
end;

function GetClient(const aServerAddress{{#authClass}}, aUserName,aPassword{{/authClass}}: string;
  aServerPort: integer; const aServerRoot: string; aHttps: boolean): TSQLRestClientHTTP;
begin
  result := TSQLRestClientHTTP.Create(aServerAddress,aServerPort,
    GetModel(aServerRoot),true,aHttps); // aOwnModel=true
  try
    if (not result.Connect) or (result.ServerTimeStamp=0) then
      raise ERestException.CreateFmt('Impossible to connect to %s:%d server',
        [aServerAddress,aServerPort]);
{{#authClass}}
    if not result.SetUser({{.}},aUserName,aPassword) then 
      raise ERestException.CreateFmt('%s:%d server rejected "%s" credentials',
        [aServerAddress,aServerPort,aUserName]);
{{/authClass}}
  except
    result.Free;
    raise;
  end;
end;

{{#soa.services}}

{ TService{{interfaceURI}} }

constructor TService{{interfaceURI}}.Create(aClient: TSQLRestClientURI);
begin
  fServiceName := '{{interfaceURI}}';
  fServiceURI := '{{uri}}';
  fInstanceImplementation := sic{{instanceCreationName}};
  fContractExpected := '{{contractExpected}}';
  inherited Create(aClient);
end;

{{#methods}}
{{verb}} TService{{interfaceURI}}.{{>method}}
var res: TVariantDynArray;
begin
  fClient.CallRemoteService(self,'{{methodName}}',{{ArgsOutputCount}}, // raise EServiceException on error
    [{{#args}}{{#dirInput}}{{#toVariant}}{{toVariant}}({{argName}}){{/toVariant}}{{^toVariant}}{{argName}}{{/toVariant}}{{commaInSingle}}{{/dirInput}}{{/args}}],res{{#resultIsServiceCustomAnswer}},true{{/resultIsServiceCustomAnswer}});
{{#args}}{{#dirOutput}}{{#isObject}}  {{argName}}.Free; // avoid memory leak
{{/isObject}}  {{argName}} := {{#fromVariant}}{{fromVariant}}({{/fromVariant}}res[{{indexOutResult}}{{#fromVariant}}){{/fromVariant}};
{{/dirOutput}}{{/args}}end;

{{/methods}}
{{/soa.services}}

end.