Skip to content

Commit 4daf959

Browse files
Merge branch 'next_major' of github.com:jaredhendrickson13/pfsense-api into next_major
2 parents 8b4d9f4 + 6fe0259 commit 4daf959

12 files changed

Lines changed: 571 additions & 79 deletions

pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/ContentHandlers/JSONContentHandler.inc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class JSONContentHandler extends ContentHandler
1010
{
1111
public string $mime_type = "application/json";
1212

13-
protected function get_content(): string
13+
public function get_content(): string
1414
{
1515
return (file_get_contents('php://input')) ?: "";
1616
}
@@ -40,4 +40,4 @@ class JSONContentHandler extends ContentHandler
4040
}
4141

4242

43-
}
43+
}

pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/ContentHandlers/URLContentHandler.inc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ use RESTAPI\Responses\ValidationError;
88

99
class URLContentHandler extends ContentHandler
1010
{
11-
public string $mime_type = "application/json";
11+
public string $mime_type = "application/x-www-form-urlencoded";
1212

13-
protected function get_content(): array
13+
public function get_content(): array
1414
{
1515
return $_GET;
1616
}
@@ -37,4 +37,4 @@ class URLContentHandler extends ContentHandler
3737
}
3838

3939

40-
}
40+
}

pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Core/ContentHandler.inc

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace RESTAPI\Core;
44

