-
Content Count
148 -
Joined
-
Last visited
-
Days Won
3
Everything posted by Andrea Magni
-
MARS now supports Delphi 10.3 Rio :-)
Andrea Magni replied to Andrea Magni's topic in MARS-Curiosity REST Library
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 -
Overriding the JWT_SECRET_PARAM_DEFAULT & JWT_ISSUER_PARAM_DEFAULT
Andrea Magni replied to Stuart Clennett's topic in MARS-Curiosity REST Library
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 -
Error when accessing TMARSNetClient ResponseStatusCode when endpoint is blacklisted
Andrea Magni replied to Stuart Clennett's topic in MARS-Curiosity REST Library
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 -
MARS now supports Delphi 10.3 Rio :-)
Andrea Magni replied to Andrea Magni's topic in MARS-Curiosity REST Library
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, -
Error when accessing TMARSNetClient ResponseStatusCode when endpoint is blacklisted
Andrea Magni replied to Stuart Clennett's topic in MARS-Curiosity REST Library
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! -
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,
-
Access Violation with a TFDMemTable placed on a datamodule inheriting from TMARSFDDataModuleResource
Andrea Magni replied to Stuart Clennett's topic in MARS-Curiosity REST Library
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 -
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
-
Error during Execute in MARSCmd
Andrea Magni replied to Stuart Clennett's topic in MARS-Curiosity REST Library
Thanks for figuring this out! I would say you should not have that file there (as it is not there in the repo https://github.com/andrea-magni/MARS/tree/master/Demos/MARSTemplate/bin ). You probably have it because you actually compiled MARSTemplate demo itself (did you?)... However it is obviously a fault of my strategy to rename files... I will try to improve (probably managing a list of files to rename and the expected final name and then have a much smarter copy strategy). Thanks again for taking the time to spot it and make it evident. I have added an issue on GitHub: https://github.com/andrea-magni/MARS/issues/52 Sincerely, Andrea -
Token/Roles not working with TMARSNetClient
Andrea Magni replied to Stuart Clennett's topic in MARS-Curiosity REST Library
I am starting to think it's something related to Delphi version (Berlin). There are entries in quality.embarcadero.com about TNetHTTPClient and I found this: https://quality.embarcadero.com/browse/RSP-14301 If the client is not sending cookies (and AuthEndorsement is set to Cookie), your situation is explained. The fact it works on my side may depend this may not affect all Berlin versions (I am using Delphi 10.1 Berlin Version 24.0.25048.9432, Berlin update 2 anniversary)... Do you have a chance to check with other Delphi versions? Or maybe someone else with Berlin may try to reproduce this? Thanks -
TFrameStand v.1.4: 10.3 Rio support
Andrea Magni replied to Andrea Magni's topic in Delphi Third-Party
BTW, TFrameStand v1.4 is now available through GetIt package manager for 10.3 Rio 🙂 -
TFrameStand v.1.4: 10.3 Rio support
Andrea Magni replied to Andrea Magni's topic in Delphi Third-Party
Hi Olivier, will have a closer look at your project ASAP. Thanks -
I've just released MARS version 1.3 ( https://github.com/andrea-magni/MARS/releases/tag/v1.3 … ). New developments will take place in the new 'develop' branch and there's already a small new feature: basic IXMLDocument support in MBR and MBW. You can consider using the released version in order to avoid unnecessary noise due to new developments, that from now on will take place in a separate 'develop' branch. Sincerely,
-
Hi, anybody had troubles performing a backup of a FB3 embedded database through the TIBBackup component? Firebird 3 uses fbclient.dll as client library also for embedded connections so it seems there's something wrong in the TIBBackup component when determining the client library to use (FireDAC.Phys.IBWrapper unit, TIBLib.GetLibraryInfo function. Given sLib var is always 'fbclient.dll' it misses it is a Firebird embedded connection). We tried to force fbclient.dll as vendor lib (giving a proper value to the DriverLink property of the TIBBackup instance, we also tried to set Embedded property of the driver link manually) but no way, it is always looking for fbembed.dll instead of the specified one (fbclient.dll). A quick and dirty workaround is to copy the DLL (fbclient) with the old name (fbembed) but I would like to avoid this, if possible. Any help would be appreciated. Maybe @Dmitry Arefiev may enlight us 😉 Thanks in advance
-
Token/Roles not working with TMARSNetClient
Andrea Magni replied to Stuart Clennett's topic in MARS-Curiosity REST Library
Thanks for you example, @Stuart Clennett This is what I get when I click the Test button: Login via TMARSNetClient successful Attempting GET with TMARSNetClient {"Now":"09.22.22","Token.UserName: ":"admin","Token.Roles:":"standard,admin"} Login via TMARSClient successful Attempting GET with TMARSClient {"Now":"09.22.22","Token.UserName: ":"admin","Token.Roles:":"standard,admin"} At this point I am starting to think there is something different in the Delphi version we are using (the authentication is actually endorsed through cookies (you may want to switch from cookies to Authorization header changing TMARSClient.AuthEndorsement property on both components and see what happens)). I am not aware of bugs in this area for Berlin but I may be missing something... Let me know please -
Error during Execute in MARSCmd
Andrea Magni replied to Stuart Clennett's topic in MARS-Curiosity REST Library
I really can't think what's going wrong... I recorded a short video, would you please try to step through it and see if something differs from what you are doing? Link: Would you try a different output path for your project? (to see if there something to do with privileges on file system?) If you like, we can have a remote session (Teamviewer or similar) to check it out together. Thanks for your patience -
Master switch for use of tokens
Andrea Magni replied to Bjørn Larsen's topic in MARS-Curiosity REST Library
Thank you for using MARS and please feel free to provide some feedback or ask questions (if needed) and I will be happy to help! Many features of my library came as users asked for this or that thing and I managed to implement it There's nothing better for a library author than seeing developers using it 🙂 -
Master switch for use of tokens
Andrea Magni replied to Bjørn Larsen's topic in MARS-Curiosity REST Library
Hi, MARS core mechanisms, including authentication and authorization, take place inside TMARSActivation class. The default implementation is attribute-driven and it is hard to have such dynamic behavior through them. But you can easily inherit your own TMARSActivation descendant and tweak some of the internals. For example, in order to fullfil your request about bypassing RBAC, you can override the ReadAuthorizationInfo method (that determines if a resource/method is public, is forbidden to everybody or has a list of allowed roles defined through attributes) and fix the result at your will (for example making every request allowed or implementing some strategy of your own based on the request, the application or the specific resource invoked.... whatever). Here attached there is a unit Server.MyActivation that implements a total override of RBAC depending on a parameter of your engine (you can set this parameter in your ini file or by code in the Server.Ignition unit). Some code from the attachment, comment out CodeSite if you are not using it (it's free and you can install it from GetIt, I use it often). TMyActivation = class(TMARSActivation) protected procedure ReadAuthorizationInfo; override; end; procedure TMyActivation.ReadAuthorizationInfo; begin inherited; CodeSite.SendMsg('[STANDARD] Authent.: ' + BoolToStr(FAuthorizationInfo.NeedsAuthentication, True) + ' Author.: ' + BoolToStr(FAuthorizationInfo.NeedsAuthorization, True)); if Engine.Parameters.ByName('DisableRBAC', False).AsBoolean then begin FAuthorizationInfo.PermitAll := True; FAuthorizationInfo.DenyAll := False; FAuthorizationInfo.AllowedRoles := []; CodeSite.SendMsg('[OVERRIDE] Authent.: ' + BoolToStr(FAuthorizationInfo.NeedsAuthentication, True) + ' Author.: ' + BoolToStr(FAuthorizationInfo.NeedsAuthorization, True)); end; end; initialization TMARSActivation.CreateActivationFunc := function (const AEngine: TMARSEngine; const AApplication: TMARSApplication; const ARequest: TWebRequest; const AResponse: TWebResponse; const AURL: TMARSURL ): IMARSActivation begin Result := TMyActivation.Create(AEngine, AApplication, ARequest, AResponse, AURL); end; The initialization section is there to set the TMARSActivation.CreateActivationFunc variable, that acts like a poor-man factory for IMARSActivation. The library will call this function, if set, to instantiate the specific TMARSActivation descendant (TMyActivation in our case). You can set the parameter in the ini file of your server: [DefaultEngine] ThreadPoolSize=100 DisableRBAC=True or setting it by code for example in the Server.Ignition file (after the FEngine.Parameters.LoadFromIniFile call): FEngine.Parameters.Values['DisableRBAC'] := True; Let me know if this solves your problem. Sincerely Server.MyActivation.pas -
Token/Roles not working with TMARSNetClient
Andrea Magni replied to Stuart Clennett's topic in MARS-Curiosity REST Library
PS: the use of TMARSNetClient (TNetHTTPClient based) with respect to TMARSClient (Indy based) brings you the benefits of using the OS http stack (including https support without having to deal with OpenSSL deployment and compression support). This may be more or less important depending on the fact you are building a multiplatform app or not... -
Error during Execute in MARSCmd
Andrea Magni replied to Stuart Clennett's topic in MARS-Curiosity REST Library
Weird... Let's assume you checked out MARS in a certain folder "MARS_BASE" (mine is 'C:\Sviluppo\Librerie\MARS'). You should have MARSCmd executable in MARS_BASE\Utils\Bin\Win32 Once you ran it, it should detect MARS base folder (it's printed in the bottom left part of the MARSCmd window) and suggest you to clone MARSTemplate from MARS_BASE\Demos\MARSTemplate, when you click Next, you should be able to change the "Replace With" value with the name of your new project, then click Next again and you should see the Destination folder valued to something like MARS_BASE\Demos\NAME_OF_YOUR_PROJECT I can see from your bug report that you are using Berlin as I am in this exact moment so I actually don't understand what's going wrong. Let me know please Sincerely -
Token/Roles not working with TMARSNetClient
Andrea Magni replied to Stuart Clennett's topic in MARS-Curiosity REST Library
Hi, I just made a quick test and everything run smooth here. Please post your code and we can investigate together. Here ( https://www.dropbox.com/s/1ak4fdru3lrs2my/TestRolesProject.zip?dl=0 ) you can find a simple project, built with MARSCmd from MARSTemplate and then I added a simple helloworld/json method allowed only for 'admin' role. The [Context] Token: TMARSToken get valued correctly and the client (included in the same groupproject) at designtime seems to work properly (you can authenticate but setting Username and Password [beware: password is the hour of the day you are running the demo] on the TMARSClientToken component and perform a POST request, inspect the Roles property and see them correctly valued) and then you can execute the helloworld/json resource (again, from the IDE clicking "GET" in the ObjectInspector) and see results (that includes an echo of Token.Username on the serverside as well as Token.Roles). The test project I attached has been built with 10.1 Berlin and then I tested it with 10.3 Rio without a glitch. Let me know, I will be happy to help you out -
ScaleMM2 with MARS: +70% performance boost :-)
Andrea Magni posted a topic in MARS-Curiosity REST Library
Just made a quick test using ScaleMM2 ( https://github.com/andremussche/scalemm ) with MARS (MinimalConsole demo, https://github.com/andrea-magni/MARS/tree/master/Demos/MinimalConsole) and it seems to push performances something like from 100 to 170% 🙂 If you are using MARS and want to give it a try, let me know how it works for you. -
ScaleMM2 with MARS: +70% performance boost :-)
Andrea Magni replied to Andrea Magni's topic in MARS-Curiosity REST Library
Thanks for the suggestions, I will give them a try but here we are a bit out of my comfort zone... Thanks @Stefan Glienke! -
ScaleMM2 with MARS: +70% performance boost :-)
Andrea Magni replied to Andrea Magni's topic in MARS-Curiosity REST Library
@David Heffernan so I guess the fact I was just brute forcing 10k requests with ab.exe (Apache Benchmark) matters as the request served was a simple hello world thing. The setup time for the execution should be way more than execution time (physiologically). It was just a simple test, but I am open to better benchmarking. Thanks -
ScaleMM2 with MARS: +70% performance boost :-)
Andrea Magni replied to Andrea Magni's topic in MARS-Curiosity REST Library
I am not actually an expert on this so low level topics but I am sure there is a big room for optimization in MARS. So far I always focused on functionalities and ease of use and there are a couple of spots I know I can easily optimize. It's on my todo list but not really a priority at this very moment. However, any help would be greatly appreciated, just in case somebody is willing to. 😉