Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions samples/delphi/console_complete/ConsoleComplete.dpr
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,31 @@ begin
Res.Send('pessoas');
end);

// Teste de integridade para a issue #357 (Group + Route + End + Put)
THorse.Group.Prefix('/api')
.Delete('/test1/:id',
procedure(Req: THorseRequest; Res: THorseResponse; Next: TNextProc)
begin
Res.Send('delete1');
end)
.Route('/test2')
.Get(
procedure(Req: THorseRequest; Res: THorseResponse; Next: TNextProc)
begin
Res.Send('get2');
end)
.Post(
procedure(Req: THorseRequest; Res: THorseResponse; Next: TNextProc)
begin
Res.Send('post2');
end)
.&End
.Put('/teste3',
procedure(Req: THorseRequest; Res: THorseResponse; Next: TNextProc)
begin
Res.Send('put3');
end);

// 12. GET * -> Rota wildcard genérica (coringão)
THorse.Get('*',
procedure(Req: THorseRequest; Res: THorseResponse; Next: TNextProc)
Expand Down
22 changes: 22 additions & 0 deletions samples/delphi/console_complete/test_integrity.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,28 @@ function Run-IntegrityTest($RouterName, $Define) {
$res = Invoke-RestMethod -Method Get -Uri "http://localhost:9085/qualquercoisa"
Assert-Response "GET /qualquercoisa (Cai no Wildcard)" $res "coringao"

# Testes de Integridade da issue #357 (Group + Route + End + Put)
# 1. DELETE /api/test1/123
$res = Invoke-RestMethod -Method Delete -Uri "http://localhost:9085/api/test1/123"
Assert-Response "DELETE /api/test1/:id (Group Delete)" $res "delete1"

# 2. GET /api/test2 (dentro do Route)
$res = Invoke-RestMethod -Method Get -Uri "http://localhost:9085/api/test2"
Assert-Response "GET /api/test2 (Group Route Get)" $res "get2"

# 3. POST /api/test2 (dentro do Route)
$res = Invoke-RestMethod -Method Post -Uri "http://localhost:9085/api/test2"
Assert-Response "POST /api/test2 (Group Route Post)" $res "post2"

# 4. PUT /api/teste3 (apos o &End do Route no mesmo grupo)
try {
$res = Invoke-RestMethod -Method Put -Uri "http://localhost:9085/api/teste3"
Assert-Response "PUT /api/teste3 (Group Route End Put)" $res "put3"
} catch {
Write-Host " [FALHA] PUT /api/teste3 falhou com erro HTTP (esperado no bug #357)" -ForegroundColor Red
$global:errors++
}

# Teste de Resiliencia: Access Violation Simulado (esperamos HTTP 500 sem cair o servidor)
try {
$resErr = curl.exe -s -i "http://localhost:9085/av-trigger"
Expand Down
2 changes: 1 addition & 1 deletion src/Horse.Core.Group.Contract.pas
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ interface
IHorseCoreGroup<T: class> = interface
['{5EB734D6-6944-473E-9C79-506647E2F5E8}']
function Prefix(const APrefix: string): IHorseCoreGroup<T>;
function Route(const APath: string): IHorseCoreRoute<T>;
function Route(const APath: string): IHorseCoreRoute<IHorseCoreGroup<T>>;
function AddCallback(const ACallback: THorseCallback): IHorseCoreGroup<T>;
{$IF DEFINED(FPC)}
function AddCallbacks(const ACallbacks: TList<THorseCallback>): IHorseCoreGroup<T>;
Expand Down
291 changes: 288 additions & 3 deletions src/Horse.Core.Group.pas
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ THorseCoreGroup<T: THorseCoreBase> = class(TInterfacedObject, IHorseCoreGroup<
public
constructor Create;
function Prefix(const APrefix: string): IHorseCoreGroup<T>;
function Route(const APath: string): IHorseCoreRoute<T>;
function Route(const APath: string): IHorseCoreRoute<IHorseCoreGroup<T>>;
function AddCallback(const ACallback: THorseCallback): IHorseCoreGroup<T>;
{$IF DEFINED(FPC)}
function AddCallbacks(const ACallbacks: TList<THorseCallback>): IHorseCoreGroup<T>;
Expand Down Expand Up @@ -82,6 +82,64 @@ THorseCoreGroup<T: THorseCoreBase> = class(TInterfacedObject, IHorseCoreGroup<
function &End: T;
end;

THorseCoreGroupRoute<T: THorseCoreBase> = class(TInterfacedObject, IHorseCoreRoute<IHorseCoreGroup<T>>)
private
FPath: string;
FGroup: IHorseCoreGroup<T>;
FHorseCore: T;
public
constructor Create(const AGroup: IHorseCoreGroup<T>; const AHorseCore: T; const APath: string);
function AddCallback(const ACallback: THorseCallback): IHorseCoreRoute<IHorseCoreGroup<T>>;
{$IF DEFINED(FPC)}
function AddCallbacks(const ACallbacks: TList<THorseCallback>): IHorseCoreRoute<IHorseCoreGroup<T>>;
{$ELSE}
function AddCallbacks(const ACallbacks: TArray<THorseCallback>): IHorseCoreRoute<IHorseCoreGroup<T>>;
{$ENDIF}
function All(const ACallback: THorseCallback): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
function All(const AMiddleware, ACallback: THorseCallback): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
function All(const ACallbacks: array of THorseCallback): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
function All(const ACallbacks: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
function Get(const ACallback: THorseCallback): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
function Get(const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
function Get(const ACallback: THorseCallbackRequest): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
{$IFNDEF FPC}
function Get(const ACallback: THorseCallbackResponse): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
{$ENDIF}
function Put(const ACallback: THorseCallback): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
function Put(const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
function Put(const ACallback: THorseCallbackRequest): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
{$IFNDEF FPC}
function Put(const ACallback: THorseCallbackResponse): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
{$ENDIF}
function Head(const ACallback: THorseCallback): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
function Head(const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
function Head(const ACallback: THorseCallbackRequest): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
{$IFNDEF FPC}
function Head(const ACallback: THorseCallbackResponse): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
{$ENDIF}
function Post(const ACallback: THorseCallback): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
function Post(const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
function Post(const ACallback: THorseCallbackRequest): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
{$IFNDEF FPC}
function Post(const ACallback: THorseCallbackResponse): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
{$ENDIF}
{$IF (DEFINED(FPC) OR (CompilerVersion > 27.0))}
function Patch(const ACallback: THorseCallback): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
function Delete(const ACallback: THorseCallback): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
function Patch(const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
function Patch(const ACallback: THorseCallbackRequest): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
{$IFNDEF FPC}
function Patch(const ACallback: THorseCallbackResponse): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
{$ENDIF}
function Delete(const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
function Delete(const ACallback: THorseCallbackRequest): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
{$IFNDEF FPC}
function Delete(const ACallback: THorseCallbackResponse): IHorseCoreRoute<IHorseCoreGroup<T>>; overload;
{$ENDIF}
{$ENDIF}
function &End: IHorseCoreGroup<T>;
end;

implementation

uses
Expand Down Expand Up @@ -242,9 +300,9 @@ function THorseCoreGroup<T>.Prefix(const APrefix: string): IHorseCoreGroup<T>;
FPrefix := '/' + APrefix.Trim(['/']);
end;

function THorseCoreGroup<T>.Route(const APath: string): IHorseCoreRoute<T>;
function THorseCoreGroup<T>.Route(const APath: string): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := THorseCoreBase(FHorseCore).BaseRoute(NormalizePath(APath)) as IHorseCoreRoute<T>;
Result := THorseCoreGroupRoute<T>.Create(Self, FHorseCore, NormalizePath(APath));
end;

function THorseCoreGroup<T>.Use(const AMiddleware, ACallback: THorseCallback): IHorseCoreGroup<T>;
Expand Down Expand Up @@ -355,4 +413,231 @@ function THorseCoreGroup<T>.Post(const APath: string; const ACallback: THorseCal
THorseCoreBase(FHorseCore).BasePost(NormalizePath(APath), ACallback);
end;

{ THorseCoreGroupRoute<T> }

constructor THorseCoreGroupRoute<T>.Create(const AGroup: IHorseCoreGroup<T>; const AHorseCore: T; const APath: string);
begin
FGroup := AGroup;
FHorseCore := AHorseCore;
FPath := APath;
end;

function THorseCoreGroupRoute<T>.AddCallback(const ACallback: THorseCallback): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BaseAddCallback(ACallback);
end;

{$IF DEFINED(FPC)}
function THorseCoreGroupRoute<T>.AddCallbacks(const ACallbacks: TList<THorseCallback>): IHorseCoreRoute<IHorseCoreGroup<T>>;
var
LCallback: THorseCallback;
begin
if Assigned(ACallbacks) then
begin
for LCallback in ACallbacks do
AddCallback(LCallback);
end;
Result := Self;
end;
{$ELSE}
function THorseCoreGroupRoute<T>.AddCallbacks(const ACallbacks: TArray<THorseCallback>): IHorseCoreRoute<IHorseCoreGroup<T>>;
var
LCallback: THorseCallback;
begin
for LCallback in ACallbacks do
AddCallback(LCallback);
Result := Self;
end;
{$ENDIF}

function THorseCoreGroupRoute<T>.All(const ACallback: THorseCallback): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BaseUse(FPath, ACallback);
end;

function THorseCoreGroupRoute<T>.All(const AMiddleware, ACallback: THorseCallback): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BaseUse(FPath, [AMiddleware, ACallback]);
end;

function THorseCoreGroupRoute<T>.All(const ACallbacks: array of THorseCallback): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BaseUse(FPath, ACallbacks);
end;

function THorseCoreGroupRoute<T>.All(const ACallbacks: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BaseUse(FPath, ACallbacks);
THorseCoreBase(FHorseCore).BaseUse(FPath, [ACallback]);
end;

function THorseCoreGroupRoute<T>.Get(const ACallback: THorseCallback): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BaseGet(FPath, ACallback);
end;

function THorseCoreGroupRoute<T>.Get(const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BaseGet(FPath, ACallback);
end;

function THorseCoreGroupRoute<T>.Get(const ACallback: THorseCallbackRequest): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BaseGet(FPath, ACallback);
end;

{$IFNDEF FPC}
function THorseCoreGroupRoute<T>.Get(const ACallback: THorseCallbackResponse): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BaseGet(FPath, ACallback);
end;
{$ENDIF}

function THorseCoreGroupRoute<T>.Put(const ACallback: THorseCallback): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BasePut(FPath, ACallback);
end;

function THorseCoreGroupRoute<T>.Put(const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BasePut(FPath, ACallback);
end;

function THorseCoreGroupRoute<T>.Put(const ACallback: THorseCallbackRequest): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BasePut(FPath, ACallback);
end;

{$IFNDEF FPC}
function THorseCoreGroupRoute<T>.Put(const ACallback: THorseCallbackResponse): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BasePut(FPath, ACallback);
end;
{$ENDIF}

function THorseCoreGroupRoute<T>.Head(const ACallback: THorseCallback): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BaseHead(FPath, ACallback);
end;

function THorseCoreGroupRoute<T>.Head(const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BaseHead(FPath, ACallback);
end;

function THorseCoreGroupRoute<T>.Head(const ACallback: THorseCallbackRequest): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BaseHead(FPath, ACallback);
end;

{$IFNDEF FPC}
function THorseCoreGroupRoute<T>.Head(const ACallback: THorseCallbackResponse): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BaseHead(FPath, ACallback);
end;
{$ENDIF}

// Implementacao de Post
function THorseCoreGroupRoute<T>.Post(const ACallback: THorseCallback): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BasePost(FPath, ACallback);
end;

function THorseCoreGroupRoute<T>.Post(const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BasePost(FPath, ACallback);
end;

function THorseCoreGroupRoute<T>.Post(const ACallback: THorseCallbackRequest): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BasePost(FPath, ACallback);
end;

{$IFNDEF FPC}
function THorseCoreGroupRoute<T>.Post(const ACallback: THorseCallbackResponse): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BasePost(FPath, ACallback);
end;
{$ENDIF}

{$IF (DEFINED(FPC) OR (CompilerVersion > 27.0))}
function THorseCoreGroupRoute<T>.Patch(const ACallback: THorseCallback): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BasePatch(FPath, ACallback);
end;

function THorseCoreGroupRoute<T>.Delete(const ACallback: THorseCallback): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BaseDelete(FPath, ACallback);
end;

function THorseCoreGroupRoute<T>.Patch(const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BasePatch(FPath, ACallback);
end;

function THorseCoreGroupRoute<T>.Patch(const ACallback: THorseCallbackRequest): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BasePatch(FPath, ACallback);
end;

{$IFNDEF FPC}
function THorseCoreGroupRoute<T>.Patch(const ACallback: THorseCallbackResponse): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BasePatch(FPath, ACallback);
end;
{$ENDIF}

function THorseCoreGroupRoute<T>.Delete(const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BaseDelete(FPath, ACallback);
end;

function THorseCoreGroupRoute<T>.Delete(const ACallback: THorseCallbackRequest): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BaseDelete(FPath, ACallback);
end;

{$IFNDEF FPC}
function THorseCoreGroupRoute<T>.Delete(const ACallback: THorseCallbackResponse): IHorseCoreRoute<IHorseCoreGroup<T>>;
begin
Result := Self;
THorseCoreBase(FHorseCore).BaseDelete(FPath, ACallback);
end;
{$ENDIF}
{$ENDIF}

function THorseCoreGroupRoute<T>.&End: IHorseCoreGroup<T>;
begin
Result := FGroup;
end;

end.
2 changes: 1 addition & 1 deletion src/Horse.Core.Route.Contract.pas
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface
Horse.Callback;

type
IHorseCoreRoute<T: class> = interface
IHorseCoreRoute<T> = interface
['{8D593D98-44B3-4FD2-A21B-BA29F784B3AA}']
function AddCallback(const ACallback: THorseCallback): IHorseCoreRoute<T>;
{$IF DEFINED(FPC)}
Expand Down
Loading