5+
use ReflectionException;
6+
use ReflectionMethod;
57
use RESTAPI\Responses\MediaTypeError;
68
use RESTAPI\Responses\NotAcceptableError;
79
use RESTAPI\Responses\ServerError;
@@ -12,8 +14,7 @@ require_once("RESTAPI/autoloader.inc");
1214
/**
1315
* Defines a class that is responsible for decoding request content from remote clients, and encoding response content to
1416
* be sent to the remote client. Children of this class must correspond with the valid HTTP MIME type this
15-
* ContentHandler is designed to interact with. Note: ContentHandlers should not interact with the remote client
16-
* directly. That is the Endpoint's job. Only use ContentHandlers to encode and decode content in the associated format.
17+
* ContentHandler is designed to interact with.
1718
* @link MIME-Types https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types.
1819
*/
1920
class ContentHandler
@@ -29,7 +30,7 @@ class ContentHandler
2930
* @return mixed The unprocessed content from the remote client.
3031
* @throws ServerError When this method is called but not overridden by the child class.
3132
*/
32-
protected function get_content(): mixed {
33+
public function get_content(): mixed {
3334
throw new ServerError(
3435
message: "Child ContentHanlder classes must override the get_content() method.",
3536
response_id: "CONTENT_HANDLER_GET_CONTENT_NOT_OVERRIDDEN"
@@ -61,7 +62,7 @@ class ContentHandler
6162
final public function encode(mixed $content): mixed {
6263
$encoded_content = $this->_encode($content);
6364
header("content-type: $this->mime_type");
64-
header("content-length: ".strlen($this->mime_type));
65+
header("content-length: ".strlen($encoded_content));
6566
return $encoded_content;
6667
}
6768

@@ -83,29 +84,33 @@ class ContentHandler
8384

8485
/**
8586
* Obtains and decodes the content from the format corresponding with this ContentHandler's MIME type.
87+
* @param mixed $content The content to be decoded in this ContentHandler's format. If no value is specified,
88+
* the content will automatically be populated using the `get_content()` method.
8689
* @return mixed The content in this ContentHandler's respective format.
8790
*/
88-
final public function decode(): mixed {
89-
$content = $this->get_content();
91+
final public function decode(mixed $content = null): mixed {
92+
$content = ($content) ?: $this->get_content();
9093
return $this->_decode($content);
9194
}
9295

9396
/**
9497
* Checks if this ContentHandler object is capable of encoding content.
9598
* @return bool Returns true if this ContentHandler can encode content, otherwise false.
99+
* @throws ReflectionException When the _encode() method is missing entirely.
96100
*/
97101
final public function can_encode(): bool {
98-
$content_handler = get_called_class();
99-
return method_exists($content_handler, "_encode");
102+
$reflector = new ReflectionMethod($this, '_encode');
103+
return ($reflector->getDeclaringClass()->getName() === get_class($this));
100104
}
101105

102106
/**
103107
* Checks if this ContentHandler object is capable of decoding content.
104108
* @return bool Returns true if this ContentHandler can decode content, otherwise false.
109+
* @throws ReflectionException When the _decode() method is missing entirely.
105110
*/
106111
final public function can_decode(): bool {
107-
$content_handler = get_called_class();
108-
return method_exists($content_handler, "_decode");
112+
$reflector = new ReflectionMethod($this, '_decode');
113+
return ($reflector->getDeclaringClass()->getName() === get_class($this));
109114
}
110115

111116
/**

pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Fields/ForeignModelField.inc

Lines changed: 63 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,10 @@ use RESTAPI\Responses\ServerError;
1717
* route to its parent Gateway model object.
1818
*/
1919
class ForeignModelField extends Field {
20+
/**
21+
* @var Model $model Contains the Model object of the foreign Model this Field's value relates to.
22+
*/
2023
public Model $model;
21-
public string $model_name;
22-
public array $model_query;
23-
public string $model_field;
24-
public string $model_field_internal;
2524

2625
/**
2726
* Defines the ForeignModelField object and sets its options.
@@ -39,6 +38,9 @@ class ForeignModelField extends Field {
3938
* referenced by this field. For example, say this field's value must be set to an existing FirewallAlias object's
4039
* `name`, but you only want to allow `port` type aliases; you would set $model_name to `FirewallAlias`, $model_field to
4140
* `name`, and $model_query to `["type" => "port"]`. Defaults to all existing $model objects.
41+
* @param array $parent_model_query When the assigned $model_name class has a parent Model, use this variable to
42+
* limit the scope of which parent Model objects can have their child Models included. By default, the child
43+
* Models of ALL parent Models are included.
4244
* @param bool $required If `true`, this field is required to have a value at all times.
4345
* @param bool $unique If `true`, this field must be unique from all other parent model objects. Enabling this
4446
* option requires the Model $context to be set AND the Model $context must have a `config_path` set.
@@ -90,10 +92,11 @@ class ForeignModelField extends Field {
9092
* @param string $help_text Set a description for this field. This description will be used in API documentation.
9193
*/
9294
public function __construct(
93-
string $model_name,
94-
string $model_field,
95-
string $model_field_internal = "",
96-
array $model_query = [],
95+
public string $model_name,
96+
public string $model_field,
97+
public string $model_field_internal = "",
98+
public array $model_query = [],
99+
public array $parent_model_query = [],
97100
bool $required = false,
98101
bool $unique = false,
99102
mixed $default = null,
@@ -121,8 +124,6 @@ class ForeignModelField extends Field {
121124
{
122125
# Assign properties unique to this Field object
123126
$model_name = "\\RESTAPI\\Models\\$model_name";
124-
$this->model_query = $model_query;
125-
$this->model_field = $model_field;
126127
$this->model_field_internal = ($model_field_internal) ?: $model_field;
127128

128129
# Ensure the properties assigned are allowed and that the assigned $model_name can be constructed
@@ -161,7 +162,7 @@ class ForeignModelField extends Field {
161162
* Checks that the object to be constructed has no conflicts.
162163
* @param string $model_name The foreign Model class's FQN.
163164
*/
164-
private function check_construct(string $model_name) {
165+
private function check_construct(string $model_name): void {
165166
# Ensure the assigned $model_name is an existing Model class
166167
if (!class_exists($model_name)) {
167168
throw new ServerError(
@@ -226,11 +227,8 @@ class ForeignModelField extends Field {
226227
$internal_value = $this->model->{$this->model_field_internal}->_from_internal($internal_value);
227228
}
228229

229-
# Add the query parameters needed to locate the related Model object
230-
$query_params = array_merge($this->model_query, [$this->model_field_internal => $internal_value]);
231-
232230
# Query for the Model object this value relates to.
233-
$query_modelset = $this->model->query($query_params);
231+
$query_modelset = $this->__get_matches($this->model_field_internal, $internal_value);
234232

235233
# If the model object exists, return the `model_field` value.
236234
if ($query_modelset->exists()) {
@@ -247,11 +245,8 @@ class ForeignModelField extends Field {
247245
* @return array|string|null The internal value(s) suitable for writing to the pfSense configuration.
248246
*/
249247
protected function _to_internal(mixed $representation_value) : array|string|null {
250-
# Add the query parameters needed to locate the related Model object
251-
$query_params = array_merge($this->model_query, [$this->model_field => $representation_value]);
252-
253-
# Query for the Model object this value relates to.
254-
$query_modelset = $this->model->query($query_params);
248+
# Obtain Model objects that matches this field's criteria
249+
$query_modelset = $this->__get_matches($this->model_field, $representation_value);
255250

256251
# If the model object exists, return the existing `model_field_internal` value.
257252
if ($query_modelset->exists()) {
@@ -267,12 +262,9 @@ class ForeignModelField extends Field {
267262
* @param mixed $value The value being validated. In the event that this is a `many` field, this method will
268263
* receive each value of the array individually, not the array value itself.
269264
*/
270-
public function validate_extra(mixed $value) {
271-
# Add the query parameters needed to locate the related Model object
272-
$query_params = array_merge($this->model_query, [$this->model_field => $value]);
273-
274-
# Query for the Model object this value relates to.
275-
$query_modelset = $this->model->query($query_params);
265+
public function validate_extra(mixed $value): void {
266+
# Obtain Models that match this Field's criteria
267+
$query_modelset = $this->__get_matches($this->model_field, $value);
276268

277269
# If the model object exists, return the existing `model_field_internal` value.
278270
if (!$query_modelset->exists()) {
@@ -284,17 +276,55 @@ class ForeignModelField extends Field {
284276
}
285277
}
286278

279+
/**
280+
* Obtains the ModelSet of Model objects that are in-scope for this field using the $model_query and
281+
* $parent_model_query properties.
282+
* @return ModelSet A ModelSet of Model objects that are in-scope for this field
283+
*/
284+
public function get_in_scope_models(): ModelSet {
285+
# Variables
286+
$models = new ModelSet();
287+
288+
# Obtain the parent Models if the assigned $model has a $parent_model_class assigned to it.
289+
if ($this->model->parent_model_class) {
290+
$parent_model_class = $this->model->get_parent_model();
291+
$parent_model = new $parent_model_class();
292+
$parent_models = $parent_model->query($this->parent_model_query);
293+
294+
# Loop through each identified parent and add its children into the ModelSet
295+
foreach ($parent_models->model_objects as $parent) {
296+
$parent_children = $this->model->query(parent_id: $parent->id);
297+
$models->model_objects = array_merge($models->model_objects, $parent_children->model_objects);
298+
}
299+
}
300+
# Otherwise, just use all of this $model's current objects
301+
else {
302+
$models = $this->model->read_all();
303+
}
304+
305+
# Query for the Model object this value relates to.
306+
return $models->query($this->model_query);
307+
}
308+
309+
/**
310+
* Obtains a ModelSet of the Model(s) that match this field's criteria.
311+
* @param string $field_name The name of the field used to check for matching values. This is typically set to the
312+
* same value as $this->field_name.
313+
* @param mixed $field_value The value of the $field_name that indicates there is a match. This is typically set
314+
* to the same value as $this->value.
315+
*/
316+
private function __get_matches(string $field_name, mixed $field_value): ModelSet {
317+
return $this->get_in_scope_models()->query(query_params: [$field_name => $field_value]);
318+
}
319+
287320
/**
288321
* Obtains the Model object associated with this field's current value. This is only applicable when this is not
289322
* a $many enabled field.
290323
* @returns Model|null Returns the Model object associated with this Field's current value.
291324
*/
292325
public function get_related_model() : Model|null {
293-
# Add the query parameters needed to locate the related Model object
294-
$query_params = array_merge($this->model_query, [$this->model_field => $this->value]);
295-
296-
# Query for the Model object this value relates to.
297-
$query_modelset = $this->model->query($query_params);
326+
# Get the Model objects that match this field's criteria
327+
$query_modelset = $this->__get_matches($this->model_field, $this->value);
298328

299329
# Return the related model object if it exists
300330
if ($query_modelset->exists()) {
@@ -317,11 +347,8 @@ class ForeignModelField extends Field {
317347

318348
# Loop through each current value and query for model objects that match them
319349
foreach ($this->value as $value) {
320-
# Add the query parameters needed to locate the related Model object
321-
$query_params = array_merge($this->model_query, [$this->model_field => $value]);
322-
323-
# Query for the Model object this value relates to.
324-
$query_modelset = $this->model->query($query_params);
350+
# Obtain Model objects that match this value
351+
$query_modelset = $this->__get_matches($this->model_field, $value);
325352

326353
# Only add the Model object if it exists
327354
if ($query_modelset->exists()) {

0 commit comments

Comments
 (0)