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
8 changes: 8 additions & 0 deletions app/Http/Controllers/API/v1/CategoryController.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ public function index(Request $request): JsonResponse
new OA\Property(property: 'description', description: 'The description of the category', type: 'string'),
new OA\Property(property: 'icon', description: 'The icon of the category (file or icon string)', type: 'string'),
new OA\Property(property: 'icon_type', description: 'The type of the icon (icon or emoji or image)', type: 'string'),
new OA\Property(property: 'updated_at', type: 'string', format: 'date-time'),
]
)
),
Expand Down Expand Up @@ -133,6 +134,7 @@ public function update(Request $request, int $id): JsonResponse
'description' => 'sometimes|string',
'icon' => 'nullable',
'icon_type' => 'required_with:icon|string|in:icon,image,emoji',
'updated_at' => ['nullable', new Iso8601DateTime],
]);

if ($validator->fails()) {
Expand All @@ -142,12 +144,18 @@ public function update(Request $request, int $id): JsonResponse
$data = $validator->validated();
$user = $request->user();

if (isset($data['updated_at'])) {
$data['updated_at'] = format_iso8601_to_sql($data['updated_at']);
}

$category = $user->categories()->find($id);

if (! $category) {
return $this->failure('Category not found', 404);
}
try {
$this->checkUpdatedAt($category, $data);

DB::transaction(function () use ($data, $request, &$category) {
$this->updateModel($category, $data, $request);
});
Expand Down
8 changes: 8 additions & 0 deletions app/Http/Controllers/API/v1/GroupController.php
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ public function show(int $id): JsonResponse
),
new OA\Property(property: 'icon', description: 'The icon of the group (file or icon string)', type: 'string'),
new OA\Property(property: 'icon_type', description: 'The type of the icon (icon or emoji or image)', type: 'string'),
new OA\Property(property: 'updated_at', type: 'string', format: 'date-time'),
]
)
),
Expand Down Expand Up @@ -252,6 +253,7 @@ public function update(Request $request, int $id): JsonResponse
'description' => 'sometimes|string|max:255',
'icon' => 'nullable',
'icon_type' => 'required_with:icon|string|in:icon,image,emoji',
'updated_at' => ['nullable', new Iso8601DateTime],
]);

if ($validator->fails()) {
Expand All @@ -262,10 +264,16 @@ public function update(Request $request, int $id): JsonResponse
$group = $user->groups()->find($id);
$data = $validator->validated();

if (isset($data['updated_at'])) {
$data['updated_at'] = format_iso8601_to_sql($data['updated_at']);
}

if (! $group) {
return $this->failure('Group not found', 404);
}
try {
$this->checkUpdatedAt($group, $data);

DB::transaction(function () use ($data, $request, &$group) {
$this->updateModel($group, $data, $request);
});
Expand Down
8 changes: 8 additions & 0 deletions app/Http/Controllers/API/v1/PartyController.php
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ public function show(int $id): JsonResponse
new OA\Property(property: 'description', type: 'string', example: 'income from John Doe'),
new OA\Property(property: 'icon', description: 'The icon of the party (file or icon string)', type: 'string'),
new OA\Property(property: 'icon_type', description: 'The type of the icon (icon or emoji or image)', type: 'string'),
new OA\Property(property: 'updated_at', type: 'string', format: 'date-time'),
]
)
),
Expand Down Expand Up @@ -256,15 +257,22 @@ public function update(Request $request, int $id): JsonResponse
'icon' => 'nullable',
'icon_type' => 'required_with:icon|string|in:icon,image,emoji',
'type' => 'sometimes|string|in:individual,organization,business,partnership,non_profit,government_agency,educational_institution,healthcare_provider',
'updated_at' => ['nullable', new Iso8601DateTime],
]);

$user = $request->user();
$party = $user->parties()->find($id);

if (isset($validatedData['updated_at'])) {
$validatedData['updated_at'] = format_iso8601_to_sql($validatedData['updated_at']);
}

