Jump to content

Recommended Posts

Hi,

I would like to develop a frontend using Angular.

I have a MARS server, which works well for GET calls with a FB FireDac DB. Since my frontend isn't a delphi client, I use a simple json format and not json/firedac input/output.

I struggle a little bit with the POST calls. I try to post with POSTMAN. Which json format do I have to use to get an update of my DB ?

For now, I have a 'Database not found' from my server.

 

Part of my code:

type
  [Path('/maindata')]
  TMainDataResource = class(TMARSFDDataModuleResource)
    FDGUIxWaitCursor1: TFDGUIxWaitCursor;
    qItems: TFDQuery;
    qOneItem: TFDQuery;
    conDB: TFDConnection;
  private
  protected
    [Context] FD: TMARSFireDAC;
  public
    [GET, Path('/allItems'), Produces(TMediaType.APPLICATION_JSON), IsReference]
    function getAllItems: TArray<TFDDataSet>;
    [POST, Path('/allItems'), Consumes(TMediaType.APPLICATION_JSON), IsReference]
    function postAllItems([BodyParam] const ADeltas: TArray<TFDMemTable>):TArray<TMARSFDApplyUpdatesRes>;
    [GET, Path('/oneItem'), Produces(TMediaType.APPLICATION_JSON), IsReference]
    function getOneItem: TArray<TFDDataSet>;
    [POST, Path('/oneItem'), Consumes(TMediaType.APPLICATION_JSON), IsReference]
    function postOneItem([BodyParam] const ADeltas: TArray<TFDMemTable>):TArray<TMARSFDApplyUpdatesRes>;
  end;

implementation

{%CLASSGROUP 'Vcl.Controls.TControl'}

{$R *.dfm}

uses
  MARS.Core.Registry;

{ TMainDataResource }

function TMainDataResource.getAllItems: TArray<TFDDataSet>;
begin
  Result := [qItems];
end;

function TMainDataResource.postAllItems([BodyParam] const ADeltas: TArray<TFDMemTable>):TArray<TMARSFDApplyUpdatesRes>;
begin
  Result:=FD.ApplyUpdates([qItems], ADeltas);
end;

function TMainDataResource.getOneItem: TArray<TFDDataSet>;
begin
  FD.InjectParamValues(TFDAdaptedDataSet(qOneItem).Command);
  Result := [qOneItem];
end;

function TMainDataResource.postOneItem([BodyParam] const ADeltas: TArray<TFDMemTable>):TArray<TMARSFDApplyUpdatesRes>;
begin
  Result:=FD.ApplyUpdates([qOneItem], ADeltas);
end;

 

The json structure for POST:

{

  "qItems": [

  {

    "id": 1,

    "name": "stuff"

  },

  {

    "id": 2,

    "name": "other stuff"

  }

  ]

}

 

which is in fact the same structure I get from the GET calls.

What can I do to get it work ?

 

Thanks,

 

Jean

 

Share this post


Link to post

Hi @Jean Vandromme,

when the client is not a Delphi application it is hard to rely on FireDAC specific features such as delta changes apply mechanism.

I would keep the GET methods as you have written them but I would change the POST methods to something like:

 

    [POST, Path('/allItems'), Consumes(TMediaType.APPLICATION_JSON)]
    function postAllItems([BodyParam] const AChanges: TJSONObject):TJSONObject;

Then you should go through AChanges JSON object to extract data and apply changes to the DB (using the FD helper object to build some SQL or opening the qItems dataset, making some locate, edit data and post dataset.

This is a very basic approach (not so handy IMHO).

 

Another, more convenient IMHO, option is to define a record structure mimicking the JSON structure you want to accept as description of data changes and let MARS materialize it from the client's request.

 

TItem = record
  id: Integer;
  name: string
end;

(...)

    [POST, Path('/allItems'), Consumes(TMediaType.APPLICATION_JSON)]
    function postAllItems([BodyParam] const AChanges: TArray<TItem>): TJSONObject;

 

Once you have AChanges materialized automatically by MARS, you can take advantage of the TRecord<TItem>.ToDataSet implementation to apply changes to the dataset (beware of locating the right record in advance or query the record using the id for best performance).

 

Let me know if it is clear enough... If not I can provide some demo to show you what I am suggesting to do.

 

Sincerely,

Andrea

 

 

 

  • Like 1

Share this post


Link to post

Hi Andrea,

Thanks a lot, I know where I have to go now. I will loose some interesting MARS features doing so but if it's the only way.

The problem with the 2d approach is that I have tables with a lot of columns (more than 100), and I thought I could avoid making models for all of them.

One thing though, what do you mean by TRecord<TItem>.ToDataset implementation ? Is it a Firedac thing or a MARS thing ?

 

Thanks

 

Jean

Share this post


Link to post

TRecord<TItem>.ToDataset (and FromDataset as well) is a MARS feature.

The TRecord<T> class (defined in MARS.Rtti.Utils unit, https://github.com/andrea-magni/MARS/blob/master/Source/MARS.Rtti.Utils.pas) is there to provide some utility functions around records.

 

If you don't want to declare record types for large tables, you can skip (Delphi) records and go for a more raw level approach using JSON objects (and then some how map each JSON object to the corresponding DB record). Your declaration would become something like:

 

    [POST, Path('/allItems'), Consumes(TMediaType.APPLICATION_JSON)]
    function postAllItems([BodyParam] const AChanges: TJSONArray): TJSONObject;

 

This way, if the client sends changes like the following JSON array:

[

  { "id": 123,
    "name": "Andrea",
    ... 
  },

  { "id": 456,
    "name": "Jean",
    ...
  }, 
  
  ... 
]

 

You have a chance to iterate over array's elements and then find a decent strategy to update data in the DB (consider client may or not send all fields back, depending on the client-side technology/strategy used).

 

If you find yourself in the need of some specific MARS utility/feature, just let me know (here or on GitHub).

 

Sincerely,

Andrea

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×