├── .gitignore ├── ConfigU.pas ├── DataAccessLayerU.pas ├── EntitiesControllerU.pas ├── LICENSE ├── README.md ├── SecurityHeadersMiddlewareU.pas ├── WebModuleU.dfm ├── WebModuleU.pas ├── bin ├── config.json └── data.json ├── buildlocal.bat ├── mockjsonapi.dpr ├── mockjsonapi.dproj ├── mockjsonapi_Icon.ico └── tests ├── locustfile.py ├── pip-selfcheck.json ├── requirements.txt ├── test.data.json ├── unittests.py └── venv └── Lib └── site-packages └── locust └── static └── img └── favicon.ico /.gitignore: -------------------------------------------------------------------------------- 1 | # Uncomment these types if you want even more clean repository. But be careful. 2 | # It can make harm to an existing project source. Read explanations below. 3 | # 4 | # Resource files are binaries containing manifest, project icon and version info. 5 | # They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files. 6 | #*.res 7 | # 8 | # Type library file (binary). In old Delphi versions it should be stored. 9 | # Since Delphi 2009 it is produced from .ridl file and can safely be ignored. 10 | #*.tlb 11 | # 12 | # Diagram Portfolio file. Used by the diagram editor up to Delphi 7. 13 | # Uncomment this if you are not using diagrams or use newer Delphi version. 14 | #*.ddp 15 | # 16 | # Visual LiveBindings file. Added in Delphi XE2. 17 | # Uncomment this if you are not using LiveBindings Designer. 18 | #*.vlb 19 | # 20 | # Deployment Manager configuration file for your project. Added in Delphi XE2. 21 | # Uncomment this if it is not mobile development and you do not use remote debug feature. 22 | #*.deployproj 23 | # 24 | # C++ object files produced when C/C++ Output file generation is configured. 25 | # Uncomment this if you are not using external objects (zlib library for example). 26 | #*.obj 27 | # 28 | 29 | # Delphi compiler-generated binaries (safe to delete) 30 | *.exe 31 | *.dll 32 | *.bpl 33 | *.bpi 34 | *.dcp 35 | *.so 36 | *.apk 37 | *.drc 38 | *.map 39 | *.dres 40 | *.rsm 41 | *.tds 42 | *.dcu 43 | *.lib 44 | *.a 45 | *.o 46 | *.ocx 47 | 48 | # Delphi autogenerated files (duplicated info) 49 | *.cfg 50 | *.hpp 51 | *Resource.rc 52 | 53 | # Delphi local files (user-specific info) 54 | *.local 55 | *.identcache 56 | *.projdata 57 | *.tvsconfig 58 | *.dsk 59 | 60 | # Delphi history and backups 61 | __history/ 62 | __recovery/ 63 | *.~* 64 | 65 | # Castalia statistics file (since XE7 Castalia is distributed with Delphi) 66 | *.stat 67 | 68 | #Specific Python ignore 69 | tests\venv 70 | tests\__pycache__ 71 | tests\.idea 72 | 73 | tests/venv/ 74 | mockjsonapi.res 75 | tests/__pycache__/ 76 | -------------------------------------------------------------------------------- /ConfigU.pas: -------------------------------------------------------------------------------- 1 | // *************************************************************************** 2 | // 3 | // MockJSONAPI 4 | // 5 | // Copyright (c) 2020 Daniele Teti 6 | // 7 | // https://github.com/danieleteti/mockjsonapi 8 | // 9 | // *************************************************************************** 10 | // 11 | // Licensed under the Apache License, Version 2.0 (the "License"); 12 | // you may not use this file except in compliance with the License. 13 | // You may obtain a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, software 18 | // distributed under the License is distributed on an "AS IS" BASIS, 19 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | // See the License for the specific language governing permissions and 21 | // limitations under the License. 22 | // 23 | // *************************************************************************** } 24 | 25 | unit ConfigU; 26 | 27 | interface 28 | 29 | uses 30 | JsonDataObjects; 31 | 32 | const 33 | VERSION = '1.1.1'; 34 | 35 | type 36 | TConfig = class sealed 37 | private 38 | class var sInstance: TConfig; 39 | 40 | var 41 | fConfigDict: TJsonObject; 42 | constructor Create; 43 | class function GetConfig: TConfig; static; 44 | public 45 | destructor Destroy; override; 46 | function GetString(const ConfigName: string): string; 47 | function GetInteger(const ConfigName: string): Integer; 48 | class destructor Destroy; 49 | class property Instance: TConfig read GetConfig; 50 | end; 51 | 52 | implementation 53 | 54 | uses 55 | System.IOUtils, System.SysUtils; 56 | 57 | { TConfig } 58 | 59 | constructor TConfig.Create; 60 | begin 61 | fConfigDict := nil; 62 | if TFile.Exists('config.json') then 63 | begin 64 | fConfigDict := TJsonObject.ParseUtf8(UTF8Encode(TFile.ReadAllText('config.json'))) as TJsonObject; 65 | end 66 | else 67 | raise Exception.Create('Cannot find "config.json" file'); 68 | end; 69 | 70 | destructor TConfig.Destroy; 71 | begin 72 | fConfigDict.Free; 73 | fConfigDict := nil; 74 | end; 75 | 76 | class destructor TConfig.Destroy; 77 | begin 78 | sInstance.Free; 79 | end; 80 | 81 | class function TConfig.GetConfig: TConfig; 82 | begin 83 | if not Assigned(sInstance) then 84 | sInstance := TConfig.Create; 85 | Result := sInstance; 86 | end; 87 | 88 | function TConfig.GetInteger(const ConfigName: string): Integer; 89 | begin 90 | Result := fConfigDict.I[ConfigName]; 91 | end; 92 | 93 | function TConfig.GetString(const ConfigName: string): string; 94 | begin 95 | Result := fConfigDict.S[ConfigName]; 96 | end; 97 | 98 | end. 99 | -------------------------------------------------------------------------------- /DataAccessLayerU.pas: -------------------------------------------------------------------------------- 1 | // *************************************************************************** 2 | // 3 | // MockJSONAPI 4 | // 5 | // Copyright (c) 2020 Daniele Teti 6 | // 7 | // https://github.com/danieleteti/mockjsonapi 8 | // 9 | // *************************************************************************** 10 | // 11 | // Licensed under the Apache License, Version 2.0 (the "License"); 12 | // you may not use this file except in compliance with the License. 13 | // You may obtain a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, software 18 | // distributed under the License is distributed on an "AS IS" BASIS, 19 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | // See the License for the specific language governing permissions and 21 | // limitations under the License. 22 | // 23 | // *************************************************************************** } 24 | 25 | unit DataAccessLayerU; 26 | 27 | interface 28 | 29 | uses 30 | JsonDataObjects, System.SysUtils, System.Classes; 31 | 32 | const 33 | OID_KEY_NAME = '_oid'; 34 | 35 | type 36 | ENotFound = class(Exception) 37 | 38 | end; 39 | 40 | EInvalidJSON = class(Exception) 41 | 42 | end; 43 | 44 | {$SCOPEDENUMS ON} 45 | 46 | TExecAction = (readonly, ReadAndWrite); 47 | 48 | IDALService = interface 49 | ['{B03B66EF-C713-403B-A09B-8EDE0A6385B3}'] 50 | function GetEntity(const EntityName: string; const EntityID: string) 51 | : TJsonObject; 52 | procedure DeleteEntity(const EntityName: string; const EntityID: string); 53 | procedure DeleteResource(const EntityName: string); 54 | function GetEntities(const EntityName: string): TJsonArray; 55 | function CreateEntity(const EntityName: string; 56 | const JsonObject: TJsonObject): string; 57 | procedure UpdateEntity(const JsonData: TJsonObject; 58 | const EntityName, EntityID: string); 59 | end; 60 | 61 | TDALService = class(TInterfacedObject, IDALService) 62 | private 63 | class var cLock: TObject; 64 | 65 | const 66 | cJSONFileName: string = 'data.json'; 67 | protected 68 | function GetJSONObjectByOID(const JsonData: TJsonObject; 69 | const EntityName, EntityID: string): TJsonObject; 70 | function IndexOfObject(const JsonData: TJsonObject; 71 | const EntityName, EntityID: string): Integer; 72 | function GetNewOID: string; 73 | procedure ExecWithLock(const Proc: TProc; 74 | const Action: TExecAction = TExecAction.ReadOnly); 75 | function GetEntity(const EntityName: string; const EntityID: string) 76 | : TJsonObject; 77 | procedure DeleteEntity(const EntityName: string; const EntityID: string); 78 | procedure DeleteResource(const EntityName: string); 79 | function GetEntities(const EntityName: string): TJsonArray; 80 | function CreateEntity(const EntityName: string; 81 | const JsonObject: TJsonObject): string; 82 | procedure UpdateEntity(const JsonData: TJsonObject; 83 | const EntityName, EntityID: string); 84 | public 85 | class constructor Create; 86 | class destructor Destroy; 87 | 88 | end; 89 | 90 | function GetDAL: IDALService; 91 | 92 | implementation 93 | 94 | function GetDAL: IDALService; 95 | begin 96 | Result := TDALService.Create; 97 | end; 98 | 99 | { TDALService } 100 | 101 | class constructor TDALService.Create; 102 | begin 103 | cLock := TObject.Create; 104 | end; 105 | 106 | function TDALService.CreateEntity(const EntityName: string; 107 | const JsonObject: TJsonObject): string; 108 | var 109 | lRes: string; 110 | begin 111 | lRes := ''; 112 | ExecWithLock( 113 | procedure(JsonData: TJsonObject) 114 | var 115 | lObj: TJsonObject; 116 | begin 117 | lObj := JsonData.A[EntityName].AddObject; 118 | lObj.Assign(JsonObject); 119 | if not lObj.Contains(OID_KEY_NAME) then 120 | lObj.S[OID_KEY_NAME] := GetNewOID; 121 | lRes := lObj.S[OID_KEY_NAME]; 122 | end, TExecAction.ReadAndWrite); 123 | Result := lRes; 124 | end; 125 | 126 | procedure TDALService.DeleteEntity(const EntityName, EntityID: string); 127 | begin 128 | ExecWithLock( 129 | procedure(JsonData: TJsonObject) 130 | var 131 | lEntityIndex: Integer; 132 | begin 133 | lEntityIndex := IndexOfObject(JsonData, EntityName, EntityID); 134 | if lEntityIndex > -1 then 135 | JsonData.A[EntityName].Delete(lEntityIndex); 136 | { no exception if no data found } 137 | end, TExecAction.ReadAndWrite); 138 | end; 139 | 140 | procedure TDALService.DeleteResource(const EntityName: string); 141 | begin 142 | ExecWithLock( 143 | procedure(JsonData: TJsonObject) 144 | begin 145 | JsonData.ExtractArray(EntityName).Free; 146 | end, TExecAction.ReadAndWrite); 147 | end; 148 | 149 | class destructor TDALService.Destroy; 150 | begin 151 | FreeAndNil(cLock); 152 | end; 153 | 154 | procedure TDALService.ExecWithLock(const Proc: TProc; 155 | const Action: TExecAction); 156 | var 157 | lJSONData: TJsonObject; 158 | begin 159 | TMonitor.Enter(cLock); 160 | try 161 | try 162 | lJSONData := TJsonObject.ParseFromFile(cJSONFileName) as TJsonObject; 163 | except 164 | on E: EJsonParserException do 165 | begin 166 | raise EInvalidJSON.Create('data.json is not a valid json file. ' + 167 | E.Message); 168 | end; 169 | end; 170 | try 171 | Proc(lJSONData); 172 | if Action = TExecAction.ReadAndWrite then 173 | begin 174 | lJSONData.SaveToFile(cJSONFileName, False); 175 | end; 176 | finally 177 | lJSONData.Free; 178 | end; 179 | finally 180 | TMonitor.Exit(cLock); 181 | end; 182 | end; 183 | 184 | function TDALService.GetEntities(const EntityName: string): TJsonArray; 185 | var 186 | lRes: TJsonArray; 187 | begin 188 | lRes := nil; 189 | ExecWithLock( 190 | procedure(JsonObject: TJsonObject) 191 | begin 192 | lRes := TJsonArray.Create; 193 | lRes.Assign(JsonObject.A[EntityName]); 194 | end); 195 | Result := lRes; 196 | end; 197 | 198 | function TDALService.GetEntity(const EntityName: string; const EntityID: string) 199 | : TJsonObject; 200 | var 201 | lRes: TJsonObject; 202 | begin 203 | ExecWithLock( 204 | procedure(JsonObject: TJsonObject) 205 | var 206 | lJSON: TJsonObject; 207 | begin 208 | lJSON := GetJSONObjectByOID(JsonObject, EntityName, EntityID); 209 | lRes := TJsonObject.Create; 210 | lRes.Assign(lJSON); 211 | end); 212 | Result := lRes; 213 | end; 214 | 215 | function TDALService.GetJSONObjectByOID(const JsonData: TJsonObject; 216 | const EntityName, EntityID: string): TJsonObject; 217 | var 218 | lArr: TJsonArray; 219 | lEntityIndex: Integer; 220 | begin 221 | lArr := JsonData.A[EntityName]; 222 | lEntityIndex := IndexOfObject(JsonData, EntityName, EntityID); 223 | if lEntityIndex > -1 then 224 | begin 225 | Result := lArr.O[lEntityIndex]; 226 | end 227 | else 228 | begin 229 | raise ENotFound.Create('Not Found'); 230 | end; 231 | end; 232 | 233 | function TDALService.GetNewOID: string; 234 | begin 235 | Result := TGuid.NewGuid.ToString.Replace('{', '').Replace('}', ''); 236 | end; 237 | 238 | function TDALService.IndexOfObject(const JsonData: TJsonObject; 239 | const EntityName, EntityID: string): Integer; 240 | var 241 | lArr: TJsonArray; 242 | I: Integer; 243 | begin 244 | Result := -1; 245 | lArr := JsonData.A[EntityName]; 246 | for I := 0 to lArr.Count - 1 do 247 | begin 248 | if lArr.O[I].S[OID_KEY_NAME] = EntityID then 249 | begin 250 | Result := I; 251 | break; 252 | end; 253 | end; 254 | end; 255 | 256 | procedure TDALService.UpdateEntity( 257 | 258 | const JsonData: TJsonObject; 259 | 260 | const EntityName, EntityID: string); 261 | begin 262 | ExecWithLock( 263 | procedure(FullJsonData: TJsonObject) 264 | var 265 | lJSON: TJsonObject; 266 | begin 267 | lJSON := GetJSONObjectByOID(FullJsonData, EntityName, EntityID); 268 | lJSON.Assign(JsonData); 269 | lJSON.S[OID_KEY_NAME] := EntityID; 270 | end, TExecAction.ReadAndWrite); 271 | end; 272 | 273 | end. 274 | -------------------------------------------------------------------------------- /EntitiesControllerU.pas: -------------------------------------------------------------------------------- 1 | // *************************************************************************** 2 | // 3 | // MockJSONAPI 4 | // 5 | // Copyright (c) 2020 Daniele Teti 6 | // 7 | // https://github.com/danieleteti/mockjsonapi 8 | // 9 | // *************************************************************************** 10 | // 11 | // Licensed under the Apache License, Version 2.0 (the "License"); 12 | // you may not use this file except in compliance with the License. 13 | // You may obtain a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, software 18 | // distributed under the License is distributed on an "AS IS" BASIS, 19 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | // See the License for the specific language governing permissions and 21 | // limitations under the License. 22 | // 23 | // *************************************************************************** } 24 | 25 | unit EntitiesControllerU; 26 | 27 | interface 28 | 29 | uses 30 | MVCFramework, MVCFramework.Commons, MVCFramework.Serializer.Commons; 31 | 32 | type 33 | 34 | [MVCPath('/api')] 35 | TEntitiesController = class(TMVCController) 36 | public 37 | [MVCPath('/($resourcename)')] 38 | [MVCHTTPMethods([httpGET])] 39 | procedure GetEntities(const resourcename: string); 40 | 41 | [MVCPath('/($resourcename)/($entityid)')] 42 | [MVCHTTPMethods([httpGET])] 43 | procedure GetEntity(const resourcename: string; const entityid: string); 44 | 45 | [MVCPath('/($resourcename)/($entityid)')] 46 | [MVCHTTPMethods([httpDELETE])] 47 | procedure DeleteEntity(const resourcename: string; const entityid: string); 48 | 49 | [MVCPath('/($resourcename)/($entityid)')] 50 | [MVCHTTPMethods([httpPUT])] 51 | procedure UpdateEntity(const resourcename: string; const entityid: string); 52 | 53 | [MVCPath('/($resourcename)')] 54 | [MVCHTTPMethods([httpDELETE])] 55 | procedure DeleteResource(const resourcename: string); 56 | 57 | [MVCPath('/($resourcename)')] 58 | [MVCHTTPMethods([httpPOST])] 59 | procedure CreateEntities(const resourcename: string); 60 | end; 61 | 62 | implementation 63 | 64 | uses 65 | System.SysUtils, MVCFramework.Logger, System.StrUtils, DataAccessLayerU, 66 | JsonDataObjects; 67 | 68 | { TEntitiesController } 69 | 70 | procedure TEntitiesController.CreateEntities(const resourcename: string); 71 | var 72 | lJson: TJsonObject; 73 | lOID, lResourceName: string; 74 | lXRef: string; 75 | begin 76 | ContentType := TMVCMediaType.APPLICATION_JSON; 77 | lJson := TJsonObject.Parse(Context.Request.Body) as TJsonObject; 78 | try 79 | lResourceName := resourcename.ToLower; 80 | Context.Response.StatusCode := http_status.Created; 81 | lOID := GetDAL.CreateEntity(lResourceName, lJson); 82 | lXRef := '/api/' + lResourceName + '/' + lOID; 83 | Context.Response.SetCustomHeader('X-REF', lXRef); 84 | StatusCode := HTTP_STATUS.Created; 85 | Context.Response.SetCustomHeader('Location', lXRef); 86 | Render(ObjectDict().Add('data', StrDict(['status', 'xref'], ['ok', lXRef]))); 87 | //Render201Created(lXRef); 88 | finally 89 | lJson.Free; 90 | end; 91 | end; 92 | 93 | procedure TEntitiesController.DeleteEntity(const resourcename, 94 | entityid: string); 95 | begin 96 | GetDAL.DeleteEntity(resourcename, entityid); 97 | StatusCode := http_status.OK; 98 | Render(ObjectDict().Add('data', StrDict(['status'], ['ok']))); 99 | end; 100 | 101 | procedure TEntitiesController.DeleteResource(const resourcename: string); 102 | begin 103 | GetDAL.DeleteResource(resourcename); 104 | StatusCode := http_status.OK; 105 | Render(ObjectDict().Add('data', StrDict(['status'], ['ok']))); 106 | end; 107 | 108 | procedure TEntitiesController.GetEntities(const resourcename: string); 109 | begin 110 | try 111 | Render(ObjectDict().Add('data', GetDAL.GetEntities(resourcename))); 112 | except 113 | on E: ENotFound do 114 | begin 115 | Render(http_status.NotFound, E.Message); 116 | end; 117 | end; 118 | end; 119 | 120 | procedure TEntitiesController.GetEntity(const resourcename: string; 121 | const entityid: string); 122 | begin 123 | try 124 | Render(ObjectDict().Add('data', GetDAL.GetEntity(resourcename, entityid))); 125 | except 126 | on E: ENotFound do 127 | begin 128 | Render(http_status.NotFound, E.Message); 129 | end; 130 | end; 131 | end; 132 | 133 | procedure TEntitiesController.UpdateEntity(const resourcename, 134 | entityid: string); 135 | var 136 | lJson: TJsonObject; 137 | lResourceName: string; 138 | lXRef: string; 139 | begin 140 | lResourceName := resourceName; 141 | lJson := StrToJSONObject(Context.Request.Body); 142 | try 143 | GetDAL.UpdateEntity(lJson, resourcename, entityid); 144 | lXRef := '/api/' + lResourceName + '/' + entityid; 145 | StatusCode := HTTP_STATUS.OK; 146 | Context.Response.SetCustomHeader('Location', lXRef); 147 | Context.Response.SetCustomHeader('X-REF', lXRef); 148 | Render(ObjectDict().Add('data', StrDict(['status', 'xref'], ['ok', lXRef]))); 149 | finally 150 | lJson.Free; 151 | end; 152 | end; 153 | 154 | end. 155 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MockJSONAPI Server 2 | MockJSONAPI is a mock server for a generic JSON API built with Delphi and delphimvcframework. Can be used with any client technology and language. When mockjsonapi server receives a requests it matches the request against the `data.json` that have been configured. 3 | If you are an experienced user, grab the latest [binary release here](https://github.com/danieleteti/mockjsonapi/releases/latest) otherwise, keep reading the docs. 4 | 5 | ## Getting started 6 | Let's say that you need to develop a REST client (e.g. mobile app, web client, web SPA or a desktop thin client) and you need some endpoints to use. The usual problem in this case is that you need to start to develop the server before you can show something to the end user. MockJSONAPI server soves this problem giving to the developer a bare-bone REST server with the standard CRUD interface. The data are stored into a single JSON file. 7 | 8 | The configuration of the server are really trivial. The `data.json` file contains all the data that will be served by the server. This is a sample `data.json` file: 9 | 10 | ```json 11 | { 12 | "projects": [ 13 | { 14 | "_oid": 1, 15 | "department": "marketing", 16 | "description": "Falcon Fly" 17 | }, 18 | { 19 | "_oid": 2, 20 | "department": "R&D", 21 | "description": "NextGen" 22 | } 23 | ], 24 | "departments": [], 25 | "employee": [] 26 | } 27 | 28 | ``` 29 | 30 | MockJSONAPI server supports *all* the resources that you need. So you can ask to the API any resourceusing an HTTP `GET`. If that resource exists in the `data.json` file, then will be returned to the client, otherwise an empty array will be returned. 31 | 32 | If you try to create a new entity under a specific resource, then that resource will be automaticaly created and stored in the file. 33 | I strongly suggest to play with the server using curl or Postman. Just launch `mockjsonapi.exe` and follow the curl session below. 34 | 35 | Here's a typical curl session which uses the `mockjsonapi` server. 36 | 37 | 38 | Let's ask for a non existent resource. 39 | ``` 40 | Daniele@DANIELETETI C:\Users\Daniele 41 | $ curl -i -XGET http://localhost:8080/api/customers 42 | HTTP/1.1 200 OK 43 | Connection: keep-alive 44 | Content-Type: application/json; charset=utf-8 45 | Content-Length: 11 46 | Date: Mon, 09 Nov 2020 16:48:02 GMT 47 | Server: DelphiMVCFramework 48 | X-Powered-By: DMVCFramework 3.2.1 (carbon) 49 | Access-Control-Allow-Origin: * 50 | Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE 51 | Access-Control-Allow-Headers: Content-Type, Accept, jwtusername, jwtpassword, authentication, authorization 52 | Access-Control-Allow-Credentials: true 53 | X-XSS-Protection: 1; mode = block 54 | X-Content-Type-Options: nosniff 55 | X-MOCK-JSON-API: 1.1.0 56 | 57 | {"data":[]} 58 | ``` 59 | As expected, the api returns an empty array in `data` property (not an error, just an empty array). 60 | 61 | Now, we try to get a specific entity from the resource, and we expect an error in this case. 62 | ``` 63 | Daniele@DANIELETETI C:\Users\Daniele 64 | $ curl -i -XGET http://localhost:8080/api/customers/3 65 | HTTP/1.1 404 Not Found 66 | Connection: close 67 | Content-Type: application/json; charset=utf-8 68 | Content-Length: 139 69 | Date: Mon, 09 Nov 2020 16:49:01 GMT 70 | Server: DelphiMVCFramework 71 | X-Powered-By: DMVCFramework 3.2.1 (carbon) 72 | Access-Control-Allow-Origin: * 73 | Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE 74 | Access-Control-Allow-Headers: Content-Type, Accept, jwtusername, jwtpassword, authentication, authorization 75 | Access-Control-Allow-Credentials: true 76 | X-XSS-Protection: 1; mode = block 77 | X-Content-Type-Options: nosniff 78 | X-MOCK-JSON-API: 1.1.0 79 | 80 | {"classname":"","detailedmessage":"","apperrorcode":0,"items":[],"statuscode":404,"reasonstring":"error","message":"Not Found","data":null} 81 | ``` 82 | Here's the error. All the errors are returned as json object. 83 | 84 | 85 | Now we create an actual object into the resource. The resource will be created automatically. 86 | ``` 87 | Daniele@DANIELETETI C:\Users\Daniele 88 | $ curl -i -XPOST http://localhost:8080/api/customers --data "{\"name\":\"Daniele Teti\"}" 89 | HTTP/1.1 201 Created 90 | Connection: keep-alive 91 | Content-Type: application/json; charset=utf-8 92 | Content-Length: 85 93 | Date: Mon, 09 Nov 2020 16:50:13 GMT 94 | Server: DelphiMVCFramework 95 | X-Powered-By: DMVCFramework 3.2.1 (carbon) 96 | Access-Control-Allow-Origin: * 97 | Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE 98 | Access-Control-Allow-Headers: Content-Type, Accept, jwtusername, jwtpassword, authentication, authorization 99 | Access-Control-Allow-Credentials: true 100 | X-XSS-Protection: 1; mode = block 101 | X-Content-Type-Options: nosniff 102 | X-MOCK-JSON-API: 1.1.0 103 | X-REF: /api/customers/61616787-1F08-4187-ADBA-BCB8CA065E96 104 | Location: /api/customers/61616787-1F08-4187-ADBA-BCB8CA065E96 105 | 106 | {"data":{"status":"ok","xref":"/api/customers/61616787-1F08-4187-ADBA-BCB8CA065E96"}} 107 | ``` 108 | The resource and the entity have been created. The entity is available for `GET` requests at the URL returned by the `X-REF` header. Let's try to retrieve the object just created. 109 | 110 | ``` 111 | Daniele@DANIELETETI C:\Users\Daniele 112 | $ curl -i -XGET http://localhost:8080/api/customers/61616787-1F08-4187-ADBA-BCB8CA065E96 113 | HTTP/1.1 200 OK 114 | Connection: keep-alive 115 | Content-Type: application/json; charset=utf-8 116 | Content-Length: 69 117 | Date: Mon, 09 Nov 2020 16:51:13 GMT 118 | Server: DelphiMVCFramework 119 | X-Powered-By: DMVCFramework 3.2.1 (carbon) 120 | Access-Control-Allow-Origin: * 121 | Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE 122 | Access-Control-Allow-Headers: Content-Type, Accept, jwtusername, jwtpassword, authentication, authorization 123 | Access-Control-Allow-Credentials: true 124 | X-XSS-Protection: 1; mode = block 125 | X-Content-Type-Options: nosniff 126 | X-MOCK-JSON-API: 1.1.0 127 | 128 | {"name":"Daniele Teti","_oid":"61616787-1F08-4187-ADBA-BCB8CA065E96"} 129 | ``` 130 | Got It! Here's the JSON object just created. As you can see, MockJSONAPI server adds a standard `_oid` property as `ObjectIdentitied` that you can use to retrieve the object. 131 | 132 | Let's create another entity. 133 | ``` 134 | Daniele@DANIELETETI C:\Users\Daniele 135 | $ curl -i -XPOST http://localhost:8080/api/customers --data "{\"name\":\"Bruce Banner\"}" 136 | HTTP/1.1 201 Created 137 | Connection: keep-alive 138 | Content-Type: application/json; charset=utf-8 139 | Content-Length: 85 140 | Date: Mon, 09 Nov 2020 16:51:53 GMT 141 | Server: DelphiMVCFramework 142 | X-Powered-By: DMVCFramework 3.2.1 (carbon) 143 | Access-Control-Allow-Origin: * 144 | Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE 145 | Access-Control-Allow-Headers: Content-Type, Accept, jwtusername, jwtpassword, authentication, authorization 146 | Access-Control-Allow-Credentials: true 147 | X-XSS-Protection: 1; mode = block 148 | X-Content-Type-Options: nosniff 149 | X-MOCK-JSON-API: 1.1.0 150 | X-REF: /api/customers/4F1EB312-AD4F-4EDA-9F64-4CACAAB798E3 151 | Location: /api/customers/4F1EB312-AD4F-4EDA-9F64-4CACAAB798E3 152 | 153 | {"data":{"status":"ok","xref":"/api/customers/4F1EB312-AD4F-4EDA-9F64-4CACAAB798E3"}} 154 | ``` 155 | 156 | To retrieve the full collection you can send a GET request to the resource without parameter, as following. 157 | ``` 158 | Daniele@DANIELETETI C:\Users\Daniele 159 | $ curl -i -XGET http://localhost:8080/api/customers 160 | HTTP/1.1 200 OK 161 | Connection: keep-alive 162 | Content-Type: application/json; charset=utf-8 163 | Content-Length: 150 164 | Date: Mon, 09 Nov 2020 16:59:49 GMT 165 | Server: DelphiMVCFramework 166 | X-Powered-By: DMVCFramework 3.2.1 (carbon) 167 | Access-Control-Allow-Origin: * 168 | Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE 169 | Access-Control-Allow-Headers: Content-Type, Accept, jwtusername, jwtpassword, authentication, authorization 170 | Access-Control-Allow-Credentials: true 171 | X-XSS-Protection: 1; mode = block 172 | X-Content-Type-Options: nosniff 173 | X-MOCK-JSON-API: 1.1.0 174 | 175 | {"data":[{"name":"Daniele Teti","_oid":"61616787-1F08-4187-ADBA-BCB8CA065E96"},{"name":"Bruce Banner","_oid":"4F1EB312-AD4F-4EDA-9F64-4CACAAB798E3"}]} 176 | ``` 177 | 178 | ## Endpoints supported by MockJSONAPI Server 179 | 180 | ### Gets the entire resource list 181 | ``` 182 | GET /api/resourcename 183 | ``` 184 | 185 | ### Gets the resource with `_oid = 1` 186 | ``` 187 | GET /api/resourcename/1 188 | ``` 189 | 190 | ### Create a new resource from the request body 191 | ``` 192 | POST /api/resourcename 193 | {...body...} 194 | ``` 195 | 196 | ### Updates the resource with `_oid = 1' using the request body 197 | ``` 198 | PUT /api/resourcename/1 199 | {...body...} 200 | ``` 201 | 202 | ### Deletes resource with `_oid = 1` 203 | ``` 204 | DELETE /api/resourcename/1 205 | ``` 206 | 207 | ### Deletes resource `resourcename` 208 | 209 | ``` 210 | DELETE /api/resourcename 211 | ``` 212 | 213 | ## How to use it 214 | 215 | Using MockJSONAPI Server is really siple. Just run the executable with the `data.json` file in the exe folder (the release already contains a sample `data.json` file). You are productive in seconds starting to use the Mock API. If you want to "load" data in the server storage, just change the `data.json` with your own data. 216 | -------------------------------------------------------------------------------- /SecurityHeadersMiddlewareU.pas: -------------------------------------------------------------------------------- 1 | // *************************************************************************** 2 | // 3 | // MockJSONAPI 4 | // 5 | // Copyright (c) 2020 Daniele Teti 6 | // 7 | // https://github.com/danieleteti/mockjsonapi 8 | // 9 | // *************************************************************************** 10 | // 11 | // Licensed under the Apache License, Version 2.0 (the "License"); 12 | // you may not use this file except in compliance with the License. 13 | // You may obtain a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, software 18 | // distributed under the License is distributed on an "AS IS" BASIS, 19 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | // See the License for the specific language governing permissions and 21 | // limitations under the License. 22 | // 23 | // *************************************************************************** } 24 | 25 | unit SecurityHeadersMiddlewareU; 26 | 27 | interface 28 | 29 | uses 30 | MVCFramework; 31 | 32 | type 33 | TSecurityHeadersMiddleware = class(TInterfacedObject, IMVCMiddleware) 34 | public 35 | procedure OnBeforeRouting(AContext: TWebContext; var AHandled: Boolean); 36 | procedure OnBeforeControllerAction(AContext: TWebContext; 37 | const AControllerQualifiedClassName: string; const AActionName: string; 38 | var AHandled: Boolean); 39 | procedure OnAfterControllerAction(AContext: TWebContext; 40 | const AControllerQualifiedClassName: string; const AActionName: string; 41 | const AHandled: Boolean); 42 | procedure OnAfterRouting(AContext: TWebContext; const AHandled: Boolean); 43 | 44 | end; 45 | 46 | implementation 47 | 48 | { TSecurityHeadersMiddleware } 49 | 50 | uses ConfigU; 51 | 52 | procedure TSecurityHeadersMiddleware.OnAfterControllerAction(AContext: TWebContext; 53 | const AControllerQualifiedClassName: string; const AActionName: string; 54 | const AHandled: Boolean); 55 | begin 56 | end; 57 | 58 | procedure TSecurityHeadersMiddleware.OnAfterRouting(AContext: TWebContext; 59 | const AHandled: Boolean); 60 | begin 61 | // 62 | end; 63 | 64 | procedure TSecurityHeadersMiddleware.OnBeforeControllerAction 65 | (AContext: TWebContext; const AControllerQualifiedClassName, 66 | AActionName: string; var AHandled: Boolean); 67 | begin 68 | // 69 | end; 70 | 71 | procedure TSecurityHeadersMiddleware.OnBeforeRouting(AContext: TWebContext; 72 | var AHandled: Boolean); 73 | begin 74 | AContext.Response.SetCustomHeader('X-XSS-Protection', '1; mode = block'); 75 | AContext.Response.SetCustomHeader('X-Content-Type-Options', 'nosniff'); 76 | AContext.Response.SetCustomHeader('X-MOCK-JSON-API', VERSION); 77 | end; 78 | 79 | end. 80 | -------------------------------------------------------------------------------- /WebModuleU.dfm: -------------------------------------------------------------------------------- 1 | object MainWM: TMainWM 2 | OnCreate = WebModuleCreate 3 | OnDestroy = WebModuleDestroy 4 | Actions = <> 5 | Height = 230 6 | Width = 415 7 | end 8 | -------------------------------------------------------------------------------- /WebModuleU.pas: -------------------------------------------------------------------------------- 1 | // *************************************************************************** 2 | // 3 | // MockJSONAPI 4 | // 5 | // Copyright (c) 2020 Daniele Teti 6 | // 7 | // https://github.com/danieleteti/mockjsonapi 8 | // 9 | // *************************************************************************** 10 | // 11 | // Licensed under the Apache License, Version 2.0 (the "License"); 12 | // you may not use this file except in compliance with the License. 13 | // You may obtain a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, software 18 | // distributed under the License is distributed on an "AS IS" BASIS, 19 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | // See the License for the specific language governing permissions and 21 | // limitations under the License. 22 | // 23 | // *************************************************************************** } 24 | 25 | unit WebModuleU; 26 | 27 | interface 28 | 29 | uses System.SysUtils, 30 | System.Classes, 31 | Web.HTTPApp, 32 | MVCFramework; 33 | 34 | type 35 | TMainWM = class(TWebModule) 36 | procedure WebModuleCreate(Sender: TObject); 37 | procedure WebModuleDestroy(Sender: TObject); 38 | private 39 | FMVC: TMVCEngine; 40 | public 41 | { Public declarations } 42 | end; 43 | 44 | var 45 | WebModuleClass: TComponentClass = TMainWM; 46 | 47 | implementation 48 | 49 | {$R *.dfm} 50 | 51 | uses System.IOUtils, MVCFramework.Commons, MVCFramework.Middleware.CORS, 52 | EntitiesControllerU, 53 | SecurityHeadersMiddlewareU; 54 | 55 | procedure TMainWM.WebModuleCreate(Sender: TObject); 56 | begin 57 | FMVC := TMVCEngine.Create(Self, 58 | procedure(Config: TMVCConfig) 59 | begin 60 | // session timeout (0 means session cookie) 61 | Config[TMVCConfigKey.SessionTimeout] := '0'; 62 | // default content-type 63 | Config[TMVCConfigKey.DefaultContentType] := 64 | TMVCConstants.DEFAULT_CONTENT_TYPE; 65 | // default content charset 66 | Config[TMVCConfigKey.DefaultContentCharset] := 67 | TMVCConstants.DEFAULT_CONTENT_CHARSET; 68 | // unhandled actions are permitted? 69 | Config[TMVCConfigKey.AllowUnhandledAction] := 'false'; 70 | // default view file extension 71 | Config[TMVCConfigKey.DefaultViewFileExtension] := 'html'; 72 | // view path 73 | Config[TMVCConfigKey.ViewPath] := 'templates'; 74 | // Enable Server Signature in response 75 | Config[TMVCConfigKey.ExposeServerSignature] := 'true'; 76 | 77 | Config[TMVCConfigKey.LoadSystemControllers] := 'false'; 78 | end); 79 | FMVC 80 | .AddController(TEntitiesController) 81 | .AddMiddleware(TCORSMiddleware.Create) 82 | .AddMiddleware(TSecurityHeadersMiddleware.Create); 83 | end; 84 | 85 | procedure TMainWM.WebModuleDestroy(Sender: TObject); 86 | begin 87 | FMVC.Free; 88 | end; 89 | 90 | end. 91 | -------------------------------------------------------------------------------- /bin/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "port": 8080 3 | } -------------------------------------------------------------------------------- /bin/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "projects": [ 3 | { 4 | "department": "marketing", 5 | "description": "Falcon Fly" 6 | }, 7 | { 8 | "department": "R&D", 9 | "description": "NextGen" 10 | } 11 | ], 12 | "resource0661": [ 13 | { 14 | "key": "changed", 15 | "_oid": "DF0A1517-9A2B-46CB-BA1B-64A438C8C99D" 16 | }, 17 | { 18 | "key": "value01", 19 | "_oid": "4218DC90-6E0C-4F15-9D84-EFEDA223232F" 20 | }, 21 | { 22 | "key": "value02", 23 | "_oid": "F2CF3435-3358-4438-A625-3B5145968BB3" 24 | }, 25 | { 26 | "key": "value03", 27 | "_oid": "C5774506-05F4-4625-836E-B0CF2C342F77" 28 | }, 29 | { 30 | "key": "value04", 31 | "_oid": "93AB50ED-A110-421B-B4A2-5A9B651992E1" 32 | }, 33 | { 34 | "key": "value05", 35 | "_oid": "878C8C3F-1053-4FB3-BC93-798E25159078" 36 | }, 37 | { 38 | "key": "value06", 39 | "_oid": "CE5D82FD-52DB-4E0E-B447-6DECFF416757" 40 | }, 41 | { 42 | "key": "value07", 43 | "_oid": "EDD51C4B-4E94-4B7D-BBDC-908DAA3FDF51" 44 | }, 45 | { 46 | "key": "value08", 47 | "_oid": "8FA41951-E16B-408E-AE68-C9E5F6A309FE" 48 | }, 49 | { 50 | "key": "value09", 51 | "_oid": "5BAC6E95-71B1-41A4-926E-2F653134A9FE" 52 | } 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /buildlocal.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | echo Building project... 3 | call rsvars.bat 4 | msbuild /t:build /p:config=Debug "mockjsonapi.dproj" 5 | if errorlevel 1 exit 1 -------------------------------------------------------------------------------- /mockjsonapi.dpr: -------------------------------------------------------------------------------- 1 | // *************************************************************************** 2 | // 3 | // MockJSONAPI 4 | // 5 | // Copyright (c) 2020-2023 Daniele Teti 6 | // 7 | // https://github.com/danieleteti/mockjsonapi 8 | // 9 | // *************************************************************************** 10 | // 11 | // Licensed under the Apache License, Version 2.0 (the "License"); 12 | // you may not use this file except in compliance with the License. 13 | // You may obtain a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, software 18 | // distributed under the License is distributed on an "AS IS" BASIS, 19 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | // See the License for the specific language governing permissions and 21 | // limitations under the License. 22 | // 23 | // *************************************************************************** } 24 | 25 | program mockjsonapi; 26 | 27 | {$APPTYPE CONSOLE} 28 | 29 | uses 30 | MVCFramework.Console, 31 | System.SysUtils, 32 | MVCFramework.Logger, 33 | MVCFramework.Commons, 34 | MVCFramework.Signal, 35 | Web.ReqMulti, 36 | Web.WebReq, 37 | Web.WebBroker, 38 | IdHTTPWebBrokerBridge, 39 | EntitiesControllerU in 'EntitiesControllerU.pas', 40 | WebModuleU in 'WebModuleU.pas' {MainWM: TWebModule}, 41 | DataAccessLayerU in 'DataAccessLayerU.pas', 42 | ConfigU in 'ConfigU.pas', 43 | SecurityHeadersMiddlewareU in 'SecurityHeadersMiddlewareU.pas'; 44 | 45 | {$R *.res} 46 | 47 | 48 | 49 | procedure Logo; 50 | begin 51 | WriteLn; 52 | TextColor(DarkRed); 53 | WriteLn(' __ __ ____ _____ _ __ _ _____ ____ _ _ _____ _____ '); 54 | WriteLn(' | \/ |/ __ \ / ____| |/ / | |/ ____|/ __ \| \ | | /\ | __ \_ _|'); 55 | WriteLn(' | \ / | | | | | | '' / | | (___ | | | | \| | / \ | |__) || | '); 56 | WriteLn(' | |\/| | | | | | | < _ | |\___ \| | | | . ` | / /\ \ | ___/ | | '); 57 | TextColor(Red); 58 | WriteLn(' | | | | |__| | |____| . \ |__| |____) | |__| | |\ |/ ____ \| | _| |_ '); 59 | WriteLn(' |_| |_|\____/ \_____|_|\_\____/|_____/ \____/|_| \_/_/ \_\_| |_____|'); 60 | WriteLn(' '); 61 | TextColor(White); 62 | WriteLn(' version ' + VERSION); 63 | WriteLn; 64 | end; 65 | 66 | procedure RunServer(APort: Integer = 8080); 67 | var 68 | lServer: TIdHTTPWebBrokerBridge; 69 | begin 70 | TextColor(White); 71 | TextBackground(Black); 72 | Logo; 73 | TextColor(Green); 74 | Writeln('** Built with DMVCFramework Server ** build ' + DMVCFRAMEWORK_VERSION); 75 | TextColor(White); 76 | Writeln('Usage help: https://github.com/danieleteti/mockjsonapi'); 77 | 78 | LServer := TIdHTTPWebBrokerBridge.Create(nil); 79 | try 80 | LServer.DefaultPort := APort; 81 | LServer.KeepAlive := True; 82 | LServer.MaxConnections := 0; 83 | LServer.ListenQueue := 200; 84 | lServer.Active := True; 85 | Writeln('mockjsonapi is listening on http://localhost:' + APort.ToString); 86 | TextColor(Red); 87 | WriteLn('CTRL+C to EXIT'); 88 | WaitForTerminationSignal; 89 | TextColor(Yellow); 90 | WriteLn('bye bye...'); 91 | finally 92 | LServer.Free; 93 | end; 94 | end; 95 | 96 | begin 97 | ReportMemoryLeaksOnShutdown := True; 98 | IsMultiThread := True; 99 | try 100 | if WebRequestHandler <> nil then 101 | WebRequestHandler.WebModuleClass := WebModuleClass; 102 | WebRequestHandlerProc.MaxConnections := 1024; 103 | RunServer(TConfig.Instance.GetInteger('port')); 104 | except 105 | on E: Exception do 106 | begin 107 | LogE(E.ClassName + ': ' + E.Message); 108 | Writeln(E.ClassName, ': ', E.Message); 109 | end; 110 | end; 111 | try 112 | ResetConsole; 113 | except 114 | end; 115 | 116 | end. 117 | -------------------------------------------------------------------------------- /mockjsonapi.dproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | {C58BC2A3-F9E7-4570-BB44-4407DC604197} 4 | 20.2 5 | None 6 | mockjsonapi.dpr 7 | True 8 | Debug 9 | Win32 10 | 1 11 | Console 12 | mockjsonapi 13 | 14 | 15 | true 16 | 17 | 18 | true 19 | Base 20 | true 21 | 22 | 23 | true 24 | Base 25 | true 26 | 27 | 28 | true 29 | Base 30 | true 31 | 32 | 33 | true 34 | Base 35 | true 36 | 37 | 38 | true 39 | Base 40 | true 41 | 42 | 43 | true 44 | Cfg_1 45 | true 46 | true 47 | 48 | 49 | true 50 | Base 51 | true 52 | 53 | 54 | .\$(Platform)\$(Config) 55 | .\$(Platform)\$(Config) 56 | false 57 | false 58 | false 59 | false 60 | false 61 | RESTComponents;emsclientfiredac;DataSnapFireDAC;FireDACIBDriver;emsclient;FireDACCommon;RESTBackendComponents;soapserver;CloudService;FireDACCommonDriver;inet;FireDAC;FireDACSqliteDriver;soaprtl;soapmidas;$(DCC_UsePackage) 62 | System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) 63 | true 64 | $(BDS)\bin\delphi_PROJECTICON.ico 65 | $(BDS)\bin\delphi_PROJECTICNS.icns 66 | $(DMVC);$(DCC_UnitSearchPath) 67 | VCL;$(DCC_Framework) 68 | mockjsonapi 69 | 70 | 71 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_192x192.png 72 | package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= 73 | Debug 74 | annotation-1.2.0.dex.jar;asynclayoutinflater-1.0.0.dex.jar;billing-4.0.0.dex.jar;browser-1.0.0.dex.jar;cloud-messaging.dex.jar;collection-1.0.0.dex.jar;coordinatorlayout-1.0.0.dex.jar;core-1.5.0-rc02.dex.jar;core-common-2.0.1.dex.jar;core-runtime-2.0.1.dex.jar;cursoradapter-1.0.0.dex.jar;customview-1.0.0.dex.jar;documentfile-1.0.0.dex.jar;drawerlayout-1.0.0.dex.jar;firebase-annotations-16.0.0.dex.jar;firebase-common-20.0.0.dex.jar;firebase-components-17.0.0.dex.jar;firebase-datatransport-18.0.0.dex.jar;firebase-encoders-17.0.0.dex.jar;firebase-encoders-json-18.0.0.dex.jar;firebase-iid-interop-17.1.0.dex.jar;firebase-installations-17.0.0.dex.jar;firebase-installations-interop-17.0.0.dex.jar;firebase-measurement-connector-19.0.0.dex.jar;firebase-messaging-22.0.0.dex.jar;fmx.dex.jar;fragment-1.0.0.dex.jar;google-play-licensing.dex.jar;interpolator-1.0.0.dex.jar;javax.inject-1.dex.jar;legacy-support-core-ui-1.0.0.dex.jar;legacy-support-core-utils-1.0.0.dex.jar;lifecycle-common-2.0.0.dex.jar;lifecycle-livedata-2.0.0.dex.jar;lifecycle-livedata-core-2.0.0.dex.jar;lifecycle-runtime-2.0.0.dex.jar;lifecycle-service-2.0.0.dex.jar;lifecycle-viewmodel-2.0.0.dex.jar;listenablefuture-1.0.dex.jar;loader-1.0.0.dex.jar;localbroadcastmanager-1.0.0.dex.jar;play-services-ads-20.1.0.dex.jar;play-services-ads-base-20.1.0.dex.jar;play-services-ads-identifier-17.0.0.dex.jar;play-services-ads-lite-20.1.0.dex.jar;play-services-base-17.5.0.dex.jar;play-services-basement-17.6.0.dex.jar;play-services-cloud-messaging-16.0.0.dex.jar;play-services-drive-17.0.0.dex.jar;play-services-games-21.0.0.dex.jar;play-services-location-18.0.0.dex.jar;play-services-maps-17.0.1.dex.jar;play-services-measurement-base-18.0.0.dex.jar;play-services-measurement-sdk-api-18.0.0.dex.jar;play-services-places-placereport-17.0.0.dex.jar;play-services-stats-17.0.0.dex.jar;play-services-tasks-17.2.0.dex.jar;print-1.0.0.dex.jar;room-common-2.1.0.dex.jar;room-runtime-2.1.0.dex.jar;slidingpanelayout-1.0.0.dex.jar;sqlite-2.0.1.dex.jar;sqlite-framework-2.0.1.dex.jar;swiperefreshlayout-1.0.0.dex.jar;transport-api-3.0.0.dex.jar;transport-backend-cct-3.0.0.dex.jar;transport-runtime-3.0.0.dex.jar;user-messaging-platform-1.0.0.dex.jar;versionedparcelable-1.1.1.dex.jar;viewpager-1.0.0.dex.jar;work-runtime-2.1.0.dex.jar 75 | 76 | 77 | $(BDS)\bin\Artwork\Android\FM_LauncherIcon_192x192.png 78 | package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= 79 | Debug 80 | annotation-1.2.0.dex.jar;asynclayoutinflater-1.0.0.dex.jar;billing-4.0.0.dex.jar;browser-1.0.0.dex.jar;cloud-messaging.dex.jar;collection-1.0.0.dex.jar;coordinatorlayout-1.0.0.dex.jar;core-1.5.0-rc02.dex.jar;core-common-2.0.1.dex.jar;core-runtime-2.0.1.dex.jar;cursoradapter-1.0.0.dex.jar;customview-1.0.0.dex.jar;documentfile-1.0.0.dex.jar;drawerlayout-1.0.0.dex.jar;firebase-annotations-16.0.0.dex.jar;firebase-common-20.0.0.dex.jar;firebase-components-17.0.0.dex.jar;firebase-datatransport-18.0.0.dex.jar;firebase-encoders-17.0.0.dex.jar;firebase-encoders-json-18.0.0.dex.jar;firebase-iid-interop-17.1.0.dex.jar;firebase-installations-17.0.0.dex.jar;firebase-installations-interop-17.0.0.dex.jar;firebase-measurement-connector-19.0.0.dex.jar;firebase-messaging-22.0.0.dex.jar;fmx.dex.jar;fragment-1.0.0.dex.jar;google-play-licensing.dex.jar;interpolator-1.0.0.dex.jar;javax.inject-1.dex.jar;legacy-support-core-ui-1.0.0.dex.jar;legacy-support-core-utils-1.0.0.dex.jar;lifecycle-common-2.0.0.dex.jar;lifecycle-livedata-2.0.0.dex.jar;lifecycle-livedata-core-2.0.0.dex.jar;lifecycle-runtime-2.0.0.dex.jar;lifecycle-service-2.0.0.dex.jar;lifecycle-viewmodel-2.0.0.dex.jar;listenablefuture-1.0.dex.jar;loader-1.0.0.dex.jar;localbroadcastmanager-1.0.0.dex.jar;play-services-ads-20.1.0.dex.jar;play-services-ads-base-20.1.0.dex.jar;play-services-ads-identifier-17.0.0.dex.jar;play-services-ads-lite-20.1.0.dex.jar;play-services-base-17.5.0.dex.jar;play-services-basement-17.6.0.dex.jar;play-services-cloud-messaging-16.0.0.dex.jar;play-services-drive-17.0.0.dex.jar;play-services-games-21.0.0.dex.jar;play-services-location-18.0.0.dex.jar;play-services-maps-17.0.1.dex.jar;play-services-measurement-base-18.0.0.dex.jar;play-services-measurement-sdk-api-18.0.0.dex.jar;play-services-places-placereport-17.0.0.dex.jar;play-services-stats-17.0.0.dex.jar;play-services-tasks-17.2.0.dex.jar;print-1.0.0.dex.jar;room-common-2.1.0.dex.jar;room-runtime-2.1.0.dex.jar;slidingpanelayout-1.0.0.dex.jar;sqlite-2.0.1.dex.jar;sqlite-framework-2.0.1.dex.jar;swiperefreshlayout-1.0.0.dex.jar;transport-api-3.0.0.dex.jar;transport-backend-cct-3.0.0.dex.jar;transport-runtime-3.0.0.dex.jar;user-messaging-platform-1.0.0.dex.jar;versionedparcelable-1.1.1.dex.jar;viewpager-1.0.0.dex.jar;work-runtime-2.1.0.dex.jar 81 | 82 | 83 | DBXSqliteDriver;fmxase;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;tethering;svnui;FireDACADSDriver;frxe25;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;svn;Intraweb;DBXOracleDriver;Spring.Data;inetdb;RaizeComponentsVcl;FmxTeeUI;emsedge;RaizeComponentsVclDb;fmx;fmxdae;frxDB25;vclib;frxTee25;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;DataSnapConnectors;VCLRESTComponents;JclDeveloperTools;vclie;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;IndyIPCommon;bindcompdbx;vcl;IndyIPServer;DBXSybaseASEDriver;IndySystem;FireDACDb2Driver;dsnapcon;DMVC_IDE_Expert_D102Tokyo;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;Jcl;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;Tee;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;vclwinx;FireDACDSDriver;rtl;ibxbindings;DbxClientDriver;frx25;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;JclVcl;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;JclContainers;DataSnapServerMidas;$(DCC_UsePackage) 84 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) 85 | Debug 86 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= 87 | 1033 88 | .\bin 89 | (None) 90 | mockjsonapi_Icon.ico 91 | D:\DEV\dmvcframework\releases\3_0_0_hydrogen_RC12\sources;D:\DEV\dmvcframework\releases\3_0_0_hydrogen_RC12\lib\loggerpro;D:\DEV\dmvcframework\releases\3_0_0_hydrogen_RC12\lib\dmustache;$(DCC_UnitSearchPath) 92 | 93 | 94 | DBXSqliteDriver;fmxase;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;tethering;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;Intraweb;DBXOracleDriver;Spring.Data;inetdb;RaizeComponentsVcl;FmxTeeUI;emsedge;RaizeComponentsVclDb;fmx;fmxdae;vclib;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;DataSnapConnectors;VCLRESTComponents;JclDeveloperTools;vclie;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;IndyIPCommon;bindcompdbx;vcl;IndyIPServer;DBXSybaseASEDriver;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;Jcl;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;Tee;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;vclwinx;FireDACDSDriver;rtl;ibxbindings;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;JclVcl;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;JclContainers;DataSnapServerMidas;$(DCC_UsePackage) 95 | 96 | 97 | DEBUG;$(DCC_Define) 98 | true 99 | false 100 | true 101 | true 102 | true 103 | 104 | 105 | false 106 | 1033 107 | mockjsonapi_Icon.ico 108 | 109 | 110 | false 111 | RELEASE;$(DCC_Define) 112 | 0 113 | 0 114 | 115 | 116 | 117 | MainSource 118 | 119 | 120 | 121 |
MainWM
122 | TWebModule 123 |
124 | 125 | 126 | 127 | 128 | Base 129 | 130 | 131 | Cfg_1 132 | Base 133 | 134 | 135 | Cfg_2 136 | Base 137 | 138 |
139 | 140 | Delphi.Personality.12 141 | Console 142 | 143 | 144 | 145 | mockjsonapi.dpr 146 | 147 | 148 | Microsoft Office 2000 Sample Automation Server Wrapper Components 149 | Microsoft Office XP Sample Automation Server Wrapper Components 150 | 151 | 152 | 153 | 154 | 155 | 156 | true 157 | 158 | 159 | 160 | 161 | true 162 | 163 | 164 | 165 | 166 | 167 | true 168 | 169 | 170 | 171 | 172 | 173 | 174 | 1 175 | 176 | 177 | 0 178 | 179 | 180 | 181 | 182 | res\xml 183 | 1 184 | 185 | 186 | res\xml 187 | 1 188 | 189 | 190 | 191 | 192 | library\lib\armeabi 193 | 1 194 | 195 | 196 | library\lib\armeabi 197 | 1 198 | 199 | 200 | 201 | 202 | library\lib\armeabi-v7a 203 | 1 204 | 205 | 206 | 207 | 208 | library\lib\mips 209 | 1 210 | 211 | 212 | library\lib\mips 213 | 1 214 | 215 | 216 | 217 | 218 | library\lib\armeabi-v7a 219 | 1 220 | 221 | 222 | library\lib\arm64-v8a 223 | 1 224 | 225 | 226 | 227 | 228 | library\lib\armeabi-v7a 229 | 1 230 | 231 | 232 | 233 | 234 | res\drawable 235 | 1 236 | 237 | 238 | res\drawable 239 | 1 240 | 241 | 242 | 243 | 244 | res\drawable-anydpi-v21 245 | 1 246 | 247 | 248 | res\drawable-anydpi-v21 249 | 1 250 | 251 | 252 | 253 | 254 | res\values 255 | 1 256 | 257 | 258 | res\values 259 | 1 260 | 261 | 262 | 263 | 264 | res\values-v21 265 | 1 266 | 267 | 268 | res\values-v21 269 | 1 270 | 271 | 272 | 273 | 274 | res\values-v31 275 | 1 276 | 277 | 278 | res\values-v31 279 | 1 280 | 281 | 282 | 283 | 284 | res\drawable-anydpi-v26 285 | 1 286 | 287 | 288 | res\drawable-anydpi-v26 289 | 1 290 | 291 | 292 | 293 | 294 | res\drawable 295 | 1 296 | 297 | 298 | res\drawable 299 | 1 300 | 301 | 302 | 303 | 304 | res\drawable 305 | 1 306 | 307 | 308 | res\drawable 309 | 1 310 | 311 | 312 | 313 | 314 | res\drawable 315 | 1 316 | 317 | 318 | res\drawable 319 | 1 320 | 321 | 322 | 323 | 324 | res\drawable-anydpi-v33 325 | 1 326 | 327 | 328 | res\drawable-anydpi-v33 329 | 1 330 | 331 | 332 | 333 | 334 | res\values 335 | 1 336 | 337 | 338 | res\values 339 | 1 340 | 341 | 342 | 343 | 344 | res\values-night-v21 345 | 1 346 | 347 | 348 | res\values-night-v21 349 | 1 350 | 351 | 352 | 353 | 354 | res\drawable 355 | 1 356 | 357 | 358 | res\drawable 359 | 1 360 | 361 | 362 | 363 | 364 | res\drawable-xxhdpi 365 | 1 366 | 367 | 368 | res\drawable-xxhdpi 369 | 1 370 | 371 | 372 | 373 | 374 | res\drawable-xxxhdpi 375 | 1 376 | 377 | 378 | res\drawable-xxxhdpi 379 | 1 380 | 381 | 382 | 383 | 384 | res\drawable-ldpi 385 | 1 386 | 387 | 388 | res\drawable-ldpi 389 | 1 390 | 391 | 392 | 393 | 394 | res\drawable-mdpi 395 | 1 396 | 397 | 398 | res\drawable-mdpi 399 | 1 400 | 401 | 402 | 403 | 404 | res\drawable-hdpi 405 | 1 406 | 407 | 408 | res\drawable-hdpi 409 | 1 410 | 411 | 412 | 413 | 414 | res\drawable-xhdpi 415 | 1 416 | 417 | 418 | res\drawable-xhdpi 419 | 1 420 | 421 | 422 | 423 | 424 | res\drawable-mdpi 425 | 1 426 | 427 | 428 | res\drawable-mdpi 429 | 1 430 | 431 | 432 | 433 | 434 | res\drawable-hdpi 435 | 1 436 | 437 | 438 | res\drawable-hdpi 439 | 1 440 | 441 | 442 | 443 | 444 | res\drawable-xhdpi 445 | 1 446 | 447 | 448 | res\drawable-xhdpi 449 | 1 450 | 451 | 452 | 453 | 454 | res\drawable-xxhdpi 455 | 1 456 | 457 | 458 | res\drawable-xxhdpi 459 | 1 460 | 461 | 462 | 463 | 464 | res\drawable-xxxhdpi 465 | 1 466 | 467 | 468 | res\drawable-xxxhdpi 469 | 1 470 | 471 | 472 | 473 | 474 | res\drawable-small 475 | 1 476 | 477 | 478 | res\drawable-small 479 | 1 480 | 481 | 482 | 483 | 484 | res\drawable-normal 485 | 1 486 | 487 | 488 | res\drawable-normal 489 | 1 490 | 491 | 492 | 493 | 494 | res\drawable-large 495 | 1 496 | 497 | 498 | res\drawable-large 499 | 1 500 | 501 | 502 | 503 | 504 | res\drawable-xlarge 505 | 1 506 | 507 | 508 | res\drawable-xlarge 509 | 1 510 | 511 | 512 | 513 | 514 | res\values 515 | 1 516 | 517 | 518 | res\values 519 | 1 520 | 521 | 522 | 523 | 524 | res\drawable-anydpi-v24 525 | 1 526 | 527 | 528 | res\drawable-anydpi-v24 529 | 1 530 | 531 | 532 | 533 | 534 | res\drawable 535 | 1 536 | 537 | 538 | res\drawable 539 | 1 540 | 541 | 542 | 543 | 544 | res\drawable-night-anydpi-v21 545 | 1 546 | 547 | 548 | res\drawable-night-anydpi-v21 549 | 1 550 | 551 | 552 | 553 | 554 | res\drawable-anydpi-v31 555 | 1 556 | 557 | 558 | res\drawable-anydpi-v31 559 | 1 560 | 561 | 562 | 563 | 564 | res\drawable-night-anydpi-v31 565 | 1 566 | 567 | 568 | res\drawable-night-anydpi-v31 569 | 1 570 | 571 | 572 | 573 | 574 | 1 575 | 576 | 577 | 1 578 | 579 | 580 | 0 581 | 582 | 583 | 584 | 585 | 1 586 | .framework 587 | 588 | 589 | 1 590 | .framework 591 | 592 | 593 | 1 594 | .framework 595 | 596 | 597 | 0 598 | 599 | 600 | 601 | 602 | 1 603 | .dylib 604 | 605 | 606 | 1 607 | .dylib 608 | 609 | 610 | 1 611 | .dylib 612 | 613 | 614 | 0 615 | .dll;.bpl 616 | 617 | 618 | 619 | 620 | 1 621 | .dylib 622 | 623 | 624 | 1 625 | .dylib 626 | 627 | 628 | 1 629 | .dylib 630 | 631 | 632 | 1 633 | .dylib 634 | 635 | 636 | 1 637 | .dylib 638 | 639 | 640 | 1 641 | .dylib 642 | 643 | 644 | 0 645 | .bpl 646 | 647 | 648 | 649 | 650 | 0 651 | 652 | 653 | 0 654 | 655 | 656 | 0 657 | 658 | 659 | 0 660 | 661 | 662 | 0 663 | 664 | 665 | 0 666 | 667 | 668 | 0 669 | 670 | 671 | 0 672 | 673 | 674 | 0 675 | 676 | 677 | 678 | 679 | 1 680 | 681 | 682 | 1 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | Contents\Resources 691 | 1 692 | 693 | 694 | Contents\Resources 695 | 1 696 | 697 | 698 | Contents\Resources 699 | 1 700 | 701 | 702 | 703 | 704 | library\lib\armeabi-v7a 705 | 1 706 | 707 | 708 | library\lib\arm64-v8a 709 | 1 710 | 711 | 712 | 1 713 | 714 | 715 | 1 716 | 717 | 718 | 1 719 | 720 | 721 | 1 722 | 723 | 724 | 1 725 | 726 | 727 | 1 728 | 729 | 730 | 1 731 | 732 | 733 | 0 734 | 735 | 736 | 737 | 738 | library\lib\armeabi-v7a 739 | 1 740 | 741 | 742 | 743 | 744 | 1 745 | 746 | 747 | 1 748 | 749 | 750 | 1 751 | 752 | 753 | 754 | 755 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 756 | 1 757 | 758 | 759 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 760 | 1 761 | 762 | 763 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 764 | 1 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 1 773 | 774 | 775 | 1 776 | 777 | 778 | 1 779 | 780 | 781 | 782 | 783 | Assets 784 | 1 785 | 786 | 787 | Assets 788 | 1 789 | 790 | 791 | 792 | 793 | Assets 794 | 1 795 | 796 | 797 | Assets 798 | 1 799 | 800 | 801 | 802 | 803 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 804 | 1 805 | 806 | 807 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 808 | 1 809 | 810 | 811 | 812 | 813 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 814 | 1 815 | 816 | 817 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 818 | 1 819 | 820 | 821 | 822 | 823 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 824 | 1 825 | 826 | 827 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 828 | 1 829 | 830 | 831 | 832 | 833 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 834 | 1 835 | 836 | 837 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 838 | 1 839 | 840 | 841 | 842 | 843 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 844 | 1 845 | 846 | 847 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 848 | 1 849 | 850 | 851 | 852 | 853 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 854 | 1 855 | 856 | 857 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 858 | 1 859 | 860 | 861 | 862 | 863 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 864 | 1 865 | 866 | 867 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 868 | 1 869 | 870 | 871 | 872 | 873 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 874 | 1 875 | 876 | 877 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 878 | 1 879 | 880 | 881 | 882 | 883 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 884 | 1 885 | 886 | 887 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 888 | 1 889 | 890 | 891 | 892 | 893 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 894 | 1 895 | 896 | 897 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 898 | 1 899 | 900 | 901 | 902 | 903 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 904 | 1 905 | 906 | 907 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 908 | 1 909 | 910 | 911 | 912 | 913 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 914 | 1 915 | 916 | 917 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 918 | 1 919 | 920 | 921 | 922 | 923 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 924 | 1 925 | 926 | 927 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 928 | 1 929 | 930 | 931 | 932 | 933 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 934 | 1 935 | 936 | 937 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 938 | 1 939 | 940 | 941 | 942 | 943 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 944 | 1 945 | 946 | 947 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 948 | 1 949 | 950 | 951 | 952 | 953 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 954 | 1 955 | 956 | 957 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 958 | 1 959 | 960 | 961 | 962 | 963 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 964 | 1 965 | 966 | 967 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 968 | 1 969 | 970 | 971 | 972 | 973 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 974 | 1 975 | 976 | 977 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 978 | 1 979 | 980 | 981 | 982 | 983 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 984 | 1 985 | 986 | 987 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 988 | 1 989 | 990 | 991 | 992 | 993 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 994 | 1 995 | 996 | 997 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 998 | 1 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | 1012 | 1013 | 1014 | 1015 | 1016 | False 1017 | False 1018 | False 1019 | True 1020 | False 1021 | 1022 | 1023 | 12 1024 | 1025 | 1026 | 1027 | 1028 |
1029 | -------------------------------------------------------------------------------- /mockjsonapi_Icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danieleteti/mockjsonapi/57f26d383dd53fb46e5f3c03cd015404dbf8468b/mockjsonapi_Icon.ico -------------------------------------------------------------------------------- /tests/locustfile.py: -------------------------------------------------------------------------------- 1 | from locust import HttpLocust, TaskSet, task 2 | 3 | class WebsiteTasks(TaskSet): 4 | def on_start(self): 5 | pass 6 | # self.client.post("/login", { 7 | # "username": "test_user", 8 | # "password": "" 9 | # }) 10 | 11 | @task 12 | def employee_get_list(self): 13 | res = self.client.get("/api/employee") 14 | if res.status_code != 200: 15 | raise Exception("Invalid response") 16 | print("LUNGHEZZA ARRAY: " + str(len(res.json()))) 17 | 18 | @task 19 | def projects_get_list(self): 20 | self.client.get("/api/projects") 21 | 22 | 23 | class WebsiteUser(HttpLocust): 24 | task_set = WebsiteTasks 25 | min_wait = 1000 26 | max_wait = 5000 27 | -------------------------------------------------------------------------------- /tests/pip-selfcheck.json: -------------------------------------------------------------------------------- 1 | {"last_check":"2018-01-11T11:01:52Z","pypi_version":"9.0.1"} -------------------------------------------------------------------------------- /tests/requirements.txt: -------------------------------------------------------------------------------- 1 | certifi==2022.12.7 2 | chardet==3.0.4 3 | click==6.7 4 | Flask==1.0 5 | gevent==1.2.2 6 | greenlet==0.4.12 7 | idna==2.6 8 | itsdangerous==0.24 9 | Jinja2==2.11.3 10 | locustio==0.8.1 11 | MarkupSafe==1.0 12 | msgpack-python==0.5.1 13 | pyzmq==16.0.3 14 | requests==2.20.0 15 | six==1.11.0 16 | urllib3>=1.24.2 17 | Werkzeug==0.15.5 18 | -------------------------------------------------------------------------------- /tests/test.data.json: -------------------------------------------------------------------------------- 1 | { 2 | "projects": [ 3 | { 4 | "department": "marketing", 5 | "description": "Falcon Fly" 6 | }, 7 | { 8 | "department": "R&D", 9 | "description": "NextGen" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /tests/unittests.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import requests 3 | import random 4 | import shutil 5 | 6 | class MockServerTests(unittest.TestCase): 7 | URL = "http://localhost:8080" 8 | def setUp(self): 9 | shutil.copy2('test.data.json', '../bin/data.json') 10 | self.resname = self.URL + "/api/resource{:04}".format(random.randrange(1000)) 11 | 12 | def __deleteres(self): 13 | self.assertEqual(200, requests.delete(self.resname).status_code) 14 | 15 | def test_projects_get(self): 16 | resp = requests.get(self.URL + '/api/projects') 17 | self.assertEqual(200, resp.status_code) 18 | self.assertEqual('application/json; charset=utf-8', resp.headers['Content-Type']) 19 | self.assertEqual(2, len(resp.json()['data'])) 20 | 21 | def test_check_empty_collection(self): 22 | self.__deleteres() 23 | resp = requests.get(self.resname) 24 | self.assertEqual(0, len(resp.json()["data"])) 25 | 26 | def test_create_entity(self): 27 | testdict = {"key1":"value2", "key2": "value2"} 28 | self.__deleteres() 29 | resp = requests.post(self.resname, json = testdict); 30 | self.assertEqual(201, resp.status_code) 31 | xref = resp.headers['X-REF'] 32 | self.assertIsNotNone(xref) 33 | resp = requests.get(self.URL + xref) 34 | self.assertEqual(200, resp.status_code) 35 | json = resp.json()['data'] 36 | self.assertTrue("_oid" in json, "Object doesn't contain '_oid'") 37 | for k in testdict: 38 | self.assertTrue(k in json) 39 | self.assertEqual(testdict[k], json[k]) 40 | 41 | def test_create_multiple_entity(self): 42 | self.__deleteres() 43 | for i in range(10): 44 | d = dict() 45 | d['key'] = "value{:02}".format(i) 46 | resp = requests.post(self.resname, json = d) 47 | self.assertEqual(201, resp.status_code) 48 | xref = resp.headers['X-REF'] 49 | self.assertIsNotNone(xref) 50 | 51 | resp = requests.get(self.resname) 52 | self.assertEqual(200, resp.status_code) 53 | self.assertEqual(10, len(resp.json()['data'])) 54 | 55 | def test_update_entity(self): 56 | self.test_create_multiple_entity() 57 | resp = requests.get(self.resname) 58 | jobj = resp.json()['data'][0] 59 | oid = jobj['_oid'] 60 | k = jobj['_oid'] 61 | jobj['key'] = "changed" 62 | resp = requests.put(self.resname + '/' + oid, json=jobj) 63 | self.assertEqual(200, resp.status_code) 64 | resp = requests.get(self.resname + '/' + oid) 65 | self.assertEqual("changed", resp.json()['data']['key']) 66 | 67 | if __name__ == '__main__': 68 | unittest.main() -------------------------------------------------------------------------------- /tests/venv/Lib/site-packages/locust/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danieleteti/mockjsonapi/57f26d383dd53fb46e5f3c03cd015404dbf8468b/tests/venv/Lib/site-packages/locust/static/img/favicon.ico --------------------------------------------------------------------------------