Skip to content

Commit 745290b

Browse files
authored
Merge pull request #156 from fleetbase/dev-v1.6.16
v1.6.16 ~ improve custom fields added reports and alert models
2 parents f438614 + 7073d6a commit 745290b

9 files changed

Lines changed: 1783 additions & 2 deletions

‎composer.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "fleetbase/core-api",
3-
"version": "1.6.15",
3+
"version": "1.6.16",
44
"description": "Core Framework and Resources for Fleetbase API",
55
"keywords": [
66
"fleetbase",
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Support\Facades\DB;
5+
6+
return new class extends Migration {
7+
public function up(): void
8+
{
9+
// MySQL only: bail out early if not MySQL/MariaDB
10+
$driver = DB::getDriverName();
11+
if (!in_array($driver, ['mysql', 'mariadb'])) {
12+
// You can add a Postgres/SQLite path here if needed.
13+
return;
14+
}
15+
16+
// Turn off FK checks so we can alter referenced columns safely.
17+
DB::statement('SET FOREIGN_KEY_CHECKS = 0');
18+
19+
// Gather all candidate columns across all tables in the current schema
20+
// Match: column_name = 'uuid' OR column_name LIKE '%\_uuid'
21+
// Skip columns already char(36).
22+
$columns = DB::select("
23+
SELECT
24+
TABLE_NAME AS table_name,
25+
COLUMN_NAME AS column_name,
26+
COLUMN_TYPE AS column_type,
27+
IS_NULLABLE AS is_nullable,
28+
COLUMN_DEFAULT AS column_default,
29+
CHARACTER_SET_NAME AS character_set_name,
30+
COLLATION_NAME AS collation_name
31+
FROM information_schema.COLUMNS
32+
WHERE TABLE_SCHEMA = DATABASE()
33+
AND (
34+
COLUMN_NAME = 'uuid'
35+
OR COLUMN_NAME LIKE '%\\_uuid'
36+
)
37+
");
38+
39+
foreach ($columns as $col) {
40+
$table = $col->table_name;
41+
$column = $col->column_name;
42+
$ctype = strtolower($col->column_type);
43+
44+
// Skip if already char(36)
45+
if ($ctype === 'char(36)') {
46+
continue;
47+
}
48+
49+
// Build NULL / NOT NULL clause
50+
$nullClause = strtoupper($col->is_nullable) === 'YES' ? 'NULL' : 'NOT NULL';
51+
52+
// Preserve default if it is a scalar literal (ignore expressions)
53+
$defaultClause = '';
54+
if (!is_null($col->column_default)) {
55+
// MySQL returns defaults like 'some string' already unquoted in INFORMATION_SCHEMA
56+
// We need to quote strings; numeric stays numeric.
57+
$default = $col->column_default;
58+
if (is_numeric($default)) {
59+
$defaultClause = " DEFAULT {$default}";
60+
} elseif (in_array(strtolower($default), ['current_timestamp'])) {
61+
$defaultClause = " DEFAULT {$default}";
62+
} else {
63+
// Quote single quotes inside the default
64+
$default = str_replace("'", "''", $default);
65+
$defaultClause = " DEFAULT '{$default}'";
66+
}
67+
}
68+
69+
// Keep collation only if this column previously had a character set (it will on varchar/char)
70+
// (Optional) If you want to force a specific collation, set it here.
71+
$collationClause = '';
72+
if (!empty($col->collation_name)) {
73+
$collationClause = " COLLATE {$col->collation_name}";
74+
}
75+
76+
// Compose ALTER TABLE ... MODIFY
77+
$sql = sprintf(
78+
'ALTER TABLE `%s` MODIFY `%s` CHAR(36)%s %s%s',
79+
str_replace('`', '``', $table),
80+
str_replace('`', '``', $column),
81+
$collationClause,
82+
$nullClause,
83+
$defaultClause
84+
);
85+
86+
DB::statement($sql);
87+
}
88+
89+
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
90+
}
91+
92+
public function down(): void
93+
{
94+
// Irreversible safely (we don't know original lengths/types).
95+
// If you need a reversible migration, capture the original COLUMN_TYPEs
96+
// into a side table before altering, then restore from there.
97+
}
98+
};
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration {
8+
/**
9+
* Run the migrations.
10+
*/
11+
public function up(): void
12+
{
13+
Schema::create('reports', function (Blueprint $table) {
14+
$table->increments('id');
15+
$table->uuid('uuid')->index();
16+
$table->string('_key')->nullable()->index();
17+
$table->foreignUuid('company_uuid')->constrained('companies', 'uuid')->cascadeOnDelete();
18+
$table->foreignUuid('category_uuid')->nullable()->constrained('categories', 'uuid')->nullOnDelete();
19+
$table->foreignUuid('created_by_uuid')->nullable()->constrained('users', 'uuid')->nullOnDelete();
20+
$table->foreignUuid('updated_by_uuid')->nullable()->constrained('users', 'uuid')->nullOnDelete();
21+
22+
$table->string('type')->index(); // fuel, utilization, safety, location, custom, etc.
23+
$table->string('title')->nullable()->index();
24+
25+
// Report subject (asset, device, fleet, driver, vendor, etc.)
26+
$table->string('subject_type')->nullable();
27+
$table->uuid('subject_uuid')->nullable();
28+
$table->index(['subject_type', 'subject_uuid']);
29+
30+
// Temporal scope
31+
$table->timestamp('period_start')->nullable()->index();
32+
$table->timestamp('period_end')->nullable()->index();
33+
34+
// Payload
35+
$table->json('data')->nullable(); // structured results
36+
$table->text('body')->nullable(); // rendered / markdown text if needed
37+
38+
$table->softDeletes();
39+
$table->timestamps();
40+
41+
$table->index(['company_uuid', 'type']);
42+
});
43+
}
44+
45+
/**
46+
* Reverse the migrations.
47+
*/
48+
public function down(): void
49+
{
50+
Schema::dropIfExists('reports');
51+
}
52+
};
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration {
8+
/**
9+
* Run the migrations.
10+
*/
11+
public function up(): void
12+
{
13+
Schema::create('alerts', function (Blueprint $table) {
14+
$table->increments('id');
15+
$table->uuid('uuid')->index();
16+
$table->string('_key')->nullable()->index();
17+
$table->foreignUuid('company_uuid')->constrained('companies', 'uuid')->cascadeOnDelete();
18+
$table->foreignUuid('category_uuid')->nullable()->constrained('categories', 'uuid')->nullOnDelete();
19+
$table->foreignUuid('acknowledged_by_uuid')->nullable()->constrained('users', 'uuid')->nullOnDelete();
20+
$table->foreignUuid('resolved_by_uuid')->nullable()->constrained('users', 'uuid')->nullOnDelete();
21+
22+
$table->string('type')->index(); // geofence_breach, harsh_event, temp_out_of_range, offline, etc.
23+
$table->string('severity')->index(); // info, warning, critical
24+
$table->string('status')->default('open')->index(); // open, acknowledged, resolved
25+
26+
// Origin of alert / subject ie: device, sensor, asset, driver, place, etc.
27+
$table->string('subject_type')->nullable();
28+
$table->uuid('subject_uuid')->nullable();
29+
$table->index(['subject_type', 'subject_uuid']);
30+
31+
$table->text('message')->nullable();
32+
$table->json('rule')->nullable(); // serialized rule that triggered this
33+
$table->json('context')->nullable(); // snapshot at trigger time
34+
$table->json('meta')->nullable();
35+
36+
$table->timestamp('triggered_at')->nullable()->index();
37+
$table->timestamp('acknowledged_at')->nullable()->index();
38+
$table->timestamp('resolved_at')->nullable()->index();
39+
40+
$table->softDeletes();
41+
$table->timestamps();
42+
43+
$table->index(['company_uuid', 'type', 'severity']);
44+
});
45+
}
46+
47+
/**
48+
* Reverse the migrations.
49+
*/
50+
public function down(): void
51+
{
52+
Schema::dropIfExists('alerts');
53+
}
54+
};

‎src/Expansions/Response.php‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
use CompressJson\Core\Compressor;
66
use Fleetbase\Build\Expansion;
77
use Fleetbase\Support\Auth;
8+
use Fleetbase\Support\Http;
9+
use Illuminate\Contracts\Validation\Validator;
810
use Illuminate\Support\MessageBag;
11+
use Illuminate\Validation\ValidationException;
912

1013
class Response implements Expansion
1114
{
@@ -85,6 +88,40 @@ public function apiError()
8588
};
8689
}
8790

91+
/**
92+
* Handles validation errors correctly.
93+
*
94+
* @return Closure
95+
*/
96+
public function validationError()
97+
{
98+
return function (Validator $validator) {
99+
$errors = $validator->errors();
100+
101+
if (Http::isInternalRequest()) {
102+
$response = [
103+
'errors' => [$errors->first()],
104+
];
105+
106+
// if more than one error display the others
107+
if ($errors->count() > 1) {
108+
$response['errors'] = collect($errors->all())
109+
->values()
110+
->toArray();
111+
}
112+
} else {
113+
$response = ['error' => $errors->first()];
114+
if ($errors->count() > 1) {
115+
$response['errors'] = collect($errors->all())
116+
->values()
117+
->toArray();
118+
}
119+
}
120+
121+
throw new ValidationException($validator, response()->json($response, 422));
122+
};
123+
}
124+
88125
/**
89126
* Creates a compressed json response using json compression library.
90127
*

0 commit comments

Comments
 (0)