Skip to content
Open
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
16 changes: 9 additions & 7 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
# v1.6.61Faster IAM user authorization loading
# v1.6.62Custom fields get a public id

## Improvements

- Reduce repeated database queries when listing IAM users by loading roles, policies, and permissions in batches and reading each user's primary role once.
- Match authorization to each user's company membership, including users belonging to multiple companies and system administrators viewing users across companies. User response fields remain unchanged.
- Give every custom field a public id, so an API that hands one out names it the way the rest of the platform names a resource rather than exposing an internal uuid. `CustomField` takes `HasPublicId` with the `custom_field` prefix, and `public_id` becomes fillable.
- Mint an id on the one path that would otherwise miss it: `HasCustomFields::setCustomField()` saves a field it creates on the fly with `saveQuietly()`, which skips the hook that assigns the id.

## Reliability

- Add database-backed coverage for company isolation, missing and deleted memberships, recovery after a membership was initially absent, and matching responses between lazy and eager loading.
- Enable PHP CI and Postman checks for `release/v*` branches and support release tagging from both `release/v*` and `dev-v*` branches.
- Backfill existing rows in the migration, and add the column as nullable and indexed rather than unique-and-required, so it is safe on an already-populated `custom_fields` table.
- Cover id generation for `CustomField`, and add the column to the in-memory schemas whose saves now probe it for uniqueness.

No database migration or configuration change is required.
This is platform-wide: every custom field gains a public id, not only those used by inspections. Nothing reads the new column yet — `withCustomFields()`'s public projection emits field names and is unchanged — so the change is additive for existing consumers.

Changes: [#251](https://github.com/fleetbase/core-api/pull/251), [#250](https://github.com/fleetbase/core-api/pull/250), and release-branch CI updates in [#252](https://github.com/fleetbase/core-api/pull/252).
A database migration is required. No configuration change is needed.

Changes: [#254](https://github.com/fleetbase/core-api/pull/254).
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fleetbase/core-api",
"version": "1.6.61",
"version": "1.6.62",
"description": "Core Framework and Resources for Fleetbase API",
"keywords": [
"fleetbase",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

use Fleetbase\Models\CustomField;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
/**
* Run the migrations.
*
* Custom fields were addressed by uuid alone, which left every API that
* hands one out exposing an internal identifier where the rest of the
* platform shows a public id. Existing rows are backfilled so nothing has
* to cope with a field that has no id.
*/
public function up(): void
{
if (!Schema::hasTable('custom_fields') || Schema::hasColumn('custom_fields', 'public_id')) {
return;
}

Schema::table('custom_fields', function (Blueprint $table) {
$table->string('public_id', 191)->nullable()->after('uuid')->index();
});

CustomField::withTrashed()->whereNull('public_id')->get()->each(function (CustomField $field) {
$field->update(['public_id' => CustomField::generatePublicId('custom_field')]);
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
if (!Schema::hasTable('custom_fields') || !Schema::hasColumn('custom_fields', 'public_id')) {
return;
}

Schema::table('custom_fields', function (Blueprint $table) {
$table->dropIndex(['public_id']);
$table->dropColumn(['public_id']);
});
}
};
11 changes: 10 additions & 1 deletion src/Models/CustomField.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
use Fleetbase\Casts\Json;
use Fleetbase\Casts\PolymorphicType;
use Fleetbase\Traits\HasApiModelBehavior;
use Fleetbase\Traits\HasPublicId;
use Fleetbase\Traits\HasUuid;

class CustomField extends Model
{
use HasUuid;
use HasPublicId;
use HasApiModelBehavior;

/**
Expand All @@ -19,12 +21,19 @@ class CustomField extends Model
*/
protected $table = 'custom_fields';

/**
* The type of public Id to generate.
*
* @var string
*/
protected $publicIdType = 'custom_field';

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['company_uuid', 'category_uuid', 'subject_uuid', 'subject_type', 'name', 'label', 'type', 'for', 'component', 'options', 'required', 'editable', 'default_value', 'validation_rules', 'meta', 'description', 'help_text', 'order'];
protected $fillable = ['public_id', 'company_uuid', 'category_uuid', 'subject_uuid', 'subject_type', 'name', 'label', 'type', 'for', 'component', 'options', 'required', 'editable', 'default_value', 'validation_rules', 'meta', 'description', 'help_text', 'order'];

/**
* The attributes that are guarded.
Expand Down
7 changes: 6 additions & 1 deletion src/Traits/HasCustomFields.php
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,12 @@ public function setCustomFieldValue(string|CustomField $fieldOrKey, mixed $value
'subject_uuid' => $this->getAttribute('uuid'),
'company_uuid' => $this->getAttribute('company_uuid') ?? session('company'),
]);
$field->forceFill(['uuid' => CustomField::generateUuid()]);
// A quiet save skips the `creating` hook that mints a public id,
// so a field created on the fly would be the only one without one.
$field->forceFill([
'uuid' => CustomField::generateUuid(),
'public_id' => CustomField::generatePublicId('custom_field'),
]);
method_exists($field, 'saveQuietly') ? $field->saveQuietly() : $field->save();
// bust definition cache for subsequent lookups
$this->customFieldCache = [];
Expand Down
39 changes: 39 additions & 0 deletions tests/Unit/Models/RecordModelsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,30 @@ public function clear(): bool
$table->timestamps();
$table->softDeletes();
});
$schema->create('custom_fields', function ($table) {
$table->string('uuid')->primary();
$table->string('public_id')->nullable()->unique();
$table->string('company_uuid')->nullable();
$table->string('category_uuid')->nullable();
$table->string('subject_uuid')->nullable();
$table->string('subject_type')->nullable();
$table->string('name')->nullable();
$table->string('label')->nullable();
$table->string('type')->nullable();
$table->string('for')->nullable();
$table->string('component')->nullable();
$table->text('options')->nullable();
$table->boolean('required')->default(false);
$table->boolean('editable')->default(true);
$table->text('default_value')->nullable();
$table->text('validation_rules')->nullable();
$table->text('meta')->nullable();
$table->text('description')->nullable();
$table->text('help_text')->nullable();
$table->integer('order')->default(0);
$table->timestamps();
$table->softDeletes();
});
$schema->create('user_devices', function ($table) {
$table->string('uuid')->primary();
$table->string('public_id')->nullable()->unique();
Expand Down Expand Up @@ -249,6 +273,21 @@ public function clear(): bool
]);
});

it('generates custom field public ids', function () {
record_models_database();

$field = CustomField::query()->create([
'company_uuid' => 'company-1',
'name' => 'brakes',
'label' => 'Brakes',
'type' => 'pass-fail',
]);

expect($field->public_id)->toStartWith('custom_field_')
->and($field->public_id)->toHaveLength(strlen('custom_field_') + 10)
->and($field->uuid)->not->toBeNull();
});

it('casts custom field configuration values and keeps relationship keys stable', function () {
record_models_database();

Expand Down
1 change: 1 addition & 0 deletions tests/Unit/Traits/HasCustomFieldsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ function has_custom_fields_database(string $routeUri = 'int/v1/subjects'): HasCu
});
$schema->create('custom_fields', function ($table) {
$table->string('uuid')->primary();
$table->string('public_id')->nullable();
$table->string('company_uuid')->nullable();
$table->string('category_uuid')->nullable();
$table->string('subject_uuid')->nullable();
Expand Down
Loading