Skip to content

Commit b7c69cb

Browse files
feat: allow endpoints, models and clients to specify sort flags #646
1 parent cf114b7 commit b7c69cb

File tree

3 files changed

+80
-5
lines changed

3 files changed

+80
-5
lines changed

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

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ class Endpoint {
316316
public bool $reverse = false;
317317

318318
/**
319-
* @var string|null $sort_by
319+
* @var string|array|null $sort_by
320320
* Sets the default value(s) for the `sort_by` field in the request data. This value is used to control the sorting of
321321
* the Model objects returned by this Endpoint. This value can be overridden by the client in the request data. Use
322322
* caution when assigning this value as it may force objects to be sorted in this order when they are written to the
@@ -333,6 +333,15 @@ class Endpoint {
333333
*/
334334
public string $sort_order = 'SORT_ASC';
335335

336+
/**
337+
* @var string $sort_flags
338+
* Sets the default value for the `sort_flags` field in the request data. This value is used to control the sorting
339+
* flags of the Model objects returned by this Endpoint. This value can be overridden by the client in the request
340+
* data. This value only takes effect when the `sort_by` field is also set. This value must be the name of a valid
341+
* PHP sort flags constant (e.g. 'SORT_REGULAR', 'SORT_NATURAL')
342+
*/
343+
public string $sort_flags = 'SORT_REGULAR';
344+
336345
/**
337346
* @var bool $append
338347
* Sets the default value for the `append` field in the request data. This value is used to control how Model objects
@@ -854,6 +863,43 @@ class Endpoint {
854863
}
855864
}
856865

866+
/**
867+
* Validates the $sort_flags common control parameter for this request.
868+
* @note If the `sort_flags` field was not provided in the request data, the request will default to the
869+
* Endpoint's assigned $sort_flags property value.
870+
* @throws ValidationError When the `sort_flags` field is not a string or is not a valid sort flags constant.
871+
*/
872+
private function validate_sort_flags(): void
873+
{
874+
# Valid sort_flags options
875+
$flag_opts = [
876+
'SORT_REGULAR', 'SORT_NUMERIC', 'SORT_STRING', 'SORT_LOCALE_STRING', 'SORT_NATURAL', 'SORT_FLAG_CASE'
877+
];
878+
879+
# Only validate this field if the client specifically requested it in the request data
880+
if (isset($this->request_data['sort_flags'])) {
881+
# Ensure value is a string
882+
if (!is_string($this->request_data['sort_flags'])) {
883+
throw new ValidationError(
884+
message: 'Field `sort_flags` must be of type `string`.',
885+
response_id: 'ENDPOINT_SORT_FLAGS_FIELD_INVALID_TYPE',
886+
);
887+
}
888+
889+
# Ensure the field is a valid sort flags constant
890+
if (!in_array($this->request_data['sort_flags'], $flag_opts)) {
891+
throw new ValidationError(
892+
message: 'Field `sort_flags` must be one of: ' . json_encode($flag_opts),
893+
response_id: 'ENDPOINT_SORT_FLAGS_FIELD_UNKNOWN_SORT_FLAGS',
894+
);
895+
}
896+
897+
# Update the sort_flags property to use the client's requested value and remove it from the request data
898+
$this->sort_flags = $this->request_data['sort_flags'];
899+
unset($this->request_data['sort_flags']);
900+
}
901+
}
902+
857903
/**
858904
* Validates the $append common control parameter for this request.
859905
* @note If the `append` field was not provided in the request data, the request will default to the Endpoint's assigned
@@ -964,6 +1010,7 @@ class Endpoint {
9641010
$this->validate_reverse();
9651011
$this->validate_sort_by();
9661012
$this->validate_sort_order();
1013+
$this->validate_sort_flags();
9671014
$this->validate_append();
9681015
$this->validate_remove();
9691016
$this->validate_limit();
@@ -1082,6 +1129,7 @@ class Endpoint {
10821129
reverse: $this->reverse,
10831130
sort_by: $this->sort_by,
10841131
sort_order: constant($this->sort_order),
1132+
sort_flags: constant($this->sort_flags),
10851133
);
10861134
}
10871135
# For GET requests on many Endpoints, obtain all objects from the assigned Model.
@@ -1105,6 +1153,7 @@ class Endpoint {
11051153
# Allow the endpoint/client to override the Model's sort_by and sort_order properties
11061154
$this->model->sort_by = $this->sort_by ?? $this->model->sort_by;
11071155
$this->model->sort_order = constant($this->sort_order) ?? $this->model->sort_order;
1156+
$this->model->sort_flags = constant($this->sort_flags) ?? $this->model->sort_flags;
11081157

11091158
# Create the object and return it
11101159
return $this->model->create(apply: $this->request_data['apply'] === true);
@@ -1128,6 +1177,7 @@ class Endpoint {
11281177
# Allow the endpoint/client to override the Model's sort_by and sort_order properties
11291178
$this->model->sort_by = $this->sort_by ?? $this->model->sort_by;
11301179
$this->model->sort_order = constant($this->sort_order) ?? $this->model->sort_order;
1180+
$this->model->sort_flags = constant($this->sort_flags) ?? $this->model->sort_flags;
11311181

11321182
# Update the object and return it
11331183
return $this->model->update(

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

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,15 @@ class Model {
222222
*/
223223
public int|null $sort_order = null;
224224

225+
/**
226+
* @var int|null $sort_flags
227+
* For $many enabled Models, this property can be used to set the PHP sort flags used when writing Model objects to
228+
* config. This property is only applicable if the $sort_by_field property is also defined. This property only applies
229+
* to Models with a $config_path defined. For valid value options for this property, refer to:
230+
* https://www.php.net/manual/en/function.array-multisort.php
231+
*/
232+
public int|null $sort_flags = null;
233+
225234
/**
226235
* @var array|null $sort_by
227236
* Sets the field names this Model will use when sorting objects written to the pfSense configuration. These fields must
@@ -1703,7 +1712,12 @@ class Model {
17031712
}
17041713

17051714
# Obtain all Model objects for this Model and sort them by the requested criteria
1706-
$modelset = $this->query(parent_id: $this->parent_id, sort_by: $this->sort_by, sort_order: $this->sort_order);
1715+
$modelset = $this->query(
1716+
parent_id: $this->parent_id,
1717+
sort_by: $this->sort_by,
1718+
sort_order: $this->sort_order,
1719+
sort_flags: $this->sort_flags
1720+
);
17071721

17081722
# Loop through the sorted object and assign it's internal value
17091723
$internal_objects = [];
@@ -1915,6 +1929,7 @@ class Model {
19151929
bool $reverse = false,
19161930
?array $sort_by = null,
19171931
int $sort_order = SORT_ASC,
1932+
int $sort_flags = SORT_REGULAR,
19181933
...$vl_query_params,
19191934
): ModelSet {
19201935
# Merge the $query_params and any provided variable-length arguments into a single variable
@@ -1929,7 +1944,11 @@ class Model {
19291944
$modelset = self::read_all(parent_id: $parent_id)->query(query_params: $query_params, excluded: $excluded);
19301945

19311946
# Sort the set if a sort field was provided
1932-
$modelset = $sort_by ? $modelset->sort(fields: $sort_by, order: $sort_order, retain_ids: true) : $modelset;
1947+
if ($sort_by) {
1948+
$modelset = $modelset->sort(
1949+
fields: $sort_by, order: $sort_order, flags: $sort_flags, retain_ids: true
1950+
);
1951+
}
19331952

19341953
# Apply pagination to limit the number of objects returned and/or reverse the order if requested
19351954
$modelset->model_objects = self::paginate($modelset->model_objects, $limit, $offset);

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,16 @@ class ModelSet {
8383
* Sorts the Model objects in this ModelSet by a specific field and order.
8484
* @param string|array $fields The field(s) to sort the Model objects by.
8585
* @param int $order The order to sort the Model objects by. This must be a PHP sort order constant.
86+
* @param int $flags The flags to use when sorting the Model objects. This must be a PHP sort flags constant.
8687
* @param bool $retain_ids Retain the original Model object IDs when sorting.
8788
* @return ModelSet A new ModelSet object with the Model objects sorted by the specified field and order.
8889
*/
89-
public function sort(string|array $fields, int $order = SORT_ASC, bool $retain_ids = false): ModelSet {
90+
public function sort(
91+
string|array $fields,
92+
int $order = SORT_ASC,
93+
int $flags = SORT_REGULAR,
94+
bool $retain_ids = false
95+
): ModelSet {
9096
# Variables
9197
$model_objects = $this->model_objects;
9298
$fields = is_array($fields) ? $fields : [$fields];
@@ -107,7 +113,7 @@ class ModelSet {
107113
}
108114

109115
# Sort using array_multisort
110-
array_multisort($sort_criteria, $order, $model_objects);
116+
array_multisort($sort_criteria, $order, $flags, $model_objects);
111117

112118
# Re-assign the model object IDs if they should not be retained
113119
if (!$retain_ids) {

0 commit comments

Comments
 (0)