From 0856ab9fbaa78bc8c514c54e2acb585612d6f9cf Mon Sep 17 00:00:00 2001 From: Jared Hendrickson Date: Sun, 8 Jun 2025 13:49:15 -0600 Subject: [PATCH 1/7] fix: use replace strategy when updating RoutingGatewayGroupPriority objects #707 The 'merge' strategy does not work for this model because it's internal representation is a pipe delimited string instead of an array like usual. We must use 'replace' instead to ensure the entire object gets written correctly. --- .../usr/local/pkg/RESTAPI/Models/RoutingGatewayGroupPriority.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Models/RoutingGatewayGroupPriority.inc b/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Models/RoutingGatewayGroupPriority.inc index 5bd2bc85a..183c34002 100644 --- a/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Models/RoutingGatewayGroupPriority.inc +++ b/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Models/RoutingGatewayGroupPriority.inc @@ -22,6 +22,7 @@ class RoutingGatewayGroupPriority extends Model { $this->parent_model_class = 'RoutingGatewayGroup'; $this->config_path = 'item'; $this->subsystem = 'staticroutes'; + $this->update_strategy = 'replace'; $this->many = true; $this->many_minimum = 1; From c37dce9bf126af3d120049843fe30ba3322ee589 Mon Sep 17 00:00:00 2001 From: Jared Hendrickson Date: Sun, 8 Jun 2025 13:59:12 -0600 Subject: [PATCH 2/7] fix: don't restrict interface gateway assignment by id Addresses an issue that prevented gateways from being assigned to an interface when the REST API 'represent_interfaces_as' setting was not set to 'id'. Previously the related RoutingGateway model query used the interface's literal id value, but when interfaces are not represented by its id the query will always return nothing, therefor a gateway cannot be assigned --- .../files/usr/local/pkg/RESTAPI/Models/NetworkInterface.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Models/NetworkInterface.inc b/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Models/NetworkInterface.inc index 664fedb39..d375dd9a9 100644 --- a/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Models/NetworkInterface.inc +++ b/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Models/NetworkInterface.inc @@ -165,7 +165,7 @@ class NetworkInterface extends Model { $this->gateway = new ForeignModelField( model_name: ['RoutingGateway', 'RoutingGatewayGroup'], model_field: 'name', - model_query: ['ipprotocol' => 'inet', 'interface' => &$this->id], + model_query: ['ipprotocol' => 'inet'], allow_null: true, conditions: ['typev4' => 'static'], help_text: 'Sets the upstream gateway this interface will use. This is only applicable for WAN-type interfaces.', @@ -313,7 +313,7 @@ class NetworkInterface extends Model { $this->gatewayv6 = new ForeignModelField( model_name: ['RoutingGateway', 'RoutingGatewayGroup'], model_field: 'name', - model_query: ['ipprotocol' => 'inet6', 'interface' => &$this->id], + model_query: ['ipprotocol' => 'inet6'], allow_null: true, conditions: ['typev6' => 'staticv6'], help_text: 'Sets the upstream IPv6 gateway this interface will use. This is only applicable for WAN-type interfaces.', From a8679b57fc21f56417a2d2e58082922d8126b8ff Mon Sep 17 00:00:00 2001 From: Jared Hendrickson Date: Sun, 8 Jun 2025 18:35:14 -0600 Subject: [PATCH 3/7] tests: add test checking RoutingGatewayPriority tier change #707 --- .../Tests/APIModelsRoutingGatewayGroupPriorityTestCase.inc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Tests/APIModelsRoutingGatewayGroupPriorityTestCase.inc b/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Tests/APIModelsRoutingGatewayGroupPriorityTestCase.inc index 9be7fdab8..88ae85e84 100644 --- a/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Tests/APIModelsRoutingGatewayGroupPriorityTestCase.inc +++ b/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Tests/APIModelsRoutingGatewayGroupPriorityTestCase.inc @@ -58,6 +58,12 @@ class APIModelsRoutingGatewayGroupPriorityTestCase extends TestCase { $gateway_prio->tier->value = 3; $gateway_prio->update(); + # Ensure the priority was actually updated + $this->assert_equals( + RoutingGatewayGroupPriority::query(id: $gateway_prio->id)->first()->tier->value, + 3 + ); + # Delete the gateway group priority object and ensure it was deleted $gateway_prio->delete(); $this->assert_equals(RoutingGatewayGroupPriority::read_all(parent_id: $gateway_group->id)->count(), 1); From f21aec85d5d709f8d833323d1ab7d3abc0424dc1 Mon Sep 17 00:00:00 2001 From: Jared Hendrickson Date: Tue, 10 Jun 2025 21:15:10 -0600 Subject: [PATCH 4/7] docs(oas): include id fields in success response schemas #713 --- .../local/pkg/RESTAPI/Core/ContentHandler.inc | 43 ++++++++++++++++--- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Core/ContentHandler.inc b/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Core/ContentHandler.inc index 99c522725..101f7856a 100644 --- a/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Core/ContentHandler.inc +++ b/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Core/ContentHandler.inc @@ -269,17 +269,46 @@ class ContentHandler { * @return array This ContentHandler as an OpenAPI 'content' schema for this MIME type in a given Response object. */ public function to_openapi_schema(Response $response, Endpoint $endpoint): array { - # Format the data schema based on the endpoint for successful responses - if ($response instanceof Success and $endpoint->many) { + # Default to an empty schema in the event we cannot determine the schema + $data_schema = [ + 'oneOf' => [['type' => 'array', 'items' => ['type' => 'object']], ['type' => 'object']], + ]; + + # Ensure the 'id' field is included for success responses on many models without a parent + if ($response instanceof Success and $endpoint->model->many and !$endpoint->model->parent_model_class) { $data_schema = [ - 'type' => 'array', - 'items' => ['$ref' => "#/components/schemas/{$endpoint->model->get_class_shortname()}"], + 'allOf' => [ + ['type' => 'object', 'properties' => ['id' => ['type' => $endpoint->model->id_type]]], + ['$ref' => "#/components/schemas/{$endpoint->model->get_class_shortname()}"], + ], ]; - } elseif ($response instanceof Success and !$endpoint->many) { + } + # Ensure the 'parent_id' and 'id' fields are included for success responses on many models with a parent + elseif ($response instanceof Success and $endpoint->model->many and $endpoint->model->parent_model_class) { + $parent_type = $endpoint->model->get_parent_model()->id_type; + $data_schema = [ + 'allOf' => [ + [ + 'type' => 'object', + 'properties' => [ + 'parent_id' => ['type' => $parent_type], + 'id' => ['type' => $endpoint->model->id_type], + ], + ], + ['$ref' => "#/components/schemas/{$endpoint->model->get_class_shortname()}"], + ], + ]; + } + # Do not include any extra fields for non many models + elseif ($response instanceof Success and !$endpoint->model->many) { $data_schema = ['$ref' => "#/components/schemas/{$endpoint->model->get_class_shortname()}"]; - } else { + } + + # If this is a many endpoint, nest the data schema in an array + if ($response instanceof Success and $endpoint->many) { $data_schema = [ - 'oneOf' => [['type' => 'array', 'items' => ['type' => 'object']], ['type' => 'object']], + 'type' => 'array', + 'items' => $data_schema, ]; } From 9f72a56cf73b8d466b082ba3f0b3dcc3b3aeeb28 Mon Sep 17 00:00:00 2001 From: Jared Hendrickson Date: Tue, 10 Jun 2025 21:15:24 -0600 Subject: [PATCH 5/7] style: run prettier on changed files --- .../Tests/APIModelsRoutingGatewayGroupPriorityTestCase.inc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Tests/APIModelsRoutingGatewayGroupPriorityTestCase.inc b/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Tests/APIModelsRoutingGatewayGroupPriorityTestCase.inc index 88ae85e84..7cc41a73b 100644 --- a/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Tests/APIModelsRoutingGatewayGroupPriorityTestCase.inc +++ b/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Tests/APIModelsRoutingGatewayGroupPriorityTestCase.inc @@ -59,10 +59,7 @@ class APIModelsRoutingGatewayGroupPriorityTestCase extends TestCase { $gateway_prio->update(); # Ensure the priority was actually updated - $this->assert_equals( - RoutingGatewayGroupPriority::query(id: $gateway_prio->id)->first()->tier->value, - 3 - ); + $this->assert_equals(RoutingGatewayGroupPriority::query(id: $gateway_prio->id)->first()->tier->value, 3); # Delete the gateway group priority object and ensure it was deleted $gateway_prio->delete(); From 70e2a09a4e5a9450f65c98b8f7115228f9be367f Mon Sep 17 00:00:00 2001 From: Jared Hendrickson Date: Wed, 11 Jun 2025 08:17:39 -0600 Subject: [PATCH 6/7] tests: include missing parent_id in RoutingGatewayGroupPriority test --- .../Tests/APIModelsRoutingGatewayGroupPriorityTestCase.inc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Tests/APIModelsRoutingGatewayGroupPriorityTestCase.inc b/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Tests/APIModelsRoutingGatewayGroupPriorityTestCase.inc index 7cc41a73b..3842b82b1 100644 --- a/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Tests/APIModelsRoutingGatewayGroupPriorityTestCase.inc +++ b/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Tests/APIModelsRoutingGatewayGroupPriorityTestCase.inc @@ -59,7 +59,11 @@ class APIModelsRoutingGatewayGroupPriorityTestCase extends TestCase { $gateway_prio->update(); # Ensure the priority was actually updated - $this->assert_equals(RoutingGatewayGroupPriority::query(id: $gateway_prio->id)->first()->tier->value, 3); + $this->assert_equals( + RoutingGatewayGroupPriority::query(parent_id: $gateway_group->id, id: $gateway_prio->id)->first()->tier + ->value, + 3, + ); # Delete the gateway group priority object and ensure it was deleted $gateway_prio->delete(); From f88c9323529c8974ddd0423c905583e406748ea7 Mon Sep 17 00:00:00 2001 From: Jared Hendrickson Date: Wed, 11 Jun 2025 08:23:00 -0600 Subject: [PATCH 7/7] tests: check for new response schema in ContentHandler tests --- .../Tests/APICoreContentHandlerTestCase.inc | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Tests/APICoreContentHandlerTestCase.inc b/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Tests/APICoreContentHandlerTestCase.inc index 18c5c9207..38701e97d 100644 --- a/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Tests/APICoreContentHandlerTestCase.inc +++ b/pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Tests/APICoreContentHandlerTestCase.inc @@ -136,7 +136,17 @@ class APICoreContentHandlerTestCase extends TestCase { 'data' => [ 'type' => 'array', 'items' => [ - '$ref' => "#/components/schemas/{$endpoint->model->get_class_shortname()}", + 'allOf' => [ + [ + 'type' => 'object', + 'properties' => [ + 'id' => ['type' => $endpoint->model->id_type], + ], + ], + [ + '$ref' => "#/components/schemas/{$endpoint->model->get_class_shortname()}", + ], + ], ], ], ], @@ -165,7 +175,17 @@ class APICoreContentHandlerTestCase extends TestCase { [ 'type' => 'object', 'properties' => [ - 'data' => ['$ref' => "#/components/schemas/{$endpoint->model->get_class_shortname()}"], + 'data' => [ + 'allOf' => [ + [ + 'type' => 'object', + 'properties' => [ + 'id' => ['type' => $endpoint->model->id_type], + ], + ], + ['$ref' => "#/components/schemas/{$endpoint->model->get_class_shortname()}"], + ], + ], ], ], ],