diff --git a/frameworks-laravel/.gitignore b/frameworks-laravel/.gitignore index 3113dfe..95c2342 100644 --- a/frameworks-laravel/.gitignore +++ b/frameworks-laravel/.gitignore @@ -8,7 +8,6 @@ /.fleet /.idea /.nova -/.scribe /.phpunit.cache /.vscode /.zed @@ -23,3 +22,6 @@ Homestead.json Homestead.yaml Thumbs.db + +# Scribe +.scribe diff --git a/frameworks-laravel/.scribe/.filehashes b/frameworks-laravel/.scribe/.filehashes deleted file mode 100644 index 51da126..0000000 --- a/frameworks-laravel/.scribe/.filehashes +++ /dev/null @@ -1,4 +0,0 @@ -# GENERATED. YOU SHOULDN'T MODIFY OR DELETE THIS FILE. -# Scribe uses this file to know when you change something manually in your docs. -.scribe/intro.md=35310953ddf7f9e68771f7ed7b6189f9 -.scribe/auth.md=9bee2b1ef8a238b2e58613fa636d5f39 \ No newline at end of file diff --git a/frameworks-laravel/.scribe/auth.md b/frameworks-laravel/.scribe/auth.md deleted file mode 100644 index 8290362..0000000 --- a/frameworks-laravel/.scribe/auth.md +++ /dev/null @@ -1,3 +0,0 @@ -# Authenticating requests - -This API is not authenticated. diff --git a/frameworks-laravel/.scribe/endpoints.cache/00.yaml b/frameworks-laravel/.scribe/endpoints.cache/00.yaml deleted file mode 100644 index 151bc32..0000000 --- a/frameworks-laravel/.scribe/endpoints.cache/00.yaml +++ /dev/null @@ -1,321 +0,0 @@ -## Autogenerated by Scribe. DO NOT MODIFY. - -name: Endpoints -description: '' -endpoints: - - - custom: [] - httpMethods: - - GET - uri: api/health - metadata: - custom: [] - groupName: Endpoints - groupDescription: '' - subgroup: '' - subgroupDescription: '' - title: '' - description: '' - authenticated: false - deprecated: false - headers: - Content-Type: application/json - Accept: application/json - urlParameters: [] - cleanUrlParameters: [] - queryParameters: [] - cleanQueryParameters: [] - bodyParameters: [] - cleanBodyParameters: [] - fileParameters: [] - responses: - - - custom: [] - status: 200 - content: '{"status":"healthy","version":"unversioned","timestamp":"2025-11-03T10:55:55+00:00"}' - headers: - cache-control: 'no-cache, private' - content-type: application/json - access-control-allow-origin: '*' - description: null - responseFields: [] - auth: [] - controller: null - method: null - route: null - - - custom: [] - httpMethods: - - GET - uri: api/drivers - metadata: - custom: [] - groupName: Endpoints - groupDescription: '' - subgroup: '' - subgroupDescription: '' - title: 'Display a listing of the resource.' - description: '' - authenticated: false - deprecated: false - headers: - Content-Type: application/json - Accept: application/json - urlParameters: [] - cleanUrlParameters: [] - queryParameters: [] - cleanQueryParameters: [] - bodyParameters: [] - cleanBodyParameters: [] - fileParameters: [] - responses: - - - custom: [] - status: 200 - content: '{"data":[{"id":1,"name":"Max Verstappen","code":"VER","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":2,"name":"Sergio Perez","code":"PER","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":3,"name":"Lewis Hamilton","code":"HAM","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":4,"name":"George Russell","code":"RUS","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":5,"name":"Charles Leclerc","code":"LEC","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":6,"name":"Carlos Sainz","code":"SAI","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":7,"name":"Lando Norris","code":"NOR","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":8,"name":"Oscar Piastri","code":"PIA","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":9,"name":"Fernando Alonso","code":"ALO","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":10,"name":"Lance Stroll","code":"STR","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"}],"meta":{"count":10}}' - headers: - cache-control: 'no-cache, private' - content-type: application/json - access-control-allow-origin: '*' - description: null - responseFields: [] - auth: [] - controller: null - method: null - route: null - - - custom: [] - httpMethods: - - GET - uri: 'api/drivers/{id}' - metadata: - custom: [] - groupName: Endpoints - groupDescription: '' - subgroup: '' - subgroupDescription: '' - title: 'Display the specified resource.' - description: '' - authenticated: false - deprecated: false - headers: - Content-Type: application/json - Accept: application/json - urlParameters: - id: - custom: [] - name: id - description: 'The ID of the driver.' - required: true - example: 1 - type: integer - enumValues: [] - exampleWasSpecified: false - nullable: false - deprecated: false - cleanUrlParameters: - id: 1 - queryParameters: [] - cleanQueryParameters: [] - bodyParameters: [] - cleanBodyParameters: [] - fileParameters: [] - responses: - - - custom: [] - status: 200 - content: '{"data":{"id":1,"name":"Max Verstappen","code":"VER","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"}}' - headers: - cache-control: 'no-cache, private' - content-type: application/json - access-control-allow-origin: '*' - description: null - responseFields: [] - auth: [] - controller: null - method: null - route: null - - - custom: [] - httpMethods: - - GET - uri: api/circuits - metadata: - custom: [] - groupName: Endpoints - groupDescription: '' - subgroup: '' - subgroupDescription: '' - title: 'Display a listing of the resource.' - description: '' - authenticated: false - deprecated: false - headers: - Content-Type: application/json - Accept: application/json - urlParameters: [] - cleanUrlParameters: [] - queryParameters: [] - cleanQueryParameters: [] - bodyParameters: [] - cleanBodyParameters: [] - fileParameters: [] - responses: - - - custom: [] - status: 200 - content: '{"data":[{"id":1,"name":"Monaco Grand Prix","location":"Monte Carlo, Monaco","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":2,"name":"British Grand Prix","location":"Silverstone, United Kingdom","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":3,"name":"Italian Grand Prix","location":"Monza, Italy","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":4,"name":"Belgian Grand Prix","location":"Spa-Francorchamps, Belgium","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":5,"name":"Japanese Grand Prix","location":"Suzuka, Japan","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":6,"name":"Singapore Grand Prix","location":"Marina Bay, Singapore","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":7,"name":"Abu Dhabi Grand Prix","location":"Yas Marina, UAE","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":8,"name":"Brazilian Grand Prix","location":"Interlagos, Brazil","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":9,"name":"Australian Grand Prix","location":"Melbourne, Australia","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":10,"name":"Spanish Grand Prix","location":"Barcelona, Spain","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"}],"meta":{"count":10}}' - headers: - cache-control: 'no-cache, private' - content-type: application/json - access-control-allow-origin: '*' - description: null - responseFields: [] - auth: [] - controller: null - method: null - route: null - - - custom: [] - httpMethods: - - GET - uri: 'api/circuits/{id}' - metadata: - custom: [] - groupName: Endpoints - groupDescription: '' - subgroup: '' - subgroupDescription: '' - title: 'Display the specified resource.' - description: '' - authenticated: false - deprecated: false - headers: - Content-Type: application/json - Accept: application/json - urlParameters: - id: - custom: [] - name: id - description: 'The ID of the circuit.' - required: true - example: 1 - type: integer - enumValues: [] - exampleWasSpecified: false - nullable: false - deprecated: false - cleanUrlParameters: - id: 1 - queryParameters: [] - cleanQueryParameters: [] - bodyParameters: [] - cleanBodyParameters: [] - fileParameters: [] - responses: - - - custom: [] - status: 200 - content: '{"data":{"id":1,"name":"Monaco Grand Prix","location":"Monte Carlo, Monaco","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"}}' - headers: - cache-control: 'no-cache, private' - content-type: application/json - access-control-allow-origin: '*' - description: null - responseFields: [] - auth: [] - controller: null - method: null - route: null - - - custom: [] - httpMethods: - - GET - uri: api/races - metadata: - custom: [] - groupName: Endpoints - groupDescription: '' - subgroup: '' - subgroupDescription: '' - title: 'Display a listing of the resource.' - description: '' - authenticated: false - deprecated: false - headers: - Content-Type: application/json - Accept: application/json - urlParameters: [] - cleanUrlParameters: [] - queryParameters: [] - cleanQueryParameters: [] - bodyParameters: [] - cleanBodyParameters: [] - fileParameters: [] - responses: - - - custom: [] - status: 200 - content: '{"data":[{"id":1,"name":"2024 Monaco Grand Prix","race_date":"2024-05-26","season":"2024","created_at":"2025-10-29T17:21:39+00:00","updated_at":"2025-10-29T17:21:39+00:00","links":{"self":"http:\/\/localhost\/api\/races\/1","circuit":"http:\/\/localhost\/api\/circuits\/1","drivers":"http:\/\/localhost\/api\/drivers?race=1"}},{"id":2,"name":"2024 British Grand Prix","race_date":"2024-07-07","season":"2024","created_at":"2025-10-29T17:21:39+00:00","updated_at":"2025-10-29T17:21:39+00:00","links":{"self":"http:\/\/localhost\/api\/races\/2","circuit":"http:\/\/localhost\/api\/circuits\/2","drivers":"http:\/\/localhost\/api\/drivers?race=2"}},{"id":3,"name":"2024 Italian Grand Prix","race_date":"2024-09-01","season":"2024","created_at":"2025-10-29T17:21:39+00:00","updated_at":"2025-10-29T17:21:39+00:00","links":{"self":"http:\/\/localhost\/api\/races\/3","circuit":"http:\/\/localhost\/api\/circuits\/3","drivers":"http:\/\/localhost\/api\/drivers?race=3"}},{"id":4,"name":"2024 Belgian Grand Prix","race_date":"2024-07-28","season":"2024","created_at":"2025-10-29T17:21:39+00:00","updated_at":"2025-10-29T17:21:39+00:00","links":{"self":"http:\/\/localhost\/api\/races\/4","circuit":"http:\/\/localhost\/api\/circuits\/4","drivers":"http:\/\/localhost\/api\/drivers?race=4"}},{"id":5,"name":"2024 Japanese Grand Prix","race_date":"2024-04-07","season":"2024","created_at":"2025-10-29T17:21:40+00:00","updated_at":"2025-10-29T17:21:40+00:00","links":{"self":"http:\/\/localhost\/api\/races\/5","circuit":"http:\/\/localhost\/api\/circuits\/5","drivers":"http:\/\/localhost\/api\/drivers?race=5"}}],"meta":{"count":5}}' - headers: - cache-control: 'no-cache, private' - content-type: application/json - access-control-allow-origin: '*' - description: null - responseFields: [] - auth: [] - controller: null - method: null - route: null - - - custom: [] - httpMethods: - - GET - uri: 'api/races/{id}' - metadata: - custom: [] - groupName: Endpoints - groupDescription: '' - subgroup: '' - subgroupDescription: '' - title: 'Display the specified resource.' - description: '' - authenticated: false - deprecated: false - headers: - Content-Type: application/json - Accept: application/json - urlParameters: - id: - custom: [] - name: id - description: 'The ID of the race.' - required: true - example: 1 - type: integer - enumValues: [] - exampleWasSpecified: false - nullable: false - deprecated: false - cleanUrlParameters: - id: 1 - queryParameters: [] - cleanQueryParameters: [] - bodyParameters: [] - cleanBodyParameters: [] - fileParameters: [] - responses: - - - custom: [] - status: 200 - content: '{"data":{"id":1,"name":"2024 Monaco Grand Prix","race_date":"2024-05-26","season":"2024","created_at":"2025-10-29T17:21:39+00:00","updated_at":"2025-10-29T17:21:39+00:00","links":{"self":"http:\/\/localhost\/api\/races\/1","circuit":"http:\/\/localhost\/api\/circuits\/1","drivers":"http:\/\/localhost\/api\/drivers?race=1"}}}' - headers: - cache-control: 'no-cache, private' - content-type: application/json - access-control-allow-origin: '*' - description: null - responseFields: [] - auth: [] - controller: null - method: null - route: null diff --git a/frameworks-laravel/.scribe/endpoints/00.yaml b/frameworks-laravel/.scribe/endpoints/00.yaml deleted file mode 100644 index b7775ac..0000000 --- a/frameworks-laravel/.scribe/endpoints/00.yaml +++ /dev/null @@ -1,319 +0,0 @@ -name: Endpoints -description: '' -endpoints: - - - custom: [] - httpMethods: - - GET - uri: api/health - metadata: - custom: [] - groupName: Endpoints - groupDescription: '' - subgroup: '' - subgroupDescription: '' - title: '' - description: '' - authenticated: false - deprecated: false - headers: - Content-Type: application/json - Accept: application/json - urlParameters: [] - cleanUrlParameters: [] - queryParameters: [] - cleanQueryParameters: [] - bodyParameters: [] - cleanBodyParameters: [] - fileParameters: [] - responses: - - - custom: [] - status: 200 - content: '{"status":"healthy","version":"unversioned","timestamp":"2025-11-03T10:55:55+00:00"}' - headers: - cache-control: 'no-cache, private' - content-type: application/json - access-control-allow-origin: '*' - description: null - responseFields: [] - auth: [] - controller: null - method: null - route: null - - - custom: [] - httpMethods: - - GET - uri: api/drivers - metadata: - custom: [] - groupName: Endpoints - groupDescription: '' - subgroup: '' - subgroupDescription: '' - title: 'Display a listing of the resource.' - description: '' - authenticated: false - deprecated: false - headers: - Content-Type: application/json - Accept: application/json - urlParameters: [] - cleanUrlParameters: [] - queryParameters: [] - cleanQueryParameters: [] - bodyParameters: [] - cleanBodyParameters: [] - fileParameters: [] - responses: - - - custom: [] - status: 200 - content: '{"data":[{"id":1,"name":"Max Verstappen","code":"VER","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":2,"name":"Sergio Perez","code":"PER","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":3,"name":"Lewis Hamilton","code":"HAM","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":4,"name":"George Russell","code":"RUS","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":5,"name":"Charles Leclerc","code":"LEC","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":6,"name":"Carlos Sainz","code":"SAI","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":7,"name":"Lando Norris","code":"NOR","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":8,"name":"Oscar Piastri","code":"PIA","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":9,"name":"Fernando Alonso","code":"ALO","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":10,"name":"Lance Stroll","code":"STR","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"}],"meta":{"count":10}}' - headers: - cache-control: 'no-cache, private' - content-type: application/json - access-control-allow-origin: '*' - description: null - responseFields: [] - auth: [] - controller: null - method: null - route: null - - - custom: [] - httpMethods: - - GET - uri: 'api/drivers/{id}' - metadata: - custom: [] - groupName: Endpoints - groupDescription: '' - subgroup: '' - subgroupDescription: '' - title: 'Display the specified resource.' - description: '' - authenticated: false - deprecated: false - headers: - Content-Type: application/json - Accept: application/json - urlParameters: - id: - custom: [] - name: id - description: 'The ID of the driver.' - required: true - example: 1 - type: integer - enumValues: [] - exampleWasSpecified: false - nullable: false - deprecated: false - cleanUrlParameters: - id: 1 - queryParameters: [] - cleanQueryParameters: [] - bodyParameters: [] - cleanBodyParameters: [] - fileParameters: [] - responses: - - - custom: [] - status: 200 - content: '{"data":{"id":1,"name":"Max Verstappen","code":"VER","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"}}' - headers: - cache-control: 'no-cache, private' - content-type: application/json - access-control-allow-origin: '*' - description: null - responseFields: [] - auth: [] - controller: null - method: null - route: null - - - custom: [] - httpMethods: - - GET - uri: api/circuits - metadata: - custom: [] - groupName: Endpoints - groupDescription: '' - subgroup: '' - subgroupDescription: '' - title: 'Display a listing of the resource.' - description: '' - authenticated: false - deprecated: false - headers: - Content-Type: application/json - Accept: application/json - urlParameters: [] - cleanUrlParameters: [] - queryParameters: [] - cleanQueryParameters: [] - bodyParameters: [] - cleanBodyParameters: [] - fileParameters: [] - responses: - - - custom: [] - status: 200 - content: '{"data":[{"id":1,"name":"Monaco Grand Prix","location":"Monte Carlo, Monaco","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":2,"name":"British Grand Prix","location":"Silverstone, United Kingdom","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":3,"name":"Italian Grand Prix","location":"Monza, Italy","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":4,"name":"Belgian Grand Prix","location":"Spa-Francorchamps, Belgium","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":5,"name":"Japanese Grand Prix","location":"Suzuka, Japan","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":6,"name":"Singapore Grand Prix","location":"Marina Bay, Singapore","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":7,"name":"Abu Dhabi Grand Prix","location":"Yas Marina, UAE","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":8,"name":"Brazilian Grand Prix","location":"Interlagos, Brazil","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":9,"name":"Australian Grand Prix","location":"Melbourne, Australia","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"},{"id":10,"name":"Spanish Grand Prix","location":"Barcelona, Spain","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"}],"meta":{"count":10}}' - headers: - cache-control: 'no-cache, private' - content-type: application/json - access-control-allow-origin: '*' - description: null - responseFields: [] - auth: [] - controller: null - method: null - route: null - - - custom: [] - httpMethods: - - GET - uri: 'api/circuits/{id}' - metadata: - custom: [] - groupName: Endpoints - groupDescription: '' - subgroup: '' - subgroupDescription: '' - title: 'Display the specified resource.' - description: '' - authenticated: false - deprecated: false - headers: - Content-Type: application/json - Accept: application/json - urlParameters: - id: - custom: [] - name: id - description: 'The ID of the circuit.' - required: true - example: 1 - type: integer - enumValues: [] - exampleWasSpecified: false - nullable: false - deprecated: false - cleanUrlParameters: - id: 1 - queryParameters: [] - cleanQueryParameters: [] - bodyParameters: [] - cleanBodyParameters: [] - fileParameters: [] - responses: - - - custom: [] - status: 200 - content: '{"data":{"id":1,"name":"Monaco Grand Prix","location":"Monte Carlo, Monaco","created_at":"2025-10-29T17:21:39.000000Z","updated_at":"2025-10-29T17:21:39.000000Z"}}' - headers: - cache-control: 'no-cache, private' - content-type: application/json - access-control-allow-origin: '*' - description: null - responseFields: [] - auth: [] - controller: null - method: null - route: null - - - custom: [] - httpMethods: - - GET - uri: api/races - metadata: - custom: [] - groupName: Endpoints - groupDescription: '' - subgroup: '' - subgroupDescription: '' - title: 'Display a listing of the resource.' - description: '' - authenticated: false - deprecated: false - headers: - Content-Type: application/json - Accept: application/json - urlParameters: [] - cleanUrlParameters: [] - queryParameters: [] - cleanQueryParameters: [] - bodyParameters: [] - cleanBodyParameters: [] - fileParameters: [] - responses: - - - custom: [] - status: 200 - content: '{"data":[{"id":1,"name":"2024 Monaco Grand Prix","race_date":"2024-05-26","season":"2024","created_at":"2025-10-29T17:21:39+00:00","updated_at":"2025-10-29T17:21:39+00:00","links":{"self":"http:\/\/localhost\/api\/races\/1","circuit":"http:\/\/localhost\/api\/circuits\/1","drivers":"http:\/\/localhost\/api\/drivers?race=1"}},{"id":2,"name":"2024 British Grand Prix","race_date":"2024-07-07","season":"2024","created_at":"2025-10-29T17:21:39+00:00","updated_at":"2025-10-29T17:21:39+00:00","links":{"self":"http:\/\/localhost\/api\/races\/2","circuit":"http:\/\/localhost\/api\/circuits\/2","drivers":"http:\/\/localhost\/api\/drivers?race=2"}},{"id":3,"name":"2024 Italian Grand Prix","race_date":"2024-09-01","season":"2024","created_at":"2025-10-29T17:21:39+00:00","updated_at":"2025-10-29T17:21:39+00:00","links":{"self":"http:\/\/localhost\/api\/races\/3","circuit":"http:\/\/localhost\/api\/circuits\/3","drivers":"http:\/\/localhost\/api\/drivers?race=3"}},{"id":4,"name":"2024 Belgian Grand Prix","race_date":"2024-07-28","season":"2024","created_at":"2025-10-29T17:21:39+00:00","updated_at":"2025-10-29T17:21:39+00:00","links":{"self":"http:\/\/localhost\/api\/races\/4","circuit":"http:\/\/localhost\/api\/circuits\/4","drivers":"http:\/\/localhost\/api\/drivers?race=4"}},{"id":5,"name":"2024 Japanese Grand Prix","race_date":"2024-04-07","season":"2024","created_at":"2025-10-29T17:21:40+00:00","updated_at":"2025-10-29T17:21:40+00:00","links":{"self":"http:\/\/localhost\/api\/races\/5","circuit":"http:\/\/localhost\/api\/circuits\/5","drivers":"http:\/\/localhost\/api\/drivers?race=5"}}],"meta":{"count":5}}' - headers: - cache-control: 'no-cache, private' - content-type: application/json - access-control-allow-origin: '*' - description: null - responseFields: [] - auth: [] - controller: null - method: null - route: null - - - custom: [] - httpMethods: - - GET - uri: 'api/races/{id}' - metadata: - custom: [] - groupName: Endpoints - groupDescription: '' - subgroup: '' - subgroupDescription: '' - title: 'Display the specified resource.' - description: '' - authenticated: false - deprecated: false - headers: - Content-Type: application/json - Accept: application/json - urlParameters: - id: - custom: [] - name: id - description: 'The ID of the race.' - required: true - example: 1 - type: integer - enumValues: [] - exampleWasSpecified: false - nullable: false - deprecated: false - cleanUrlParameters: - id: 1 - queryParameters: [] - cleanQueryParameters: [] - bodyParameters: [] - cleanBodyParameters: [] - fileParameters: [] - responses: - - - custom: [] - status: 200 - content: '{"data":{"id":1,"name":"2024 Monaco Grand Prix","race_date":"2024-05-26","season":"2024","created_at":"2025-10-29T17:21:39+00:00","updated_at":"2025-10-29T17:21:39+00:00","links":{"self":"http:\/\/localhost\/api\/races\/1","circuit":"http:\/\/localhost\/api\/circuits\/1","drivers":"http:\/\/localhost\/api\/drivers?race=1"}}}' - headers: - cache-control: 'no-cache, private' - content-type: application/json - access-control-allow-origin: '*' - description: null - responseFields: [] - auth: [] - controller: null - method: null - route: null diff --git a/frameworks-laravel/.scribe/endpoints/custom.0.yaml b/frameworks-laravel/.scribe/endpoints/custom.0.yaml deleted file mode 100644 index 4b02352..0000000 --- a/frameworks-laravel/.scribe/endpoints/custom.0.yaml +++ /dev/null @@ -1,53 +0,0 @@ -# To include an endpoint that isn't a part of your Laravel app (or belongs to a vendor package), -# you can define it in a custom.*.yaml file, like this one. -# Each custom file should contain an array of endpoints. Here's an example: -# See https://scribe.knuckles.wtf/laravel/documenting/custom-endpoints#extra-sorting-groups-in-custom-endpoint-files for more options - -#- httpMethods: -# - POST -# uri: api/doSomething/{param} -# metadata: -# groupName: The group the endpoint belongs to. Can be a new group or an existing group. -# groupDescription: A description for the group. You don't need to set this for every endpoint; once is enough. -# subgroup: You can add a subgroup, too. -# title: Do something -# description: 'This endpoint allows you to do something.' -# authenticated: false -# headers: -# Content-Type: application/json -# Accept: application/json -# urlParameters: -# param: -# name: param -# description: A URL param for no reason. -# required: true -# example: 2 -# type: integer -# queryParameters: -# speed: -# name: speed -# description: How fast the thing should be done. Can be `slow` or `fast`. -# required: false -# example: fast -# type: string -# bodyParameters: -# something: -# name: something -# description: The things we should do. -# required: true -# example: -# - string 1 -# - string 2 -# type: 'string[]' -# responses: -# - status: 200 -# description: 'When the thing was done smoothly.' -# content: # Your response content can be an object, an array, a string or empty. -# { -# "hey": "ho ho ho" -# } -# responseFields: -# hey: -# name: hey -# description: Who knows? -# type: string # This is optional diff --git a/frameworks-laravel/.scribe/intro.md b/frameworks-laravel/.scribe/intro.md deleted file mode 100644 index e89564d..0000000 --- a/frameworks-laravel/.scribe/intro.md +++ /dev/null @@ -1,11 +0,0 @@ -# Introduction - - API for managing Formula 1 races, drivers, and circuits - - - - Welcome to the F1 Race API documentation! - This documentation aims to provide all the information you need to work with our API. - diff --git a/frameworks-laravel/README.md b/frameworks-laravel/README.md index 57888ee..629437b 100644 --- a/frameworks-laravel/README.md +++ b/frameworks-laravel/README.md @@ -93,7 +93,7 @@ php artisan scribe:generate ## OpenAPI -The API description is exported by [Scribe](https://scribe.knuckles.wtf/laravel/) and can be found at `frameworks-laravel/storage/app/private/scribe/openapi.yaml`. +The API description is exported by [Scribe](https://scribe.knuckles.wtf/laravel/) and can be found in `./storage/app/private/scribe/openapi.yaml`. ## License diff --git a/frameworks-laravel/app/Http/Controllers/Api/HealthController.php b/frameworks-laravel/app/Http/Controllers/Api/HealthController.php index 7e55f26..9559e6e 100644 --- a/frameworks-laravel/app/Http/Controllers/Api/HealthController.php +++ b/frameworks-laravel/app/Http/Controllers/Api/HealthController.php @@ -7,6 +7,13 @@ class HealthController extends Controller { + /** + * Healthcheck + * + * Check that the service is up. If everything is okay, you'll get a 200 OK response. + * + * Otherwise, the request will fail with a 400 error, and a response listing the failed services. + */ public function show(): JsonResponse { return response()->json([ diff --git a/frameworks-laravel/app/Http/Controllers/Api/RaceController.php b/frameworks-laravel/app/Http/Controllers/Api/RaceController.php index 759c690..ebcbe08 100644 --- a/frameworks-laravel/app/Http/Controllers/Api/RaceController.php +++ b/frameworks-laravel/app/Http/Controllers/Api/RaceController.php @@ -7,12 +7,19 @@ use App\Http\Resources\RaceCollection; use App\Models\Race; use Illuminate\Http\Request; +use Knuckles\Scribe\Attributes\{Authenticated, Group, BodyParam, QueryParam}; +#[Group(name: 'Races', description: 'A series of endpoints that allow programmatic access to managing F1 races.', authenticated: true)] class RaceController extends Controller { /** - * Display a listing of the resource. + * Get races + * + * A collection of race resources, newest first, optionally filtered by circuit or season query parameters. */ + #[Authenticated] + #[QueryParam(name: 'season', type: 'string', description: 'Filter the results by season year', required: false, example: '2024')] + #[QueryParam(name: 'circuit', type: 'string', description: 'Filter the results by circuit name', required: false, example: 'Monaco')] public function index(Request $request): RaceCollection { $query = Race::query(); @@ -29,8 +36,16 @@ public function index(Request $request): RaceCollection } /** - * Store a newly created resource in storage. + * Create a race + * + * Allows authenticated users to submit a new Race resource to the system. */ + #[Authenticated] + #[BodyParam(name: 'name', type: 'string', description: 'The name of the race.', required: true, example: 'Monaco Grand Prix')] + #[BodyParam(name: 'race_date', type: 'string', description: 'The date and time the race takes place, RFC 3339 in local timezone.', required: true, example: '2024-05-26T14:53:59')] + #[BodyParam(name: 'circuit_id', type: 'string', description: 'The Unique Identifier for the circuit where the race will be held.', required: true, example: '1234-1234-1234-1234')] + #[BodyParam(name: 'season', type: 'string', description: 'The season year for this race.', required: true, example: '2024')] + #[BodyParam(name: 'driver_ids', type: 'array', description: 'An array of Unique Identifiers for drivers participating in the race.', required: false, example: [ "5678-5678-5678-5678", "6789-6789-6789-6789" ])] public function store(Request $request): RaceResource { $validated = $request->validate([ diff --git a/frameworks-laravel/resources/views/scribe/index.blade.php b/frameworks-laravel/resources/views/scribe/index.blade.php index 7d47a05..6240ef5 100644 --- a/frameworks-laravel/resources/views/scribe/index.blade.php +++ b/frameworks-laravel/resources/views/scribe/index.blade.php @@ -72,7 +72,7 @@
- +
Check that the service is up. If everything is okay, you'll get a 200 OK response.
+Otherwise, the request will fail with a 400 error, and a response listing the failed services.
Example request:@@ -176,7 +180,7 @@
{
"status": "healthy",
"version": "unversioned",
- "timestamp": "2025-11-03T10:55:55+00:00"
+ "timestamp": "2025-11-16T15:08:22+00:00"
}
@@ -943,12 +947,13 @@
- +requires authentication
- +A collection of race resources, newest first, optionally filtered by circuit or season query parameters.
Example request:@@ -956,7 +961,7 @@
curl --request GET \
- --get "http://localhost/api/races" \
+ --get "http://localhost/api/races?season=2024&circuit=Monaco" \
--header "Content-Type: application/json" \
--header "Accept: application/json"
{
- "data": [
- {
- "id": 1,
- "name": "2024 Monaco Grand Prix",
- "race_date": "2024-05-26",
- "season": "2024",
- "created_at": "2025-10-29T17:21:39+00:00",
- "updated_at": "2025-10-29T17:21:39+00:00",
- "links": {
- "self": "http://localhost/api/races/1",
- "circuit": "http://localhost/api/circuits/1",
- "drivers": "http://localhost/api/drivers?race=1"
- }
- },
- {
- "id": 2,
- "name": "2024 British Grand Prix",
- "race_date": "2024-07-07",
- "season": "2024",
- "created_at": "2025-10-29T17:21:39+00:00",
- "updated_at": "2025-10-29T17:21:39+00:00",
- "links": {
- "self": "http://localhost/api/races/2",
- "circuit": "http://localhost/api/circuits/2",
- "drivers": "http://localhost/api/drivers?race=2"
- }
- },
- {
- "id": 3,
- "name": "2024 Italian Grand Prix",
- "race_date": "2024-09-01",
- "season": "2024",
- "created_at": "2025-10-29T17:21:39+00:00",
- "updated_at": "2025-10-29T17:21:39+00:00",
- "links": {
- "self": "http://localhost/api/races/3",
- "circuit": "http://localhost/api/circuits/3",
- "drivers": "http://localhost/api/drivers?race=3"
- }
- },
- {
- "id": 4,
- "name": "2024 Belgian Grand Prix",
- "race_date": "2024-07-28",
- "season": "2024",
- "created_at": "2025-10-29T17:21:39+00:00",
- "updated_at": "2025-10-29T17:21:39+00:00",
- "links": {
- "self": "http://localhost/api/races/4",
- "circuit": "http://localhost/api/circuits/4",
- "drivers": "http://localhost/api/drivers?race=4"
- }
- },
- {
- "id": 5,
- "name": "2024 Japanese Grand Prix",
- "race_date": "2024-04-07",
- "season": "2024",
- "created_at": "2025-10-29T17:21:40+00:00",
- "updated_at": "2025-10-29T17:21:40+00:00",
- "links": {
- "self": "http://localhost/api/races/5",
- "circuit": "http://localhost/api/circuits/5",
- "drivers": "http://localhost/api/drivers?race=5"
- }
- }
- ],
+ "data": [],
"meta": {
- "count": 5
+ "count": 0
}
}
@@ -1082,7 +1028,7 @@
+ season
+string
+optional
+
+
+ Filter the results by season year Example: 2024
circuit
+string
+optional
+
+
+ Filter the results by circuit name Example: Monaco
+
+ +Allows authenticated users to submit a new Race resource to the system.
+ + +Example request:+ + +
curl --request POST \
+ "http://localhost/api/races" \
+ --header "Content-Type: application/json" \
+ --header "Accept: application/json" \
+ --data "{
+ \"name\": \"Monaco Grand Prix\",
+ \"circuit_id\": \"1234-1234-1234-1234\",
+ \"race_date\": \"2024-05-26T14:53:59\",
+ \"season\": \"2024\",
+ \"driver_ids\": [
+ \"5678-5678-5678-5678\",
+ \"6789-6789-6789-6789\"
+ ]
+}"
+const url = new URL(
+ "http://localhost/api/races"
+);
+
+const headers = {
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+};
+
+let body = {
+ "name": "Monaco Grand Prix",
+ "circuit_id": "1234-1234-1234-1234",
+ "race_date": "2024-05-26T14:53:59",
+ "season": "2024",
+ "driver_ids": [
+ "5678-5678-5678-5678",
+ "6789-6789-6789-6789"
+ ]
+};
+
+fetch(url, {
+ method: "POST",
+ headers,
+ body: JSON.stringify(body),
+}).then(response => response.json());Received response: ++
+
+
+ Request failed with error:+
+
+Tip: Check that you're properly connected to the network.
+If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
+You can check the Dev Tools console for debugging information.
+
+
+
diff --git a/frameworks-laravel/routes/api.php b/frameworks-laravel/routes/api.php
index 6738487..2958424 100644
--- a/frameworks-laravel/routes/api.php
+++ b/frameworks-laravel/routes/api.php
@@ -4,25 +4,22 @@
use App\Http\Controllers\Api\DriverController;
use App\Http\Controllers\Api\HealthController;
use App\Http\Controllers\Api\RaceController;
-use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
// API routes (no version prefix, base /api is automatic)
+
// Health check
Route::get('/health', [HealthController::class, 'show']);
// Drivers
Route::get('/drivers', [DriverController::class, 'index']);
Route::get('/drivers/{id}', [DriverController::class, 'show']);
-// GET-only demo: removed create/update/delete routes
// Circuits
Route::get('/circuits', [CircuitController::class, 'index']);
Route::get('/circuits/{id}', [CircuitController::class, 'show']);
-// GET-only demo: removed create/update/delete routes
// Races
Route::get('/races', [RaceController::class, 'index']);
Route::get('/races/{id}', [RaceController::class, 'show']);
-// GET-only demo: removed create/update/delete routes
-// Removed granular driver attach/detach endpoints to simplify API
+Route::post('/races', [RaceController::class, 'store']);
diff --git a/frameworks-laravel/tests/Feature/CircuitsTest.php b/frameworks-laravel/tests/Feature/CircuitsTest.php
new file mode 100644
index 0000000..ec5c9cb
--- /dev/null
+++ b/frameworks-laravel/tests/Feature/CircuitsTest.php
@@ -0,0 +1,66 @@
+seed();
+ });
+
+ test('returns list of all circuits', function () {
+ $response = $this->getJson('/api/circuits');
+
+ $response->assertStatus(200)
+ ->assertJsonStructure(['data', 'meta']);
+
+ $data = $response->json('data');
+ expect($data)->toBeArray()->not->toBeEmpty();
+ });
+
+ test('returns correct data structure', function () {
+ $response = $this->getJson('/api/circuits');
+
+ $data = $response->json('data');
+ expect($data)->toBeArray();
+
+ if (count($data) > 0) {
+ $circuit = $data[0];
+ expect($circuit)->toHaveKeys(['id', 'name', 'location']);
+ }
+ });
+});
+
+describe('GET /api/circuits/{id}', function () {
+
+ beforeEach(function () {
+ $this->seed();
+ });
+
+ test('returns a specific circuit by id', function () {
+ $response = $this->getJson('/api/circuits');
+ $circuits = $response->json('data');
+ expect($circuits)->toBeArray()->not->toBeEmpty();
+
+ $firstCircuit = $circuits[0];
+
+ $response = $this->getJson("/api/circuits/{$firstCircuit['id']}");
+
+ $response->assertStatus(200)
+ ->assertJsonStructure(['data' => ['id', 'name', 'location']])
+ ->assertJson([
+ 'data' => [
+ 'id' => $firstCircuit['id'],
+ 'name' => $firstCircuit['name'],
+ 'location' => $firstCircuit['location'],
+ ],
+ ]);
+ });
+
+ test('returns 404 for non-existent circuit', function () {
+ $response = $this->getJson('/api/circuits/99999');
+ $response->assertStatus(404);
+ });
+});
diff --git a/frameworks-laravel/tests/Feature/DriversTest.php b/frameworks-laravel/tests/Feature/DriversTest.php
new file mode 100644
index 0000000..41939a9
--- /dev/null
+++ b/frameworks-laravel/tests/Feature/DriversTest.php
@@ -0,0 +1,66 @@
+seed();
+ });
+
+ test('returns list of all drivers', function () {
+ $response = $this->getJson('/api/drivers');
+
+ $response->assertStatus(200)
+ ->assertJsonStructure(['data', 'meta']);
+
+ $data = $response->json('data');
+ expect($data)->toBeArray()->not->toBeEmpty();
+ });
+
+ test('returns correct data structure', function () {
+ $response = $this->getJson('/api/drivers');
+
+ $data = $response->json('data');
+ expect($data)->toBeArray();
+
+ if (count($data) > 0) {
+ $driver = $data[0];
+ expect($driver)->toHaveKeys(['id', 'name', 'code']);
+ }
+ });
+});
+
+describe('GET /api/drivers/{id}', function () {
+
+ beforeEach(function () {
+ $this->seed();
+ });
+
+ test('returns a specific driver by id', function () {
+ $response = $this->getJson('/api/drivers');
+ $drivers = $response->json('data');
+ expect($drivers)->toBeArray()->not->toBeEmpty();
+
+ $firstDriver = $drivers[0];
+
+ $response = $this->getJson("/api/drivers/{$firstDriver['id']}");
+
+ $response->assertStatus(200)
+ ->assertJsonStructure(['data' => ['id', 'name', 'code']])
+ ->assertJson([
+ 'data' => [
+ 'id' => $firstDriver['id'],
+ 'name' => $firstDriver['name'],
+ 'code' => $firstDriver['code'],
+ ],
+ ]);
+ });
+
+ test('returns 404 for non-existent driver', function () {
+ $response = $this->getJson('/api/drivers/99999');
+ $response->assertStatus(404);
+ });
+});
diff --git a/frameworks-laravel/tests/Feature/HealthTest.php b/frameworks-laravel/tests/Feature/HealthTest.php
new file mode 100644
index 0000000..9634638
--- /dev/null
+++ b/frameworks-laravel/tests/Feature/HealthTest.php
@@ -0,0 +1,19 @@
+seed();
+ });
+
+ test('returns success status', function () {
+ $response = $this->getJson('/api/health');
+
+ $response->assertStatus(200)
+ ->assertJsonStructure(['status']);
+ });
+});
diff --git a/frameworks-laravel/tests/Feature/RacesTest.php b/frameworks-laravel/tests/Feature/RacesTest.php
new file mode 100644
index 0000000..27b2b6d
--- /dev/null
+++ b/frameworks-laravel/tests/Feature/RacesTest.php
@@ -0,0 +1,59 @@
+seed();
+ });
+
+ test('returns list of all races', function () {
+ $response = $this->getJson('/api/races');
+
+ $response->assertStatus(200)
+ ->assertJsonStructure(['data', 'meta']);
+
+ $data = $response->json('data');
+ expect($data)->toBeArray()->not->toBeEmpty();
+ });
+
+ test('returns correct data structure', function () {
+ $response = $this->getJson('/api/races');
+
+ $data = $response->json('data');
+ expect($data)->toBeArray();
+
+ if (count($data) > 0) {
+ $race = $data[0];
+ expect($race)->toHaveKeys(['id', 'name', 'season']);
+ }
+ });
+});
+
+describe('GET /api/races/{id}', function () {
+
+ beforeEach(function () {
+ $this->seed();
+ });
+
+ test('returns a specific race by id', function () {
+ $response = $this->getJson('/api/races');
+ $races = $response->json('data');
+ expect($races)->toBeArray()->not->toBeEmpty();
+
+ $firstRace = $races[0];
+
+ $response = $this->getJson("/api/races/{$firstRace['id']}");
+
+ $response->assertStatus(200)
+ ->assertJsonStructure(['data' => ['id', 'name', 'season', 'race_date', 'links']]);
+ });
+
+ test('returns 404 for non-existent race', function () {
+ $response = $this->getJson('/api/races/99999');
+ $response->assertStatus(404);
+ });
+});
diff --git a/frameworks-laravel/tests/Pest.php b/frameworks-laravel/tests/Pest.php
new file mode 100644
index 0000000..a7b28ca
--- /dev/null
+++ b/frameworks-laravel/tests/Pest.php
@@ -0,0 +1,3 @@
+in('Feature');