Jump to content

Andrea Magni

  • Content Count

  • Joined

  • Last visited

Community Reputation

22 Excellent


About Andrea Magni

  • Birthday 05/24/1982

Technical Information

  • Delphi-Version
    Delphi 10.3 Rio

Recent Profile Visitors

172 profile views
  1. Andrea Magni

    Generating one-to-many on the fly with Live Bindings

    Another (small) addition (on the same path): 1) change SanitizeStr to this: function TForm1.SanitizeStr(const AString: string; const ADelimiter: string): string; begin Result := AString .Replace(' ' + ADelimiter, ADelimiter, [rfReplaceAll]) .Replace(ADelimiter + ' ', ADelimiter, [rfReplaceAll]); end; 2) change the SourceExpression to 'Owner.SanitizeStr(Text, Owner.ListBox1.Items.Delimiter)' And you can avoid setting the Items.Delimiter for your listbox. "It's a small step for man but..." :-)
  2. Andrea Magni

    Generating one-to-many on the fly with Live Bindings

    Not that I think that this would be a significant improvement with respect to your approach, but you can avoid the OnAssigningValue event-handler by introducing this public function to your form: function TForm1.SanitizeStr(const AString: string): string; begin Result := AString.Replace(' ,', ',', [rfReplaceAll]).Replace(', ', ',', [rfReplaceAll]); end; Then use as SourceExpression of your TBindExpression this value: 'Owner.SanitizeStr(Text)'. AFAIK "Owner" here stands for the owner of the BindingList and the LB expression engine is capable of calling a method on the underlying object (your form instance). Same lines of pascal code but one line left out from the XFM file (the one defining the event handler for OnAssigningValue event) and you can declare your function at your will (no prototype to match for the event handler). Does this match your definition of Better = writing less code? :-) Sincerely
  3. Basic tutorial on how to enable app shortcuts for your FMX apps. Delphi 10.3 Rio required (as the app shortcuts are available on Android since version 7.1, API Level 25). https://blog.andreamagni.eu/2019/02/how-to-add-android-app-shortcuts-to-a-fmx-application/ Sincerely, Andrea
  4. Andrea Magni

    ISAPI in Delphi 10.2,10.3 TranslateURI

    No worries 🙂 If you need some information about the library, feel free to ask me (there is a dedicated subforum in the third party group). Sincerely, Andrea
  5. Andrea Magni

    ISAPI in Delphi 10.2,10.3 TranslateURI

    There's no entry for my MARS REST library (https://github.com/andrea-magni/MARS) in your poll... I am happily doing web backend development since years, using DataSnap at first. Using MARS since it exists 😉
  6. Andrea Magni

    Adding a second app to a MARS Engine

    Hi, everything seems fine, just be sure your call to TMARSResourceRegistry.Instantce.RegisterResource<THealthCheck> takes place *before* the second call to AddApplication (in TServerEngine.CreateEngine method). I usually make this happen adding the name of the unit where THealthCheck to the uses clause list of the Server.Ignition.pas file (in the MARSTemplate demo, you can spot the Server.Resources unit listed there for the same reason). I am considering, in a future version of MARS, to switch from this 'initialization based' way of registering resources to a more 'configuration based' one. Still have to think about it and choose a simple approach to this problem. Sincerely, Andrea
  7. Andrea Magni

    MARS now supports Delphi 10.3 Rio :-)

    In most of the cases, it is just a matter of replacing the TMARSClient with TMARSNetClient. Beware of references to the original component (i.e. TMARSApplication or TMARSClientToken). Also beware the master branch has a defect for 10.3 Rio packages (fixed in the develop branch). Sincerely
  8. Andrea Magni

    MARS now supports Delphi 10.3 Rio :-)

    Hi, there are two implementation available TMARSIndyClient and TMARSNetClient. TMARSClient is actually an alias for TMARSIndyClient, to preserve backward compatibility but you can freely decide to use TMARSNetClient (as I do most of the times). Thanks for the kind words! Sincerely, Andrea
  9. Hi @Stuart Clennett, you can easily provide values for JWT configuration parameters through the server application configuration. MARSTemplate based applications looks for an ini file (same name of the server application) structured this way: [YourEngineName] YourAppName.YourParam=YourValue So, if you just cloned MARSTemplate with MARSCmd utility, you can change JWT secret and issuer this way: [DefaultEngine] DefaultApp.JWT.Secret=Andrea123 DefaultApp.JWT.Issuer=Andrea Beware if you change the engine name (form DefaultEngine) or the application name (from DefaultApp) you'll need to correct your ini file. Check you Server.Ignition.pas file and locate this line: FEngine.Parameters.LoadFromIniFile; This is where MARS tries to load configuration from the INI file. If you want to hard-code (or read from different sources) parameters values you can either add this line, keeping in mind you are actually defining a value for a parameter named 'JWT.Issuer' of an application named 'DefaultApp' (and the application name has to match): FEngine.Parameters.LoadFromIniFile; FEngine.Parameters.Values['DefaultApp.JWT.Issuer'] := 'My Issuer'; Or directly define the 'JWT.Issuer' (or any other parameter) to the configuration of the specific application: // Application configuration LApp := FEngine.AddApplication('DefaultApp', '/default', [ 'Server.Resources.*']); LApp.Parameters.Values['JWT.Issuer'] := 'My Issuer'; There is no much difference between these two options, but the second one may be handy if the name of the application is not a constant. A list of available JWT-related parameters (and their default values) is available here: https://github.com/andrea-magni/MARS/blob/master/Source/MARS.Utils.JWT.pas Sorry for the late (holiday time plus I've been sick too). Sincerely, Andrea
  10. It's easy, basically: 1) fork your copy of MARS repository (stuartclennet/MARS) 2) clone that instead of mine (andrea-magni/MARS) 3) commit your changes there and push to your GitHub repository 4) create a Pull Request (from the GitHub web interface or using other tools, I tend to use the web interface or the TortoiseGit functionalities) and I will have a chance to review your PR and merge it to the official MARS repository This way your contribution will show up both in your account and in the contributors list of MARS official repository. As you like, I get notified by email when someone post here as well when someone opens a new issue on github. If it is clearly a bug, then I woul open a github issue, if there's room for discussion maybe I would post here. Thanks. Sincerely, Andrea
  11. Andrea Magni

    MARS now supports Delphi 10.3 Rio :-)

    Ciao Alberto, thanks for the kind words and I hope you'll enjoy MARS. Feel free to ask questions here or open issues on GitHub if you run into troubles. MARS is a library suitable for creating REST Server projects and also has a client library to build REST Client projects. You can use them independently one from the other (this means MARS REST servers can be 100% Delphi agnostics and this also stands for the Client library that can consume any REST service out there) or use them together and get some Delphi-to-Delphi specific advantages (like some FireDAC integrations). Installation instructions are available here: https://github.com/andrea-magni/MARS/blob/master/docs/Installation.md Let me know if you need help or if you have questions. Sincerely,
  12. Thanks again for spotting this bug. If you like your contribution to this project to be more visible, please set up a GitHub account (if you haven't yet) and add a pull request for this (or future) corrections. I can obviously apply your changes to MARS but your contribution will not be tracked by GitHub (even if I always try to mention the author in the commit message in this situations). I have merged your changes with this commit ( https://github.com/andrea-magni/MARS/commit/9f45d60e7adb65a36432840baf2cf3e526a8f518 ) and also fixed LastCmdSuccess method. I went for returning -1 in ResponseStatusCode when FLastResponse is not available (rather than zero). BTW I didn't know about Charles proxy ( https://www.charlesproxy.com/ ) I had some troubles configuring WireShark and Fiddler inside a VM but I can see Charles runs perfectly with no clue. Seems very nice so far, thanks!
  13. Andrea Magni

    Custom media type

    Apart from the registration order, you can define a higher affinity of your writer in order to supersede the standard ones. Just switch the value returned by the affinity function in the RegisterWriter call from TMARSMessageBodyRegistry.AFFINITY_MEDIUM to TMARSMessageBodyRegistry.AFFINITY_HIGH and your writer will have a priority over the standard record writer. I would then write the WriteTo implementation like this: procedure TScimPlusJSONWriter.WriteTo(const AValue: TValue; const AMediaType: TMediaType; AOutputStream: TStream; const AActivation: IMARSActivation); var LType: TRttiType; begin LType := AActivation.MethodReturnType; if not Assigned(LType) then Exit; // procedure? if LType.IsRecord or LType.IsDynamicArrayOfRecord then TMARSMessageBodyWriter.WriteWith<TRecordWriter>(AValue, AMediaType, AOutputStream, AActivation) else if LType.IsObjectOfType<TJSONValue> then TMARSMessageBodyWriter.WriteWith<TJSONValueWriter>(AValue, AMediaType, AOutputStream, AActivation); AActivation.Response.ContentEncoding := 'UTF-8'; end; TMARSMessageBodyWriter.WriteWith<> is a new utility function I just introduced in the "develop" branch (MARS.Core.MessageBodyWriter unit, https://github.com/andrea-magni/MARS/commit/e818d7e261e7ef2b9a1c2c714adae459c5585c56#diff-97e07c61cdc46902bf53d808b8117104R157 ). You can simply instantiate the corresponding MessageBodyWriter class as we did before. Sincerely,
  14. Hi @Stuart Clennett, it's a bug! 🙂 Thanks for spotting it. The TMARSFireDAC.InjectMacroValues and TMARSFireDAC.InjectParamValues implementations assume every TFDAdaptedDataSet actually has a Command (not nil). TFDMemTable do not have Command assigned. Workaround: if you do not need your memory table to be included as the result of Retrieve method, you can decorate it with the RESTExclude attribute: [RESTExclude] FDMemTable1: TFDMemTable; However, I just fixed this in the "develop" branch of MARS repository: https://github.com/andrea-magni/MARS/commit/b6299926671b00e75981c47a74375d9c51c529ca Another workaround would be to change your copy of TMARSFDDataModuleResource.Retrieve and change this line: if LDataSet is TFDAdaptedDataSet then to this: if (LDataSet is TFDAdaptedDataSet) and Assigned(TFDAdaptedDataSet(LDataset).Command) then The MARS.Data.FireDAC.DataModule has nothing special, it is provided as an example of use, you can copy it, change it... it is obviously the counter-part of a TMARSFDResource but there no direct deal with the specific class, just matter of the shape of its methods (types returned, GET/POST selection, ...). Thanks, Andrea
  15. Andrea Magni

    Custom media type

    Hi @Bjørn Larsen, You have a number of possibilities and I would choose one way or another depending on the actual use case. If you need a single (or just a few) methods to produce application/scim+json content type, I would probably follow the way to let MARS produce standard JSON and then fix the ContentType/ContentEncoding. There's a handy way to do this through the Context attribute (MARS will inject a reference to the Response object you will be able to use it across all the methods you need): [Path('helloworld')] THelloWorldResource = class protected [Context] Response: TWebResponse; // add unit Web.HTTPApp to uses clause public [GET, Produces(TMediaType.APPLICATION_JSON)] function User1: TJSONObject; end; (...) function THelloWorldResource.User1: TJSONObject; begin Result := TJSONObject.Create; Result.WriteStringValue('id', TGUID.NewGuid.ToString); Response.ContentType := 'application/scim+json'; Response.ContentEncoding := 'UTF-8'; end; If you have several methods (in a single resource) that need to produce application/scim+json and you want to generalize this "response patching" one for all, you may take advantage of another MARS functionality: AfterInvoke code execution. It comes in a couple of variations, the first one will let you add a method (PatchResponse) and decorate it in order to have MARS execute it after each request to that resource: [Path('helloworld')] THelloWorldResource = class protected [Context] Response: TWebResponse; public [GET, Produces(TMediaType.APPLICATION_JSON)] function User1: TJSONObject; [AfterInvoke] procedure PatchResponse; end; (...) procedure THelloWorldResource.PatchResponse; begin Response.ContentType := 'application/scim+json'; Response.ContentEncoding := 'UTF-8'; end; function THelloWorldResource.User1: TJSONObject; begin Result := TJSONObject.Create; Result.WriteStringValue('id', TGUID.NewGuid.ToString); end; This way, your User1 (and other) method will not be polluted by the "response patching" and at the same time you have a single copy of the "response patching" code (DRY principle). Another AfterInvoke possibility is to define it at a more general level by calling TMARSActivation.RegisterAfterInvoke: TMARSActivation.RegisterAfterInvoke( procedure (const AActivation: IMARSActivation) begin if AActivation.ResourceInstance is THelloWorldResource then // or whatever other strategy begin AActivation.Response.ContentType := 'application/scim+json'; AActivation.Response.ContentEncoding := 'UTF-8'; end; end ); This has the advantage to be very wide (possibly spanning over each MARS engine/application) and does not require the injection of the Response object at resource level. You can setup your own strategy to determine when it's the case to patch the response or not (i.e. use some custom attribute yourself to decorate resources/methods or check the current ContentType or whatever... the AActivation parameter will provide you a very detailed context over the current MARS activation including Request, Response, selected class, selected method, method return value and so on). These are all "patching" options. There is another option available though: define a new media type. From the example you posted, I can't see if you are returning standard or custom types (i.e. TJSONObject instances or TYourClass instances/records). In both cases you can define a new MBW (MessageBodyWriter) for a new media type (application/scim+json) and have full control over Response generation. It's easy, I am attaching here a unit (MBW.ScimPlusJSON.pas) with a new MBW implementation for application/scim+json that will match any request with a Produces('application/scim+json') attribute and will act the same of the standard JSON MBW with the addition of setting the ContentEncoding of the Response. (ContenType will automatically be set by MARS). Just be sure to include the unit in the uses list of your resources unit or in the Server.Ignition unit. You resource then will look like this: [Path('helloworld')] THelloWorldResource = class protected [Context] Response: TWebResponse; public [GET, Produces(MEDIATYPE_APPLICATION_SCIM_PLUS_JSON)] function User1: TJSONObject; end; (...) function THelloWorldResource.User1: TJSONObject; begin Result := TJSONObject.Create; Result.WriteStringValue('id', TGUID.NewGuid.ToString); end; Hope this helps you to solve your problem and please let me know if something is unclear or you need anything else. Sincerely, Andrea MBW.ScimPlusJSON.pas