2018-09-25 15:36:53 +02:00
// *************************************************************************** }
//
// Delphi MVC Framework
//
2024-01-02 17:04:27 +01:00
// Copyright (c) 2010-2024 Daniele Teti and the DMVCFramework Team
2018-09-25 15:36:53 +02:00
//
// https://github.com/danieleteti/delphimvcframework
//
// ***************************************************************************
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ***************************************************************************
unit MVCFramework. ActiveRecordController;
interface
uses
System. SysUtils,
MVCFramework,
MVCFramework. Commons,
MVCFramework. ActiveRecord,
FireDAC. Stan. Def,
FireDAC. Stan. Pool,
FireDAC. Stan. Async,
2018-09-28 13:01:46 +02:00
FireDAC. Comp . Client,
2018-10-23 16:18:34 +02:00
MVCFramework. RQL. Parser,
System. Generics. Collections,
2024-10-01 16:16:27 +02:00
MVCFramework. Serializer. Commons, MVCFramework. Swagger. Commons;
2018-09-25 15:36:53 +02:00
type
{$SCOPEDENUMS ON}
TMVCActiveRecordAction = ( Create, Retrieve, Update, Delete) ;
TMVCActiveRecordAuthFunc = TFunc< TWebContext, TMVCActiveRecordClass, TMVCActiveRecordAction, Boolean > ;
TMVCActiveRecordController = class( TMVCController)
private
fAuthorization: TMVCActiveRecordAuthFunc;
2021-09-09 23:06:49 +02:00
fURLSegment: string ;
2018-09-25 15:36:53 +02:00
protected
2018-10-23 16:18:34 +02:00
function GetMaxRecordCount: Integer ;
2018-09-25 15:36:53 +02:00
function CheckAuthorization( aClass: TMVCActiveRecordClass; aAction: TMVCActiveRecordAction) : Boolean ; virtual ;
public
2024-01-20 10:36:45 +01:00
constructor Create(
2022-08-01 19:11:42 +02:00
const aAuthorization: TMVCActiveRecordAuthFunc = nil ;
const aURLSegment: String = '' ) ; reintroduce ; overload ;
2018-09-25 15:36:53 +02:00
destructor Destroy; override ;
2024-10-01 16:16:27 +02:00
function GetURLSegment: String ;
2018-09-25 15:36:53 +02:00
[ MVCPath( '/($entityname)' ) ]
[ MVCHTTPMethod( [ httpGET] ) ]
2024-10-01 16:16:27 +02:00
[ MVCSwagSummary( TSwaggerConst. USE_DEFAULT_SUMMARY_TAGS, 'Retrieve a list of {singularmodel}' , 'Get{pluralmodel}' ) ]
[ MVCSwagResponses( HTTP_STATUS. OK, 'List of {singularmodel}' , SWAGUseDefaultControllerModel, True ) ]
[ MVCSwagResponses( HTTP_STATUS. BadRequest, '' , TMVCErrorResponse) ]
[ MVCSwagParam( TMVCSwagParamLocation. plQuery, 'rql' , 'RQL filter used to filter the list of {singularmodel}' , SWAGUseDefaultControllerModel, TMVCSwagParamType. ptString, False ) ]
2018-09-25 15:36:53 +02:00
procedure GetEntities( const entityname: string ) ; virtual ;
2018-09-27 12:26:50 +02:00
[ MVCPath( '/($entityname)/searches' ) ]
2024-10-01 16:16:27 +02:00
[ MVCHTTPMethod( [ httpGET] ) ]
[ MVCSwagSummary( TSwaggerConst. USE_DEFAULT_SUMMARY_TAGS, 'Searches through {pluralmodel} and returns a list of {singularmodel}' , 'Get{pluralmodel}BySearch' ) ]
[ MVCSwagResponses( HTTP_STATUS. OK, 'List of {singularmodel}' , SWAGUseDefaultControllerModel, True ) ]
[ MVCSwagResponses( HTTP_STATUS. BadRequest, '' , TMVCErrorResponse) ]
[ MVCSwagParam( TMVCSwagParamLocation. plQuery, 'rql' , 'RQL filter used to filter the list of {singularmodel}' , SWAGUseDefaultControllerModel, TMVCSwagParamType. ptString, False ) ]
2018-09-27 12:26:50 +02:00
procedure GetEntitiesByRQL( const entityname: string ) ; virtual ;
2024-10-01 16:16:27 +02:00
[ MVCPath( '/($entityname)/searches' ) ]
[ MVCHTTPMethod( [ httpPOST] ) ]
[ MVCSwagSummary( TSwaggerConst. USE_DEFAULT_SUMMARY_TAGS, 'Searches through {pluralmodel} and returns a list of {singularmodel}' , 'Get{pluralmodel}BySearchAsPOST' ) ]
[ MVCSwagResponses( HTTP_STATUS. OK, 'List of {singularmodel}' , SWAGUseDefaultControllerModel, True ) ]
[ MVCSwagResponses( HTTP_STATUS. BadRequest, '' , TMVCErrorResponse) ]
[ MVCSwagParam( TMVCSwagParamLocation. plQuery, 'rql' , 'RQL filter used to filter the list of {singularmodel}' , SWAGUseDefaultControllerModel, TMVCSwagParamType. ptString, False ) ]
procedure GetEntitiesByRQLwithPOST( const entityname: string ) ; virtual ;
2018-09-25 15:36:53 +02:00
[ MVCPath( '/($entityname)/($id)' ) ]
[ MVCHTTPMethod( [ httpGET] ) ]
2024-10-01 16:16:27 +02:00
[ MVCSwagSummary( TSwaggerConst. USE_DEFAULT_SUMMARY_TAGS, 'Gets a {singularmodel} entity or 404 not found' , 'Get{singularmodel}ByID' ) ]
[ MVCSwagResponses( HTTP_STATUS. OK, 'One {singularmodel}' , SWAGUseDefaultControllerModel) ]
[ MVCSwagResponses( HTTP_STATUS. NotFound, 'Error' , TMVCErrorResponse) ]
[ MVCSwagResponses( HTTP_STATUS. BadRequest, '' , TMVCErrorResponse) ]
2018-09-25 15:36:53 +02:00
procedure GetEntity( const entityname: string ; const id: Integer ) ; virtual ;
[ MVCPath( '/($entityname)' ) ]
[ MVCHTTPMethod( [ httpPOST] ) ]
2024-10-01 16:16:27 +02:00
[ MVCSwagSummary( TSwaggerConst. USE_DEFAULT_SUMMARY_TAGS, 'Creates a {singularmodel} and returns a new id' , 'Create{singularmodel}' ) ]
[ MVCSwagResponses( HTTP_STATUS. Created, 'One {singularmodel}' , '' ) ]
[ MVCSwagResponses( HTTP_STATUS. NotFound, 'Error' , TMVCErrorResponse) ]
[ MVCSwagResponses( HTTP_STATUS. BadRequest, '' , TMVCErrorResponse) ]
[ MVCSwagParam( TMVCSwagParamLocation. plBody, '{singularmodel}' , 'A single entity of type {singularmodel}' , SWAGUseDefaultControllerModel, TMVCSwagParamType. ptString, True ) ]
2018-09-25 15:36:53 +02:00
procedure CreateEntity( const entityname: string ) ; virtual ;
[ MVCPath( '/($entityname)/($id)' ) ]
[ MVCHTTPMethod( [ httpPUT] ) ]
2024-10-01 16:16:27 +02:00
[ MVCSwagSummary( TSwaggerConst. USE_DEFAULT_SUMMARY_TAGS, 'Updates a {singularmodel} by id' , 'Update{singularmodel}ByID' ) ]
[ MVCSwagResponses( HTTP_STATUS. OK, 'One {singularmodel}' , SWAGUseDefaultControllerModel) ]
[ MVCSwagResponses( HTTP_STATUS. NotFound, 'Error' , TMVCErrorResponse) ]
[ MVCSwagResponses( HTTP_STATUS. BadRequest, '' , TMVCErrorResponse) ]
[ MVCSwagParam( TMVCSwagParamLocation. plBody, '{singularmodel}' , 'A single entity of type {singularmodel}' , SWAGUseDefaultControllerModel, TMVCSwagParamType. ptString, True ) ]
2018-09-25 15:36:53 +02:00
procedure UpdateEntity( const entityname: string ; const id: Integer ) ; virtual ;
[ MVCPath( '/($entityname)/($id)' ) ]
[ MVCHTTPMethod( [ httpDELETE] ) ]
2024-10-01 16:16:27 +02:00
[ MVCSwagSummary( TSwaggerConst. USE_DEFAULT_SUMMARY_TAGS, 'Deletes a {singularmodel} by id' , 'Delete{singularmodel}ByID' ) ]
[ MVCSwagResponses( HTTP_STATUS. NoContent, '' ) ]
[ MVCSwagResponses( HTTP_STATUS. NotFound, 'Error' , TMVCErrorResponse) ]
[ MVCSwagResponses( HTTP_STATUS. BadRequest, '' , TMVCErrorResponse) ]
2018-09-25 15:36:53 +02:00
procedure DeleteEntity( const entityname: string ; const id: Integer ) ; virtual ;
end ;
2018-10-23 16:18:34 +02:00
[ MVCNameCase( ncLowerCase) ]
TMVCActiveRecordListResponse = class
private
FList: TMVCActiveRecordList;
FMetadata: TMVCStringDictionary;
FOwns: Boolean ;
public
constructor Create( AList: TMVCActiveRecordList; AOwns: Boolean = True ) ; virtual ;
destructor Destroy; override ;
[ MVCListOf( TMVCActiveRecord) ]
2020-06-18 14:49:06 +02:00
[ MVCNameAs( 'data' ) ]
2018-10-23 16:18:34 +02:00
property Items: TMVCActiveRecordList read FList;
[ MVCNameAs( 'meta' ) ]
property Metadata: TMVCStringDictionary read FMetadata;
end ;
2018-09-25 15:36:53 +02:00
implementation
uses
2018-09-27 12:26:50 +02:00
MVCFramework. Logger,
2024-01-20 10:36:45 +01:00
JsonDataObjects,
Data. DB;
2018-09-25 15:36:53 +02:00
procedure TMVCActiveRecordController. GetEntities( const entityname: string ) ;
var
lARClassRef: TMVCActiveRecordClass;
2018-09-28 13:01:46 +02:00
lRQL: string ;
lInstance: TMVCActiveRecord;
lMapping: TMVCFieldsMapping;
2018-10-14 18:23:20 +02:00
lProcessor: IMVCEntityProcessor;
lHandled: Boolean ;
2021-09-09 23:06:49 +02:00
lARResp: TMVCActiveRecordList;
lStrDict : TMVCStringDictionary;
2018-09-25 15:36:53 +02:00
begin
2018-10-14 18:23:20 +02:00
lProcessor : = nil ;
if ActiveRecordMappingRegistry. FindProcessorByURLSegment( entityname, lProcessor) then
begin
lHandled : = False ;
lProcessor. GetEntities( Context, self, entityname, lHandled) ;
if lHandled then
begin
Exit;
end ;
end ;
if not ActiveRecordMappingRegistry. FindEntityClassByURLSegment( entityname, lARClassRef) then
begin
2019-02-21 20:17:11 +01:00
raise EMVCException. CreateFmt( 'Cannot find entity nor processor for entity "%s"' , [ entityname] ) ;
2018-10-14 18:23:20 +02:00
end ;
2018-09-25 15:36:53 +02:00
if not CheckAuthorization( lARClassRef, TMVCActiveRecordAction. Retrieve) then
begin
2024-01-20 10:36:45 +01:00
Render( TMVCErrorResponse. Create( HTTP_STATUS. Forbidden, 'Cannot read ' + entityname) ) ;
2018-09-25 15:36:53 +02:00
Exit;
end ;
2018-10-14 18:23:20 +02:00
2018-09-28 13:01:46 +02:00
lRQL : = Context. Request. QueryStringParam( 'rql' ) ;
try
LogD( '[RQL PARSE]: ' + lRQL) ;
lInstance : = lARClassRef. Create( True ) ;
try
lMapping : = lInstance. GetMapping;
finally
lInstance. Free;
end ;
2018-10-23 16:18:34 +02:00
2021-09-09 23:06:49 +02:00
lARResp : = TMVCActiveRecord. SelectRQL( lARClassRef, lRQL, GetMaxRecordCount) ;
2018-10-23 16:18:34 +02:00
try
2021-09-09 23:06:49 +02:00
lStrDict : = StrDict( [ 'page_size' ] , [ lARResp. Count. ToString] ) ;
try
if Context. Request. QueryStringParam( 'count' ) . ToLower = 'true' then
begin
lStrDict. Add( 'count' , TMVCActiveRecord. Count( lARClassRef, lRQL) . ToString) ;
end ;
Render( ObjectDict( False )
. Add( 'data' , lARResp,
procedure( const AObject: TObject; const Links: IMVCLinks)
begin
//Links.AddRefLink.Add(HATEOAS.HREF, fURLSegment + '/' + )
case TMVCActiveRecord( AObject) . GetPrimaryKeyFieldType of
ftInteger:
Links. AddRefLink. Add( HATEOAS. HREF, fURLSegment + '/' + TMVCActiveRecord( AObject) . GetPK. AsInt64. ToString)
end ;
end )
. Add( 'meta' , lStrDict) ) ;
finally
lStrDict. Free;
2020-06-19 16:01:07 +02:00
end ;
2021-09-09 23:06:49 +02:00
finally
lARResp. Free;
2018-10-23 16:18:34 +02:00
end ;
2018-09-28 13:01:46 +02:00
except
on E: ERQLCompilerNotFound do
begin
LogE( 'RQL Compiler not found. Did you included MVCFramework.RQL.AST2<yourdatabase>.pas?' ) ;
raise ;
end ;
end ;
2018-09-25 15:36:53 +02:00
end ;
2018-09-27 12:26:50 +02:00
procedure TMVCActiveRecordController. GetEntitiesByRQL( const entityname: string ) ;
2024-10-01 16:16:27 +02:00
begin
GetEntities( entityname) ;
end ;
procedure TMVCActiveRecordController. GetEntitiesByRQLwithPOST( const entityname: string ) ;
2018-09-27 12:26:50 +02:00
var
lRQL: string ;
lJSON: TJsonObject;
begin
2024-10-01 16:16:27 +02:00
lJSON : = TJsonObject. Parse( Context. Request. Body) as TJsonObject;
try
if Assigned( lJSON) then
begin
lRQL : = lJSON. s[ 'rql' ] ;
end
else
begin
lRQL : = '' ;
2018-09-28 13:01:46 +02:00
end ;
2024-10-01 16:16:27 +02:00
finally
lJSON. Free;
2018-09-27 12:26:50 +02:00
end ;
2024-10-01 16:16:27 +02:00
Context. Request. QueryStringParams. Values[ 'rql' ] : = lRQL;
2018-09-28 13:01:46 +02:00
GetEntities( entityname) ;
2018-09-27 12:26:50 +02:00
end ;
2024-10-01 16:16:27 +02:00
2018-09-25 15:36:53 +02:00
procedure TMVCActiveRecordController. GetEntity( const entityname: string ; const id: Integer ) ;
var
lAR: TMVCActiveRecord;
2018-10-14 18:23:20 +02:00
lARClass: TMVCActiveRecordClass;
2018-10-23 16:18:34 +02:00
lProcessor: IMVCEntityProcessor;
lHandled: Boolean ;
2023-09-22 09:43:35 +02:00
lResponse: IMVCResponse;
2018-09-25 15:36:53 +02:00
begin
2018-10-23 16:18:34 +02:00
lProcessor : = nil ;
if ActiveRecordMappingRegistry. FindProcessorByURLSegment( entityname, lProcessor) then
begin
lHandled : = False ;
lProcessor. GetEntity( Context, self, entityname, id, lHandled) ;
if lHandled then
begin
Exit;
end ;
end ;
2018-10-14 18:23:20 +02:00
if not ActiveRecordMappingRegistry. FindEntityClassByURLSegment( entityname, lARClass) then
begin
2024-01-20 10:36:45 +01:00
raise EMVCException. CreateFmt( HTTP_STATUS. NotFound, 'Cannot find entity %s' , [ entityname] ) ;
2018-10-14 18:23:20 +02:00
end ;
lAR : = lARClass. Create;
2018-09-25 15:36:53 +02:00
try
if not CheckAuthorization( TMVCActiveRecordClass( lAR. ClassType) , TMVCActiveRecordAction. Retrieve) then
begin
2024-01-20 10:36:45 +01:00
Render( TMVCErrorResponse. Create( HTTP_STATUS. Forbidden, 'Cannot read ' + entityname) ) ;
2018-09-25 15:36:53 +02:00
Exit;
end ;
if lAR. LoadByPK( id) then
begin
2023-09-22 09:43:35 +02:00
lResponse : = MVCResponseBuilder
. StatusCode( HTTP_STATUS. OK)
. Body( ObjectDict( false ) . Add( 'data' , lAR) )
. Build;
2018-09-25 15:36:53 +02:00
end
else
begin
2023-09-22 09:43:35 +02:00
lResponse : = MVCResponseBuilder
. StatusCode( HTTP_STATUS. NotFound)
. Body( entityname. ToLower + ' not found' )
. Build;
2018-09-25 15:36:53 +02:00
end ;
2023-09-22 09:43:35 +02:00
TMVCRenderer. InternalRenderMVCResponse( Self, TMVCResponse( lResponse) ) ;
2018-09-25 15:36:53 +02:00
finally
lAR. Free;
end ;
end ;
2018-10-23 16:18:34 +02:00
function TMVCActiveRecordController. GetMaxRecordCount: Integer ;
begin
Result : = StrToIntDef( Config[ TMVCConfigKey. MaxEntitiesRecordCount] , 2 0 ) ;
end ;
2024-10-01 16:16:27 +02:00
function TMVCActiveRecordController. GetURLSegment: String ;
begin
Result : = fURLSegment;
end ;
2020-06-18 14:49:06 +02:00
function TMVCActiveRecordController. CheckAuthorization( aClass: TMVCActiveRecordClass;
aAction: TMVCActiveRecordAction) : Boolean ;
2018-09-25 15:36:53 +02:00
begin
if Assigned( fAuthorization) then
begin
Result : = fAuthorization( Context, aClass, aAction) ;
end
else
begin
Result : = True ;
end ;
end ;
2024-01-20 10:36:45 +01:00
constructor TMVCActiveRecordController. Create(
2021-09-09 23:06:49 +02:00
const aAuthorization: TMVCActiveRecordAuthFunc;
const aURLSegment: String ) ;
2022-08-01 19:11:42 +02:00
begin
inherited Create;
fURLSegment : = aURLSegment;
fAuthorization : = aAuthorization;
end ;
2018-09-25 15:36:53 +02:00
procedure TMVCActiveRecordController. CreateEntity( const entityname: string ) ;
var
lAR: TMVCActiveRecord;
2018-10-14 18:23:20 +02:00
lARClass: TMVCActiveRecordClass;
lProcessor: IMVCEntityProcessor;
lHandled: Boolean ;
2018-09-25 15:36:53 +02:00
begin
2018-10-14 18:23:20 +02:00
lProcessor : = nil ;
if ActiveRecordMappingRegistry. FindProcessorByURLSegment( entityname, lProcessor) then
begin
lHandled : = False ;
lProcessor. CreateEntity( Context, self, entityname, lHandled) ;
if lHandled then
begin
Exit;
end ;
end ;
if not ActiveRecordMappingRegistry. FindEntityClassByURLSegment( entityname, lARClass) then
begin
2024-01-20 10:36:45 +01:00
raise EMVCException. CreateFmt( HTTP_STATUS. NotFound, 'Cannot find entity %s' , [ entityname] ) ;
2018-10-14 18:23:20 +02:00
end ;
lAR : = lARClass. Create;
2018-09-25 15:36:53 +02:00
try
if not CheckAuthorization( TMVCActiveRecordClass( lAR. ClassType) , TMVCActiveRecordAction. Create) then
begin
2024-01-20 10:36:45 +01:00
Render( TMVCErrorResponse. Create( HTTP_STATUS. Forbidden, 'Cannot create ' + entityname) ) ;
2018-09-25 15:36:53 +02:00
Exit;
end ;
Context. Request. BodyFor< TMVCActiveRecord> ( lAR) ;
lAR. Insert;
2020-06-18 14:49:06 +02:00
Context. Response. CustomHeaders. Values[ 'X-REF' ] : = Context. Request. PathInfo + '/' + lAR. GetPK. AsInt64. ToString;
2018-09-25 15:36:53 +02:00
if Context. Request. QueryStringParam( 'refresh' ) . ToLower = 'true' then
begin
2024-01-20 10:36:45 +01:00
RenderStatusMessage( HTTP_STATUS. Created, entityname. ToLower + ' created' , '' , lAR, False ) ;
2018-10-23 16:18:34 +02:00
end
else
begin
2024-01-20 10:36:45 +01:00
RenderStatusMessage( HTTP_STATUS. Created, entityname. ToLower + ' created' ) ;
2018-09-25 15:36:53 +02:00
end ;
finally
lAR. Free;
end ;
end ;
procedure TMVCActiveRecordController. UpdateEntity( const entityname: string ; const id: Integer ) ;
var
lAR: TMVCActiveRecord;
2018-10-14 18:23:20 +02:00
lARClass: TMVCActiveRecordClass;
2019-03-18 14:08:34 +01:00
lProcessor: IMVCEntityProcessor;
lHandled: Boolean ;
2018-09-25 15:36:53 +02:00
begin
2019-03-18 14:08:34 +01:00
lProcessor : = nil ;
if ActiveRecordMappingRegistry. FindProcessorByURLSegment( entityname, lProcessor) then
begin
lHandled : = False ;
2020-06-18 14:49:06 +02:00
lProcessor. UpdateEntity( Context, self, entityname, id, lHandled) ;
2019-03-18 14:08:34 +01:00
if lHandled then
begin
Exit;
end ;
end ;
2020-06-18 14:49:06 +02:00
2018-10-14 18:23:20 +02:00
if not ActiveRecordMappingRegistry. FindEntityClassByURLSegment( entityname, lARClass) then
begin
2024-01-20 10:36:45 +01:00
raise EMVCException. CreateFmt( HTTP_STATUS. NotFound, 'Cannot find class for entity %s' , [ entityname] ) ;
2018-10-14 18:23:20 +02:00
end ;
lAR : = lARClass. Create;
2018-09-25 15:36:53 +02:00
try
if not CheckAuthorization( TMVCActiveRecordClass( lAR. ClassType) , TMVCActiveRecordAction. Update) then
begin
2024-01-20 10:36:45 +01:00
Render( TMVCErrorResponse. Create( HTTP_STATUS. Forbidden, 'Cannot update ' + entityname) ) ;
2018-09-25 15:36:53 +02:00
Exit;
end ;
2018-10-14 18:23:20 +02:00
lAR. CheckAction( TMVCEntityAction. eaUpdate) ;
2018-09-25 15:36:53 +02:00
if not lAR. LoadByPK( id) then
2024-01-20 10:36:45 +01:00
raise EMVCException. CreateFmt( HTTP_STATUS. NotFound, 'Cannot find entity %s' , [ entityname] ) ;
2018-09-25 15:36:53 +02:00
Context. Request. BodyFor< TMVCActiveRecord> ( lAR) ;
lAR. SetPK( id) ;
lAR. Update;
2020-06-18 14:49:06 +02:00
Context. Response. CustomHeaders. Values[ 'X-REF' ] : = Context. Request. PathInfo;
2018-09-25 15:36:53 +02:00
if Context. Request. QueryStringParam( 'refresh' ) . ToLower = 'true' then
begin
2024-01-20 10:36:45 +01:00
RenderStatusMessage( HTTP_STATUS. OK, entityname. ToLower + ' updated' , '' , lAR, False ) ;
2018-09-25 15:36:53 +02:00
end
else
begin
2024-01-20 10:36:45 +01:00
RenderStatusMessage( HTTP_STATUS. OK, entityname. ToLower + ' updated' ) ;
2018-09-25 15:36:53 +02:00
end ;
finally
lAR. Free;
end ;
end ;
procedure TMVCActiveRecordController. DeleteEntity( const entityname: string ; const id: Integer ) ;
var
lAR: TMVCActiveRecord;
2018-10-14 18:23:20 +02:00
lARClass: TMVCActiveRecordClass;
2021-02-04 14:11:33 +01:00
lProcessor: IMVCEntityProcessor;
lHandled: Boolean ;
2018-09-25 15:36:53 +02:00
begin
2021-02-04 14:11:33 +01:00
lProcessor : = nil ;
if ActiveRecordMappingRegistry. FindProcessorByURLSegment( entityname, lProcessor) then
begin
lHandled : = False ;
lProcessor. DeleteEntity( Context, self, entityname, id, lHandled) ;
if lHandled then
begin
Exit;
end ;
end ;
2018-10-14 18:23:20 +02:00
if not ActiveRecordMappingRegistry. FindEntityClassByURLSegment( entityname, lARClass) then
begin
2024-01-20 10:36:45 +01:00
raise EMVCException. CreateFmt( HTTP_STATUS. NotFound, 'Cannot find class for entity %s' , [ entityname] ) ;
2018-10-14 18:23:20 +02:00
end ;
lAR : = lARClass. Create;
2018-09-25 15:36:53 +02:00
try
2023-09-05 12:29:38 +02:00
if not CheckAuthorization( TMVCActiveRecordClass( lAR. ClassType) ,
2020-06-18 14:49:06 +02:00
TMVCActiveRecordAction. Delete) then
2018-09-25 15:36:53 +02:00
begin
2024-01-20 10:36:45 +01:00
Render( TMVCErrorResponse. Create( HTTP_STATUS. Forbidden, 'Cannot delete ' + entityname) ) ;
2018-09-25 15:36:53 +02:00
Exit;
end ;
2020-06-18 14:49:06 +02:00
{
HTTP DELETE is an idempotent operation. Invoking it multiple times consecutively must result in
the same behavior as the first. Meaning: you shouldn' t return HTTP 4 0 4 .
}
if lAR. LoadByPK( id) then
begin
lAR. SetPK( id) ;
lAR. Delete;
end ;
2024-01-20 10:36:45 +01:00
Render( HTTP_STATUS. OK, entityname. ToLower + ' deleted' ) ;
2018-09-25 15:36:53 +02:00
finally
lAR. Free;
end ;
end ;
destructor TMVCActiveRecordController. Destroy;
begin
inherited ;
end ;
2018-10-23 16:18:34 +02:00
{ TObjectListSetHolder }
constructor TMVCActiveRecordListResponse. Create( AList: TMVCActiveRecordList; AOwns: Boolean = True ) ;
begin
inherited Create;
FOwns : = AOwns;
FList : = AList;
FMetadata : = TMVCStringDictionary. Create;
end ;
destructor TMVCActiveRecordListResponse. Destroy;
begin
if FOwns then
begin
FList. Free
end ;
FMetadata. Free;
inherited ;
end ;
2018-09-25 15:36:53 +02:00
end .