if (! $party) {
return $this->failure('Party not found', 404);
}
try {
$this->checkUpdatedAt($party, $validatedData);

DB::transaction(function () use ($validatedData, $request, &$party) {
$this->updateModel($party, $validatedData, $request);
});
Expand Down
2 changes: 2 additions & 0 deletions app/Http/Controllers/API/v1/TransactionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,8 @@ public function update(Request $request, $id): JsonResponse
return $this->failure($e->getMessage(), $e->getStatusCode());
}
try {
$this->checkUpdatedAt($transaction, $validatedData);

$transaction = DB::transaction(function () use ($validatedData, $transaction, $recurring_transaction_data, $request) {

$transaction->update(array_filter($validatedData, fn ($value) => $value !== null));
Expand Down
8 changes: 8 additions & 0 deletions app/Http/Controllers/API/v1/WalletController.php
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ public function show(Request $request, int $id): JsonResponse
new OA\Property(property: 'description', type: 'string', example: 'Updated wallet description'),
new OA\Property(property: 'icon', description: 'The icon of the wallet (file or icon string)', type: 'string'),
new OA\Property(property: 'icon_type', description: 'The type of the icon (icon or emoji or image)', type: 'string'),
new OA\Property(property: 'updated_at', type: 'string', format: 'date-time'),
]
)
),
Expand Down Expand Up @@ -260,15 +261,22 @@ public function update(Request $request, int $id): JsonResponse
'balance' => 'sometimes|numeric|decimal:0,4',
'icon' => 'nullable',
'icon_type' => 'required_with:icon|string|in:icon,image,emoji',
'updated_at' => ['nullable', new Iso8601DateTime],
]);
$user = $request->user();

if (isset($validatedData['updated_at'])) {
$validatedData['updated_at'] = format_iso8601_to_sql($validatedData['updated_at']);
}

$wallet = $user->wallets()->find($id);

if (! $wallet) {
return $this->failure('Wallet not found', 404);
}
try {
$this->checkUpdatedAt($wallet, $validatedData);

DB::transaction(function () use ($validatedData, $request, &$wallet) {
$this->updateModel($wallet, $validatedData, $request);
});
Expand Down
13 changes: 13 additions & 0 deletions app/Http/Traits/ApiQueryable.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,17 @@ private function updateModel(Model $model, array $validatedData, Request $reques
}
FileService::updateIcon($model, $validatedData, $request);
}

protected function checkUpdatedAt(Model $model, array &$validatedData): void
{
if (isset($validatedData['updated_at'])) {
// Ensure the updated at is always greater than the created at
$created_at = $model->created_at;
$updated_at = Carbon::parse($validatedData['updated_at']);
if ($updated_at->lt($created_at)) {
// Remove the value so that laravel defaults to now();
unset($validatedData['updated_at']);
}
}
}
}
16 changes: 16 additions & 0 deletions public/docs/api.json
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,10 @@
"icon_type": {
"description": "The type of the icon (icon or emoji or image)",
"type": "string"
},
"updated_at": {
"type": "string",
"format": "date-time"
}
},
"type": "object"
Expand Down Expand Up @@ -501,6 +505,10 @@
"icon_type": {
"description": "The type of the icon (icon or emoji or image)",
"type": "string"
},
"updated_at": {
"type": "string",
"format": "date-time"
}
},
"type": "object"
Expand Down Expand Up @@ -945,6 +953,10 @@
"icon_type": {
"description": "The type of the icon (icon or emoji or image)",
"type": "string"
},
"updated_at": {
"type": "string",
"format": "date-time"
}
},
"type": "object"
Expand Down Expand Up @@ -1740,6 +1752,10 @@
"icon_type": {
"description": "The type of the icon (icon or emoji or image)",
"type": "string"
},
"updated_at": {
"type": "string",
"format": "date-time"
}
},
"type": "object"
Expand Down
24 changes: 24 additions & 0 deletions tests/Feature/SyncableModelsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use App\Models\Transaction;
use App\Models\Transfer;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

Expand Down Expand Up @@ -75,4 +76,27 @@ public function transfer_creates_a_sync_state_on_creation()
$this->assertEquals($transfer->syncState->syncable_type, Transfer::class);
$this->assertEquals($transfer->syncState->syncable_id, $transfer->id);
}

/** @test */
public function use_current_date_as_updated_at_if_the_updated_at_is_less_than_created_at_on_update()
{
$user = User::factory()->create();

/** @var Transaction */
$transaction = Transaction::factory()->create([
'user_id' => $user->id,
]);

$response = $this->actingAs($user)->putJson('/api/v1/transactions/'.$transaction['id'], [
'amount' => 200,
'updated_at' => '2020-05-01T15:17:54.120Z',
]);

$response->assertStatus(200);
$transaction = Transaction::find($transaction['id']);
$parsed_date = Carbon::parse('2020-05-01T15:17:54.120Z');

$this->assertTrue($parsed_date->lt($transaction->updated_at));

}
}