diff --git a/CHANGELOG.md b/CHANGELOG.md index b86eebde..5eb7b159 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,33 @@ The complete changelog for the Costs to Expect REST API, our changelog follows the format defined at https://keepachangelog.com/en/1.0.0/ +## [v2.23.0] - 2022-04-12 +## Added +- We have updated the `/auth/user` route, the route will now show any active created tokens. +- We have added `device_name` as an optional field on sign-in, if set, the generated token will be prefixed with the device name. +- We have added an `include-permitted-users` parameter when requesting a resource type, you will be able to see all the permitted users without having to go down the tree. +- If an API response includes a related object, the first field should be the URI to the relevant collection or resource, we have started updating responses. +- We have added a `auth/user/tokens` route to show the active tokens, you can view an individual token as well as delete a token. +- We have added a notification for failed jobs, if the `ClearCache` job fails we will get an email, luckily, it doesn't ever fail :) +- We have added the ability to assign permitted users, if you have access to a resource type you can assign a known user to the resource type. +- We have added a view permitted user endpoint. +- We have added the ability to delete a permitted user, you can delete any permitted user with access to the resource type, including yourself. +- We have added initial tests for the permitted user routes. + +## Changed +- We have updated sign-in to clear tokens that have not been used for a year. +- We have added additional validation to `/auth/login` to match the create password routes. +- We have removed additional references to our `item-type` entity class, keep code in the individual item type namespaces. +- We have converted out `Mailables` to `Notifications` and they get send via the queue. +- We have updated the `partial-transfers` route to use methods per item types, this way we can correctly return a 405 when an item doesn't support partial transfers. +- We have updated the `transfers` route to use methods per item types, this was we can correctly return a 405 when an item doesn't support transfers. +- We have localised all response messages in the Authentication controller to match the rest of the API. + +## Fixed +- We have fixed our Authentication tests, we no longer overwrite the initial user, additionally, we have updated three tests to return success on a 422, not a 401. +- We have corrected a couple of parameter conversions, two parameters not correctly being converted to Booleans. +- Unable to delete an `allocated-expense`, need to clear the partial transfers table. + ## [v2.22.0] - 2022-01-26 After being away from the code for a while I've made some changes. I've reduced the complexity around different items types because things had started to get a little complex and I know what is coming next so want to clear out as much unnecessary code as possible. This is just a first pass, I'm sure there will be more but I have many other planned tickets to get on with. diff --git a/README.md b/README.md index baa92b06..e574c40a 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Docker services, `api` and `mysql`, we will need to exec into the `api` service set up our app. Firstly, we need to check we are trying to access the right location, -execute `docker compose exec api ls`. You should see a list of the files and +execute `docker compose exec costs.api.app ls`. You should see a list of the files and directories at the project root. Next, we need to configure the API by setting out local .ENV file our .env, @@ -58,9 +58,9 @@ installing all dependencies and running our migrations. * Copy the `.env.example` file and name the copy `.env`. Set all the empty values, all drivers have been set to our defaults, sessions, cache, and the queue default to the database driver. -* `docker compose exec api php artisan key:generate` -* `docker compose exec api php artisan migrate` -* `docker compose exec api php artisan queue:work` +* `docker compose exec costs.api.app php artisan key:generate` +* `docker compose exec costs.api.app php artisan migrate` +* `docker compose exec costs.api.app php artisan queue:work` * Run an OPTIONS request on `http://[your.domail.local:8080]/v2/resource_types`, you will see an OPTIONS response, alternatively a GET request to `http://[your.domail.local:8080]/v1` will show all the defined routes. * You can create a user by POSTing to `http://[your.domail.local:8080]/v2/auth/register`. @@ -73,7 +73,7 @@ you will also need to set `MAIL_FROM_ADDRESS` and `MAIL_TO_ADDRESS`. You may nee * Collections will return an array and a 200. * Items will return a single object and a 200. -* Successful POST requests will return a single object and a 201. +* Successful POST requests will return a single object and a 201, there are minor exceptions where we may return a 204. * Successful PATCH requests will return 204. * Successful DELETE requests will return a 204. * Non 2xx results will return an object with a message field and optionally a fields array. When we @@ -133,6 +133,11 @@ additionally, the same is true if you are assigned as a permitted user to a reso | POST | v2/auth/update-profile | | GET/HEAD | v2/auth/user | | OPTIONS | v2/auth/user | +| GET/HEAD | v2/auth/user/tokens | +| OPTIONS | v2/auth/user/tokens | +| DELETE | v2/auth/user/tokens/{token_id} | +| GET/HEAD | v2/auth/user/tokens/{token_id} | +| OPTIONS | v2/auth/user/tokens/{token_id} | | GET/HEAD | v2/changelog | | OPTIONS | v2/changelog | | GET/HEAD | v2/currencies | @@ -175,8 +180,12 @@ additionally, the same is true if you are assigned as a permitted user to a reso | GET/HEAD | v2/resource-types/{resource_type_id}/partial-transfers/{item_partial_transfer_id} | | OPTIONS | v2/resource-types/{resource_type_id}/partial-transfers/{item_partial_transfer_id} | | DELETE | v2/resource-types/{resource_type_id}/partial-transfers/{item_partial_transfer_id} | +| POST | v2/resource-types/{resource_type_id}/permitted-users | | GET/HEAD | v2/resource-types/{resource_type_id}/permitted-users | | OPTIONS | v2/resource-types/{resource_type_id}/permitted-users | +| GET/HEAD | v2/resource-types/{resource_type_id}/permitted-users/{permitted_user_id} | +| OPTIONS | v2/resource-types/{resource_type_id}/permitted-users/{permitted_user_id} | +| DELETE | v2/resource-types/{resource_type_id}/permitted-users/{permitted_user_id} | | GET/HEAD | v2/resource-types/{resource_type_id}/resources | | OPTIONS | v2/resource-types/{resource_type_id}/resources | | POST | v2/resource-types/{resource_type_id}/resources | @@ -222,7 +231,7 @@ additionally, the same is true if you are assigned as a permitted user to a reso Eventually, there will be a summary route for every API collection GET endpoint. Until that point, the summary routes that exists are detailed below. Some allow GET -parameters to breakdown the data, one example being +parameters to break down the data, one example being `v2/summary/resource-types/{resource_type_id}/items`. Review the OPTIONS request for each summary route to see the supported parameters, these should diff --git a/app/AllowedValue/ResourceItem.php b/app/AllowedValue/ResourceItem.php deleted file mode 100644 index 95fecb70..00000000 --- a/app/AllowedValue/ResourceItem.php +++ /dev/null @@ -1,240 +0,0 @@ -hash = new Hash(); - - $this->entity = $entity; - - $this->model = new EntityLimits(); - } - - /** - * @return array - */ - public function allowedValues( - int $resource_type_id, - int $resource_id, - array $viewable_resource_types, - array $available_parameters, - array $defined_parameters, - bool $include_currencies = false - ): array - { - $years = []; - if (array_key_exists('year', $available_parameters)) { - $years = $this->allowedValuesForYear( - $resource_type_id, - $resource_id - ); - } - - $months = []; - if (array_key_exists('month', $available_parameters)) { - $months = $this->allowedValuesForMonth(); - } - - $categories = []; - if (array_key_exists('category', $available_parameters)) { - $categories = $this->allowedValuesForCategory( - $resource_type_id, - $viewable_resource_types - ); - } - - $subcategories = []; - - if ( - array_key_exists('category', $available_parameters) === true && - array_key_exists('subcategory', $available_parameters) === true && - array_key_exists('category', $defined_parameters) === true && - $defined_parameters['category'] !== null - ) { - $subcategories = $this->allowedValuesForSubcategory( - $resource_type_id, - $defined_parameters['category'] - ); - } - - $currencies = []; - - if ($include_currencies === true) { - $currencies = $this->allowedValuesForCurrency(); - } - - return array_merge( - $years, - $months, - $categories, - $subcategories, - $currencies - ); - } - - protected function allowedValuesForCategory( - int $resource_type_id, - array $viewable_resource_types - ): array - { - $parameters = ['category' => ['allowed_values' => []]]; - - $categories = (new Category())->paginatedCollection( - $resource_type_id, - $viewable_resource_types, - 0, - 100 - ); - - foreach ($categories as $category) { - $category_id = $this->hash->encode('category', $category['category_id']); - - $parameters['category']['allowed_values'][$category_id] = [ - 'value' => $category_id, - 'name' => $category['category_name'], - 'description' => trans('item-type-' . $this->entity->type() . - '/allowed-values.description-prefix-category') . - $category['category_name'] . - trans('item-type-' . $this->entity->type() . - '/allowed-values.description-suffix-category') - ]; - } - - return $parameters; - } - - /** - * @return array - */ - protected function allowedValuesForMonth(): array - { - $parameters = ['month' => ['allowed_values' => []]]; - - for ($i=1; $i < 13; $i++) { - $parameters['month']['allowed_values'][$i] = [ - 'value' => $i, - 'name' => date("F", mktime(0, 0, 0, $i, 10)), - 'description' => trans('item-type-' . $this->entity->type() . - '/allowed-values.description-prefix-month') . - date("F", mktime(0, 0, 0, $i, 1)) - ]; - } - - return $parameters; - } - - protected function allowedValuesForCurrency(): array - { - $parameters = ['currency_id' => ['allowed_values' => []]]; - - $currencies = (new \App\Models\Currency())->minimisedCollection(); - - foreach ($currencies as $currency) { - $id = $this->hash->encode('currency', $currency['currency_id']); - - if ($id === false) { - \App\Response\Responses::unableToDecode(); - } - - $parameters['currency_id']['allowed_values'][$id] = [ - 'value' => $id, - 'name' => $currency['currency_name'], - 'description' => $currency['currency_name'] - ]; - } - - return $parameters; - } - - /** - * @return array - */ - protected function allowedValuesForSubcategory( - int $resource_type_id, - int $category_id - ): array - { - $parameters = ['subcategory' => ['allowed_values' => []]]; - - $subcategories = (new Subcategory())->paginatedCollection( - $resource_type_id, - $category_id - ); - - array_map( - function($subcategory) use (&$parameters) { - $subcategory_id = $this->hash->encode('subcategory', $subcategory['subcategory_id']); - $parameters['subcategory']['allowed_values'][$subcategory_id] = [ - 'value' => $subcategory_id, - 'name' => $subcategory['subcategory_name'], - 'description' => trans('item-type-' . $this->entity->type() . '/allowed-values.description-prefix-subcategory') . - $subcategory['subcategory_name'] . trans('item-type-' . $this->entity->type() . '/allowed-values.description-suffix-subcategory') - ]; - }, - $subcategories - ); - - return $parameters; - } - - protected function allowedValuesForYear( - int $resource_type_id, - int $resource_id - ): array - { - $parameters = ['year' => ['allowed_values' => []]]; - - $min_year = null; - $max_year = null; - - $item_type = \App\ItemType\Entity::itemType($resource_type_id); - switch($item_type) { - case 'allocated-expense': - $min_year = $this->model->minimumYearByResourceTypeAndResource( - $resource_type_id, - $resource_id, - 'item_type_allocated_expense', - 'effective_date' - ); - $max_year = $this->model->maximumYearByResourceTypeAndResource( - $resource_type_id, - $resource_id, - 'item_type_allocated_expense', - 'effective_date' - ); - break; - default: - // Do nothing - break; - } - - if ($min_year !== null && $max_year !== null) { - for ($i = $min_year; $i <= $max_year; $i++) { - $parameters['year']['allowed_values'][$i] = [ - 'value' => $i, - 'name' => $i, - 'description' => trans('item-type-' . $this->entity->type() . - '/allowed-values.description-prefix-year') . $i - ]; - } - } - - return $parameters; - } -} diff --git a/app/AllowedValue/ResourceTypeItem.php b/app/AllowedValue/ResourceTypeItem.php deleted file mode 100644 index 75b3d261..00000000 --- a/app/AllowedValue/ResourceTypeItem.php +++ /dev/null @@ -1,191 +0,0 @@ -hash = new Hash(); - - $this->entity = $entity; - - $this->model = new EntityLimits(); - } - - protected function allowedValuesForYear(int $resource_type_id): array - { - $parameters = ['year' => ['allowed_values' => []]]; - - $min_year = null; - $max_year = null; - - $item_type = \App\ItemType\Entity::itemType($resource_type_id); - switch ($item_type) { - case 'allocated-expense': - $min_year = $this->model->minimumYearByResourceType( - $resource_type_id, - 'item_type_allocated_expense', - 'effective_date' - ); - $max_year = $this->model->maximumYearByResourceType( - $resource_type_id, - 'item_type_allocated_expense', - 'effective_date' - ); - break; - default: - // No nothing - break; - } - - if ($min_year !== null && $max_year !== null) { - for ($i = $min_year; $i <= $max_year; $i++) { - $parameters['year']['allowed_values'][$i] = [ - 'value' => $i, - 'name' => $i, - 'description' => trans('resource-type-item-type-' . $this->entity->type() . - '/allowed-values.description-prefix-year') . $i - ]; - } - } - - return $parameters; - } - - protected function allowedValuesForMonth(): array - { - $parameters = ['month' => ['allowed_values' => []]]; - - for ($i=1; $i < 13; $i++) { - $parameters['month']['allowed_values'][$i] = [ - 'value' => $i, - 'name' => date("F", mktime(0, 0, 0, $i, 10)), - 'description' => trans('resource-type-item-type-' . $this->entity->type() . - '/allowed-values.description-prefix-month') . - date("F", mktime(0, 0, 0, $i, 1)) - ]; - } - - return $parameters; - } - - protected function allowedValuesForCategory( - int $resource_type_id, - array $viewable_resource_types - ): array - { - $parameters = ['category' => ['allowed_values' => []]]; - - $categories = (new Category())->paginatedCollection( - $resource_type_id, - $viewable_resource_types, - 0, - 100 - ); - - foreach ($categories as $category) { - $category_id = $this->hash->encode('category', $category['category_id']); - - $parameters['category']['allowed_values'][$category_id] = [ - 'value' => $category_id, - 'name' => $category['category_name'], - 'description' => trans('resource-type-item-type-' . $this->entity->type() . - '/allowed-values.description-prefix-category') . - $category['category_name'] . - trans('resource-type-item-type-' . $this->entity->type() . - '/allowed-values.description-suffix-category') - ]; - } - - return $parameters; - } - - protected function allowedValuesForSubcategory( - int $resource_type_id, - int $category_id - ): array - { - $parameters = ['subcategory' => ['allowed_values' => []]]; - - $subcategories = (new Subcategory())->paginatedCollection( - $resource_type_id, - $category_id - ); - - array_map( - function($subcategory) { - $subcategory_id = $this->hash->encode('subcategory', $subcategory['subcategory_id']); - - $parameters['subcategory']['allowed_values'][$subcategory_id] = [ - 'value' => $subcategory_id, - 'name' => $subcategory['subcategory_name'], - 'description' => trans('resource-type-item-type-' . $this->entity->type() . '/allowed-values.description-prefix-subcategory') . - $subcategory['subcategory_name'] . trans('resource-type-item-type-' . $this->entity->type() . '/allowed-values.description-suffix-subcategory') - ]; - }, - $subcategories - ); - - return $parameters; - } - - public function allowedValues( - int $resource_type_id, - array $viewable_resource_types, - array $available_parameters, - array $defined_parameters - ): array - { - $years = []; - if (array_key_exists('year', $available_parameters) === true) { - $years = $this->allowedValuesForYear($resource_type_id); - } - - $months = []; - if (array_key_exists('month', $available_parameters) === true) { - $months = $this->allowedValuesForMonth(); - } - - $categories = []; - if (array_key_exists('category', $available_parameters) === true) { - $categories = $this->allowedValuesForCategory( - $resource_type_id, - $viewable_resource_types - ); - } - - $subcategories = []; - if ( - array_key_exists('category', $available_parameters) === true && - array_key_exists('subcategory', $available_parameters) === true && - array_key_exists('category', $defined_parameters) === true && - $defined_parameters['category'] !== null - ) { - $subcategories = $this->allowedValuesForSubcategory( - $resource_type_id, - $defined_parameters['category'] - ); - } - - return array_merge( - $years, - $months, - $categories, - $subcategories - ); - } -} diff --git a/app/Cache/KeyGroup.php b/app/Cache/KeyGroup.php index f080a09c..17bc94b8 100644 --- a/app/Cache/KeyGroup.php +++ b/app/Cache/KeyGroup.php @@ -9,6 +9,9 @@ class KeyGroup private Key $key; + public const PERMITTED_USER_CREATE = 'permitted_user_create'; + public const PERMITTED_USER_DELETE = 'permitted_user_delete'; + public const RESOURCE_CREATE = 'resource_create'; public const RESOURCE_DELETE = 'resource_delete'; public const RESOURCE_UPDATE = 'resource_update'; @@ -110,12 +113,20 @@ public function keys(string $group_key): array case self::RESOURCE_TYPE_CREATE: case self::RESOURCE_TYPE_DELETE: + case self::PERMITTED_USER_DELETE: return [ $this->key->resourceTypes(), $this->key->permittedResourceTypes(), $this->key->viewableResourceTypes() ]; + case self::PERMITTED_USER_CREATE: + return [ + $this->key->permittedUsers( + (int) $this->parameters['resource_type_id'] + ) + ]; + default: // We need to write to a failed job of errors table so we can see the // errors in the database diff --git a/app/Http/Controllers/Authentication.php b/app/Http/Controllers/Authentication.php index 613673b3..9fe2dc07 100644 --- a/app/Http/Controllers/Authentication.php +++ b/app/Http/Controllers/Authentication.php @@ -11,6 +11,7 @@ use App\Option\Auth\Register; use App\Option\Auth\UpdatePassword; use App\Option\Auth\UpdateProfile; +use App\Response\Responses; use App\User; use Exception; use Illuminate\Http; @@ -56,7 +57,7 @@ public function createPassword(Request $request): Http\JsonResponse if ($tokens === null || Hash::check($token, $tokens->token) === false) { return response()->json( [ - 'message'=>'Sorry, the email and or token you supplied are invalid' + 'message'=> trans('auth.email-or-token-invalid') ], 401 ); @@ -79,7 +80,7 @@ public function createPassword(Request $request): Http\JsonResponse if ($validator->fails()) { return response()->json( [ - 'message' => 'Validation error, please review the below', + 'message' => trans('responses.validation'), 'fields' => $validator->errors() ], 422 @@ -102,10 +103,10 @@ public function createPassword(Request $request): Http\JsonResponse return response()->json([], 204); } - return response()->json(['message' => 'Unable to fetch your account to create password, please try again later'], 404); + return response()->json(['message' => trans('auth.unable-to-find-account')], 404); } catch (Exception $e) { Log::error($e->getMessage()); - return response()->json(['message' => 'Unable to create password, please try again later'], 500); + return response()->json(['message' => trans('auth.unable-to-create-password')], 500); } } @@ -128,7 +129,7 @@ public function createNewPassword(Request $request): Http\JsonResponse if ($tokens === null || Hash::check($token, $tokens->token) === false) { return response()->json( [ - 'message'=>'Sorry, the email and token you supplied are invalid' + 'message'=> trans('auth.email-or-token-invalid') ], 404 ); @@ -151,7 +152,7 @@ public function createNewPassword(Request $request): Http\JsonResponse if ($validator->fails()) { return response()->json( [ - 'message' => 'Validation error, please review the below', + 'message' => trans('responses.validation'), 'fields' => $validator->errors() ], 422 @@ -174,10 +175,10 @@ public function createNewPassword(Request $request): Http\JsonResponse return response()->json([], 204); } - return response()->json(['message' => 'Unable to fetch your account to create password, please try again later'], 500); + return response()->json(['message' => trans('auth.unable-to-find-account')], 500); } catch (Exception $e) { Log::error($e->getMessage()); - return response()->json(['message' => 'Unable to create password, please try again later'], 500); + return response()->json(['message' => trans('auth.unable-to-create-password')], 500); } } @@ -200,7 +201,7 @@ public function forgotPassword(Request $request): Http\JsonResponse if ($validator->fails()) { return response()->json( [ - 'message' => 'Validation error, please review the errors below', + 'message' => trans('responses.validation'), 'fields' => $validator->errors() ], 422 @@ -232,12 +233,12 @@ public function forgotPassword(Request $request): Http\JsonResponse } } catch (Exception $e) { Log::error($e->getMessage()); - return response()->json(['error' => 'Unable to process your forgot password request, please try again later'], 500); + return response()->json(['error' => trans('auth.unable-process-forgot-password')], 500); } return response()->json( [ - 'message' => 'Request received, please check your email for instructions on how to create your new password', + 'message' => trans('auth.success-forgot-password-request'), 'uris' => [ 'create-new-password' => [ 'uri' => Config::get('api.app.version.prefix') . '/auth/create-new-password?token=' . $create_token . '&email=' . $email, @@ -252,7 +253,7 @@ public function forgotPassword(Request $request): Http\JsonResponse ); } - return response()->json(['message' => 'Unable to fetch your user account, please try again later'], 404); + return response()->json(['message' => trans('auth.unable-to-find-account')], 404); } public function optionsForgotPassword(): Http\JsonResponse @@ -264,6 +265,34 @@ public function optionsForgotPassword(): Http\JsonResponse public function login(Request $request): Http\JsonResponse { + $validator = Validator::make( + $request->all(), + [ + 'email' => [ + 'required', + 'string' + ], + 'password' => [ + 'required', + 'min:12' + ], + 'device_name' => [ + 'sometimes', + 'string' + ] + ] + ); + + if ($validator->fails()) { + return response()->json( + [ + 'message' => trans('responses.validation'), + 'fields' => $validator->errors() + ], + 422 + ); + } + if ( Auth::attempt( [ @@ -275,7 +304,15 @@ public function login(Request $request): Http\JsonResponse $user = Auth::user(); if ($user !== null) { - $token = $request->user()->createToken('costs-to-expect-api'); + + $request->user()->revokeOldTokens(); + + $token_name = 'costs-to-expect-api'; + if ($request->input('device_name') !== null) { + $token_name = str::slug($request->input('device_name')) . ':' . $token_name; + } + + $token = $request->user()->createToken($token_name); return response()->json( [ 'id' => $this->hash->user()->encode($user->id), @@ -286,10 +323,10 @@ public function login(Request $request): Http\JsonResponse ); } - return response()->json(['message' => 'Unauthorised, credentials invalid'], 401); + return response()->json(['message' => trans('auth.failed')], 401); } - return response()->json(['message' => 'Unauthorised, credentials invalid'], 401); + return response()->json(['message' => trans('auth.failed')], 401); } public function optionsLogin(): Http\JsonResponse @@ -303,7 +340,7 @@ public function logout(): Http\JsonResponse { Auth::logout(); - return response()->json(['message' => 'Account signed out'], 200); + return response()->json(['message' => trans('auth.signed-out')], 200); } public function register(Request $request): Http\JsonResponse @@ -319,7 +356,7 @@ public function register(Request $request): Http\JsonResponse if ($validator->fails()) { return response()->json( [ - 'message' => 'Validation error, please review the below', + 'message' => trans('responses.validation'), 'fields' => $validator->errors() ], 422 @@ -353,12 +390,12 @@ public function register(Request $request): Http\JsonResponse } catch (Exception $e) { Log::error($e->getMessage()); - return response()->json(['error' => 'Unable to create the account, please try again later'], 500); + return response()->json(['error' => trans('auth.unable-to-create-account')], 500); } return response()->json( [ - 'message' => 'Account created, please check you email for information on how to create your password', + 'message' => trans('auth.success-account-created'), 'uris' => [ 'create-password' => [ 'uri' => Config::get('api.app.version.prefix') . '/auth/create-password?token=' . $create_token . '&email=' . $email, @@ -399,7 +436,7 @@ public function updatePassword(Request $request): Http\JsonResponse if ($validator->fails()) { return response()->json( [ - 'message' => 'Validation error, please review the messages', + 'message' => trans('responses.validation'), 'fields' => $validator->errors() ], 422 @@ -415,7 +452,7 @@ public function updatePassword(Request $request): Http\JsonResponse return response()->json([], 204); } - return response()->json(['message' => 'Unauthorised, credentials invalid'], 401); + return response()->json(['message' => trans('auth.failed')], 401); } public function optionsUpdateProfile(): Http\JsonResponse @@ -454,7 +491,7 @@ public function updateProfile(Request $request): Http\JsonResponse if ($validator->fails()) { return response()->json( [ - 'message' => 'Validation error, please review the messages', + 'message' => trans('responses.validation'), 'fields' => $validator->errors() ], 422 @@ -473,7 +510,7 @@ public function updateProfile(Request $request): Http\JsonResponse } if (count($fields) === 0) { - return response()->json(['message' => 'You have provided any fields to change'], 400); + return response()->json(['message' => trans('responses.patch-empty')], 400); } try { @@ -485,13 +522,13 @@ public function updateProfile(Request $request): Http\JsonResponse } catch (Exception $e) { Log::error($e->getMessage()); - return response()->json(['message' => 'Unable to update your profile, please try again'], 401); + return response()->json(['message' => trans('auth.unable-to-update-profile')], 401); } return response()->json([], 204); } - return response()->json(['message' => 'Unauthorised, credentials invalid'], 401); + return response()->json(['message' => trans('auth.failed')], 401); } public function user(): Http\JsonResponse @@ -499,15 +536,111 @@ public function user(): Http\JsonResponse $user = auth()->guard('api')->user(); if ($user !== null) { + + $tokens = []; + foreach ($user->tokens as $token) { + $tokens[] = [ + 'id' => $token->id, + 'name' => $token->name, + 'token' => $token->token, + 'created' => $token->created_at, + 'last_used_at' => $token->last_used_at + ]; + } + $user = [ 'id' => $this->hash->user()->encode($user->id), 'name' => $user->name, - 'email' => $user->email + 'email' => $user->email, + 'tokens' => [ + 'uri' => route('auth.user.token.list', [], false), + 'count' => count($tokens), + 'collection' => $tokens + ] ]; + return response()->json($user); } - return response()->json(['message' => 'Unauthorised, credentials invalid'], 401); + return response()->json(['message' => trans('auth.failed')], 401); + } + + public function tokens(): Http\JsonResponse + { + $user = auth()->guard('api')->user(); + + if ($user !== null) { + + $tokens = []; + foreach ($user->tokens as $token) { + $tokens[] = [ + 'id' => $token->id, + 'name' => $token->name, + 'token' => $token->token, + 'created' => $token->created_at, + 'last_used_at' => $token->last_used_at + ]; + } + + return response()->json($tokens); + } + + return response()->json(['message' => trans('auth.failed')], 401); + } + + public function token($token_id): Http\JsonResponse + { + $user = auth()->guard('api')->user(); + + if ($user !== null) { + + $tokens = []; + foreach ($user->tokens as $token) { + $tokens[$token->id] = [ + 'id' => $token->id, + 'name' => $token->name, + 'token' => $token->token, + 'created' => $token->created_at, + 'last_used_at' => $token->last_used_at + ]; + } + + if (array_key_exists($token_id, $tokens)) { + return response()->json($tokens[$token_id]); + } + + return Responses::notFound(); + } + + return response()->json(['message' => trans('auth.failed')], 401); + } + + public function deleteToken($token_id): Http\JsonResponse + { + $user = auth()->guard('api')->user(); + + if ($user !== null) { + + $tokens = []; + foreach ($user->tokens as $token) { + $tokens[$token->id] = [ + 'id' => $token->id, + 'name' => $token->name, + 'token' => $token->token, + 'created' => $token->created_at, + 'last_used_at' => $token->last_used_at + ]; + } + + if (array_key_exists($token_id, $tokens)) { + $user->tokens()->where('id', $token_id)->delete(); + return Responses::successNoContent(); + } + + return Responses::notFound(); + } + + return response()->json(['message' => trans('auth.failed')], 401); } public function optionsUser(): Http\JsonResponse @@ -518,4 +651,25 @@ public function optionsUser(): Http\JsonResponse return $response->create()->response(); } + + public function optionsTokens(): Http\JsonResponse + { + $user = auth()->guard('api')->user(); + + $response = new \App\Option\Auth\Tokens(['view'=> $user !== null && $user->id !== null]); + + return $response->create()->response(); + } + + public function optionsToken(): Http\JsonResponse + { + $user = auth()->guard('api')->user(); + + $response = new \App\Option\Auth\Token([ + 'view'=> $user !== null && $user->id !== null, + 'manage'=> $user !== null && $user->id !== null, + ]); + + return $response->create()->response(); + } } diff --git a/app/Http/Controllers/ItemManage.php b/app/Http/Controllers/ItemManage.php index 3dd70ff3..b01e927f 100644 --- a/app/Http/Controllers/ItemManage.php +++ b/app/Http/Controllers/ItemManage.php @@ -7,6 +7,7 @@ use App\ItemType\Entity; use App\Jobs\ClearCache; use App\Models\Item; +use App\Models\ItemPartialTransfer; use App\Models\ItemTransfer; use App\Response\Responses; use Exception; @@ -649,6 +650,7 @@ private function deleteAllocatedExpense( try { DB::transaction(static function() use ($item_id, $item_type_instance, $item_instance) { (new ItemTransfer())->deleteTransfers($item_id); + (new ItemPartialTransfer())->deleteTransfers($item_id); $item_type_instance->delete(); $item_instance->delete(); }); diff --git a/app/Http/Controllers/ItemPartialTransferManage.php b/app/Http/Controllers/ItemPartialTransferManage.php index 20b0c371..e0671ad4 100644 --- a/app/Http/Controllers/ItemPartialTransferManage.php +++ b/app/Http/Controllers/ItemPartialTransferManage.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers; +use App\ItemType\Entity; use App\Jobs\ClearCache; use App\Models\ItemPartialTransfer; use App\Request\Validate\ItemPartialTransfer as ItemPartialTransferValidator; @@ -12,22 +13,12 @@ use Illuminate\Http\JsonResponse; /** - * Partial transfer of items - * * @author Dean Blackborough * @copyright Dean Blackborough 2018-2022 * @license https://github.com/costs-to-expect/api/blob/master/LICENSE */ class ItemPartialTransferManage extends Controller { - /** - * Delete the requested partial transfer - * - * @param $resource_type_id - * @param $item_partial_transfer_id - * - * @return JsonResponse - */ public function delete( $resource_type_id, $item_partial_transfer_id @@ -37,6 +28,20 @@ public function delete( \App\Response\Responses::notFoundOrNotAccessible(trans('entities.item-partial-transfer')); } + $item_type = Entity::itemType((int) $resource_type_id); + + return match ($item_type) { + 'allocated-expense' => $this->deleteAllocatedExpense((int) $resource_type_id, (int) $item_partial_transfer_id), + 'game', 'simple-expense', 'simple-item' => Responses::notSupported(), + default => throw new \OutOfRangeException('No item type definition for ' . $item_type, 500), + }; + } + + private function deleteAllocatedExpense( + int $resource_type_id, + int $item_partial_transfer_id + ): JsonResponse + { $cache_job_payload = (new \App\Cache\JobPayload()) ->setGroupKey(\App\Cache\KeyGroup::ITEM_PARTIAL_TRANSFER_DELETE) ->setRouteParameters([ @@ -74,6 +79,21 @@ public function transfer( \App\Response\Responses::notFoundOrNotAccessible(trans('entities.item')); } + $item_type = Entity::itemType((int) $resource_type_id); + + return match ($item_type) { + 'allocated-expense' => $this->transferAllocatedExpense((int) $resource_type_id, (int) $resource_id, (int) $item_id), + 'game', 'simple-expense', 'simple-item' => Responses::notSupported(), + default => throw new \OutOfRangeException('No item type definition for ' . $item_type, 500), + }; + } + + private function transferAllocatedExpense( + int $resource_type_id, + int $resource_id, + int $item_id + ): JsonResponse + { $validator = (new ItemPartialTransferValidator)->create( [ 'resource_type_id' => $resource_type_id, @@ -96,7 +116,7 @@ public function transfer( ->setRouteParameters([ 'resource_type_id' => $resource_type_id ]) - ->setPermittedUser($this->writeAccessToResourceType((int) $resource_type_id)) + ->setPermittedUser($this->writeAccessToResourceType(($resource_type_id))) ->setUserId($this->user_id); try { @@ -119,8 +139,8 @@ public function transfer( } $item_partial_transfer = (new ItemPartialTransfer())->single( - (int) $resource_type_id, - (int) $partial_transfer->id + $resource_type_id, + $partial_transfer->id ); if ($item_partial_transfer === null) { diff --git a/app/Http/Controllers/ItemTransferManage.php b/app/Http/Controllers/ItemTransferManage.php index 6091807c..fce31379 100644 --- a/app/Http/Controllers/ItemTransferManage.php +++ b/app/Http/Controllers/ItemTransferManage.php @@ -2,18 +2,18 @@ namespace App\Http\Controllers; +use App\ItemType\Entity; use App\Jobs\ClearCache; use App\Models\Item; use App\Models\ItemTransfer; use App\Request\Validate\ItemTransfer as ItemTransferValidator; +use App\Response\Responses; use Exception; use Illuminate\Database\QueryException; use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\DB; /** - * Transfer items - * * @author Dean Blackborough * @copyright Dean Blackborough 2018-2022 * @license https://github.com/costs-to-expect/api/blob/master/LICENSE @@ -30,6 +30,21 @@ public function transfer( \App\Response\Responses::notFoundOrNotAccessible(trans('entities.item')); } + $item_type = Entity::itemType((int) $resource_type_id); + + return match ($item_type) { + 'allocated-expense', 'simple-expense' => $this->transferItem((int) $resource_type_id, (int) $resource_id, (int) $item_id), + 'game', 'simple-item' => Responses::notSupported(), + default => throw new \OutOfRangeException('No item type definition for ' . $item_type, 500), + }; + } + + private function transferItem( + int $resource_type_id, + int $resource_id, + int $item_id + ): JsonResponse + { $user_id = $this->user_id; $validator = (new ItemTransferValidator)->create( @@ -69,12 +84,12 @@ public function transfer( $item_transfer = new ItemTransfer([ 'resource_type_id' => $resource_type_id, - 'from' => (int)$resource_id, + 'from' => $resource_id, 'to' => $new_resource_id, 'item_id' => $item_id, 'transferred_by' => $user_id ]); - $item_transfer->save(); + return $item_transfer->save(); }); ClearCache::dispatch($cache_job_payload->payload()); diff --git a/app/Http/Controllers/ItemTransferView.php b/app/Http/Controllers/ItemTransferView.php index 69969b40..79e2f26a 100644 --- a/app/Http/Controllers/ItemTransferView.php +++ b/app/Http/Controllers/ItemTransferView.php @@ -2,41 +2,48 @@ namespace App\Http\Controllers; +use App\ItemType\Entity; use App\Models\ItemTransfer; -use App\Option\ItemTransferCollection; -use App\Option\ItemTransferItem; -use App\Option\ItemTransferTransfer; +use App\Option\ItemTransfer\AllocatedExpense; +use App\Option\ItemTransfer\AllocatedExpenseCollection; +use App\Option\ItemTransfer\AllocatedExpenseTransfer; +use App\Option\ItemTransfer\SimpleExpense; +use App\Option\ItemTransfer\SimpleExpenseCollection; +use App\Option\ItemTransfer\SimpleExpenseTransfer; use App\Request\Parameter; use App\Response\Header; use App\Response\Pagination as UtilityPagination; +use App\Response\Responses; use App\Transformers\ItemTransfer as ItemTransferTransformer; use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\Config; /** - * Transfer items - * * @author Dean Blackborough * @copyright Dean Blackborough 2018-2022 * @license https://github.com/costs-to-expect/api/blob/master/LICENSE */ class ItemTransferView extends Controller { - /** - * Return the item transfers collection - * - * @param string $resource_type_id - * - * @return JsonResponse - */ public function index($resource_type_id): JsonResponse { if ($this->viewAccessToResourceType((int) $resource_type_id) === false) { \App\Response\Responses::notFoundOrNotAccessible(trans('entities.item-transfer')); } + $item_type = Entity::itemType((int) $resource_type_id); + + return match ($item_type) { + 'allocated-expense', 'simple-expense' => $this->collection((int) $resource_type_id), + 'game', 'simple-item' => Responses::notSupported(), + default => throw new \OutOfRangeException('No item type definition for ' . $item_type, 500), + }; + } + + private function collection(int $resource_type_id): JsonResponse + { $cache_control = new \App\Cache\Control( - $this->writeAccessToResourceType((int) $resource_type_id), + $this->writeAccessToResourceType($resource_type_id), $this->user_id ); $cache_control->setTtlOneWeek(); @@ -51,7 +58,7 @@ public function index($resource_type_id): JsonResponse ); $total = (new ItemTransfer())->total( - (int) $resource_type_id, + $resource_type_id, $this->viewable_resource_types, $parameters ); @@ -62,7 +69,7 @@ public function index($resource_type_id): JsonResponse parameters(); $transfers = (new ItemTransfer())->paginatedCollection( - (int)$resource_type_id, + $resource_type_id, $this->viewable_resource_types, $pagination_parameters['offset'], $pagination_parameters['limit'], @@ -88,20 +95,32 @@ static function ($transfer) { return response()->json($cache_collection->collection(), 200, $cache_collection->headers()); } - /** - * Generate the OPTIONS request for the transfers collection - * - * @param $resource_type_id - * - * @return JsonResponse - */ public function optionsIndex($resource_type_id): JsonResponse { if ($this->viewAccessToResourceType((int) $resource_type_id) === false) { - \App\Response\Responses::notFoundOrNotAccessible(trans('entities.resource-type')); + \App\Response\Responses::notFoundOrNotAccessible(trans('entities.item')); } - $response = new ItemTransferCollection($this->permissions((int) $resource_type_id)); + $item_type = Entity::itemType((int) $resource_type_id); + + return match ($item_type) { + 'allocated-expense' => $this->optionsAllocatedExpenseCollection((int) $resource_type_id), + 'simple-expense' => $this->optionsSimpleExpenseCollection((int) $resource_type_id), + 'game', 'simple-item' => Responses::notSupported(), + default => throw new \OutOfRangeException('No item type definition for ' . $item_type, 500), + }; + } + + private function optionsAllocatedExpenseCollection(int $resource_type_id): JsonResponse + { + $response = new AllocatedExpenseCollection($this->permissions($resource_type_id)); + + return $response->create()->response(); + } + + private function optionsSimpleExpenseCollection(int $resource_type_id): JsonResponse + { + $response = new SimpleExpenseCollection($this->permissions($resource_type_id)); return $response->create()->response(); } @@ -112,7 +131,26 @@ public function optionsShow($resource_type_id, $item_transfer_id): JsonResponse \App\Response\Responses::notFoundOrNotAccessible(trans('entities.resource-type')); } - $response = new ItemTransferItem($this->permissions((int) $resource_type_id)); + $item_type = Entity::itemType((int) $resource_type_id); + + return match ($item_type) { + 'allocated-expense' => $this->optionsAllocatedExpenseShow((int) $resource_type_id), + 'simple-expense' => $this->optionsSimpleExpenseShow((int) $resource_type_id), + 'game', 'simple-item' => Responses::notSupported(), + default => throw new \OutOfRangeException('No item type definition for ' . $item_type, 500), + }; + } + + private function optionsAllocatedExpenseShow(int $resource_type_id): JsonResponse + { + $response = new AllocatedExpense($this->permissions($resource_type_id)); + + return $response->create()->response(); + } + + private function optionsSimpleExpenseShow(int $resource_type_id): JsonResponse + { + $response = new SimpleExpense($this->permissions($resource_type_id)); return $response->create()->response(); } @@ -127,26 +165,50 @@ public function optionsTransfer( \App\Response\Responses::notFoundOrNotAccessible(trans('entities.item')); } - $response = new ItemTransferTransfer($this->permissions((int) $resource_type_id)); + $item_type = Entity::itemType((int) $resource_type_id); + + return match ($item_type) { + 'allocated-expense' => $this->optionsAllocatedExpenseTransfer((int) $resource_type_id, (int) $resource_id), + 'simple-expense' => $this->optionsSimpleExpenseTransfer((int) $resource_type_id, (int) $resource_id), + 'game', 'simple-item' => Responses::notSupported(), + default => throw new \OutOfRangeException('No item type definition for ' . $item_type, 500), + }; + } + + private function optionsAllocatedExpenseTransfer( + int $resource_type_id, + int $resource_id + ): JsonResponse + { + $response = new AllocatedExpenseTransfer($this->permissions($resource_type_id)); return $response->setDynamicAllowedFields( - (new \App\AllowedValue\Resource())->allowedValues( - $resource_type_id, - $resource_id - ) - )-> - create()-> - response(); + (new \App\AllowedValue\Resource())->allowedValues( + $resource_type_id, + $resource_id + ) + )-> + create()-> + response(); + } + + private function optionsSimpleExpenseTransfer( + int $resource_type_id, + int $resource_id + ): JsonResponse + { + $response = new SimpleExpenseTransfer($this->permissions($resource_type_id)); + + return $response->setDynamicAllowedFields( + (new \App\AllowedValue\Resource())->allowedValues( + $resource_type_id, + $resource_id + ) + )-> + create()-> + response(); } - /** - * Return a single item transfer - * - * @param $resource_type_id - * @param $item_transfer_id - * - * @return JsonResponse - */ public function show( $resource_type_id, $item_transfer_id @@ -156,9 +218,48 @@ public function show( \App\Response\Responses::notFoundOrNotAccessible(trans('entities.item-transfer')); } + $item_type = Entity::itemType((int) $resource_type_id); + + return match ($item_type) { + 'allocated-expense' => $this->allocatedExpense((int) $resource_type_id, (int) $item_transfer_id), + 'simple-expense' => $this->simpleExpense((int) $resource_type_id, (int) $item_transfer_id), + 'game', 'simple-item' => Responses::notSupported(), + default => throw new \OutOfRangeException('No item type definition for ' . $item_type, 500), + }; + } + + private function allocatedExpense( + int $resource_type_id, + int $item_transfer_id + ): JsonResponse + { + $item_transfer = (new ItemTransfer())->single( + $resource_type_id, + $item_transfer_id + ); + + if ($item_transfer === null) { + return \App\Response\Responses::notFound(trans('entities.item_transfer')); + } + + $headers = new Header(); + $headers->item(); + + return response()->json( + (new ItemTransferTransformer($item_transfer))->asArray(), + 200, + $headers->headers() + ); + } + + private function simpleExpense( + int $resource_type_id, + int $item_transfer_id + ): JsonResponse + { $item_transfer = (new ItemTransfer())->single( - (int) $resource_type_id, - (int) $item_transfer_id + $resource_type_id, + $item_transfer_id ); if ($item_transfer === null) { diff --git a/app/Http/Controllers/PermittedUserManage.php b/app/Http/Controllers/PermittedUserManage.php new file mode 100644 index 00000000..1b17b1a9 --- /dev/null +++ b/app/Http/Controllers/PermittedUserManage.php @@ -0,0 +1,122 @@ + + * @copyright Dean Blackborough 2018-2022 + * @license https://github.com/costs-to-expect/api/blob/master/LICENSE + */ +class PermittedUserManage extends Controller +{ + public function create(string $resource_type_id): JsonResponse + { + if ($this->writeAccessToResourceType((int) $resource_type_id) === false) { + \App\Response\Responses::notFoundOrNotAccessible(trans('entities.resource-type')); + } + + $resource_type = (new ResourceType())->single( + $resource_type_id, + $this->viewable_resource_types + ); + + $validator = (new PermittedUserValidator)->create([ + 'resource_type_id' => $resource_type['resource_type_id'] + ]); + + if ($validator->fails()) { + return \App\Request\BodyValidation::returnValidationErrors($validator); + } + + $cache_job_payload = (new \App\Cache\JobPayload()) + ->setGroupKey(\App\Cache\KeyGroup::PERMITTED_USER_CREATE) + ->setRouteParameters([ + 'resource_type_id' => $resource_type_id + ]) + ->setPermittedUser($this->writeAccessToResourceType((int) $resource_type_id)) + ->setUserId($this->user_id); + + try { + DB::transaction(function() use ($resource_type_id) { + + $user = DB::table('users') + ->where('email', '=', request()->input('email')) + ->first(); + + if ($user === null) { + throw new \RuntimeException('User cannot be found'); + } + + $permitted_user = new \App\Models\PermittedUser(); + $permitted_user->resource_type_id = $resource_type_id; + $permitted_user->user_id = $user->id; + $permitted_user->added_by = $this->user_id; + + if ($permitted_user->save() === true) { + $permitted_user_cache_job_payload = (new \App\Cache\JobPayload()) + ->setGroupKey(\App\Cache\KeyGroup::RESOURCE_TYPE_CREATE) + ->setRouteParameters([]) + ->setPermittedUser($this->writeAccessToResourceType((int)$resource_type_id)) + ->setUserId($user->id); + + ClearCache::dispatch($permitted_user_cache_job_payload->payload()); + } else { + throw new \RuntimeException('Unable to assign user or create clear cache request'); + } + }); + + ClearCache::dispatch($cache_job_payload->payload()); + + } catch (Exception $e) { + return Responses::failedToSaveModelForCreate(); + } + + return Responses::successNoContent(); + } + + public function delete( + string $resource_type_id, + string $permitted_user_id + ): JsonResponse + { + if ($this->writeAccessToResourceType((int) $resource_type_id) === false) { + \App\Response\Responses::notFoundOrNotAccessible(trans('entities.permitted-user')); + } + + $permitted_user = (new PermittedUser())->instance($resource_type_id, $permitted_user_id); + + if ($permitted_user === null) { + return Responses::failedToSelectModelForUpdateOrDelete(); + } + + $cache_job_payload = (new \App\Cache\JobPayload()) + ->setGroupKey(\App\Cache\KeyGroup::PERMITTED_USER_DELETE) + ->setRouteParameters([]) + ->setPermittedUser($this->writeAccessToResourceType((int) $resource_type_id)) + ->setUserId($this->user_id); + + try { + DB::transaction(function() use ($permitted_user) { + $permitted_user->delete(); + }); + + ClearCache::dispatch($cache_job_payload->payload()); + + return Responses::successNoContent(); + } catch (QueryException $e) { + return Responses::foreignKeyConstraintError(); + } catch (Exception $e) { + return Responses::notFound(trans('entities.permitted-user')); + } + } +} diff --git a/app/Http/Controllers/PermittedUserView.php b/app/Http/Controllers/PermittedUserView.php index 6a0cf8e8..682bdf86 100644 --- a/app/Http/Controllers/PermittedUserView.php +++ b/app/Http/Controllers/PermittedUserView.php @@ -4,6 +4,7 @@ use App\Models\PermittedUser; use App\Option\PermittedUserCollection; +use App\Option\PermittedUserItem; use App\Request\Parameter; use App\Response\Header; use App\Response\Pagination as UtilityPagination; @@ -12,8 +13,6 @@ use Illuminate\Support\Facades\Config; /** - * Manage permitted users - * * @author Dean Blackborough * @copyright Dean Blackborough 2018-2022 * @license https://github.com/costs-to-expect/api/blob/master/LICENSE @@ -87,13 +86,31 @@ static function ($permitted_user) { return response()->json($cache_collection->collection(), 200, $cache_collection->headers()); } - /** - * Generate the OPTIONS request for the permitted users collection - * - * @param string $resource_type_id - * - * @return JsonResponse - */ + public function show( + string $resource_type_id, + string $permitted_user_id + ): JsonResponse + { + if ($this->viewAccessToResourceType((int) $resource_type_id) === false) { + \App\Response\Responses::notFoundOrNotAccessible(trans('entities.resource')); + } + + $permitted_user = (new PermittedUser())->single($resource_type_id, $permitted_user_id); + + if ($permitted_user === null) { + return \App\Response\Responses::notFound(trans('entities.permitted-user')); + } + + $headers = new Header(); + $headers->item(); + + return response()->json( + (new PermittedUserTransformer($permitted_user))->asArray(), + 200, + $headers->headers() + ); + } + public function optionsIndex(string $resource_type_id): JsonResponse { if ($this->viewAccessToResourceType((int) $resource_type_id) === false) { @@ -104,4 +121,24 @@ public function optionsIndex(string $resource_type_id): JsonResponse return $response->create()->response(); } + + public function optionsShow(string $resource_type_id, string $permitted_user_id): JsonResponse + { + if ($this->viewAccessToResourceType((int) $resource_type_id) === false) { + \App\Response\Responses::notFoundOrNotAccessible(trans('entities.resource')); + } + + $permitted_user = (new PermittedUser())->single( + $resource_type_id, + $permitted_user_id + ); + + if ($permitted_user === null) { + return \App\Response\Responses::notFound(trans('entities.permitted-user')); + } + + $response = new PermittedUserItem($this->permissions((int) $resource_type_id)); + + return $response->create()->response(); + } } diff --git a/app/Http/Controllers/ResourceManage.php b/app/Http/Controllers/ResourceManage.php index 8b8e2108..fa84598f 100644 --- a/app/Http/Controllers/ResourceManage.php +++ b/app/Http/Controllers/ResourceManage.php @@ -23,8 +23,6 @@ */ class ResourceManage extends Controller { - protected bool $allow_entire_collection = true; - /** * Create a new resource * diff --git a/app/Http/Controllers/ResourceTypeManage.php b/app/Http/Controllers/ResourceTypeManage.php index 0d5e8eea..34f131a2 100644 --- a/app/Http/Controllers/ResourceTypeManage.php +++ b/app/Http/Controllers/ResourceTypeManage.php @@ -100,7 +100,7 @@ public function delete( } $resource_type_item_type = (new ResourceTypeItemType())->instance($resource_type_id); - $permitted_user = (new PermittedUser())->instance($resource_type_id, $this->user_id); + $permitted_user = (new PermittedUser())->instanceByUserId($resource_type_id, $this->user_id); $resource_type = (new ResourceType())->find($resource_type_id); $categories = (new Category())->total( diff --git a/app/Http/Controllers/ResourceTypeView.php b/app/Http/Controllers/ResourceTypeView.php index b3c71957..4ed16706 100644 --- a/app/Http/Controllers/ResourceTypeView.php +++ b/app/Http/Controllers/ResourceTypeView.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use App\AllowedValue\ItemType; +use App\Models\PermittedUser; use App\Models\Resource; use App\Models\ResourceType; use App\Option\ResourceTypeCollection; @@ -17,8 +18,6 @@ use Illuminate\Support\Facades\Config; /** - * Manage resource types - * * @author Dean Blackborough * @copyright Dean Blackborough 2018-2022 * @license https://github.com/costs-to-expect/api/blob/master/LICENSE @@ -101,7 +100,7 @@ public function show($resource_type_id): JsonResponse $parameters = Parameter\Request::fetch(array_keys(Config::get('api.resource-type.parameters.item'))); $resource_type = (new ResourceType())->single( - $resource_type_id, + (int) $resource_type_id, $this->viewable_resource_types ); @@ -109,21 +108,27 @@ public function show($resource_type_id): JsonResponse return Responses::notFound(trans('entities.resource-type')); } - $resources = []; + $transformer_relations = []; + if ( array_key_exists('include-resources', $parameters) === true && $parameters['include-resources'] === true ) { - $resources = (new Resource())->paginatedCollection( - $resource_type_id - ); + $transformer_relations['resources'] = (new Resource())->paginatedCollection((int) $resource_type_id); + } + + if ( + array_key_exists('include-permitted-users', $parameters) === true && + $parameters['include-permitted-users'] === true + ) { + $transformer_relations['permitted_users'] = (new PermittedUser())->paginatedCollection((int) $resource_type_id); } $headers = new Header(); $headers->item()->addParameters(Parameter\Request::xHeader()); return response()->json( - (new ResourceTypeTransformer($resource_type, ['resources' => $resources]))->asArray(), + (new ResourceTypeTransformer($resource_type, $transformer_relations))->asArray(), 200, $headers->headers() ); diff --git a/app/Http/Middleware/ConvertRouteParameters.php b/app/Http/Middleware/ConvertRouteParameters.php index 2c1a57da..98d98fb5 100644 --- a/app/Http/Middleware/ConvertRouteParameters.php +++ b/app/Http/Middleware/ConvertRouteParameters.php @@ -30,6 +30,7 @@ public function handle($request, Closure $next) 'category_id' => 'category', 'subcategory_id' => 'subcategory', 'resource_type_id' => 'resource-type', + 'permitted_user_id' => 'permitted-user', 'resource_id' => 'resource', 'item_id' => 'item', 'item_category_id' => 'item-category', diff --git a/app/ItemType/AllocatedExpense/AllowedValue/Item.php b/app/ItemType/AllocatedExpense/AllowedValue/Item.php index 9de965b3..ba4a5207 100644 --- a/app/ItemType/AllocatedExpense/AllowedValue/Item.php +++ b/app/ItemType/AllocatedExpense/AllowedValue/Item.php @@ -4,6 +4,10 @@ namespace App\ItemType\AllocatedExpense\AllowedValue; use App\ItemType\AllowedValue; +use App\Models\Category; +use App\Models\Currency; +use App\Models\Subcategory; +use App\Response\Responses; class Item extends AllowedValue { @@ -19,8 +23,6 @@ public function __construct( $viewable_resource_types ); - $this->entity = new \App\ItemType\AllocatedExpense\Item(); - $this->setAllowedValueFields(); } @@ -39,6 +41,140 @@ public function fetch(): AllowedValue return $this; } + protected function fetchValuesForCategory(): void + { + if (array_key_exists('category', $this->available_parameters) === true) { + + $allowed_values = []; + + $categories = (new Category())->paginatedCollection( + $this->resource_type_id, + $this->viewable_resource_types, + 0, + 100 + ); + + foreach ($categories as $category) { + $category_id = $this->hash->encode('category', $category['category_id']); + + $allowed_values[$category_id] = [ + 'value' => $category_id, + 'name' => $category['category_name'], + 'description' => trans('item-type-allocated-expense/allowed-values.description-prefix-category') . + $category['category_name'] . + trans('item-type-allocated-expense/allowed-values.description-suffix-category') + ]; + } + + $this->values['category'] = ['allowed_values' => $allowed_values]; + } + } + + protected function fetchValuesForCurrency(): void + { + $allowed_values = []; + + $currencies = (new Currency())->minimisedCollection(); + + foreach ($currencies as $currency) { + $id = $this->hash->encode('currency', $currency['currency_id']); + + if ($id === false) { + Responses::unableToDecode(); + } + + $allowed_values[$id] = [ + 'value' => $id, + 'name' => $currency['currency_name'], + 'description' => $currency['currency_name'] + ]; + } + + $this->values['currency_id'] = ['allowed_values' => $allowed_values]; + } + + protected function fetchValuesForMonth(): void + { + if (array_key_exists('month', $this->available_parameters) === true) { + + $allowed_values = []; + + for ($i = 1; $i < 13; $i++) { + $allowed_values[$i] = [ + 'value' => $i, + 'name' => date("F", mktime(0, 0, 0, $i, 10)), + 'description' => trans('item-type-allocated-expense/allowed-values.description-prefix-month') . + date("F", mktime(0, 0, 0, $i, 1)) + ]; + } + + $this->values['month'] = ['allowed_values' => $allowed_values]; + } + } + + protected function fetchValuesForSubcategory(): void + { + if ( + array_key_exists('category', $this->available_parameters) === true && + array_key_exists('subcategory', $this->available_parameters) === true && + array_key_exists('category', $this->defined_parameters) === true && + $this->defined_parameters['category'] !== null + ) { + + $allowed_values = []; + + $subcategories = (new Subcategory())->paginatedCollection( + $this->resource_type_id, + (int) $this->defined_parameters['category'] + ); + + foreach ($subcategories as $subcategory) { + $subcategory_id = $this->hash->encode('subcategory', $subcategory['subcategory_id']); + + $allowed_values[$subcategory_id] = [ + 'value' => $subcategory_id, + 'name' => $subcategory['subcategory_name'], + 'description' => trans('item-type-allocated-expense/allowed-values.description-prefix-subcategory') . + $subcategory['subcategory_name'] . trans('item-type-allocated-expense/allowed-values.description-suffix-subcategory') + ]; + } + + $this->values['subcategory'] = ['allowed_values' => $allowed_values]; + } + } + + protected function fetchValuesForYear(): void + { + if (array_key_exists('year', $this->available_parameters) === true) { + + $min_year = $this->range_limits->minimumYearByResourceTypeAndResource( + $this->resource_type_id, + $this->resource_id, + 'item_type_allocated_expense', + 'effective_date' + ); + $max_year = $this->range_limits->maximumYearByResourceTypeAndResource( + $this->resource_type_id, + $this->resource_id, + 'item_type_allocated_expense', + 'effective_date' + ); + + $allowed_values = []; + + for ($i = $min_year; $i <= $max_year; $i++) { + $allowed_values[$i] = [ + 'value' => $i, + 'name' => $i, + 'description' => trans('item-type-allocated-expense/allowed-values.description-prefix-year') . $i + ]; + } + + $this->values['year'] = ['allowed_values' => $allowed_values]; + + } + } + protected function setAllowedValueFields(): void { $this->values = [ diff --git a/app/ItemType/AllocatedExpense/AllowedValue/ResourceTypeItem.php b/app/ItemType/AllocatedExpense/AllowedValue/ResourceTypeItem.php index c1850594..ac97af73 100644 --- a/app/ItemType/AllocatedExpense/AllowedValue/ResourceTypeItem.php +++ b/app/ItemType/AllocatedExpense/AllowedValue/ResourceTypeItem.php @@ -3,8 +3,11 @@ namespace App\ItemType\AllocatedExpense\AllowedValue; -use App\ItemType\AllocatedExpense\Item; use App\ItemType\ResourceTypeAllowedValue; +use App\Models\Category; +use App\Models\Currency; +use App\Models\Subcategory; +use App\Response\Responses; class ResourceTypeItem extends ResourceTypeAllowedValue { @@ -18,8 +21,6 @@ public function __construct( $viewable_resource_types ); - $this->entity = new Item(); - $this->setAllowedValueFields(); } @@ -48,4 +49,135 @@ protected function setAllowedValueFields(): void 'currency_id' => null ]; } + + protected function fetchValuesForCategory(): void + { + if (array_key_exists('category', $this->available_parameters) === true) { + + $allowed_values = []; + + $categories = (new Category())->paginatedCollection( + $this->resource_type_id, + $this->viewable_resource_types, + 0, + 100 + ); + + foreach ($categories as $category) { + $category_id = $this->hash->encode('category', $category['category_id']); + + $allowed_values[$category_id] = [ + 'value' => $category_id, + 'name' => $category['category_name'], + 'description' => trans('resource-type-item-type-allocated-expense/allowed-values.description-prefix-category') . + $category['category_name'] . + trans('resource-type-item-type-allocated-expense/allowed-values.description-suffix-category') + ]; + } + + $this->values['category'] = ['allowed_values' => $allowed_values]; + } + } + + protected function fetchValuesForCurrency(): void + { + $allowed_values = []; + + $currencies = (new Currency())->minimisedCollection(); + + foreach ($currencies as $currency) { + $id = $this->hash->encode('currency', $currency['currency_id']); + + if ($id === false) { + Responses::unableToDecode(); + } + + $allowed_values[$id] = [ + 'value' => $id, + 'name' => $currency['currency_name'], + 'description' => $currency['currency_name'] + ]; + } + + $this->values['currency_id'] = ['allowed_values' => $allowed_values]; + } + + protected function fetchValuesForMonth(): void + { + if (array_key_exists('month', $this->available_parameters) === true) { + + $allowed_values = []; + + for ($i = 1; $i < 13; $i++) { + $allowed_values[$i] = [ + 'value' => $i, + 'name' => date("F", mktime(0, 0, 0, $i, 10)), + 'description' => trans('resource-type-item-type-allocated-expense/allowed-values.description-prefix-month') . + date("F", mktime(0, 0, 0, $i, 1)) + ]; + } + + $this->values['month'] = ['allowed_values' => $allowed_values]; + } + } + + protected function fetchValuesForSubcategory(): void + { + if ( + array_key_exists('category', $this->available_parameters) === true && + array_key_exists('subcategory', $this->available_parameters) === true && + array_key_exists('category', $this->defined_parameters) === true && + $this->defined_parameters['category'] !== null + ) { + + $allowed_values = []; + + $subcategories = (new Subcategory())->paginatedCollection( + $this->resource_type_id, + (int) $this->defined_parameters['category'] + ); + + foreach ($subcategories as $subcategory) { + $subcategory_id = $this->hash->encode('subcategory', $subcategory['subcategory_id']); + + $allowed_values[$subcategory_id] = [ + 'value' => $subcategory_id, + 'name' => $subcategory['subcategory_name'], + 'description' => trans('resource-type-item-type-allocated-expense/allowed-values.description-prefix-subcategory') . + $subcategory['subcategory_name'] . trans('resource-type-item-type-allocated-expense/allowed-values.description-suffix-subcategory') + ]; + } + + $this->values['subcategory'] = ['allowed_values' => $allowed_values]; + } + } + + protected function fetchValuesForYear(): void + { + if (array_key_exists('year', $this->available_parameters) === true) { + + $allowed_values = []; + + $min_year = $this->range_limits->minimumYearByResourceType( + $this->resource_type_id, + 'item_type_allocated_expense', + 'effective_date' + ); + $max_year = $this->range_limits->maximumYearByResourceType( + $this->resource_type_id, + 'item_type_allocated_expense', + 'effective_date' + ); + + for ($i = $min_year; $i <= $max_year; $i++) { + $allowed_values[$i] = [ + 'value' => $i, + 'name' => $i, + 'description' => trans('resource-type-item-type-allocated-expense/allowed-values.description-prefix-year') . $i + ]; + } + + $this->values['year'] = ['allowed_values' => $allowed_values]; + } + } } diff --git a/app/ItemType/AllowedValue.php b/app/ItemType/AllowedValue.php index ce553893..f1f78bc1 100644 --- a/app/ItemType/AllowedValue.php +++ b/app/ItemType/AllowedValue.php @@ -3,19 +3,13 @@ namespace App\ItemType; -use App\Models\Category; -use App\Models\Currency; use App\Models\EntityLimits; -use App\Models\Subcategory; use App\Request\Hash; -use App\Response\Responses; abstract class AllowedValue { protected Hash $hash; - protected ItemType $entity; - protected EntityLimits $range_limits; protected int $resource_type_id; @@ -69,155 +63,4 @@ public function allowedValues(): array return $this->values; } - - protected function fetchValuesForCategory(): void - { - if (array_key_exists('category', $this->available_parameters) === true) { - - $allowed_values = []; - - $categories = (new Category())->paginatedCollection( - $this->resource_type_id, - $this->viewable_resource_types, - 0, - 100 - ); - - foreach ($categories as $category) { - $category_id = $this->hash->encode('category', $category['category_id']); - - $allowed_values[$category_id] = [ - 'value' => $category_id, - 'name' => $category['category_name'], - 'description' => trans('item-type-' . $this->entity->type() . - '/allowed-values.description-prefix-category') . - $category['category_name'] . - trans('item-type-' . $this->entity->type() . - '/allowed-values.description-suffix-category') - ]; - } - - $this->values['category'] = ['allowed_values' => $allowed_values]; - } - } - - protected function fetchValuesForMonth(): void - { - if (array_key_exists('month', $this->available_parameters) === true) { - - $allowed_values = []; - - for ($i = 1; $i < 13; $i++) { - $allowed_values[$i] = [ - 'value' => $i, - 'name' => date("F", mktime(0, 0, 0, $i, 10)), - 'description' => trans('item-type-' . $this->entity->type() . - '/allowed-values.description-prefix-month') . - date("F", mktime(0, 0, 0, $i, 1)) - ]; - } - - $this->values['month'] = ['allowed_values' => $allowed_values]; - } - } - - protected function fetchValuesForCurrency(): void - { - $allowed_values = []; - - $currencies = (new Currency())->minimisedCollection(); - - foreach ($currencies as $currency) { - $id = $this->hash->encode('currency', $currency['currency_id']); - - if ($id === false) { - Responses::unableToDecode(); - } - - $allowed_values[$id] = [ - 'value' => $id, - 'name' => $currency['currency_name'], - 'description' => $currency['currency_name'] - ]; - } - - $this->values['currency_id'] = ['allowed_values' => $allowed_values]; - } - - protected function fetchValuesForSubcategory(): void - { - if ( - array_key_exists('category', $this->available_parameters) === true && - array_key_exists('subcategory', $this->available_parameters) === true && - array_key_exists('category', $this->defined_parameters) === true && - $this->defined_parameters['category'] !== null - ) { - - $allowed_values = []; - - $subcategories = (new Subcategory())->paginatedCollection( - $this->resource_type_id, - (int) $this->defined_parameters['category'] - ); - - foreach ($subcategories as $subcategory) { - $subcategory_id = $this->hash->encode('subcategory', $subcategory['subcategory_id']); - - $allowed_values[$subcategory_id] = [ - 'value' => $subcategory_id, - 'name' => $subcategory['subcategory_name'], - 'description' => trans('item-type-' . $this->entity->type() . '/allowed-values.description-prefix-subcategory') . - $subcategory['subcategory_name'] . trans('item-type-' . $this->entity->type() . '/allowed-values.description-suffix-subcategory') - ]; - } - - $this->values['subcategory'] = ['allowed_values' => $allowed_values]; - } - } - - protected function fetchValuesForYear(): void - { - if (array_key_exists('year', $this->available_parameters) === true) { - - $min_year = null; - $max_year = null; - - $item_type = Entity::itemType($this->resource_type_id); - switch ($item_type) { - case 'allocated-expense': - $min_year = $this->range_limits->minimumYearByResourceTypeAndResource( - $this->resource_type_id, - $this->resource_id, - 'item_type_allocated_expense', - 'effective_date' - ); - $max_year = $this->range_limits->maximumYearByResourceTypeAndResource( - $this->resource_type_id, - $this->resource_id, - 'item_type_allocated_expense', - 'effective_date' - ); - break; - default: - // Do nothing - break; - } - - $allowed_values = []; - - if ($min_year !== null && $max_year !== null) { - for ($i = $min_year; $i <= $max_year; $i++) { - $allowed_values[$i] = [ - 'value' => $i, - 'name' => $i, - 'description' => trans('item-type-' . $this->entity->type() . - '/allowed-values.description-prefix-year') . $i - ]; - } - } - - $this->values['year'] = ['allowed_values' => $allowed_values]; - - } - } } diff --git a/app/ItemType/Game/AllowedValue/Item.php b/app/ItemType/Game/AllowedValue/Item.php index f97b6023..1636ac52 100644 --- a/app/ItemType/Game/AllowedValue/Item.php +++ b/app/ItemType/Game/AllowedValue/Item.php @@ -21,8 +21,6 @@ public function __construct( $viewable_resource_types ); - $this->entity = new \App\ItemType\Game\Item(); - $this->setAllowedValueFields(); } @@ -59,11 +57,9 @@ protected function fetchValuesForWinner(): void $allowed_values[$winner_id] = [ 'value' => $winner_id, 'name' => $winner['category_name'], - 'description' => trans('item-type-' . $this->entity->type() . - '/allowed-values.description-prefix-winner_id') . + 'description' => trans('item-type-game/allowed-values.description-prefix-winner_id') . $winner['category_name'] . - trans('item-type-' . $this->entity->type() . - '/allowed-values.description-suffix-winner_id') + trans('item-type-game/allowed-values.description-suffix-winner_id') ]; } diff --git a/app/ItemType/Game/AllowedValue/ResourceTypeItem.php b/app/ItemType/Game/AllowedValue/ResourceTypeItem.php index 55740a9c..e7944187 100644 --- a/app/ItemType/Game/AllowedValue/ResourceTypeItem.php +++ b/app/ItemType/Game/AllowedValue/ResourceTypeItem.php @@ -20,8 +20,6 @@ public function __construct( $viewable_resource_types ); - $this->entity = new Item(); - $this->setAllowedValueFields(); } @@ -58,11 +56,9 @@ protected function fetchValuesForWinner(): void $allowed_values[$winner_id] = [ 'value' => $winner_id, 'name' => $winner['category_name'], - 'description' => trans('resource-type-item-type-' . $this->entity->type() . - '/allowed-values.description-prefix-winner_id') . + 'description' => trans('resource-type-item-type-game/allowed-values.description-prefix-winner_id') . $winner['category_name'] . - trans('resource-type-item-type-' . $this->entity->type() . - '/allowed-values.description-suffix-winner_id') + trans('resource-type-item-type-game/allowed-values.description-suffix-winner_id') ]; } diff --git a/app/AllowedValue/Winner.php b/app/ItemType/Game/AllowedValue/Winner.php similarity index 95% rename from app/AllowedValue/Winner.php rename to app/ItemType/Game/AllowedValue/Winner.php index 081dfc99..65bcfc3f 100644 --- a/app/AllowedValue/Winner.php +++ b/app/ItemType/Game/AllowedValue/Winner.php @@ -1,7 +1,7 @@ values; } - - protected function fetchValuesForCategory(): void - { - if (array_key_exists('category', $this->available_parameters) === true) { - - $allowed_values = []; - - $categories = (new Category())->paginatedCollection( - $this->resource_type_id, - $this->viewable_resource_types, - 0, - 100 - ); - - foreach ($categories as $category) { - $category_id = $this->hash->encode('category', $category['category_id']); - - $allowed_values[$category_id] = [ - 'value' => $category_id, - 'name' => $category['category_name'], - 'description' => trans('resource-type-item-type-' . $this->entity->type() . - '/allowed-values.description-prefix-category') . - $category['category_name'] . - trans('resource-type-item-type-' . $this->entity->type() . - '/allowed-values.description-suffix-category') - ]; - } - - $this->values['category'] = ['allowed_values' => $allowed_values]; - } - } - - protected function fetchValuesForMonth(): void - { - if (array_key_exists('month', $this->available_parameters) === true) { - - $allowed_values = []; - - for ($i = 1; $i < 13; $i++) { - $allowed_values[$i] = [ - 'value' => $i, - 'name' => date("F", mktime(0, 0, 0, $i, 10)), - 'description' => trans('resource-type-item-type-' . $this->entity->type() . - '/allowed-values.description-prefix-month') . - date("F", mktime(0, 0, 0, $i, 1)) - ]; - } - - $this->values['month'] = ['allowed_values' => $allowed_values]; - } - } - - protected function fetchValuesForCurrency(): void - { - $allowed_values = []; - - $currencies = (new Currency())->minimisedCollection(); - - foreach ($currencies as $currency) { - $id = $this->hash->encode('currency', $currency['currency_id']); - - if ($id === false) { - Responses::unableToDecode(); - } - - $allowed_values[$id] = [ - 'value' => $id, - 'name' => $currency['currency_name'], - 'description' => $currency['currency_name'] - ]; - } - - $this->values['currency_id'] = ['allowed_values' => $allowed_values]; - } - - protected function fetchValuesForSubcategory(): void - { - if ( - array_key_exists('category', $this->available_parameters) === true && - array_key_exists('subcategory', $this->available_parameters) === true && - array_key_exists('category', $this->defined_parameters) === true && - $this->defined_parameters['category'] !== null - ) { - - $allowed_values = []; - - $subcategories = (new Subcategory())->paginatedCollection( - $this->resource_type_id, - (int) $this->defined_parameters['category'] - ); - - foreach ($subcategories as $subcategory) { - $subcategory_id = $this->hash->encode('subcategory', $subcategory['subcategory_id']); - - $allowed_values[$subcategory_id] = [ - 'value' => $subcategory_id, - 'name' => $subcategory['subcategory_name'], - 'description' => trans('resource-type-item-type-' . $this->entity->type() . '/allowed-values.description-prefix-subcategory') . - $subcategory['subcategory_name'] . trans('resource-type-item-type-' . $this->entity->type() . '/allowed-values.description-suffix-subcategory') - ]; - } - - $this->values['subcategory'] = ['allowed_values' => $allowed_values]; - } - } - - protected function fetchValuesForYear(): void - { - if (array_key_exists('year', $this->available_parameters) === true) { - - $allowed_values = []; - - $min_year = null; - $max_year = null; - - $item_type = \App\ItemType\Entity::itemType($this->resource_type_id); - switch ($item_type) { - case 'allocated-expense': - $min_year = $this->range_limits->minimumYearByResourceType( - $this->resource_type_id, - 'item_type_allocated_expense', - 'effective_date' - ); - $max_year = $this->range_limits->maximumYearByResourceType( - $this->resource_type_id, - 'item_type_allocated_expense', - 'effective_date' - ); - break; - default: - // Do nothing - break; - } - - if ($min_year !== null && $max_year !== null) { - for ($i = $min_year; $i <= $max_year; $i++) { - $allowed_values[$i] = [ - 'value' => $i, - 'name' => $i, - 'description' => trans('resource-type-item-type-' . $this->entity->type() . - '/allowed-values.description-prefix-year') . $i - ]; - } - } - - $this->values['year'] = ['allowed_values' => $allowed_values]; - } - } } diff --git a/app/ItemType/SimpleExpense/AllowedValue/Item.php b/app/ItemType/SimpleExpense/AllowedValue/Item.php index 7f10bc5f..6256e78e 100644 --- a/app/ItemType/SimpleExpense/AllowedValue/Item.php +++ b/app/ItemType/SimpleExpense/AllowedValue/Item.php @@ -4,6 +4,10 @@ namespace App\ItemType\SimpleExpense\AllowedValue; use App\ItemType\AllowedValue; +use App\Models\Category; +use App\Models\Currency; +use App\Models\Subcategory; +use App\Response\Responses; class Item extends AllowedValue { @@ -19,8 +23,6 @@ public function __construct( $viewable_resource_types ); - $this->entity = new \App\ItemType\SimpleExpense\Item(); - $this->setAllowedValueFields(); } @@ -43,4 +45,87 @@ protected function setAllowedValueFields(): void 'currency_id' => null ]; } + + protected function fetchValuesForCategory(): void + { + if (array_key_exists('category', $this->available_parameters) === true) { + + $allowed_values = []; + + $categories = (new Category())->paginatedCollection( + $this->resource_type_id, + $this->viewable_resource_types, + 0, + 100 + ); + + foreach ($categories as $category) { + $category_id = $this->hash->encode('category', $category['category_id']); + + $allowed_values[$category_id] = [ + 'value' => $category_id, + 'name' => $category['category_name'], + 'description' => trans('item-type-simple-expense/allowed-values.description-prefix-category') . + $category['category_name'] . + trans('item-type-simple-expense/allowed-values.description-suffix-category') + ]; + } + + $this->values['category'] = ['allowed_values' => $allowed_values]; + } + } + + protected function fetchValuesForCurrency(): void + { + $allowed_values = []; + + $currencies = (new Currency())->minimisedCollection(); + + foreach ($currencies as $currency) { + $id = $this->hash->encode('currency', $currency['currency_id']); + + if ($id === false) { + Responses::unableToDecode(); + } + + $allowed_values[$id] = [ + 'value' => $id, + 'name' => $currency['currency_name'], + 'description' => $currency['currency_name'] + ]; + } + + $this->values['currency_id'] = ['allowed_values' => $allowed_values]; + } + + protected function fetchValuesForSubcategory(): void + { + if ( + array_key_exists('category', $this->available_parameters) === true && + array_key_exists('subcategory', $this->available_parameters) === true && + array_key_exists('category', $this->defined_parameters) === true && + $this->defined_parameters['category'] !== null + ) { + + $allowed_values = []; + + $subcategories = (new Subcategory())->paginatedCollection( + $this->resource_type_id, + (int) $this->defined_parameters['category'] + ); + + foreach ($subcategories as $subcategory) { + $subcategory_id = $this->hash->encode('subcategory', $subcategory['subcategory_id']); + + $allowed_values[$subcategory_id] = [ + 'value' => $subcategory_id, + 'name' => $subcategory['subcategory_name'], + 'description' => trans('item-type-simple-expense/allowed-values.description-prefix-subcategory') . + $subcategory['subcategory_name'] . trans('item-type-simple-expense/allowed-values.description-suffix-subcategory') + ]; + } + + $this->values['subcategory'] = ['allowed_values' => $allowed_values]; + } + } } diff --git a/app/ItemType/SimpleExpense/AllowedValue/ResourceTypeItem.php b/app/ItemType/SimpleExpense/AllowedValue/ResourceTypeItem.php index 2c8e6c52..7b8f3bc0 100644 --- a/app/ItemType/SimpleExpense/AllowedValue/ResourceTypeItem.php +++ b/app/ItemType/SimpleExpense/AllowedValue/ResourceTypeItem.php @@ -4,7 +4,10 @@ namespace App\ItemType\SimpleExpense\AllowedValue; use App\ItemType\ResourceTypeAllowedValue; -use App\ItemType\SimpleExpense\Item; +use App\Models\Category; +use App\Models\Currency; +use App\Models\Subcategory; +use App\Response\Responses; class ResourceTypeItem extends ResourceTypeAllowedValue { @@ -18,8 +21,6 @@ public function __construct( $viewable_resource_types ); - $this->entity = new Item(); - $this->setAllowedValueFields(); } @@ -42,4 +43,87 @@ protected function setAllowedValueFields(): void 'currency_id' => null ]; } + + protected function fetchValuesForCategory(): void + { + if (array_key_exists('category', $this->available_parameters) === true) { + + $allowed_values = []; + + $categories = (new Category())->paginatedCollection( + $this->resource_type_id, + $this->viewable_resource_types, + 0, + 100 + ); + + foreach ($categories as $category) { + $category_id = $this->hash->encode('category', $category['category_id']); + + $allowed_values[$category_id] = [ + 'value' => $category_id, + 'name' => $category['category_name'], + 'description' => trans('resource-type-item-type-simple-expense/allowed-values.description-prefix-category') . + $category['category_name'] . + trans('resource-type-item-type-simple-expense/allowed-values.description-suffix-category') + ]; + } + + $this->values['category'] = ['allowed_values' => $allowed_values]; + } + } + + protected function fetchValuesForCurrency(): void + { + $allowed_values = []; + + $currencies = (new Currency())->minimisedCollection(); + + foreach ($currencies as $currency) { + $id = $this->hash->encode('currency', $currency['currency_id']); + + if ($id === false) { + Responses::unableToDecode(); + } + + $allowed_values[$id] = [ + 'value' => $id, + 'name' => $currency['currency_name'], + 'description' => $currency['currency_name'] + ]; + } + + $this->values['currency_id'] = ['allowed_values' => $allowed_values]; + } + + protected function fetchValuesForSubcategory(): void + { + if ( + array_key_exists('category', $this->available_parameters) === true && + array_key_exists('subcategory', $this->available_parameters) === true && + array_key_exists('category', $this->defined_parameters) === true && + $this->defined_parameters['category'] !== null + ) { + + $allowed_values = []; + + $subcategories = (new Subcategory())->paginatedCollection( + $this->resource_type_id, + (int) $this->defined_parameters['category'] + ); + + foreach ($subcategories as $subcategory) { + $subcategory_id = $this->hash->encode('subcategory', $subcategory['subcategory_id']); + + $allowed_values[$subcategory_id] = [ + 'value' => $subcategory_id, + 'name' => $subcategory['subcategory_name'], + 'description' => trans('resource-type-item-type-simple-expense/allowed-values.description-prefix-subcategory') . + $subcategory['subcategory_name'] . trans('resource-type-item-type-simple-expense/allowed-values.description-suffix-subcategory') + ]; + } + + $this->values['subcategory'] = ['allowed_values' => $allowed_values]; + } + } } diff --git a/app/ItemType/SimpleItem/AllowedValue/Item.php b/app/ItemType/SimpleItem/AllowedValue/Item.php index a3aec28e..5dc9f3b2 100644 --- a/app/ItemType/SimpleItem/AllowedValue/Item.php +++ b/app/ItemType/SimpleItem/AllowedValue/Item.php @@ -19,8 +19,6 @@ public function __construct( $viewable_resource_types ); - $this->entity = new \App\ItemType\SimpleItem\Item(); - $this->setAllowedValueFields(); } diff --git a/app/ItemType/SimpleItem/AllowedValue/ResourceTypeItem.php b/app/ItemType/SimpleItem/AllowedValue/ResourceTypeItem.php index c9ec8854..d3b23ba2 100644 --- a/app/ItemType/SimpleItem/AllowedValue/ResourceTypeItem.php +++ b/app/ItemType/SimpleItem/AllowedValue/ResourceTypeItem.php @@ -18,8 +18,6 @@ public function __construct( $viewable_resource_types ); - $this->entity = new Item(); - $this->setAllowedValueFields(); } diff --git a/app/Jobs/ClearCache.php b/app/Jobs/ClearCache.php index 8904050a..12e6b841 100644 --- a/app/Jobs/ClearCache.php +++ b/app/Jobs/ClearCache.php @@ -8,11 +8,13 @@ use App\Cache\Trash; use App\Models\ResourceAccess; use App\Models\ResourceType; +use App\User; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; +use Throwable; /** * Clear the requested cache keys @@ -27,11 +29,6 @@ class ClearCache implements ShouldQueue protected array $payload; - /** - * Create a new job instance. - * - * @return void - */ public function __construct(array $payload) { $this->payload = $payload; @@ -75,4 +72,15 @@ public function handle() $cache_control->clearMatchingCacheKeys($cache_keys); } } + + public function failed(Throwable $exception) + { + $user = User::query()->find(1); + $user->notify(new \App\Notifications\FailedJob([ + 'message' => $exception->getMessage(), + 'file' => $exception->getFile(), + 'line' => $exception->getLine(), + 'trace' => $exception->getTraceAsString() + ])); + } } diff --git a/app/Listeners/CaptureAndSendInternalError.php b/app/Listeners/CaptureAndSendInternalError.php index f7e0fe0e..3073c048 100644 --- a/app/Listeners/CaptureAndSendInternalError.php +++ b/app/Listeners/CaptureAndSendInternalError.php @@ -3,9 +3,7 @@ namespace App\Listeners; use App\Events\InternalError; -use App\Mail\InternalError as InternalErrorMail; -use Illuminate\Support\Facades\Config; -use Illuminate\Support\Facades\Mail; +use App\User; class CaptureAndSendInternalError { @@ -28,10 +26,8 @@ public function __construct() public function handle(InternalError $event) { if (isset($event->internal_error) && count($event->internal_error) > 0) { - Mail::to(Config::get('api.mail.internal-error.to'))-> - send( - new InternalErrorMail($event->internal_error) - ); + $user = User::query()->find(1); + $user->notify(new \App\Notifications\InternalError($event->internal_error)); } } } diff --git a/app/Listeners/CaptureAndSendRequestError.php b/app/Listeners/CaptureAndSendRequestError.php index def2ed00..6201f1a7 100644 --- a/app/Listeners/CaptureAndSendRequestError.php +++ b/app/Listeners/CaptureAndSendRequestError.php @@ -3,9 +3,7 @@ namespace App\Listeners; use App\Events\RequestError; -use App\Mail\RequestError as RequestErrorMail; -use Illuminate\Support\Facades\Config; -use Illuminate\Support\Facades\Mail; +use App\User; class CaptureAndSendRequestError { @@ -28,10 +26,8 @@ public function __construct() public function handle(RequestError $event) { if (isset($event->request_error) && count($event->request_error) > 0) { - Mail::to(Config::get('api.mail.request-error.to'))-> - send( - new RequestErrorMail($event->request_error) - ); + $user = User::query()->find(1); + $user->notify(new \App\Notifications\RequestError($event->request_error)); } } } diff --git a/app/Mail/InternalError.php b/app/Mail/InternalError.php deleted file mode 100644 index 1ca876ba..00000000 --- a/app/Mail/InternalError.php +++ /dev/null @@ -1,43 +0,0 @@ -api_from_mail = Config::get('api.mail.internal-error.from_mail'); - $this->api_from_name = Config::get('api.mail.internal-error.from_name'); - $this->internal_error = $internal_error; - } - - /** - * Build the message. - * - * @return $this - */ - public function build() - { - return $this->from($this->api_from_mail, $this->api_from_name)-> - view('mail.internal-error')-> - subject('Costs to Expect API: Internal error'); - } -} diff --git a/app/Mail/RequestError.php b/app/Mail/RequestError.php deleted file mode 100644 index 16a36331..00000000 --- a/app/Mail/RequestError.php +++ /dev/null @@ -1,43 +0,0 @@ -api_from_mail = Config::get('api.mail.request-error.from_mail'); - $this->api_from_name = Config::get('api.mail.request-error.from_name'); - $this->request_error = $request_error; - } - - /** - * Build the message. - * - * @return $this - */ - public function build() - { - return $this->from($this->api_from_mail, $this->api_from_name)-> - view('mail.request-error')-> - subject('Costs to Expect API: Request error'); - } -} diff --git a/app/Method/Method.php b/app/Method/Method.php index 3247d622..e1d8ab0d 100644 --- a/app/Method/Method.php +++ b/app/Method/Method.php @@ -10,16 +10,16 @@ */ abstract class Method { - protected bool $authentication; + protected string $authentication; - protected bool $authenticated; + protected string $authenticated; protected string $description; public function __construct() { - $this->authentication = false; - $this->authenticated = false; + $this->authentication = 'false'; + $this->authenticated = 'false'; $this->description = ''; } @@ -27,7 +27,11 @@ public function setAuthenticationRequirement( bool $status = false ): Method { - $this->authentication = $status; + if ($status === true) { + $this->authentication = 'true'; + } else { + $this->authentication = 'false'; + } return $this; } @@ -36,7 +40,11 @@ public function setAuthenticationStatus( bool $status = false ): Method { - $this->authenticated = $status; + if ($status === true) { + $this->authenticated = 'true'; + } else { + $this->authenticated = 'false'; + } return $this; } diff --git a/app/Models/ItemPartialTransfer.php b/app/Models/ItemPartialTransfer.php index c584e70f..1e5ce7af 100644 --- a/app/Models/ItemPartialTransfer.php +++ b/app/Models/ItemPartialTransfer.php @@ -137,4 +137,9 @@ public function total( return $collection->count(); } + + public function deleteTransfers(int $item_id): ?int + { + return $this->where($this->table . '.item_id', '=', $item_id)->delete(); + } } diff --git a/app/Models/PermittedUser.php b/app/Models/PermittedUser.php index c713199a..075822ab 100644 --- a/app/Models/PermittedUser.php +++ b/app/Models/PermittedUser.php @@ -24,13 +24,20 @@ class PermittedUser extends Model protected $guarded = ['id']; - public function instance(int $resource_type_id, int $user_id): ?PermittedUser + public function instanceByUserId(int $resource_type_id, int $user_id): ?PermittedUser { return $this->where('resource_type_id', '=', $resource_type_id)-> where('user_id', '=', $user_id)-> first(); } + public function instance(int $resource_type_id, int $permitted_user_id): ?PermittedUser + { + return $this->where('resource_type_id', '=', $resource_type_id)-> + where('id', '=', $permitted_user_id)-> + first(); + } + public function totalCount( int $resource_type_id, array $search_parameters = [] @@ -85,4 +92,25 @@ public function paginatedCollection( ->get() ->toArray(); } + + public function single(int $resource_type_id, int $permitted_user_id): ?array + { + $result = $this->select( + 'permitted_user.id AS permitted_user_id', + 'users.name AS permitted_user_name', + 'users.email AS permitted_user_email', + 'permitted_user.created_at AS permitted_user_created_at' + ) + ->join('users', 'permitted_user.user_id', 'users.id') + ->where('permitted_user.resource_type_id', '=', $resource_type_id) + ->where('permitted_user.id', '=', $permitted_user_id) + ->get() + ->toArray(); + + if (count($result) === 0) { + return null; + } + + return $result[0]; + } } diff --git a/app/Notifications/FailedJob.php b/app/Notifications/FailedJob.php new file mode 100644 index 00000000..4106924b --- /dev/null +++ b/app/Notifications/FailedJob.php @@ -0,0 +1,56 @@ +error = $error; + } + + public function via($notifiable) + { + return ['mail']; + } + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + return (new MailMessage) + ->subject('Costs to Expect API: Failed job') + ->greeting('Hi,') + ->line('A `ClearCache` job failed to run, details below') + ->line("Message: " . $this->error['message']) + ->line("File: " . $this->error['file']) + ->line("Line: " . $this->error['line']) + ->line("Trace: " . $this->error['trace']); + } + + /** + * Get the array representation of the notification. + * + * @param mixed $notifiable + * @return array + */ + public function toArray($notifiable) + { + return [ + // + ]; + } +} diff --git a/app/Notifications/InternalError.php b/app/Notifications/InternalError.php new file mode 100644 index 00000000..bfa6ccb8 --- /dev/null +++ b/app/Notifications/InternalError.php @@ -0,0 +1,56 @@ +error = $error; + } + + public function via($notifiable) + { + return ['mail']; + } + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + return (new MailMessage) + ->subject('Costs to Expect API: Internal Error') + ->greeting('Hi,') + ->line('There was an internal error, details below') + ->line("Message: " . $this->error['message']) + ->line("File: " . $this->error['file']) + ->line("Line: " . $this->error['line']) + ->line("Trace: " . $this->error['trace']); + } + + /** + * Get the array representation of the notification. + * + * @param mixed $notifiable + * @return array + */ + public function toArray($notifiable) + { + return [ + // + ]; + } +} diff --git a/app/Notifications/RequestError.php b/app/Notifications/RequestError.php new file mode 100644 index 00000000..98cd6a93 --- /dev/null +++ b/app/Notifications/RequestError.php @@ -0,0 +1,59 @@ +error = $error; + } + + public function via($notifiable) + { + return ['mail']; + } + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + return (new MailMessage) + ->subject('Costs to Expect API: Request Error') + ->greeting('Hi,') + ->line('There was a request error, details below') + ->line("Method: " . $this->error['method']) + ->line("Expected status code: " . $this->error['expected_status_code']) + ->line("Returned status code: " . $this->error['returned_status_code']) + ->line("Requested URI: " . $this->error['request_uri']) + ->line("Source: " . $this->error['source']) + ->line("Referer: " . $this->error['referer']) + ->line("Debug: " . $this->error['debug']); + } + + /** + * Get the array representation of the notification. + * + * @param mixed $notifiable + * @return array + */ + public function toArray($notifiable) + { + return [ + // + ]; + } +} diff --git a/app/Option/ItemTransferCollection.php b/app/Option/AllocatedExpenseCollection.php similarity index 91% rename from app/Option/ItemTransferCollection.php rename to app/Option/AllocatedExpenseCollection.php index 4ed55920..64936b30 100644 --- a/app/Option/ItemTransferCollection.php +++ b/app/Option/AllocatedExpenseCollection.php @@ -5,7 +5,7 @@ use Illuminate\Support\Facades\Config; -class ItemTransferCollection extends Response +class AllocatedExpenseCollection extends Response { public function create() { diff --git a/app/Option/Auth/Token.php b/app/Option/Auth/Token.php new file mode 100644 index 00000000..725ff562 --- /dev/null +++ b/app/Option/Auth/Token.php @@ -0,0 +1,26 @@ +verbs['GET'] = $get->setDescription('route-descriptions.auth_user_token_GET')-> + setAuthenticationRequirement(true)-> + setAuthenticationStatus($this->permissions['view'])-> + option(); + + $delete = new \App\Method\DeleteRequest(); + $this->verbs['DELETE'] = $delete->setDescription('route-descriptions.auth_user_token_DELETE')-> + setAuthenticationRequirement(true)-> + setAuthenticationStatus($this->permissions['manage'])-> + option(); + + return $this; + } +} diff --git a/app/Option/Auth/Tokens.php b/app/Option/Auth/Tokens.php new file mode 100644 index 00000000..d93ea103 --- /dev/null +++ b/app/Option/Auth/Tokens.php @@ -0,0 +1,22 @@ +verbs['GET'] = $get->setSortableParameters([])-> + setSearchableParameters([])-> + setDescription('route-descriptions.auth_user_tokens_GET')-> + setAuthenticationRequirement(true)-> + setAuthenticationStatus($this->permissions['view'])-> + option(); + + return $this; + } +} diff --git a/app/Option/ItemTransfer/AllocatedExpense.php b/app/Option/ItemTransfer/AllocatedExpense.php new file mode 100644 index 00000000..272c6d71 --- /dev/null +++ b/app/Option/ItemTransfer/AllocatedExpense.php @@ -0,0 +1,19 @@ +verbs['GET'] = $get->setDescription('route-descriptions.item_transfer_GET_show')-> + setAuthenticationStatus($this->permissions['view'])-> + option(); + + return $this; + } +} diff --git a/app/Option/ItemTransfer/AllocatedExpenseCollection.php b/app/Option/ItemTransfer/AllocatedExpenseCollection.php new file mode 100644 index 00000000..490ed112 --- /dev/null +++ b/app/Option/ItemTransfer/AllocatedExpenseCollection.php @@ -0,0 +1,22 @@ +verbs['GET'] = $get->setParameters(Config::get('api.item-transfer.parameters.collection'))-> + setPaginationStatus(true)-> + setAuthenticationStatus($this->permissions['view'])-> + setDescription('route-descriptions.item_transfer_GET_index')-> + option(); + + return $this; + } +} diff --git a/app/Option/ItemTransfer/AllocatedExpenseTransfer.php b/app/Option/ItemTransfer/AllocatedExpenseTransfer.php new file mode 100644 index 00000000..e9d184ff --- /dev/null +++ b/app/Option/ItemTransfer/AllocatedExpenseTransfer.php @@ -0,0 +1,23 @@ +verbs['POST'] = $post->setFields(Config::get('api.item-transfer.fields'))-> + setDynamicFields($this->allowed_fields)-> + setDescription('route-descriptions.item_transfer_POST')-> + setAuthenticationStatus($this->permissions['manage'])-> + setAuthenticationRequirement(true)-> + option(); + + return $this; + } +} diff --git a/app/Option/ItemTransferItem.php b/app/Option/ItemTransfer/SimpleExpense.php similarity index 77% rename from app/Option/ItemTransferItem.php rename to app/Option/ItemTransfer/SimpleExpense.php index cb8e00b4..f1914600 100644 --- a/app/Option/ItemTransferItem.php +++ b/app/Option/ItemTransfer/SimpleExpense.php @@ -1,9 +1,11 @@ verbs['GET'] = $get->setParameters(Config::get('api.item-transfer.parameters.collection'))-> + setPaginationStatus(true)-> + setAuthenticationStatus($this->permissions['view'])-> + setDescription('route-descriptions.item_transfer_GET_index')-> + option(); + + return $this; + } +} diff --git a/app/Option/ItemTransferTransfer.php b/app/Option/ItemTransfer/SimpleExpenseTransfer.php similarity index 83% rename from app/Option/ItemTransferTransfer.php rename to app/Option/ItemTransfer/SimpleExpenseTransfer.php index 0638db0e..82dd26a8 100644 --- a/app/Option/ItemTransferTransfer.php +++ b/app/Option/ItemTransfer/SimpleExpenseTransfer.php @@ -1,11 +1,12 @@ verbs['GET'] = $get->setParameters(Config::get('api.permitted-user.parameters.item'))-> + setAuthenticationStatus($this->permissions['view'])-> + setDescription('route-descriptions.permitted_user_GET_show')-> + option(); + + $delete = new \App\Method\DeleteRequest(); + $this->verbs['DELETE'] = $delete->setDescription('route-descriptions.permitted_user_DELETE')-> + setAuthenticationRequirement(true)-> + setAuthenticationStatus($this->permissions['manage'])-> + option(); + + return $this; + } +} diff --git a/app/Request/Parameter/Request.php b/app/Request/Parameter/Request.php index b3bc03d0..e2aafc79 100644 --- a/app/Request/Parameter/Request.php +++ b/app/Request/Parameter/Request.php @@ -41,6 +41,9 @@ private static function find(array $parameter_names = []): void case 'include-resources'; case 'include-categories': case 'include-subcategories'; + case 'include-permitted-users'; + case 'include-unpublished': + case 'complete': self::$parameters[$parameter] = Boolean::convertedValue($request_parameters[$parameter]); break; @@ -76,6 +79,7 @@ private static function validate(?int $resource_type_id, ?int $resource_id): voi case 'include-subcategories': case 'include-resources': case 'include-unpublished': + case 'include-permitted-users': case 'complete': if ( array_key_exists($key, self::$parameters) === true && diff --git a/app/Request/Validate/PermittedUser.php b/app/Request/Validate/PermittedUser.php new file mode 100644 index 00000000..c894880d --- /dev/null +++ b/app/Request/Validate/PermittedUser.php @@ -0,0 +1,39 @@ + + * @copyright Dean Blackborough 2018-2022 + * @license https://github.com/costs-to-expect/api/blob/master/LICENSE + */ +class PermittedUser extends BaseValidator +{ + public function create(array $options = []): \Illuminate\Contracts\Validation\Validator + { + $this->requiredIndexes(['resource_type_id'], $options); + + return ValidatorFacade::make( + request()->only(['email']), + [ + 'email' => [ + 'required', + 'email', + new PermissibleUser($options['resource_type_id']) + ], + + ], + $this->translateMessages('api.resource.validation.POST.messages') + ); + } + + public function update(array $options = []): ?\Illuminate\Contracts\Validation\Validator + { + return null; + } +} diff --git a/app/Rules/PermissibleUser.php b/app/Rules/PermissibleUser.php new file mode 100644 index 00000000..0f5d9e00 --- /dev/null +++ b/app/Rules/PermissibleUser.php @@ -0,0 +1,38 @@ +resource_type_id = $resource_type_id; + } + + public function passes($attribute, $value): bool + { + $exists = DB::table('users') + ->where('email', '=', $value) + ->count(); + + $not_assigned = DB::table('permitted_user') + ->join('users', 'permitted_user.user_id', '=', 'users.id') + ->where('permitted_user.resource_type_id', '=', $this->resource_type_id) + ->where('users.email', '=', $value) + ->count(); + + return $exists !== 0 && $not_assigned === 0; + } + + public function message(): string + { + return trans(Config::get('api.permitted-user.validation.POST.messages')['email.permissible']); + } +} diff --git a/app/Transformers/Category.php b/app/Transformers/Category.php index 6c471854..f449931a 100644 --- a/app/Transformers/Category.php +++ b/app/Transformers/Category.php @@ -4,8 +4,6 @@ namespace App\Transformers; /** - * Transform the data from our queries into the format we want to display - * * @author Dean Blackborough * @copyright Dean Blackborough 2018-2022 * @license https://github.com/costs-to-expect/api/blob/master/LICENSE @@ -14,21 +12,26 @@ class Category extends Transformer { public function format(array $to_transform): void { + $category_id = $this->hash->category()->encode($to_transform['category_id']); + $resource_type_id = $this->hash->resourceType()->encode($to_transform['resource_type_id']); + $this->transformed = [ - 'id' => $this->hash->category()->encode($to_transform['category_id']), + 'id' => $category_id, 'name' => $to_transform['category_name'], 'description' => $to_transform['category_description'], 'created' => $to_transform['category_created_at'], 'resource_type' => [ - 'id' => $this->hash->resourceType()->encode($to_transform['resource_type_id']) + 'uri' => route('resource-type.show', ['resource_type_id' => $resource_type_id], false), + 'id' => $resource_type_id ] ]; if (array_key_exists('resource_type_name', $to_transform) === true) { - $this->transformed['resource_type']['name'] = $to_transform['resource_type_name']; + $this->transformed['resource_type']['name'] = $to_transform['resource_type_name']; } if (array_key_exists('category_subcategories', $to_transform)) { + $this->transformed['subcategories']['uri'] = route('subcategory.list', ['resource_type_id' => $resource_type_id, 'category_id' => $category_id], false); $this->transformed['subcategories']['count'] = $to_transform['category_subcategories']; } diff --git a/app/Transformers/Currency.php b/app/Transformers/Currency.php index 086ed8f3..32608475 100644 --- a/app/Transformers/Currency.php +++ b/app/Transformers/Currency.php @@ -4,8 +4,6 @@ namespace App\Transformers; /** - * Transform the data from our queries into the format we want to display - * * @author Dean Blackborough * @copyright Dean Blackborough 2018-2022 * @license https://github.com/costs-to-expect/api/blob/master/LICENSE diff --git a/app/Transformers/ItemCategory.php b/app/Transformers/ItemCategory.php index 19cdb034..421c3a40 100644 --- a/app/Transformers/ItemCategory.php +++ b/app/Transformers/ItemCategory.php @@ -4,8 +4,6 @@ namespace App\Transformers; /** - * Transform the data from our queries into the format we want to display - * * @author Dean Blackborough * @copyright Dean Blackborough 2018-2022 * @license https://github.com/costs-to-expect/api/blob/master/LICENSE diff --git a/app/Transformers/ItemPartialTransfer.php b/app/Transformers/ItemPartialTransfer.php index 0e3ca554..d62658ee 100644 --- a/app/Transformers/ItemPartialTransfer.php +++ b/app/Transformers/ItemPartialTransfer.php @@ -4,8 +4,6 @@ namespace App\Transformers; /** - * Transform the data from our queries into the format we want to display - * * @author Dean Blackborough * @copyright Dean Blackborough 2018-2022 * @license https://github.com/costs-to-expect/api/blob/master/LICENSE diff --git a/app/Transformers/ItemSubcategory.php b/app/Transformers/ItemSubcategory.php index cdced15c..57eaf99a 100644 --- a/app/Transformers/ItemSubcategory.php +++ b/app/Transformers/ItemSubcategory.php @@ -4,8 +4,6 @@ namespace App\Transformers; /** - * Transform the data from our queries into the format we want to display - * * @author Dean Blackborough * @copyright Dean Blackborough 2018-2022 * @license https://github.com/costs-to-expect/api/blob/master/LICENSE diff --git a/app/Transformers/ItemSubtype.php b/app/Transformers/ItemSubtype.php index 46bcca18..8a573a0b 100644 --- a/app/Transformers/ItemSubtype.php +++ b/app/Transformers/ItemSubtype.php @@ -4,8 +4,6 @@ namespace App\Transformers; /** - * Transform the data from our queries into the format we want to display - * * @author Dean Blackborough * @copyright Dean Blackborough 2018-2022 * @license https://github.com/costs-to-expect/api/blob/master/LICENSE diff --git a/app/Transformers/ItemTransfer.php b/app/Transformers/ItemTransfer.php index 1915154a..a8ceb676 100644 --- a/app/Transformers/ItemTransfer.php +++ b/app/Transformers/ItemTransfer.php @@ -4,8 +4,6 @@ namespace App\Transformers; /** - * Transform the data from our queries into the format we want to display - * * @author Dean Blackborough * @copyright Dean Blackborough 2018-2022 * @license https://github.com/costs-to-expect/api/blob/master/LICENSE diff --git a/app/Transformers/ItemType.php b/app/Transformers/ItemType.php index 900ade08..9b9ebc53 100644 --- a/app/Transformers/ItemType.php +++ b/app/Transformers/ItemType.php @@ -4,8 +4,6 @@ namespace App\Transformers; /** - * Transform the data from our queries into the format we want to display - * * @author Dean Blackborough * @copyright Dean Blackborough 2018-2022 * @license https://github.com/costs-to-expect/api/blob/master/LICENSE diff --git a/app/Transformers/PermittedUser.php b/app/Transformers/PermittedUser.php index 438a6f67..e80dd590 100644 --- a/app/Transformers/PermittedUser.php +++ b/app/Transformers/PermittedUser.php @@ -4,8 +4,6 @@ namespace App\Transformers; /** - * Transform the data from our queries into the format we want to display - * * @author Dean Blackborough * @copyright Dean Blackborough 2018-2022 * @license https://github.com/costs-to-expect/api/blob/master/LICENSE diff --git a/app/Transformers/Queue.php b/app/Transformers/Queue.php index 87168664..43ff14a2 100644 --- a/app/Transformers/Queue.php +++ b/app/Transformers/Queue.php @@ -4,8 +4,6 @@ namespace App\Transformers; /** - * Transform the data from our queries into the format we want to display - * * @author Dean Blackborough * @copyright Dean Blackborough 2018-2022 * @license https://github.com/costs-to-expect/api/blob/master/LICENSE diff --git a/app/Transformers/RequestErrorLog.php b/app/Transformers/RequestErrorLog.php index 79863890..cadcc958 100644 --- a/app/Transformers/RequestErrorLog.php +++ b/app/Transformers/RequestErrorLog.php @@ -4,8 +4,6 @@ namespace App\Transformers; /** - * Transform the data from our queries into the format we want to display - * * @author Dean Blackborough * @copyright Dean Blackborough 2018-2022 * @license https://github.com/costs-to-expect/api/blob/master/LICENSE diff --git a/app/Transformers/Resource.php b/app/Transformers/Resource.php index 6cb9b755..d386c2d6 100644 --- a/app/Transformers/Resource.php +++ b/app/Transformers/Resource.php @@ -4,8 +4,6 @@ namespace App\Transformers; /** - * Transform the data from our queries into the format we want to display - * * @author Dean Blackborough * @copyright Dean Blackborough 2018-2022 * @license https://github.com/costs-to-expect/api/blob/master/LICENSE diff --git a/app/Transformers/ResourceType.php b/app/Transformers/ResourceType.php index 8849f02a..bd18b220 100644 --- a/app/Transformers/ResourceType.php +++ b/app/Transformers/ResourceType.php @@ -6,8 +6,6 @@ use App\Transformers\Resource as ResourceTransformer; /** - * Transform the data from our queries into the format we want to display - * * @author Dean Blackborough * @copyright Dean Blackborough 2018-2022 * @license https://github.com/costs-to-expect/api/blob/master/LICENSE @@ -28,8 +26,10 @@ public function format(array $to_transform): void ]; } + $resource_type_id = $this->hash->resourceType()->encode($to_transform['resource_type_id']); + $this->transformed = [ - 'id' => $this->hash->resourceType()->encode($to_transform['resource_type_id']), + 'id' => $resource_type_id, 'name' => $to_transform['resource_type_name'], 'description' => $to_transform['resource_type_description'], 'data' => $data, @@ -42,8 +42,10 @@ public function format(array $to_transform): void array_key_exists('resource_type_item_type_name', $to_transform) === true && array_key_exists('resource_type_item_type_description', $to_transform) === true ) { + $id = $this->hash->itemType()->encode($to_transform['resource_type_item_type_id']); $this->transformed['item_type'] = [ - 'id' => $this->hash->itemType()->encode($to_transform['resource_type_item_type_id']), + 'uri' => route('item-type.show', ['item_type_id' => $id], false), + 'id' => $id, 'name' => $to_transform['resource_type_item_type_name'], 'friendly_name' => $to_transform['resource_type_item_type_friendly_name'], 'description' => $to_transform['resource_type_item_type_description'] @@ -51,6 +53,7 @@ public function format(array $to_transform): void } if (array_key_exists('resource_type_resources', $to_transform)) { + $this->transformed['resources']['uri'] = route('resource.list', ['resource_type_id' => $resource_type_id], false); $this->transformed['resources']['count'] = $to_transform['resource_type_resources']; } @@ -59,5 +62,13 @@ public function format(array $to_transform): void $this->transformed['resources']['collection'][] = (new ResourceTransformer($resource))->asArray(); } } + + if (array_key_exists('permitted_users', $this->related) === true) { + $this->transformed['permitted_users']['uri'] = route('permitted-user.list', ['resource_type_id' => $resource_type_id], false); + $this->transformed['permitted_users']['count'] = count($this->related['permitted_users']); + foreach ($this->related['permitted_users'] as $permitted_user) { + $this->transformed['permitted_users']['collection'][] = (new PermittedUser($permitted_user))->asArray(); + } + } } } diff --git a/app/Transformers/ResourceTypeItem.php b/app/Transformers/ResourceTypeItem.php deleted file mode 100644 index 014d33a9..00000000 --- a/app/Transformers/ResourceTypeItem.php +++ /dev/null @@ -1,55 +0,0 @@ - - * @copyright Dean Blackborough 2018-2022 - * @license https://github.com/costs-to-expect/api/blob/master/LICENSE - */ -class ResourceTypeItem extends Transformer -{ - protected function format(array $to_transform): void - { - $this->transformed = [ - 'id' => $this->hash->item()->encode($to_transform['item_id']), - 'name' => $to_transform['item_name'], - 'description' => $to_transform['item_description'], - 'total' => number_format((float) $to_transform['item_total'], 2, '.', ''), - 'percentage' => (int) $to_transform['item_percentage'], - 'actualised_total' => number_format((float) $to_transform['item_actualised_total'], 2, '.', ''), - 'effective_date' => $to_transform['item_effective_date'], - 'created' => $to_transform['item_created_at'], - 'resource' => [ - 'id' => $this->hash->resource()->encode($to_transform['resource_id']), - 'name' => $to_transform['resource_name'], - 'description' => $to_transform['resource_description'] - ] - ]; - - if ( - array_key_exists('category_id', $to_transform) === true && - array_key_exists('category_name', $to_transform) === true - ) { - $item['category'] = [ - 'id' => $this->hash->category()->encode($to_transform['category_id']), - 'name' => $to_transform['category_name'], - 'description' => $to_transform['category_description'] - ]; - - if ( - array_key_exists('subcategory_id', $to_transform) === true && - array_key_exists('subcategory_name', $to_transform) === true - ) { - $item['subcategory'] = [ - 'id' => $this->hash->subcategory()->encode($to_transform['subcategory_id']), - 'name' => $to_transform['subcategory_name'], - 'description' => $to_transform['subcategory_description'] - ]; - } - } - } -} diff --git a/app/Transformers/Subcategory.php b/app/Transformers/Subcategory.php index fb2f1917..82a1758a 100644 --- a/app/Transformers/Subcategory.php +++ b/app/Transformers/Subcategory.php @@ -4,8 +4,6 @@ namespace App\Transformers; /** - * Transform the data from our queries into the format we want to display - * * @author Dean Blackborough * @copyright Dean Blackborough 2018-2022 * @license https://github.com/costs-to-expect/api/blob/master/LICENSE diff --git a/app/Transformers/Transformer.php b/app/Transformers/Transformer.php index 4e9fbe44..7e7f10ab 100644 --- a/app/Transformers/Transformer.php +++ b/app/Transformers/Transformer.php @@ -21,10 +21,6 @@ abstract class Transformer protected array $transformed; - /** - * @param array $to_transform - * @param array $related Pass in optional related data arrays - */ public function __construct(array $to_transform, array $related = []) { $this->hash = new Hash(); @@ -35,11 +31,6 @@ public function __construct(array $to_transform, array $related = []) $this->format($to_transform); } - /** - * Format the data - * - * @param array $to_transform - */ abstract protected function format(array $to_transform): void; public function asJson(): ?string diff --git a/app/User.php b/app/User.php index fa7bf7d5..08971868 100644 --- a/app/User.php +++ b/app/User.php @@ -35,4 +35,9 @@ class User extends Authenticatable protected $hidden = [ 'password', 'remember_token', ]; + + public function revokeOldTokens(): void + { + $this->tokens()->where('last_used_at', '<', now()->subYear())->delete(); + } } diff --git a/composer.lock b/composer.lock index b45419dd..9e5e0230 100644 --- a/composer.lock +++ b/composer.lock @@ -242,16 +242,16 @@ }, { "name": "doctrine/dbal", - "version": "2.13.7", + "version": "2.13.8", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "6e22f6012b42d7932674857989fcf184e9e9b1c3" + "reference": "dc9b3c3c8592c935a6e590441f9abc0f9eba335b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/6e22f6012b42d7932674857989fcf184e9e9b1c3", - "reference": "6e22f6012b42d7932674857989fcf184e9e9b1c3", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/dc9b3c3c8592c935a6e590441f9abc0f9eba335b", + "reference": "dc9b3c3c8592c935a6e590441f9abc0f9eba335b", "shasum": "" }, "require": { @@ -264,13 +264,13 @@ "require-dev": { "doctrine/coding-standard": "9.0.0", "jetbrains/phpstorm-stubs": "2021.1", - "phpstan/phpstan": "1.3.0", - "phpunit/phpunit": "^7.5.20|^8.5|9.5.11", + "phpstan/phpstan": "1.4.6", + "phpunit/phpunit": "^7.5.20|^8.5|9.5.16", "psalm/plugin-phpunit": "0.16.1", "squizlabs/php_codesniffer": "3.6.2", "symfony/cache": "^4.4", "symfony/console": "^2.0.5|^3.0|^4.0|^5.0", - "vimeo/psalm": "4.16.1" + "vimeo/psalm": "4.22.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." @@ -331,7 +331,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/2.13.7" + "source": "https://github.com/doctrine/dbal/tree/2.13.8" }, "funding": [ { @@ -347,7 +347,7 @@ "type": "tidelift" } ], - "time": "2022-01-06T09:08:04+00:00" + "time": "2022-03-09T15:25:46+00:00" }, { "name": "doctrine/deprecations", @@ -579,16 +579,16 @@ }, { "name": "doctrine/lexer", - "version": "1.2.2", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "9c50f840f257bbb941e6f4a0e94ccf5db5c3f76c" + "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/9c50f840f257bbb941e6f4a0e94ccf5db5c3f76c", - "reference": "9c50f840f257bbb941e6f4a0e94ccf5db5c3f76c", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/c268e882d4dbdd85e36e4ad69e02dc284f89d229", + "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229", "shasum": "" }, "require": { @@ -596,7 +596,7 @@ }, "require-dev": { "doctrine/coding-standard": "^9.0", - "phpstan/phpstan": "1.3", + "phpstan/phpstan": "^1.3", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", "vimeo/psalm": "^4.11" }, @@ -635,7 +635,7 @@ ], "support": { "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/1.2.2" + "source": "https://github.com/doctrine/lexer/tree/1.2.3" }, "funding": [ { @@ -651,33 +651,33 @@ "type": "tidelift" } ], - "time": "2022-01-12T08:27:12+00:00" + "time": "2022-02-28T11:07:21+00:00" }, { "name": "dragonmantank/cron-expression", - "version": "v3.2.4", + "version": "v3.3.1", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "9545dea2a1d92b60c8b3d06f02025c83e999bde0" + "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/9545dea2a1d92b60c8b3d06f02025c83e999bde0", - "reference": "9545dea2a1d92b60c8b3d06f02025c83e999bde0", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/be85b3f05b46c39bbc0d95f6c071ddff669510fa", + "reference": "be85b3f05b46c39bbc0d95f6c071ddff669510fa", "shasum": "" }, "require": { "php": "^7.2|^8.0", - "webmozart/assert": "^1.7.0" + "webmozart/assert": "^1.0" }, "replace": { "mtdowling/cron-expression": "^1.0" }, "require-dev": { "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-webmozart-assert": "^0.12.7", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-webmozart-assert": "^1.0", "phpunit/phpunit": "^7.0|^8.0|^9.0" }, "type": "library", @@ -704,7 +704,7 @@ ], "support": { "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.2.4" + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.1" }, "funding": [ { @@ -712,7 +712,7 @@ "type": "github" } ], - "time": "2022-01-13T04:09:37+00:00" + "time": "2022-01-18T15:43:28+00:00" }, { "name": "egulias/email-validator", @@ -904,16 +904,16 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.4.1", + "version": "7.4.2", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "ee0a041b1760e6a53d2a39c8c34115adc2af2c79" + "reference": "ac1ec1cd9b5624694c3a40be801d94137afb12b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ee0a041b1760e6a53d2a39c8c34115adc2af2c79", - "reference": "ee0a041b1760e6a53d2a39c8c34115adc2af2c79", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ac1ec1cd9b5624694c3a40be801d94137afb12b4", + "reference": "ac1ec1cd9b5624694c3a40be801d94137afb12b4", "shasum": "" }, "require": { @@ -946,12 +946,12 @@ } }, "autoload": { - "psr-4": { - "GuzzleHttp\\": "src/" - }, "files": [ "src/functions_include.php" - ] + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1008,7 +1008,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.4.1" + "source": "https://github.com/guzzle/guzzle/tree/7.4.2" }, "funding": [ { @@ -1024,7 +1024,7 @@ "type": "tidelift" } ], - "time": "2021-12-06T18:43:05+00:00" + "time": "2022-03-20T14:16:28+00:00" }, { "name": "guzzlehttp/promises", @@ -1053,12 +1053,12 @@ } }, "autoload": { - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - }, "files": [ "src/functions_include.php" - ] + ], + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1112,16 +1112,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "2.1.0", + "version": "2.2.1", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "089edd38f5b8abba6cb01567c2a8aaa47cec4c72" + "reference": "c94a94f120803a18554c1805ef2e539f8285f9a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/089edd38f5b8abba6cb01567c2a8aaa47cec4c72", - "reference": "089edd38f5b8abba6cb01567c2a8aaa47cec4c72", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/c94a94f120803a18554c1805ef2e539f8285f9a2", + "reference": "c94a94f120803a18554c1805ef2e539f8285f9a2", "shasum": "" }, "require": { @@ -1145,7 +1145,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "2.2-dev" } }, "autoload": { @@ -1207,7 +1207,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.1.0" + "source": "https://github.com/guzzle/psr7/tree/2.2.1" }, "funding": [ { @@ -1223,7 +1223,7 @@ "type": "tidelift" } ], - "time": "2021-10-06T17:43:30+00:00" + "time": "2022-03-20T21:55:58+00:00" }, { "name": "hashids/hashids", @@ -1297,16 +1297,16 @@ }, { "name": "laravel/framework", - "version": "v8.79.0", + "version": "v8.83.7", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "8091f07558ff4a890435ff9d25fa9aca0189ad63" + "reference": "0706abf5c99d3b14149fcf79c95074dadd40b203" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/8091f07558ff4a890435ff9d25fa9aca0189ad63", - "reference": "8091f07558ff4a890435ff9d25fa9aca0189ad63", + "url": "https://api.github.com/repos/laravel/framework/zipball/0706abf5c99d3b14149fcf79c95074dadd40b203", + "reference": "0706abf5c99d3b14149fcf79c95074dadd40b203", "shasum": "" }, "require": { @@ -1339,7 +1339,7 @@ "symfony/var-dumper": "^5.4", "tijsverkoyen/css-to-inline-styles": "^2.2.2", "vlucas/phpdotenv": "^5.4.1", - "voku/portable-ascii": "^1.4.8" + "voku/portable-ascii": "^1.6.1" }, "conflict": { "tightenco/collect": "<5.5.33" @@ -1466,7 +1466,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2022-01-12T16:12:41+00:00" + "time": "2022-04-05T15:08:10+00:00" }, { "name": "laravel/helpers", @@ -1526,20 +1526,21 @@ }, { "name": "laravel/sanctum", - "version": "v2.14.0", + "version": "v2.15.0", "source": { "type": "git", "url": "https://github.com/laravel/sanctum.git", - "reference": "0647a87140c7522e75826cffcadb3ad6e01f71e9" + "reference": "5be160413b6f37dcf8758663edeab12d0e806f56" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sanctum/zipball/0647a87140c7522e75826cffcadb3ad6e01f71e9", - "reference": "0647a87140c7522e75826cffcadb3ad6e01f71e9", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/5be160413b6f37dcf8758663edeab12d0e806f56", + "reference": "5be160413b6f37dcf8758663edeab12d0e806f56", "shasum": "" }, "require": { "ext-json": "*", + "illuminate/console": "^6.9|^7.0|^8.0|^9.0", "illuminate/contracts": "^6.9|^7.0|^8.0|^9.0", "illuminate/database": "^6.9|^7.0|^8.0|^9.0", "illuminate/support": "^6.9|^7.0|^8.0|^9.0", @@ -1586,20 +1587,20 @@ "issues": "https://github.com/laravel/sanctum/issues", "source": "https://github.com/laravel/sanctum" }, - "time": "2022-01-12T15:07:43+00:00" + "time": "2022-03-28T13:53:05+00:00" }, { "name": "laravel/serializable-closure", - "version": "v1.0.5", + "version": "v1.1.1", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "25de3be1bca1b17d52ff0dc02b646c667ac7266c" + "reference": "9e4b005daa20b0c161f3845040046dc9ddc1d74e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/25de3be1bca1b17d52ff0dc02b646c667ac7266c", - "reference": "25de3be1bca1b17d52ff0dc02b646c667ac7266c", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/9e4b005daa20b0c161f3845040046dc9ddc1d74e", + "reference": "9e4b005daa20b0c161f3845040046dc9ddc1d74e", "shasum": "" }, "require": { @@ -1645,20 +1646,20 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2021-11-30T15:53:04+00:00" + "time": "2022-02-11T19:23:53+00:00" }, { "name": "laravel/tinker", - "version": "v2.7.0", + "version": "v2.7.2", "source": { "type": "git", "url": "https://github.com/laravel/tinker.git", - "reference": "5f2f9815b7631b9f586a3de7933c25f9327d4073" + "reference": "dff39b661e827dae6e092412f976658df82dbac5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/tinker/zipball/5f2f9815b7631b9f586a3de7933c25f9327d4073", - "reference": "5f2f9815b7631b9f586a3de7933c25f9327d4073", + "url": "https://api.github.com/repos/laravel/tinker/zipball/dff39b661e827dae6e092412f976658df82dbac5", + "reference": "dff39b661e827dae6e092412f976658df82dbac5", "shasum": "" }, "require": { @@ -1711,22 +1712,22 @@ ], "support": { "issues": "https://github.com/laravel/tinker/issues", - "source": "https://github.com/laravel/tinker/tree/v2.7.0" + "source": "https://github.com/laravel/tinker/tree/v2.7.2" }, - "time": "2022-01-10T08:52:49+00:00" + "time": "2022-03-23T12:38:24+00:00" }, { "name": "league/commonmark", - "version": "2.1.1", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "17d2b9cb5161a2ea1a8dd30e6991d668e503fb9d" + "reference": "32a49eb2b38fe5e5c417ab748a45d0beaab97955" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/17d2b9cb5161a2ea1a8dd30e6991d668e503fb9d", - "reference": "17d2b9cb5161a2ea1a8dd30e6991d668e503fb9d", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/32a49eb2b38fe5e5c417ab748a45d0beaab97955", + "reference": "32a49eb2b38fe5e5c417ab748a45d0beaab97955", "shasum": "" }, "require": { @@ -1734,17 +1735,20 @@ "league/config": "^1.1.1", "php": "^7.4 || ^8.0", "psr/event-dispatcher": "^1.0", - "symfony/polyfill-php80": "^1.15" + "symfony/deprecation-contracts": "^2.1 || ^3.0", + "symfony/polyfill-php80": "^1.16" }, "require-dev": { "cebe/markdown": "^1.0", "commonmark/cmark": "0.30.0", "commonmark/commonmark.js": "0.30.0", "composer/package-versions-deprecated": "^1.8", + "embed/embed": "^4.4", "erusev/parsedown": "^1.0", "ext-json": "*", "github/gfm": "0.29.0", "michelf/php-markdown": "^1.4", + "nyholm/psr7": "^1.5", "phpstan/phpstan": "^0.12.88 || ^1.0.0", "phpunit/phpunit": "^9.5.5", "scrutinizer/ocular": "^1.8.1", @@ -1759,7 +1763,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.2-dev" + "dev-main": "2.4-dev" } }, "autoload": { @@ -1816,7 +1820,7 @@ "type": "tidelift" } ], - "time": "2022-01-02T18:25:06+00:00" + "time": "2022-04-07T22:37:05+00:00" }, { "name": "league/config", @@ -1996,16 +2000,16 @@ }, { "name": "league/mime-type-detection", - "version": "1.9.0", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/thephpleague/mime-type-detection.git", - "reference": "aa70e813a6ad3d1558fc927863d47309b4c23e69" + "reference": "3e4a35d756eedc67096f30240a68a3149120dae7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/aa70e813a6ad3d1558fc927863d47309b4c23e69", - "reference": "aa70e813a6ad3d1558fc927863d47309b4c23e69", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/3e4a35d756eedc67096f30240a68a3149120dae7", + "reference": "3e4a35d756eedc67096f30240a68a3149120dae7", "shasum": "" }, "require": { @@ -2036,7 +2040,7 @@ "description": "Mime-type detection for Flysystem", "support": { "issues": "https://github.com/thephpleague/mime-type-detection/issues", - "source": "https://github.com/thephpleague/mime-type-detection/tree/1.9.0" + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.10.0" }, "funding": [ { @@ -2048,20 +2052,20 @@ "type": "tidelift" } ], - "time": "2021-11-21T11:48:40+00:00" + "time": "2022-04-11T12:49:04+00:00" }, { "name": "monolog/monolog", - "version": "2.3.5", + "version": "2.5.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "fd4380d6fc37626e2f799f29d91195040137eba9" + "reference": "4192345e260f1d51b365536199744b987e160edc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/fd4380d6fc37626e2f799f29d91195040137eba9", - "reference": "fd4380d6fc37626e2f799f29d91195040137eba9", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/4192345e260f1d51b365536199744b987e160edc", + "reference": "4192345e260f1d51b365536199744b987e160edc", "shasum": "" }, "require": { @@ -2083,7 +2087,7 @@ "phpstan/phpstan": "^0.12.91", "phpunit/phpunit": "^8.5", "predis/predis": "^1.1", - "rollbar/rollbar": "^1.3", + "rollbar/rollbar": "^1.3 || ^2 || ^3", "ruflin/elastica": ">=0.90@dev", "swiftmailer/swiftmailer": "^5.3|^6.0" }, @@ -2135,7 +2139,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/2.3.5" + "source": "https://github.com/Seldaek/monolog/tree/2.5.0" }, "funding": [ { @@ -2147,20 +2151,20 @@ "type": "tidelift" } ], - "time": "2021-10-01T21:08:31+00:00" + "time": "2022-04-08T15:43:54+00:00" }, { "name": "nesbot/carbon", - "version": "2.55.2", + "version": "2.57.0", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "8c2a18ce3e67c34efc1b29f64fe61304368259a2" + "reference": "4a54375c21eea4811dbd1149fe6b246517554e78" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/8c2a18ce3e67c34efc1b29f64fe61304368259a2", - "reference": "8c2a18ce3e67c34efc1b29f64fe61304368259a2", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/4a54375c21eea4811dbd1149fe6b246517554e78", + "reference": "4a54375c21eea4811dbd1149fe6b246517554e78", "shasum": "" }, "require": { @@ -2177,7 +2181,7 @@ "kylekatarnls/multi-tester": "^2.0", "phpmd/phpmd": "^2.9", "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^0.12.54", + "phpstan/phpstan": "^0.12.54 || ^1.0", "phpunit/phpunit": "^7.5.20 || ^8.5.14", "squizlabs/php_codesniffer": "^3.4" }, @@ -2243,7 +2247,7 @@ "type": "tidelift" } ], - "time": "2021-12-03T14:59:52+00:00" + "time": "2022-02-13T18:13:33+00:00" }, { "name": "nette/schema", @@ -2309,16 +2313,16 @@ }, { "name": "nette/utils", - "version": "v3.2.6", + "version": "v3.2.7", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "2f261e55bd6a12057442045bf2c249806abc1d02" + "reference": "0af4e3de4df9f1543534beab255ccf459e7a2c99" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/2f261e55bd6a12057442045bf2c249806abc1d02", - "reference": "2f261e55bd6a12057442045bf2c249806abc1d02", + "url": "https://api.github.com/repos/nette/utils/zipball/0af4e3de4df9f1543534beab255ccf459e7a2c99", + "reference": "0af4e3de4df9f1543534beab255ccf459e7a2c99", "shasum": "" }, "require": { @@ -2388,9 +2392,9 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v3.2.6" + "source": "https://github.com/nette/utils/tree/v3.2.7" }, - "time": "2021-11-24T15:47:23+00:00" + "time": "2022-01-24T11:29:14+00:00" }, { "name": "nikic/php-parser", @@ -2450,16 +2454,16 @@ }, { "name": "opis/closure", - "version": "3.6.2", + "version": "3.6.3", "source": { "type": "git", "url": "https://github.com/opis/closure.git", - "reference": "06e2ebd25f2869e54a306dda991f7db58066f7f6" + "reference": "3d81e4309d2a927abbe66df935f4bb60082805ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opis/closure/zipball/06e2ebd25f2869e54a306dda991f7db58066f7f6", - "reference": "06e2ebd25f2869e54a306dda991f7db58066f7f6", + "url": "https://api.github.com/repos/opis/closure/zipball/3d81e4309d2a927abbe66df935f4bb60082805ad", + "reference": "3d81e4309d2a927abbe66df935f4bb60082805ad", "shasum": "" }, "require": { @@ -2476,12 +2480,12 @@ } }, "autoload": { - "psr-4": { - "Opis\\Closure\\": "src/" - }, "files": [ "functions.php" - ] + ], + "psr-4": { + "Opis\\Closure\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2509,9 +2513,9 @@ ], "support": { "issues": "https://github.com/opis/closure/issues", - "source": "https://github.com/opis/closure/tree/3.6.2" + "source": "https://github.com/opis/closure/tree/3.6.3" }, - "time": "2021-04-09T13:42:10+00:00" + "time": "2022-01-27T09:35:39+00:00" }, { "name": "opis/json-schema", @@ -3012,16 +3016,16 @@ }, { "name": "psy/psysh", - "version": "v0.11.1", + "version": "v0.11.2", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "570292577277f06f590635381a7f761a6cf4f026" + "reference": "7f7da640d68b9c9fec819caae7c744a213df6514" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/570292577277f06f590635381a7f761a6cf4f026", - "reference": "570292577277f06f590635381a7f761a6cf4f026", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/7f7da640d68b9c9fec819caae7c744a213df6514", + "reference": "7f7da640d68b9c9fec819caae7c744a213df6514", "shasum": "" }, "require": { @@ -3032,6 +3036,9 @@ "symfony/console": "^6.0 || ^5.0 || ^4.0 || ^3.4", "symfony/var-dumper": "^6.0 || ^5.0 || ^4.0 || ^3.4" }, + "conflict": { + "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" + }, "require-dev": { "bamarni/composer-bin-plugin": "^1.2", "hoa/console": "3.17.05.02" @@ -3081,9 +3088,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.11.1" + "source": "https://github.com/bobthecow/psysh/tree/v0.11.2" }, - "time": "2022-01-03T13:58:38+00:00" + "time": "2022-02-28T15:28:54+00:00" }, { "name": "ralouphie/getallheaders", @@ -3210,25 +3217,24 @@ }, { "name": "ramsey/uuid", - "version": "4.2.3", + "version": "4.3.1", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df" + "reference": "8505afd4fea63b81a85d3b7b53ac3cb8dc347c28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df", - "reference": "fc9bb7fb5388691fd7373cd44dcb4d63bbcf24df", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/8505afd4fea63b81a85d3b7b53ac3cb8dc347c28", + "reference": "8505afd4fea63b81a85d3b7b53ac3cb8dc347c28", "shasum": "" }, "require": { "brick/math": "^0.8 || ^0.9", + "ext-ctype": "*", "ext-json": "*", - "php": "^7.2 || ^8.0", - "ramsey/collection": "^1.0", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-php80": "^1.14" + "php": "^8.0", + "ramsey/collection": "^1.0" }, "replace": { "rhumsaa/uuid": "self.version" @@ -3265,20 +3271,17 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "4.x-dev" - }, "captainhook": { "force-install": true } }, "autoload": { - "psr-4": { - "Ramsey\\Uuid\\": "src/" - }, "files": [ "src/functions.php" - ] + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3292,7 +3295,7 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.2.3" + "source": "https://github.com/ramsey/uuid/tree/4.3.1" }, "funding": [ { @@ -3304,7 +3307,7 @@ "type": "tidelift" } ], - "time": "2021-09-25T23:10:38+00:00" + "time": "2022-03-27T21:42:02+00:00" }, { "name": "swiftmailer/swiftmailer", @@ -3384,16 +3387,16 @@ }, { "name": "symfony/console", - "version": "v5.4.2", + "version": "v5.4.7", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "a2c6b7ced2eb7799a35375fb9022519282b5405e" + "reference": "900275254f0a1a2afff1ab0e11abd5587a10e1d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/a2c6b7ced2eb7799a35375fb9022519282b5405e", - "reference": "a2c6b7ced2eb7799a35375fb9022519282b5405e", + "url": "https://api.github.com/repos/symfony/console/zipball/900275254f0a1a2afff1ab0e11abd5587a10e1d6", + "reference": "900275254f0a1a2afff1ab0e11abd5587a10e1d6", "shasum": "" }, "require": { @@ -3463,7 +3466,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.2" + "source": "https://github.com/symfony/console/tree/v5.4.7" }, "funding": [ { @@ -3479,20 +3482,20 @@ "type": "tidelift" } ], - "time": "2021-12-20T16:11:12+00:00" + "time": "2022-03-31T17:09:19+00:00" }, { "name": "symfony/css-selector", - "version": "v6.0.2", + "version": "v6.0.3", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "380f86c1a9830226f42a08b5926f18aed4195f25" + "reference": "1955d595c12c111629cc814d3f2a2ff13580508a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/380f86c1a9830226f42a08b5926f18aed4195f25", - "reference": "380f86c1a9830226f42a08b5926f18aed4195f25", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/1955d595c12c111629cc814d3f2a2ff13580508a", + "reference": "1955d595c12c111629cc814d3f2a2ff13580508a", "shasum": "" }, "require": { @@ -3528,7 +3531,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v6.0.2" + "source": "https://github.com/symfony/css-selector/tree/v6.0.3" }, "funding": [ { @@ -3544,20 +3547,20 @@ "type": "tidelift" } ], - "time": "2021-12-16T22:13:01+00:00" + "time": "2022-01-02T09:55:41+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.0.0", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "c726b64c1ccfe2896cb7df2e1331c357ad1c8ced" + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/c726b64c1ccfe2896cb7df2e1331c357ad1c8ced", - "reference": "c726b64c1ccfe2896cb7df2e1331c357ad1c8ced", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", "shasum": "" }, "require": { @@ -3595,7 +3598,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.1" }, "funding": [ { @@ -3611,20 +3614,20 @@ "type": "tidelift" } ], - "time": "2021-11-01T23:48:49+00:00" + "time": "2022-01-02T09:55:41+00:00" }, { "name": "symfony/error-handler", - "version": "v5.4.2", + "version": "v5.4.7", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "e0c0dd0f9d4120a20158fc9aec2367d07d38bc56" + "reference": "060bc01856a1846e3e4385261bc9ed11a1dd7b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/e0c0dd0f9d4120a20158fc9aec2367d07d38bc56", - "reference": "e0c0dd0f9d4120a20158fc9aec2367d07d38bc56", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/060bc01856a1846e3e4385261bc9ed11a1dd7b6a", + "reference": "060bc01856a1846e3e4385261bc9ed11a1dd7b6a", "shasum": "" }, "require": { @@ -3666,7 +3669,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v5.4.2" + "source": "https://github.com/symfony/error-handler/tree/v5.4.7" }, "funding": [ { @@ -3682,20 +3685,20 @@ "type": "tidelift" } ], - "time": "2021-12-19T20:02:00+00:00" + "time": "2022-03-18T16:21:29+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v6.0.2", + "version": "v6.0.3", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "7093f25359e2750bfe86842c80c4e4a6a852d05c" + "reference": "6472ea2dd415e925b90ca82be64b8bc6157f3934" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/7093f25359e2750bfe86842c80c4e4a6a852d05c", - "reference": "7093f25359e2750bfe86842c80c4e4a6a852d05c", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/6472ea2dd415e925b90ca82be64b8bc6157f3934", + "reference": "6472ea2dd415e925b90ca82be64b8bc6157f3934", "shasum": "" }, "require": { @@ -3749,7 +3752,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v6.0.2" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.0.3" }, "funding": [ { @@ -3765,20 +3768,20 @@ "type": "tidelift" } ], - "time": "2021-12-21T10:43:13+00:00" + "time": "2022-01-02T09:55:41+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.0.0", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "aa5422287b75594b90ee9cd807caf8f0df491385" + "reference": "7bc61cc2db649b4637d331240c5346dcc7708051" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/aa5422287b75594b90ee9cd807caf8f0df491385", - "reference": "aa5422287b75594b90ee9cd807caf8f0df491385", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7bc61cc2db649b4637d331240c5346dcc7708051", + "reference": "7bc61cc2db649b4637d331240c5346dcc7708051", "shasum": "" }, "require": { @@ -3828,7 +3831,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.0.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.0.1" }, "funding": [ { @@ -3844,20 +3847,20 @@ "type": "tidelift" } ], - "time": "2021-07-15T12:33:35+00:00" + "time": "2022-01-02T09:55:41+00:00" }, { "name": "symfony/finder", - "version": "v5.4.2", + "version": "v5.4.3", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "e77046c252be48c48a40816187ed527703c8f76c" + "reference": "231313534dded84c7ecaa79d14bc5da4ccb69b7d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/e77046c252be48c48a40816187ed527703c8f76c", - "reference": "e77046c252be48c48a40816187ed527703c8f76c", + "url": "https://api.github.com/repos/symfony/finder/zipball/231313534dded84c7ecaa79d14bc5da4ccb69b7d", + "reference": "231313534dded84c7ecaa79d14bc5da4ccb69b7d", "shasum": "" }, "require": { @@ -3891,7 +3894,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v5.4.2" + "source": "https://github.com/symfony/finder/tree/v5.4.3" }, "funding": [ { @@ -3907,20 +3910,20 @@ "type": "tidelift" } ], - "time": "2021-12-15T11:06:13+00:00" + "time": "2022-01-26T16:34:36+00:00" }, { "name": "symfony/http-foundation", - "version": "v5.4.2", + "version": "v5.4.6", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "ce952af52877eaf3eab5d0c08cc0ea865ed37313" + "reference": "34e89bc147633c0f9dd6caaaf56da3b806a21465" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/ce952af52877eaf3eab5d0c08cc0ea865ed37313", - "reference": "ce952af52877eaf3eab5d0c08cc0ea865ed37313", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/34e89bc147633c0f9dd6caaaf56da3b806a21465", + "reference": "34e89bc147633c0f9dd6caaaf56da3b806a21465", "shasum": "" }, "require": { @@ -3964,7 +3967,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v5.4.2" + "source": "https://github.com/symfony/http-foundation/tree/v5.4.6" }, "funding": [ { @@ -3980,20 +3983,20 @@ "type": "tidelift" } ], - "time": "2021-12-28T17:15:56+00:00" + "time": "2022-03-05T21:03:43+00:00" }, { "name": "symfony/http-kernel", - "version": "v5.4.2", + "version": "v5.4.7", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "35b7e9868953e0d1df84320bb063543369e43ef5" + "reference": "509243b9b3656db966284c45dffce9316c1ecc5c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/35b7e9868953e0d1df84320bb063543369e43ef5", - "reference": "35b7e9868953e0d1df84320bb063543369e43ef5", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/509243b9b3656db966284c45dffce9316c1ecc5c", + "reference": "509243b9b3656db966284c45dffce9316c1ecc5c", "shasum": "" }, "require": { @@ -4076,7 +4079,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v5.4.2" + "source": "https://github.com/symfony/http-kernel/tree/v5.4.7" }, "funding": [ { @@ -4092,20 +4095,20 @@ "type": "tidelift" } ], - "time": "2021-12-29T13:20:26+00:00" + "time": "2022-04-02T06:04:20+00:00" }, { "name": "symfony/mime", - "version": "v5.4.2", + "version": "v5.4.7", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "1bfd938cf9562822c05c4d00e8f92134d3c8e42d" + "reference": "92d27a34dea2e199fa9b687e3fff3a7d169b7b1c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/1bfd938cf9562822c05c4d00e8f92134d3c8e42d", - "reference": "1bfd938cf9562822c05c4d00e8f92134d3c8e42d", + "url": "https://api.github.com/repos/symfony/mime/zipball/92d27a34dea2e199fa9b687e3fff3a7d169b7b1c", + "reference": "92d27a34dea2e199fa9b687e3fff3a7d169b7b1c", "shasum": "" }, "require": { @@ -4159,7 +4162,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v5.4.2" + "source": "https://github.com/symfony/mime/tree/v5.4.7" }, "funding": [ { @@ -4175,11 +4178,11 @@ "type": "tidelift" } ], - "time": "2021-12-28T17:15:56+00:00" + "time": "2022-03-11T16:08:05+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.24.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -4211,12 +4214,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -4241,7 +4244,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.24.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0" }, "funding": [ { @@ -4261,7 +4264,7 @@ }, { "name": "symfony/polyfill-iconv", - "version": "v1.24.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-iconv.git", @@ -4293,12 +4296,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Iconv\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Iconv\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -4324,7 +4327,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-iconv/tree/v1.24.0" + "source": "https://github.com/symfony/polyfill-iconv/tree/v1.25.0" }, "funding": [ { @@ -4344,7 +4347,7 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.24.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", @@ -4373,12 +4376,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -4405,7 +4408,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.24.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.25.0" }, "funding": [ { @@ -4425,7 +4428,7 @@ }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.24.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", @@ -4456,12 +4459,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Intl\\Idn\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -4492,7 +4495,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.24.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.25.0" }, "funding": [ { @@ -4512,7 +4515,7 @@ }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.24.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -4541,12 +4544,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, "files": [ "bootstrap.php" ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, "classmap": [ "Resources/stubs" ] @@ -4576,7 +4579,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.24.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.25.0" }, "funding": [ { @@ -4596,7 +4599,7 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.24.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", @@ -4628,12 +4631,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -4659,7 +4662,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.24.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.25.0" }, "funding": [ { @@ -4679,7 +4682,7 @@ }, { "name": "symfony/polyfill-php72", - "version": "v1.24.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", @@ -4705,12 +4708,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php72\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -4735,7 +4738,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.24.0" + "source": "https://github.com/symfony/polyfill-php72/tree/v1.25.0" }, "funding": [ { @@ -4755,7 +4758,7 @@ }, { "name": "symfony/polyfill-php73", - "version": "v1.24.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", @@ -4781,12 +4784,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" - }, "files": [ "bootstrap.php" ], + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, "classmap": [ "Resources/stubs" ] @@ -4814,7 +4817,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.24.0" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.25.0" }, "funding": [ { @@ -4834,16 +4837,16 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.24.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9" + "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/57b712b08eddb97c762a8caa32c84e037892d2e9", - "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4407588e0d3f1f52efb65fbe92babe41f37fe50c", + "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c", "shasum": "" }, "require": { @@ -4860,12 +4863,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, "files": [ "bootstrap.php" ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, "classmap": [ "Resources/stubs" ] @@ -4897,7 +4900,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.24.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.25.0" }, "funding": [ { @@ -4913,11 +4916,11 @@ "type": "tidelift" } ], - "time": "2021-09-13T13:58:33+00:00" + "time": "2022-03-04T08:16:47+00:00" }, { "name": "symfony/polyfill-php81", - "version": "v1.24.0", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", @@ -4943,12 +4946,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" - }, "files": [ "bootstrap.php" ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, "classmap": [ "Resources/stubs" ] @@ -4976,7 +4979,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.24.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.25.0" }, "funding": [ { @@ -4996,16 +4999,16 @@ }, { "name": "symfony/process", - "version": "v5.4.2", + "version": "v5.4.7", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "2b3ba8722c4aaf3e88011be5e7f48710088fb5e4" + "reference": "38a44b2517b470a436e1c944bf9b9ba3961137fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/2b3ba8722c4aaf3e88011be5e7f48710088fb5e4", - "reference": "2b3ba8722c4aaf3e88011be5e7f48710088fb5e4", + "url": "https://api.github.com/repos/symfony/process/zipball/38a44b2517b470a436e1c944bf9b9ba3961137fb", + "reference": "38a44b2517b470a436e1c944bf9b9ba3961137fb", "shasum": "" }, "require": { @@ -5038,7 +5041,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.4.2" + "source": "https://github.com/symfony/process/tree/v5.4.7" }, "funding": [ { @@ -5054,20 +5057,20 @@ "type": "tidelift" } ], - "time": "2021-12-27T21:01:00+00:00" + "time": "2022-03-18T16:18:52+00:00" }, { "name": "symfony/routing", - "version": "v5.4.0", + "version": "v5.4.3", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "9eeae93c32ca86746e5d38f3679e9569981038b1" + "reference": "44b29c7a94e867ccde1da604792f11a469958981" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/9eeae93c32ca86746e5d38f3679e9569981038b1", - "reference": "9eeae93c32ca86746e5d38f3679e9569981038b1", + "url": "https://api.github.com/repos/symfony/routing/zipball/44b29c7a94e867ccde1da604792f11a469958981", + "reference": "44b29c7a94e867ccde1da604792f11a469958981", "shasum": "" }, "require": { @@ -5128,7 +5131,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v5.4.0" + "source": "https://github.com/symfony/routing/tree/v5.4.3" }, "funding": [ { @@ -5144,25 +5147,26 @@ "type": "tidelift" } ], - "time": "2021-11-23T10:19:22+00:00" + "time": "2022-01-02T09:53:40+00:00" }, { "name": "symfony/service-contracts", - "version": "v2.4.1", + "version": "v2.5.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "d664541b99d6fb0247ec5ff32e87238582236204" + "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d664541b99d6fb0247ec5ff32e87238582236204", - "reference": "d664541b99d6fb0247ec5ff32e87238582236204", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/24d9dc654b83e91aa59f9d167b131bc3b5bea24c", + "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c", "shasum": "" }, "require": { "php": ">=7.2.5", - "psr/container": "^1.1" + "psr/container": "^1.1", + "symfony/deprecation-contracts": "^2.1|^3" }, "conflict": { "ext-psr": "<1.1|>=2" @@ -5173,7 +5177,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.4-dev" + "dev-main": "2.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -5210,7 +5214,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.4.1" + "source": "https://github.com/symfony/service-contracts/tree/v2.5.1" }, "funding": [ { @@ -5226,20 +5230,20 @@ "type": "tidelift" } ], - "time": "2021-11-04T16:37:19+00:00" + "time": "2022-03-13T20:07:29+00:00" }, { "name": "symfony/string", - "version": "v6.0.2", + "version": "v6.0.3", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "bae261d0c3ac38a1f802b4dfed42094296100631" + "reference": "522144f0c4c004c80d56fa47e40e17028e2eefc2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/bae261d0c3ac38a1f802b4dfed42094296100631", - "reference": "bae261d0c3ac38a1f802b4dfed42094296100631", + "url": "https://api.github.com/repos/symfony/string/zipball/522144f0c4c004c80d56fa47e40e17028e2eefc2", + "reference": "522144f0c4c004c80d56fa47e40e17028e2eefc2", "shasum": "" }, "require": { @@ -5260,12 +5264,12 @@ }, "type": "library", "autoload": { - "psr-4": { - "Symfony\\Component\\String\\": "" - }, "files": [ "Resources/functions.php" ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, "exclude-from-classmap": [ "/Tests/" ] @@ -5295,7 +5299,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.0.2" + "source": "https://github.com/symfony/string/tree/v6.0.3" }, "funding": [ { @@ -5311,20 +5315,20 @@ "type": "tidelift" } ], - "time": "2021-12-16T22:13:01+00:00" + "time": "2022-01-02T09:55:41+00:00" }, { "name": "symfony/translation", - "version": "v6.0.2", + "version": "v6.0.7", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "a16c33f93e2fd62d259222aebf792158e9a28a77" + "reference": "b2792b39d74cf41ea3065f27fd2ddf0b556ac7a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/a16c33f93e2fd62d259222aebf792158e9a28a77", - "reference": "a16c33f93e2fd62d259222aebf792158e9a28a77", + "url": "https://api.github.com/repos/symfony/translation/zipball/b2792b39d74cf41ea3065f27fd2ddf0b556ac7a1", + "reference": "b2792b39d74cf41ea3065f27fd2ddf0b556ac7a1", "shasum": "" }, "require": { @@ -5390,7 +5394,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v6.0.2" + "source": "https://github.com/symfony/translation/tree/v6.0.7" }, "funding": [ { @@ -5406,20 +5410,20 @@ "type": "tidelift" } ], - "time": "2021-12-25T20:10:03+00:00" + "time": "2022-03-31T17:18:25+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.0.0", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "1b6ea5a7442af5a12dba3dbd6d71034b5b234e77" + "reference": "c4183fc3ef0f0510893cbeedc7718fb5cafc9ac9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/1b6ea5a7442af5a12dba3dbd6d71034b5b234e77", - "reference": "1b6ea5a7442af5a12dba3dbd6d71034b5b234e77", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/c4183fc3ef0f0510893cbeedc7718fb5cafc9ac9", + "reference": "c4183fc3ef0f0510893cbeedc7718fb5cafc9ac9", "shasum": "" }, "require": { @@ -5468,7 +5472,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.0.0" + "source": "https://github.com/symfony/translation-contracts/tree/v3.0.1" }, "funding": [ { @@ -5484,20 +5488,20 @@ "type": "tidelift" } ], - "time": "2021-09-07T12:43:40+00:00" + "time": "2022-01-02T09:55:41+00:00" }, { "name": "symfony/var-dumper", - "version": "v5.4.2", + "version": "v5.4.6", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "1b56c32c3679002b3a42384a580e16e2600f41c1" + "reference": "294e9da6e2e0dd404e983daa5aa74253d92c05d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/1b56c32c3679002b3a42384a580e16e2600f41c1", - "reference": "1b56c32c3679002b3a42384a580e16e2600f41c1", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/294e9da6e2e0dd404e983daa5aa74253d92c05d0", + "reference": "294e9da6e2e0dd404e983daa5aa74253d92c05d0", "shasum": "" }, "require": { @@ -5557,7 +5561,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.4.2" + "source": "https://github.com/symfony/var-dumper/tree/v5.4.6" }, "funding": [ { @@ -5573,7 +5577,7 @@ "type": "tidelift" } ], - "time": "2021-12-29T10:10:35+00:00" + "time": "2022-03-02T12:42:23+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -5710,16 +5714,16 @@ }, { "name": "voku/portable-ascii", - "version": "1.5.6", + "version": "1.6.1", "source": { "type": "git", "url": "https://github.com/voku/portable-ascii.git", - "reference": "80953678b19901e5165c56752d087fc11526017c" + "reference": "87337c91b9dfacee02452244ee14ab3c43bc485a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/80953678b19901e5165c56752d087fc11526017c", - "reference": "80953678b19901e5165c56752d087fc11526017c", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/87337c91b9dfacee02452244ee14ab3c43bc485a", + "reference": "87337c91b9dfacee02452244ee14ab3c43bc485a", "shasum": "" }, "require": { @@ -5756,7 +5760,7 @@ ], "support": { "issues": "https://github.com/voku/portable-ascii/issues", - "source": "https://github.com/voku/portable-ascii/tree/1.5.6" + "source": "https://github.com/voku/portable-ascii/tree/1.6.1" }, "funding": [ { @@ -5780,7 +5784,7 @@ "type": "tidelift" } ], - "time": "2020-11-12T00:07:28+00:00" + "time": "2022-01-24T18:55:24+00:00" }, { "name": "webmozart/assert", @@ -5844,29 +5848,30 @@ "packages-dev": [ { "name": "doctrine/instantiator", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b" + "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b", - "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", + "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^8.0", + "doctrine/coding-standard": "^9", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.22" }, "type": "library", "autoload": { @@ -5893,7 +5898,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.4.0" + "source": "https://github.com/doctrine/instantiator/tree/1.4.1" }, "funding": [ { @@ -5909,7 +5914,7 @@ "type": "tidelift" } ], - "time": "2020-11-10T18:47:58+00:00" + "time": "2022-03-03T08:28:38+00:00" }, { "name": "facade/ignition-contracts", @@ -5966,16 +5971,16 @@ }, { "name": "fakerphp/faker", - "version": "v1.17.0", + "version": "v1.19.0", "source": { "type": "git", "url": "https://github.com/FakerPHP/Faker.git", - "reference": "b85e9d44eae8c52cca7aa0939483611f7232b669" + "reference": "d7f08a622b3346766325488aa32ddc93ccdecc75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/b85e9d44eae8c52cca7aa0939483611f7232b669", - "reference": "b85e9d44eae8c52cca7aa0939483611f7232b669", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/d7f08a622b3346766325488aa32ddc93ccdecc75", + "reference": "d7f08a622b3346766325488aa32ddc93ccdecc75", "shasum": "" }, "require": { @@ -5988,10 +5993,12 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.4.1", + "doctrine/persistence": "^1.3 || ^2.0", "ext-intl": "*", "symfony/phpunit-bridge": "^4.4 || ^5.2" }, "suggest": { + "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", "ext-curl": "Required by Faker\\Provider\\Image to download images.", "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", @@ -6000,7 +6007,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "v1.17-dev" + "dev-main": "v1.19-dev" } }, "autoload": { @@ -6025,9 +6032,9 @@ ], "support": { "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.17.0" + "source": "https://github.com/FakerPHP/Faker/tree/v1.19.0" }, - "time": "2021-12-05T17:14:47+00:00" + "time": "2022-02-02T17:38:57+00:00" }, { "name": "filp/whoops", @@ -6153,16 +6160,16 @@ }, { "name": "mockery/mockery", - "version": "1.4.4", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/mockery/mockery.git", - "reference": "e01123a0e847d52d186c5eb4b9bf58b0c6d00346" + "reference": "c10a5f6e06fc2470ab1822fa13fa2a7380f8fbac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/e01123a0e847d52d186c5eb4b9bf58b0c6d00346", - "reference": "e01123a0e847d52d186c5eb4b9bf58b0c6d00346", + "url": "https://api.github.com/repos/mockery/mockery/zipball/c10a5f6e06fc2470ab1822fa13fa2a7380f8fbac", + "reference": "c10a5f6e06fc2470ab1822fa13fa2a7380f8fbac", "shasum": "" }, "require": { @@ -6219,43 +6226,44 @@ ], "support": { "issues": "https://github.com/mockery/mockery/issues", - "source": "https://github.com/mockery/mockery/tree/1.4.4" + "source": "https://github.com/mockery/mockery/tree/1.5.0" }, - "time": "2021-09-13T15:28:59+00:00" + "time": "2022-01-20T13:18:17+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.10.2", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", - "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, - "replace": { - "myclabs/deep-copy": "self.version" + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" }, "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", "autoload": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, "files": [ "src/DeepCopy/deep_copy.php" - ] + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -6271,7 +6279,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" }, "funding": [ { @@ -6279,7 +6287,7 @@ "type": "tidelift" } ], - "time": "2020-11-13T09:40:50+00:00" + "time": "2022-03-03T13:19:32+00:00" }, { "name": "nunomaduro/collision", @@ -6430,16 +6438,16 @@ }, { "name": "phar-io/version", - "version": "3.1.0", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "bae7c545bef187884426f042434e561ab1ddb182" + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182", - "reference": "bae7c545bef187884426f042434e561ab1ddb182", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "shasum": "" }, "require": { @@ -6475,9 +6483,9 @@ "description": "Library for handling version information and constraints", "support": { "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.1.0" + "source": "https://github.com/phar-io/version/tree/3.2.1" }, - "time": "2021-02-23T14:00:09+00:00" + "time": "2022-02-21T01:04:05+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -6591,16 +6599,16 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.6.0", + "version": "1.6.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706" + "reference": "77a32518733312af16a44300404e945338981de3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/93ebd0014cab80c4ea9f5e297ea48672f1b87706", - "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3", + "reference": "77a32518733312af16a44300404e945338981de3", "shasum": "" }, "require": { @@ -6635,9 +6643,9 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" }, - "time": "2022-01-04T19:58:01+00:00" + "time": "2022-03-15T21:29:03+00:00" }, { "name": "phpspec/prophecy", @@ -6708,16 +6716,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.10", + "version": "9.2.15", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "d5850aaf931743067f4bfc1ae4cbd06468400687" + "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d5850aaf931743067f4bfc1ae4cbd06468400687", - "reference": "d5850aaf931743067f4bfc1ae4cbd06468400687", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2e9da11878c4202f97915c1cb4bb1ca318a63f5f", + "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f", "shasum": "" }, "require": { @@ -6773,7 +6781,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.10" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.15" }, "funding": [ { @@ -6781,7 +6789,7 @@ "type": "github" } ], - "time": "2021-12-05T09:12:13+00:00" + "time": "2022-03-07T09:28:20+00:00" }, { "name": "phpunit/php-file-iterator", @@ -7026,16 +7034,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.11", + "version": "9.5.20", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "2406855036db1102126125537adb1406f7242fdd" + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2406855036db1102126125537adb1406f7242fdd", - "reference": "2406855036db1102126125537adb1406f7242fdd", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba", + "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba", "shasum": "" }, "require": { @@ -7051,7 +7059,7 @@ "phar-io/version": "^3.0.2", "php": ">=7.3", "phpspec/prophecy": "^1.12.1", - "phpunit/php-code-coverage": "^9.2.7", + "phpunit/php-code-coverage": "^9.2.13", "phpunit/php-file-iterator": "^3.0.5", "phpunit/php-invoker": "^3.1.1", "phpunit/php-text-template": "^2.0.3", @@ -7065,7 +7073,7 @@ "sebastian/global-state": "^5.0.1", "sebastian/object-enumerator": "^4.0.3", "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^2.3.4", + "sebastian/type": "^3.0", "sebastian/version": "^3.0.2" }, "require-dev": { @@ -7086,11 +7094,11 @@ } }, "autoload": { - "classmap": [ - "src/" - ], "files": [ "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -7113,7 +7121,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.11" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20" }, "funding": [ { @@ -7125,7 +7133,7 @@ "type": "github" } ], - "time": "2021-12-25T07:07:57+00:00" + "time": "2022-04-01T12:37:26+00:00" }, { "name": "roave/security-advisories", @@ -7133,42 +7141,49 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "a1ce0a793ff447c9085f350d2fa8744efbdb5c1e" + "reference": "44e64dc3426a056b16041e56619b96619564b28d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/a1ce0a793ff447c9085f350d2fa8744efbdb5c1e", - "reference": "a1ce0a793ff447c9085f350d2fa8744efbdb5c1e", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/44e64dc3426a056b16041e56619b96619564b28d", + "reference": "44e64dc3426a056b16041e56619b96619564b28d", "shasum": "" }, "conflict": { "3f/pygmentize": "<1.2", - "adodb/adodb-php": "<5.20.12", + "admidio/admidio": "<4.1.9", + "adodb/adodb-php": "<=5.20.20|>=5.21,<=5.21.3", "akaunting/akaunting": "<2.1.13", + "alextselegidis/easyappointments": "<1.4.3", "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1", "amazing/media2click": ">=1,<1.3.3", "amphp/artax": "<1.0.6|>=2,<2.0.6", "amphp/http": "<1.0.1", "amphp/http-client": ">=4,<4.4", "anchorcms/anchor-cms": "<=0.12.7", + "andreapollastri/cipi": "<=3.1.15", "api-platform/core": ">=2.2,<2.2.10|>=2.3,<2.3.6", + "appwrite/server-ce": "<0.11.1|>=0.12,<0.12.2", "area17/twill": "<1.2.5|>=2,<2.5.3", "asymmetricrypt/asymmetricrypt": ">=0,<9.9.99", "aws/aws-sdk-php": ">=3,<3.2.1", "bagisto/bagisto": "<0.1.5", "barrelstrength/sprout-base-email": "<1.2.7", "barrelstrength/sprout-forms": "<3.9", + "barryvdh/laravel-translation-manager": "<0.6.2", "baserproject/basercms": "<4.5.4", "billz/raspap-webgui": "<=2.6.6", "bk2k/bootstrap-package": ">=7.1,<7.1.2|>=8,<8.0.8|>=9,<9.0.4|>=9.1,<9.1.3|>=10,<10.0.10|>=11,<11.0.3", + "bmarshall511/wordpress_zero_spam": "<5.2.13", "bolt/bolt": "<3.7.2", "bolt/core": "<4.1.13", "bottelet/flarepoint": "<2.2.1", "brightlocal/phpwhois": "<=4.2.5", "buddypress/buddypress": "<7.2.1", "bugsnag/bugsnag-laravel": ">=2,<2.0.2", + "bytefury/crater": "<6.0.2", "cachethq/cachet": "<2.5.1", - "cakephp/cakephp": ">=1.3,<1.3.18|>=2,<2.4.99|>=2.5,<2.5.99|>=2.6,<2.6.12|>=2.7,<2.7.6|>=3,<3.5.18|>=3.6,<3.6.15|>=3.7,<3.7.7", + "cakephp/cakephp": "<4.0.6", "cardgate/magento2": "<2.0.33", "cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4", "cartalyst/sentry": "<=2.1.6", @@ -7177,17 +7192,19 @@ "cesnet/simplesamlphp-module-proxystatistics": "<3.1", "codeception/codeception": "<3.1.3|>=4,<4.1.22", "codeigniter/framework": "<=3.0.6", - "codeigniter4/framework": "<4.1.6", + "codeigniter4/framework": "<4.1.9", "codiad/codiad": "<=2.8.4", "composer/composer": "<1.10.23|>=2-alpha.1,<2.1.9", - "concrete5/concrete5": "<8.5.5", + "concrete5/concrete5": "<9", "concrete5/core": "<8.5.7", "contao-components/mediaelement": ">=2.14.2,<2.21.1", "contao/core": ">=2,<3.5.39", - "contao/core-bundle": ">=4,<4.4.56|>=4.5,<4.9.18|>=4.10,<4.11.7|= 4.10.0", + "contao/core-bundle": "<4.9.18|>=4.10,<4.11.7|= 4.10.0", "contao/listing-bundle": ">=4,<4.4.8", + "contao/managed-edition": "<=1.5", "craftcms/cms": "<3.7.14", "croogo/croogo": "<3.0.7", + "cuyz/valinor": ">=0.5,<0.7", "datadog/dd-trace": ">=0.30,<0.30.2", "david-garcia/phpwhois": "<=4.3.1", "derhansen/sf_event_mgt": "<4.3.1|>=5,<5.1.1", @@ -7201,15 +7218,16 @@ "doctrine/mongodb-odm": ">=1,<1.0.2", "doctrine/mongodb-odm-bundle": ">=2,<3.0.1", "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1|>=2.8.3,<2.8.4", - "dolibarr/dolibarr": "<=14.0.4|>= 3.3.beta1, < 13.0.2", - "dompdf/dompdf": ">=0.6,<0.6.2", - "drupal/core": ">=7,<7.80|>=8,<8.9.16|>=9,<9.1.12|>=9.2,<9.2.4", + "dolibarr/dolibarr": "<16|>= 3.3.beta1, < 13.0.2", + "dompdf/dompdf": "<1.2.1", + "drupal/core": ">=7,<7.88|>=8,<9.2.13|>=9.3,<9.3.6", "drupal/drupal": ">=7,<7.80|>=8,<8.9.16|>=9,<9.1.12|>=9.2,<9.2.4", "dweeves/magmi": "<=0.7.24", "ecodev/newsletter": "<=4", + "ectouch/ectouch": "<=2.7.2", "elgg/elgg": "<3.3.24|>=4,<4.0.5", "endroid/qr-code-bundle": "<3.4.2", - "enshrined/svg-sanitize": "<0.13.1", + "enshrined/svg-sanitize": "<0.15", "erusev/parsedown": "<1.7.2", "ether/logs": "<3.0.4", "ezsystems/demobundle": ">=5.4,<5.4.6.1", @@ -7217,13 +7235,13 @@ "ezsystems/ezdemo-ls-extension": ">=5.4,<5.4.2.1", "ezsystems/ezfind-ls": ">=5.3,<5.3.6.1|>=5.4,<5.4.11.1|>=2017.12,<2017.12.0.1", "ezsystems/ezplatform": "<=1.13.6|>=2,<=2.5.24", - "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<=1.5.25", + "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<1.5.27", "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1", - "ezsystems/ezplatform-kernel": "<=1.2.5|>=1.3,<=1.3.1", + "ezsystems/ezplatform-kernel": "<=1.2.5|>=1.3,<1.3.12", "ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<1.3.8", "ezsystems/ezplatform-richtext": ">=2.3,<=2.3.7", "ezsystems/ezplatform-user": ">=1,<1.0.1", - "ezsystems/ezpublish-kernel": "<=6.13.8.1|>=7,<=7.5.15.1", + "ezsystems/ezpublish-kernel": "<=6.13.8.1|>=7,<7.5.26", "ezsystems/ezpublish-legacy": "<=2017.12.7.3|>=2018.6,<=2019.3.5.1", "ezsystems/platform-ui-assets-bundle": ">=4.2,<4.2.3", "ezsystems/repository-forms": ">=2.3,<2.3.2.1", @@ -7231,13 +7249,14 @@ "facade/ignition": "<1.16.15|>=2,<2.4.2|>=2.5,<2.5.2", "feehi/cms": "<=2.1.1", "feehi/feehicms": "<=0.1.3", + "fenom/fenom": "<=2.12.1", "firebase/php-jwt": "<2", "flarum/core": ">=1,<=1.0.1", "flarum/sticky": ">=0.1-beta.14,<=0.1-beta.15", "flarum/tags": "<=0.1-beta.13", "fluidtypo3/vhs": "<5.1.1", "fooman/tcpdf": "<6.2.22", - "forkcms/forkcms": "<=5.9.2", + "forkcms/forkcms": "<5.11.1", "fossar/tcpdf-parser": "<6.2.22", "francoisjacquet/rosariosis": "<8.1.1", "friendsofsymfony/oauth2-php": "<1.3", @@ -7247,35 +7266,43 @@ "froala/wysiwyg-editor": "<3.2.7", "fuel/core": "<1.8.1", "gaoming13/wechat-php-sdk": "<=1.10.2", - "getgrav/grav": "<=1.7.24", + "genix/cms": "<=1.1.11", + "getgrav/grav": "<1.7.31", "getkirby/cms": "<3.5.8", "getkirby/panel": "<2.5.14", "gilacms/gila": "<=1.11.4", "globalpayments/php-sdk": "<2", + "google/protobuf": "<3.15", "gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3", "gree/jose": "<=2.2", "gregwar/rst": "<1.0.3", "grumpydictator/firefly-iii": "<5.6.5", "guzzlehttp/guzzle": ">=4-rc.2,<4.2.4|>=5,<5.3.1|>=6,<6.2.1", + "guzzlehttp/psr7": "<1.8.4|>=2,<2.1.1", "helloxz/imgurl": "<=2.31", "hillelcoren/invoice-ninja": "<5.3.35", "hjue/justwriting": "<=1", "hov/jobfair": "<1.0.13|>=2,<2.0.2", + "hyn/multi-tenant": ">=5.6,<5.7.2", "ibexa/post-install": "<=1.0.4", - "icecoder/icecoder": "<=8", + "icecoder/icecoder": "<=8.1", "illuminate/auth": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.10", "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<=4.1.99999|>=4.2,<=4.2.99999|>=5,<=5.0.99999|>=5.1,<=5.1.99999|>=5.2,<=5.2.99999|>=5.3,<=5.3.99999|>=5.4,<=5.4.99999|>=5.5,<=5.5.49|>=5.6,<=5.6.99999|>=5.7,<=5.7.99999|>=5.8,<=5.8.99999|>=6,<6.18.31|>=7,<7.22.4", "illuminate/database": "<6.20.26|>=7,<7.30.5|>=8,<8.40", "illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", "illuminate/view": "<6.20.42|>=7,<7.30.6|>=8,<8.75", - "impresscms/impresscms": "<=1.4.2", + "impresscms/impresscms": "<1.4.3", "in2code/femanager": "<5.5.1|>=6,<6.3.1", "intelliants/subrion": "<=4.2.1", "ivankristianto/phpwhois": "<=4.3", "jackalope/jackalope-doctrine-dbal": "<1.7.4", "james-heinrich/getid3": "<1.9.21", - "joomla/archive": "<1.1.10", + "joomla/archive": "<1.1.12|>=2,<2.0.1", + "joomla/filesystem": "<1.6.2|>=2,<2.0.1", + "joomla/filter": "<1.4.4|>=2,<2.0.1", + "joomla/input": ">=2,<2.0.2", "joomla/session": "<1.3.1", + "jsdecena/laracom": "<2.0.9", "jsmitty12/phpwhois": "<5.1", "kazist/phpwhois": "<=4.2.6", "kevinpapst/kimai2": "<1.16.7", @@ -7283,7 +7310,9 @@ "klaviyo/magento2-extension": ">=1,<3", "kreait/firebase-php": ">=3.2,<3.8.1", "la-haute-societe/tcpdf": "<6.2.22", + "laminas/laminas-form": "<2.17.1|>=3,<3.0.2|>=3.1,<3.1.1", "laminas/laminas-http": "<2.14.2", + "laravel/fortify": "<1.11.1", "laravel/framework": "<6.20.42|>=7,<7.30.6|>=8,<8.75", "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", "latte/latte": "<2.10.8", @@ -7292,8 +7321,9 @@ "league/commonmark": "<0.18.3", "league/flysystem": "<1.1.4|>=2,<2.1.1", "lexik/jwt-authentication-bundle": "<2.10.7|>=2.11,<2.11.3", - "librenms/librenms": "<=21.11", + "librenms/librenms": "<22.2.2", "limesurvey/limesurvey": "<3.27.19", + "livehelperchat/livehelperchat": "<=3.91", "livewire/livewire": ">2.2.4,<2.2.6", "lms/routes": "<2.1.1", "localizationteam/l10nmgr": "<7.4|>=8,<8.7|>=9,<9.2", @@ -7302,14 +7332,16 @@ "magento/magento1ee": ">=1,<1.14.4.3", "magento/product-community-edition": ">=2,<2.2.10|>=2.3,<2.3.2-p.2", "marcwillmann/turn": "<0.3.3", - "mautic/core": "<4|= 2.13.1", + "matyhtf/framework": "<3.0.6", + "mautic/core": "<4.2|= 2.13.1", "mediawiki/core": ">=1.27,<1.27.6|>=1.29,<1.29.3|>=1.30,<1.30.2|>=1.31,<1.31.9|>=1.32,<1.32.6|>=1.32.99,<1.33.3|>=1.33.99,<1.34.3|>=1.34.99,<1.35", - "microweber/microweber": "<1.2.8", + "microweber/microweber": "<1.3", "miniorange/miniorange-saml": "<1.4.3", "mittwald/typo3_forum": "<1.2.1", - "modx/revolution": "<2.8", + "modx/revolution": "<= 2.8.3-pl|<2.8", "monolog/monolog": ">=1.8,<1.12", - "moodle/moodle": "<3.7.9|>=3.8,<3.8.8|>=3.9,<3.9.5|>=3.10-beta,<3.10.2", + "moodle/moodle": "<3.9.13|>=3.10-beta,<3.10.10|>=3.11,<3.11.6", + "mustache/mustache": ">=2,<2.14.1", "namshi/jose": "<2.2", "neoan3-apps/template": "<1.1.1", "neos/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6", @@ -7321,15 +7353,16 @@ "nette/nette": ">=2,<2.0.19|>=2.1,<2.1.13", "nilsteampassnet/teampass": "<=2.1.27.36", "nukeviet/nukeviet": "<4.3.4", - "nystudio107/craft-seomatic": "<3.3", + "nystudio107/craft-seomatic": "<3.4.12", "nzo/url-encryptor-bundle": ">=4,<4.3.2|>=5,<5.0.1", "october/backend": "<1.1.2", "october/cms": "= 1.1.1|= 1.0.471|= 1.0.469|>=1.0.319,<1.0.469", "october/october": ">=1.0.319,<1.0.466|>=2.1,<2.1.12", "october/rain": "<1.0.472|>=1.1,<1.1.2", - "october/system": "<1.0.472|>=1.1.1,<1.1.5|>=2.1,<2.1.12", + "october/system": "<1.0.475|>=1.1,<1.1.11|>=2,<2.1.27", "onelogin/php-saml": "<2.10.4", "oneup/uploader-bundle": "<1.9.3|>=2,<2.1.5", + "open-web-analytics/open-web-analytics": "<1.7.4", "opencart/opencart": "<=3.0.3.2", "openid/php-openid": "<2.3", "openmage/magento-lts": "<19.4.15|>=20,<20.0.13", @@ -7343,27 +7376,30 @@ "passbolt/passbolt_api": "<2.11", "paypal/merchant-sdk-php": "<3.12", "pear/archive_tar": "<1.4.14", + "pear/crypt_gpg": "<1.6.7", "pegasus/google-for-jobs": "<1.5.1|>=2,<2.1.1", "personnummer/personnummer": "<3.0.2", "phanan/koel": "<5.1.4", "phpfastcache/phpfastcache": "<6.1.5|>=7,<7.1.2|>=8,<8.0.7", "phpmailer/phpmailer": "<6.5", "phpmussel/phpmussel": ">=1,<1.6", - "phpmyadmin/phpmyadmin": "<4.9.6|>=5,<5.0.3", - "phpoffice/phpexcel": "<1.8.2", + "phpmyadmin/phpmyadmin": "<5.1.3", + "phpoffice/phpexcel": "<1.8", "phpoffice/phpspreadsheet": "<1.16", "phpseclib/phpseclib": "<2.0.31|>=3,<3.0.7", "phpservermon/phpservermon": "<=3.5.2", - "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3", + "phpunit/phpunit": "<4.8.28|>=5,<5.6.3", "phpwhois/phpwhois": "<=4.2.5", "phpxmlrpc/extras": "<0.6.1", - "pimcore/pimcore": "<10.2.7", - "pocketmine/pocketmine-mp": "<4.0.5", + "pimcore/data-hub": "<1.2.4", + "pimcore/pimcore": "<10.4", + "pocketmine/bedrock-protocol": "<8.0.2", + "pocketmine/pocketmine-mp": "<4.2.4", "pressbooks/pressbooks": "<5.18", "prestashop/autoupgrade": ">=4,<4.10.1", "prestashop/contactform": ">1.0.1,<4.3", "prestashop/gamification": "<2.3.2", - "prestashop/prestashop": ">=1.7.5,<=1.7.8.1", + "prestashop/prestashop": ">=1.7,<=1.7.8.2", "prestashop/productcomments": ">=4,<4.2.1", "prestashop/ps_emailsubscription": "<2.6.1", "prestashop/ps_facetedsearch": "<3.4.1", @@ -7371,30 +7407,34 @@ "privatebin/privatebin": "<1.2.2|>=1.3,<1.3.2", "propel/propel": ">=2-alpha.1,<=2-alpha.7", "propel/propel1": ">=1,<=1.7.1", - "pterodactyl/panel": "<1.6.6", + "pterodactyl/panel": "<1.7", + "ptrofimov/beanstalk_console": "<1.7.14", "pusher/pusher-php-server": "<2.2.1", "pwweb/laravel-core": "<=0.3.6-beta", "rainlab/debugbar-plugin": "<3.1", - "remdex/livehelperchat": "<3.91", + "remdex/livehelperchat": "<3.96", "rmccue/requests": ">=1.6,<1.8", "robrichards/xmlseclibs": "<3.0.4", + "rudloff/alltube": "<3.0.3", + "s-cart/s-cart": "<6.7.2", "sabberworm/php-css-parser": ">=1,<1.0.1|>=2,<2.0.1|>=3,<3.0.1|>=4,<4.0.1|>=5,<5.0.9|>=5.1,<5.1.3|>=5.2,<5.2.1|>=6,<6.0.2|>=7,<7.0.4|>=8,<8.0.1|>=8.1,<8.1.1|>=8.2,<8.2.1|>=8.3,<8.3.1", "sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9", "scheb/two-factor-bundle": ">=0,<3.26|>=4,<4.11", "sensiolabs/connect": "<4.2.3", "serluck/phpwhois": "<=4.2.6", - "shopware/core": "<=6.4.6", - "shopware/platform": "<=6.4.6", + "shopware/core": "<=6.4.8.1", + "shopware/platform": "<=6.4.8.1", "shopware/production": "<=6.3.5.2", "shopware/shopware": "<5.7.7", - "showdoc/showdoc": "<2.10", + "shopware/storefront": "<=6.4.8.1", + "showdoc/showdoc": "<2.10.4", "silverstripe/admin": ">=1,<1.8.1", "silverstripe/assets": ">=1,<1.4.7|>=1.5,<1.5.2", "silverstripe/cms": "<4.3.6|>=4.4,<4.4.4", "silverstripe/comments": ">=1.3,<1.9.99|>=2,<2.9.99|>=3,<3.1.1", "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", - "silverstripe/framework": "<4.7.4", - "silverstripe/graphql": "<3.5.2|>=4-alpha.1,<4-alpha.2", + "silverstripe/framework": "<4.10.1", + "silverstripe/graphql": "<3.5.2|>=4-alpha.1,<4-alpha.2|= 4.0.0-alpha1", "silverstripe/registry": ">=2.1,<2.1.2|>=2.2,<2.2.1", "silverstripe/restfulserver": ">=1,<1.0.9|>=2,<2.0.4", "silverstripe/subsites": ">=2,<2.1.1", @@ -7407,12 +7447,14 @@ "simplito/elliptic-php": "<1.0.6", "slim/slim": "<2.6", "smarty/smarty": "<3.1.43|>=4,<4.0.3", - "snipe/snipe-it": "<5.3.5", + "snipe/snipe-it": "<= 6.0.0-RC-5|<5.3.11", "socalnick/scn-social-auth": "<1.15.2", "socialiteproviders/steam": "<1.1", + "spipu/html2pdf": "<5.2.4", "spoonity/tcpdf": "<6.2.22", "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", - "ssddanbrown/bookstack": "<21.12.1", + "ssddanbrown/bookstack": "<22.2.3", + "statamic/cms": "<3.2.39|>=3.3,<3.3.2", "stormpath/sdk": ">=0,<9.9.99", "studio-42/elfinder": "<2.1.59", "subrion/cms": "<=4.2.1", @@ -7420,10 +7462,10 @@ "swiftmailer/swiftmailer": ">=4,<5.4.5", "sylius/admin-bundle": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2", "sylius/grid": ">=1,<1.1.19|>=1.2,<1.2.18|>=1.3,<1.3.13|>=1.4,<1.4.5|>=1.5,<1.5.1", - "sylius/grid-bundle": ">=1,<1.1.19|>=1.2,<1.2.18|>=1.3,<1.3.13|>=1.4,<1.4.5|>=1.5,<1.5.1", + "sylius/grid-bundle": "<1.10.1", "sylius/paypal-plugin": ">=1,<1.2.4|>=1.3,<1.3.1", "sylius/resource-bundle": "<1.3.14|>=1.4,<1.4.7|>=1.5,<1.5.2|>=1.6,<1.6.4", - "sylius/sylius": "<1.6.9|>=1.7,<1.7.9|>=1.8,<1.8.3|>=1.9,<1.9.5", + "sylius/sylius": "<1.9.10|>=1.10,<1.10.11|>=1.11,<1.11.2", "symbiote/silverstripe-multivaluefield": ">=3,<3.0.99", "symbiote/silverstripe-queuedjobs": ">=3,<3.0.2|>=3.1,<3.1.4|>=4,<4.0.7|>=4.1,<4.1.2|>=4.2,<4.2.4|>=4.3,<4.3.3|>=4.4,<4.4.3|>=4.5,<4.5.1|>=4.6,<4.6.4", "symbiote/silverstripe-versionedfiles": "<=2.0.3", @@ -7432,7 +7474,7 @@ "symfony/dependency-injection": ">=2,<2.0.17|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", "symfony/error-handler": ">=4.4,<4.4.4|>=5,<5.0.4", "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1", - "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", + "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7|>=5.3.14,<=5.3.14|>=5.4.3,<=5.4.3|>=6.0.3,<=6.0.3|= 6.0.3|= 5.4.3|= 5.3.14", "symfony/http-foundation": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7", "symfony/http-kernel": ">=2,<2.8.52|>=3,<3.4.35|>=4,<4.2.12|>=4.3,<4.4.13|>=5,<5.1.5|>=5.2,<5.3.12", "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", @@ -7448,9 +7490,9 @@ "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<3.4.49|>=4,<4.4.24|>=5,<5.2.9", "symfony/security-csrf": ">=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", "symfony/security-guard": ">=2.8,<3.4.48|>=4,<4.4.23|>=5,<5.2.8", - "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<3.4.48|>=4,<4.4.23|>=5,<5.2.8|>=5.3,<5.3.2", + "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7|>=5.1,<5.2.8|>=5.3,<5.3.2", "symfony/serializer": ">=2,<2.0.11|>=4.1,<4.4.35|>=5,<5.3.12", - "symfony/symfony": ">=2,<3.4.49|>=4,<4.4.35|>=5,<5.3.12", + "symfony/symfony": ">=2,<3.4.49|>=4,<4.4.35|>=5,<5.3.12|>=5.3.14,<=5.3.14|>=5.4.3,<=5.4.3|>=6.0.3,<=6.0.3", "symfony/translation": ">=2,<2.0.17", "symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3", "symfony/var-exporter": ">=4.2,<4.2.12|>=4.3,<4.3.8", @@ -7459,6 +7501,7 @@ "t3/dce": ">=2.2,<2.6.2", "t3g/svg-sanitizer": "<1.0.3", "tecnickcom/tcpdf": "<6.2.22", + "terminal42/contao-tablelookupwizard": "<3.3.5", "thelia/backoffice-default-template": ">=2.1,<2.1.2", "thelia/thelia": ">=2.1-beta.1,<2.1.3", "theonedemon/phpwhois": "<=4.2.5", @@ -7467,9 +7510,9 @@ "topthink/framework": "<6.0.9", "topthink/think": "<=6.0.9", "topthink/thinkphp": "<=3.2.3", - "tribalsystems/zenario": "<8.8.53370", + "tribalsystems/zenario": "<9.2.55826", "truckersmp/phpwhois": "<=4.3.1", - "twig/twig": "<1.38|>=2,<2.7", + "twig/twig": "<1.38|>=2,<2.14.11|>=3,<3.3.8", "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.32|>=8,<8.7.38|>=9,<9.5.29|>=10,<10.4.19|>=11,<11.5", "typo3/cms-backend": ">=7,<=7.6.50|>=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.13|>=11,<=11.1", "typo3/cms-core": ">=6.2,<=6.2.56|>=7,<=7.6.52|>=8,<=8.7.41|>=9,<9.5.29|>=10,<10.4.19|>=11,<11.5", @@ -7485,7 +7528,7 @@ "usmanhalalit/pixie": "<1.0.3|>=2,<2.0.2", "vanilla/safecurl": "<0.9.2", "verot/class.upload.php": "<=1.0.3|>=2,<=2.0.4", - "vrana/adminer": "<4.7.9", + "vrana/adminer": "<4.8.1", "wallabag/tcpdf": "<6.2.22", "wanglelecc/laracms": "<=1.0.3", "web-auth/webauthn-framework": ">=3.3,<3.3.4", @@ -7493,6 +7536,7 @@ "wikimedia/parsoid": "<0.12.2", "willdurand/js-translation-bundle": "<2.1.1", "wp-cli/wp-cli": "<2.5", + "yeswiki/yeswiki": "<4.1", "yetiforce/yetiforce-crm": "<=6.3", "yidashi/yii2cmf": "<=2", "yii2mod/yii2-cms": "<1.9.2", @@ -7567,7 +7611,7 @@ "type": "tidelift" } ], - "time": "2022-01-12T23:15:39+00:00" + "time": "2022-04-06T19:03:57+00:00" }, { "name": "sebastian/cli-parser", @@ -7935,16 +7979,16 @@ }, { "name": "sebastian/environment", - "version": "5.1.3", + "version": "5.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "388b6ced16caa751030f6a69e588299fa09200ac" + "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac", - "reference": "388b6ced16caa751030f6a69e588299fa09200ac", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7", "shasum": "" }, "require": { @@ -7986,7 +8030,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4" }, "funding": [ { @@ -7994,7 +8038,7 @@ "type": "github" } ], - "time": "2020-09-28T05:52:38+00:00" + "time": "2022-04-03T09:37:03+00:00" }, { "name": "sebastian/exporter", @@ -8075,16 +8119,16 @@ }, { "name": "sebastian/global-state", - "version": "5.0.3", + "version": "5.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49" + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/23bd5951f7ff26f12d4e3242864df3e08dec4e49", - "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", "shasum": "" }, "require": { @@ -8127,7 +8171,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.3" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" }, "funding": [ { @@ -8135,7 +8179,7 @@ "type": "github" } ], - "time": "2021-06-11T13:31:12+00:00" + "time": "2022-02-14T08:28:10+00:00" }, { "name": "sebastian/lines-of-code", @@ -8426,28 +8470,28 @@ }, { "name": "sebastian/type", - "version": "2.3.4", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914" + "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b8cd8a1c753c90bc1a0f5372170e3e489136f914", - "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b233b84bc4465aff7b57cf1c4bc75c86d00d6dad", + "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -8470,7 +8514,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/2.3.4" + "source": "https://github.com/sebastianbergmann/type/tree/3.0.0" }, "funding": [ { @@ -8478,7 +8522,7 @@ "type": "github" } ], - "time": "2021-06-15T12:49:02+00:00" + "time": "2022-03-15T09:54:48+00:00" }, { "name": "sebastian/version", @@ -8596,5 +8640,5 @@ "ext-json": "*" }, "platform-dev": [], - "plugin-api-version": "2.1.0" + "plugin-api-version": "2.2.0" } diff --git a/config/api/app/version.php b/config/api/app/version.php index f7f2486f..3dc6ef86 100644 --- a/config/api/app/version.php +++ b/config/api/app/version.php @@ -3,9 +3,9 @@ declare(strict_types=1); return [ - 'version'=> 'v2.22.0', + 'version'=> 'v2.23.0', 'prefix' => 'v2', - 'release_date' => '2022-01-26', + 'release_date' => '2022-04-12', 'changelog' => [ 'api' => '/v2/changelog', 'markdown' => 'https://github.com/costs-to-expect/api/blob/master/CHANGELOG.md' diff --git a/config/api/permitted-user/fields.php b/config/api/permitted-user/fields.php index 37e19ec4..93044b0a 100644 --- a/config/api/permitted-user/fields.php +++ b/config/api/permitted-user/fields.php @@ -3,14 +3,11 @@ declare(strict_types=1); return [ - 'user_id' => [ - 'field' => 'user_id', - 'title' => 'permitted-user/fields.title-user_id', - 'description' => 'permitted-user/fields.description-user_id', - 'type' => 'string', - 'validation' => [ - 'length' => 10 - ], + 'email' => [ + 'field' => 'email', + 'title' => 'permitted-user/fields.title-email', + 'description' => 'permitted-user/fields.description-email', + 'type' => 'email', 'required' => true ] ]; diff --git a/config/api/permitted-user/parameters.php b/config/api/permitted-user/parameters.php new file mode 100644 index 00000000..bf1e8391 --- /dev/null +++ b/config/api/permitted-user/parameters.php @@ -0,0 +1,8 @@ + [], + 'item' => [] +]; diff --git a/config/api/permitted-user/validation.php b/config/api/permitted-user/validation.php new file mode 100644 index 00000000..8bc3e357 --- /dev/null +++ b/config/api/permitted-user/validation.php @@ -0,0 +1,36 @@ + [ + 'fields' => [ + 'email' => [ + 'required', + 'email' + ] + ], + 'messages' => [ + 'email.permissible' => 'permitted-user/validation.email-permissible' + ] + ], + 'PATCH' => [ + 'fields' => [ + 'description' => [ + 'sometimes', + 'string' + ], + 'data' => [ + 'sometimes', + 'json' + ], + 'public' => [ + 'sometimes', + 'boolean' + ] + ], + 'messages' => [ + 'name.unique' => 'resource-type/validation.name-unique' + ] + ] +]; diff --git a/config/api/resource-type/parameters.php b/config/api/resource-type/parameters.php index db40ee95..602c9ff7 100644 --- a/config/api/resource-type/parameters.php +++ b/config/api/resource-type/parameters.php @@ -10,6 +10,13 @@ 'description' => 'resource-type/parameters.description-include-resources', 'type' => 'boolean', 'required' => false + ], + 'include-permitted-users' => [ + 'field' => 'include-permitted-users', + 'title' => 'resource-type/parameters.title-include-permitted-users', + 'description' => 'resource-type/parameters.description-include-permitted-users', + 'type' => 'boolean', + 'required' => false ] ] ]; diff --git a/package.json b/package.json deleted file mode 100644 index e81ab87f..00000000 --- a/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "private": true, - "scripts": { - "dev": "npm run development", - "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", - "watch": "npm run development -- --watch", - "watch-poll": "npm run watch -- --watch-poll", - "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", - "prod": "npm run production", - "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" - }, - "devDependencies": { - "axios": "^0.18", - "bootstrap": "^4.0.0", - "popper.js": "^1.12", - "cross-env": "^5.1", - "jquery": "^3.2", - "laravel-mix": "^2.0", - "lodash": "^4.17.4", - "vue": "^2.5.7" - } -} diff --git a/public/api/schema/permitted-user.json b/public/api/schema/permitted-user.json index 1fdcc0f1..f43efe08 100644 --- a/public/api/schema/permitted-user.json +++ b/public/api/schema/permitted-user.json @@ -12,12 +12,12 @@ }, "created": { "type": "string" - }, - "required": [ - "id", - "name", - "email", - "created" - ] - } + } + }, + "required": [ + "id", + "name", + "email", + "created" + ] } diff --git a/public/api/schema/resource-type.json b/public/api/schema/resource-type.json index a4eba9a5..e905e0d9 100644 --- a/public/api/schema/resource-type.json +++ b/public/api/schema/resource-type.json @@ -43,6 +43,18 @@ ] }, "resources": { + "type": "object", + "properties": { + "count": { + "type": "integer" + } + }, + "required": [ + "count" + ] + + }, + "permitted_users": { "type": "object", "properties": { "count": { diff --git a/public/package.json b/public/package.json index 23073991..6ec6c694 100644 --- a/public/package.json +++ b/public/package.json @@ -3,7 +3,7 @@ "version": "2", "description": "The Costs to Expect API", "dependencies": { - "bootstrap": "^4.6", + "bootstrap": "^4.6.1", "jquery": "^3.5.1", "popper.js": "^1.16.1" }, diff --git a/public/yarn.lock b/public/yarn.lock index 2e2e7f64..0ce68dda 100644 --- a/public/yarn.lock +++ b/public/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -bootstrap@^4.6: - version "4.6.0" - resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.6.0.tgz#97b9f29ac98f98dfa43bf7468262d84392552fd7" - integrity sha512-Io55IuQY3kydzHtbGvQya3H+KorS/M9rSNyfCGCg9WZ4pyT/lCxIlpJgG1GXW/PswzC84Tr2fBYi+7+jFVQQBw== +bootstrap@^4.6.1: + version "4.6.1" + resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.6.1.tgz#bc25380c2c14192374e8dec07cf01b2742d222a2" + integrity sha512-0dj+VgI9Ecom+rvvpNZ4MUZJz8dcX7WCX+eTID9+/8HgOkv3dsRzi8BGeZJCQU6flWQVYxwTQnEZFrmJSEO7og== jquery@^3.5.1: version "3.5.1" diff --git a/resources/lang/en/auth.php b/resources/lang/en/auth.php index e5506df2..762a30b0 100644 --- a/resources/lang/en/auth.php +++ b/resources/lang/en/auth.php @@ -15,5 +15,15 @@ 'failed' => 'These credentials do not match our records.', 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', + 'signed-out' => 'Account signed out', + + 'email-or-token-invalid' => 'Sorry, the email and or token you supplied are invalid', + 'unable-to-find-account' => 'Unable to fetch your account to create password, please try again later', + 'unable-to-create-password' => 'Unable to create password, please try again later', + 'unable-to-create-account' => 'Unable to create the account, please try again later', + 'unable-process-forgot-password' => 'Unable to process your forgot password request, please try again later', + 'unable-to-update-profile' => 'Unable to update your profile, please try again', + 'success-forgot-password-request' => 'Request received, please check your email for instructions on how to create your new password', + 'success-account-created' => 'Account created, please check you email for information on how to create your password' ]; diff --git a/resources/lang/en/entities.php b/resources/lang/en/entities.php index 175d1000..3aff1878 100644 --- a/resources/lang/en/entities.php +++ b/resources/lang/en/entities.php @@ -4,6 +4,7 @@ return [ 'resource-type' => 'Resource Type', + 'permitted-user' => 'Permitted User', 'resource' => 'Resource', 'category' => 'Category', 'subcategory' => 'Subcategory', diff --git a/resources/lang/en/permitted-user/fields.php b/resources/lang/en/permitted-user/fields.php index 3245ab7c..826c4ce1 100644 --- a/resources/lang/en/permitted-user/fields.php +++ b/resources/lang/en/permitted-user/fields.php @@ -3,6 +3,6 @@ declare(strict_types=1); return [ - 'title-user_id' => 'User Id', - 'description-user_id' => 'The user to assign to the resource type' + 'title-email' => 'Email', + 'description-email' => 'The email address of the user you want to assign access' ]; diff --git a/resources/lang/en/permitted-user/parameters.php b/resources/lang/en/permitted-user/parameters.php new file mode 100644 index 00000000..0dae23de --- /dev/null +++ b/resources/lang/en/permitted-user/parameters.php @@ -0,0 +1,5 @@ + 'The given user cannot be assigned to the resource type, they either don\'t exist to us or are already assigned to the resource type', +]; diff --git a/resources/lang/en/resource-type/parameters.php b/resources/lang/en/resource-type/parameters.php index 9ad81b0f..36c1cb5b 100644 --- a/resources/lang/en/resource-type/parameters.php +++ b/resources/lang/en/resource-type/parameters.php @@ -4,5 +4,8 @@ return [ 'title-include-resources' => 'Include resources?', - 'description-include-resources' => 'Optionally include the resources for each resource type' + 'description-include-resources' => 'Optionally include the resources for each resource type', + + 'title-include-permitted-users' => 'Include permitted users?', + 'description-include-permitted-users' => 'Optionally include the permitted users for each resource type' ]; diff --git a/resources/lang/en/resource/parameters.php b/resources/lang/en/resource/parameters.php index b8028686..0dae23de 100644 --- a/resources/lang/en/resource/parameters.php +++ b/resources/lang/en/resource/parameters.php @@ -2,6 +2,4 @@ declare(strict_types=1); -return [ - -]; +return []; diff --git a/resources/lang/en/route-descriptions.php b/resources/lang/en/route-descriptions.php index 12508f64..3be0171e 100644 --- a/resources/lang/en/route-descriptions.php +++ b/resources/lang/en/route-descriptions.php @@ -11,6 +11,9 @@ 'auth_update_password_POST' => 'Update your account password', 'auth_update_profile_POST' => 'Update your account profile', 'auth_user_GET' => 'Return the account details for the signed-in user', + 'auth_user_tokens_GET' => 'Return the tokens for the signed-in user', + 'auth_user_token_GET' => 'Return the requested token for the signed-in user', + 'auth_user_token_DELETE' => 'Delete the requested token for the signed-in user', 'auth_check_GET' => 'Check to see if the user is authenticated', 'api_GET_index' => 'Return all the API routes', @@ -89,7 +92,9 @@ 'item_partial_transfer_DELETE' => 'Delete the selected partial transfer', 'permitted_user_GET_index' => 'Return the permitted users', + 'permitted_user_GET_show' => 'Return the selected permitted user', 'permitted_user_POST' => 'Assign a permitted user', + 'permitted_user_DELETE' => 'Delete the selected permitted user', 'request_GET_error-log' => 'Return the error log', 'request_GET_cache' => 'Return the number of cached keys for the authenticated user', diff --git a/resources/views/mail/internal-error.blade.php b/resources/views/mail/internal-error.blade.php deleted file mode 100644 index 9a54cc5d..00000000 --- a/resources/views/mail/internal-error.blade.php +++ /dev/null @@ -1,34 +0,0 @@ - - - - Costs to Expect API: Internal error - - - - -

Captured Costs to Expect API Internal Error

- - - - - - - - - - - - - - - - - - -
Message:{{ $internal_error['message'] }}
File:{{ $internal_error['file'] }}
Line:{{ $internal_error['line'] }}
Trace:{{ $internal_error['trace'] }}
- - diff --git a/resources/views/mail/request-error.blade.php b/resources/views/mail/request-error.blade.php deleted file mode 100644 index ea0d61e1..00000000 --- a/resources/views/mail/request-error.blade.php +++ /dev/null @@ -1,46 +0,0 @@ - - - - Costs to Expect API: Request error - - - - -

Captured Costs to Expect API Request Error

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Method{{ $request_error['method'] }}
Expected Status Code:{{ $request_error['expected_status_code'] }}
Returned Status Code:{{ $request_error['returned_status_code'] }}
Requested URI:{{ $request_error['request_uri'] }}
Source:{{ $request_error['source'] }}
Referer (if set):{{ $request_error['referer'] }}
Debug information (if set):{{ $request_error['debug'] }}
- - diff --git a/resources/views/welcome.blade.php b/resources/views/welcome.blade.php index d250825f..b90a4570 100644 --- a/resources/views/welcome.blade.php +++ b/resources/views/welcome.blade.php @@ -229,49 +229,36 @@ function gtag(){dataLayer.push(arguments);}

Added

    -
  • We have added additional tests for the `ResourceManage` controller
  • -
  • We have added tests for the `ResourceTypeView` controller
  • -
  • We have added a logout route
  • -
  • We have added an OPTIONS request for `/auth/create-new-password`
  • -
  • We have added an OPTIONS request for `/auth/create-password`
  • -
  • We have added an OPTIONS request for `/auth/forgot-password`
  • -
  • We have added an OPTIONS request for `/auth/login`
  • -
  • We have added an OPTIONS request for `/auth/register`
  • -
  • We have added an OPTIONS request for `/auth/update-password`
  • -
  • We have added an OPTIONS request for `/auth/update-profile`
  • -
  • We have added an OPTIONS request for `/auth/user`
  • -
  • We have added an OPTIONS request for `/auth/check`
  • +
  • We have updated the `/auth/user` route, the route will now show any active created tokens
  • +
  • We have added `device_name` as an optional field on sign-in, if set, the generated token will be prefixed with the device name
  • +
  • We have added an `include-permitted-users` parameter when requesting a resource type, you will be able to see all the permitted users without having to go down the tree
  • +
  • If an API response includes a related object, the first field should be the URI to the relevant collection or resource, we have started updating responses
  • +
  • We have added a `auth/user/tokens` route to show the active tokens, you can view an individual token as well as delete a token
  • +
  • We have added a notification for failed jobs, if the `ClearCache` job fails we will get an email, luckily, it doesn't ever fail :)
  • +
  • We have added the ability to assign permitted users, if you have access to a resource type you can assign a known user to the resource type
  • +
  • We have added a view permitted user endpoint
  • +
  • We have added the ability to delete a permitted user, you can delete any permitted user with access to the resource type, including yourself
  • +
  • We have added initial tests for the permitted user routes

Changed

    -
  • We have made a couple of minor changes to the Docker setup
  • -
  • We have updated the README because of minor Docker changes and corrected the table layouts in the README file
  • -
  • We have updated all front-end and back-end dependencies
  • -
  • We have updated the copyright, we are now in 2022
  • -
  • We have added additional feature tests and removed some duplication in the tests, the README details the current test status
  • -
  • General refactoring, switched to method injection and logging exception messages
  • -
  • We are switching to named routes and have updated some of the route files, more will be updated as additional tests are created
  • -
  • We have done a quick review of each of the model classes and fixed a few tiny issues
  • -
  • We have reviewed all the `ItemType` classes, improved organisation with additional namespaces, renamed classes and methods, all with the goal being to try and make everything clearer
  • -
  • We have reviewed all item based controllers and switched to methods per item type rather than hiding all the logic in larger item classes. There is slightly more duplication but this will allow us to more easily customise each item type as new ones are added, I'm looking at you forecasting
  • -
  • We have updated the item/categories routes and will return a 405 when a category is not supported for the item type
  • -
  • We have updated the item/subcategories routes and will return a 405 when a subcategory is not supported for the item type
  • -
  • The Authentication controller no longer extends from the base app controller, it was doing some unnecessary work
  • +
  • We have updated sign-in to clear tokens that have not been used for a year
  • +
  • We have added additional validation to `/auth/login` to match all the create password routes
  • +
  • We have removed additional references to our `item-type` entity class, keep code in the individual item type namespaces
  • +
  • We have converted out `Mailables` to `Notifications` and they get send via the queue
  • +
  • We have updated the `partial-transfers` route to use methods per item types, this way we can correctly return a 405 when an item doesn't support partial transfers
  • +
  • We have updated the `transfers` route to use methods per item types, this was we can correctly return a 405 when an item doesn't support transfers
  • +
  • We have localised all response messages in the Authentication controller to match the rest of the API

Fixed

    -
  • We have fixed the `delete_resource_type_success` test, wrong route
  • -
  • The `notFoundOrNotAccessible` response will optionally return a 403 if not accessible and not a 404
  • -
- -

Removed

- -
    -
  • We have removed a few files not used by the API
  • +
  • We have fixed our Authentication tests, we no longer overwrite the initial user, additionally, we have updated three tests to return success on a 422, not a 401
  • +
  • We have corrected a couple of parameter conversions, two parameters not correctly being converted to Booleans
  • +
  • Unable to delete an `allocated-expense`, need to clear the partial transfers table
diff --git a/routes/api/auth.php b/routes/api/auth.php index 99f70a66..0dc37d4f 100644 --- a/routes/api/auth.php +++ b/routes/api/auth.php @@ -63,6 +63,16 @@ function () { [Authentication::class, 'optionsUser'] ); + Route::options( + 'auth/user/tokens', + [Authentication::class, 'optionsTokens'] + ); + + Route::options( + 'auth/user/tokens/{token_id}', + [Authentication::class, 'optionsToken'] + ); + Route::options( 'auth/check', [Authentication::class, 'optionsCheck'] @@ -114,6 +124,21 @@ static function () { 'auth/user', [Authentication::class, 'user'] )->name('auth.user'); + + Route::get( + 'auth/user/tokens', + [Authentication::class, 'tokens'] + )->name('auth.user.token.list'); + + Route::get( + 'auth/user/tokens/{token_id}', + [Authentication::class, 'token'] + )->name('auth.user.token.show'); + + Route::delete( + 'auth/user/tokens/{token_id}', + [Authentication::class, 'deleteToken'] + )->name('auth.user.token.delete'); } ); diff --git a/routes/api/private-routes.php b/routes/api/private-routes.php index bbf038f2..77d648ee 100644 --- a/routes/api/private-routes.php +++ b/routes/api/private-routes.php @@ -1,5 +1,6 @@ name('permitted-user.create'); + Route::post( 'resource-types/{resource_type_id}/resources', [ResourceManage::class, 'create'] @@ -69,6 +75,11 @@ static function () { [ResourceTypeManage::class, 'delete'] )->name('resource-type.delete'); + Route::delete( + 'resource-types/{resource_type_id}/permitted-users/{permitted_user_id}', + [PermittedUserManage::class, 'delete'] + )->name('permitted-user.delete'); + Route::delete( 'resource-types/{resource_type_id}/categories/{category_id}', 'CategoryManage@delete' diff --git a/routes/api/public-routes.php b/routes/api/public-routes.php index b3b1caa6..c4c686e6 100644 --- a/routes/api/public-routes.php +++ b/routes/api/public-routes.php @@ -1,5 +1,6 @@ name('item-type.show'); Route::options( 'item-types/{item_type_id}', @@ -155,8 +156,8 @@ static function () { Route::get( 'resource-types/{resource_type_id}/categories/{category_id}/subcategories', - 'SubcategoryView@index' - ); + [\App\Http\Controllers\SubcategoryView::class, 'index'] + )->name('subcategory.list'); Route::options( 'resource-types/{resource_type_id}/categories/{category_id}/subcategories', @@ -205,13 +206,23 @@ static function () { Route::get( 'resource-types/{resource_type_id}/permitted-users', - 'PermittedUserView@index' - ); + [PermittedUserView::class, 'index'] + )->name('permitted-user.list'); Route::options( 'resource-types/{resource_type_id}/permitted-users', - 'PermittedUserView@optionsIndex' - ); + [PermittedUserView::class, 'optionsIndex'] + )->name('permitted-user.options'); + + Route::get( + 'resource-types/{resource_type_id}/permitted-users/{permitted_user_id}', + [PermittedUserView::class, 'show'] + )->name('permitted-user.show'); + + Route::options( + 'resource-types/{resource_type_id}/permitted-users/{permitted_user_id}', + [PermittedUserView::class, 'optionsShow'] + )->name('permitted-user.show.options'); Route::get( 'resource-types/{resource_type_id}/resources', diff --git a/tests/Feature/Http/Controllers/AuthenticationTest.php b/tests/Feature/Http/Controllers/AuthenticationTest.php index bd7a523a..cde2bd98 100644 --- a/tests/Feature/Http/Controllers/AuthenticationTest.php +++ b/tests/Feature/Http/Controllers/AuthenticationTest.php @@ -603,7 +603,7 @@ public function login_errors_with_no_email(): void ] ); - $response->assertStatus(401); + $response->assertStatus(422); } /** @test */ @@ -616,7 +616,7 @@ public function login_errors_with_no_name(): void ] ); - $response->assertStatus(401); + $response->assertStatus(422); } /** @test */ @@ -628,7 +628,7 @@ public function login_errors_with_no_payload(): void ] ); - $response->assertStatus(401); + $response->assertStatus(422); } /** @test */ @@ -729,7 +729,7 @@ public function update_password_fails_no_payload(): void /** @test */ public function update_password_success(): void { - $this->actingAs(User::find(1)); + $this->actingAs(User::find($this->getARandomUser()->id)); $new_password = $this->faker->password(12); @@ -775,7 +775,7 @@ public function update_profile_fails_no_payload(): void /** @test */ public function update_profile_success(): void { - $this->actingAs(User::find(1)); + $this->actingAs(User::find($this->getARandomUser()->id)); $response = $this->post( 'v2/auth/update-profile', diff --git a/tests/Feature/Http/Controllers/PermittedUserManageTest.php b/tests/Feature/Http/Controllers/PermittedUserManageTest.php new file mode 100644 index 00000000..9c4f855c --- /dev/null +++ b/tests/Feature/Http/Controllers/PermittedUserManageTest.php @@ -0,0 +1,85 @@ +actingAs(User::find(1)); + + $id = $this->createAndReturnResourceTypeId(); + + $response = $this->postPermittedUser( + $id, + [] + ); + + $response->assertStatus(422); + } + + /** @test */ + public function create_permitted_user_fails_user_does_not_exist(): void + { + $this->actingAs(User::find(1)); + + $id = $this->createAndReturnResourceTypeId(); + + $response = $this->postPermittedUser( + $id, + [ + 'email' => $this->faker->email + ] + ); + + $response->assertStatus(422); + } + + /** @test */ + public function create_permitted_user_success(): void + { + $this->actingAs(User::find(1)); + + $id = $this->createAndReturnResourceTypeId(); + $user = $this->getARandomUser(); + + $response = $this->postPermittedUser( + $id, + [ + 'email' => $user->email, + ] + ); + + $response->assertStatus(204); + } + + /** @test */ + public function delete_permitted_user_success(): void + { + $this->actingAs(User::find(1)); + + $resource_type_id = $this->createAndReturnResourceTypeId(); + $user = $this->getARandomUser(); + + $response = $this->postPermittedUser( + $resource_type_id, + [ + 'email' => $user->email, + ] + ); + + $response->assertStatus(204); + + $response = $this->getPermittedUsers(['resource_type_id'=> $resource_type_id]); + $response->assertStatus(200); + + $permitted_user_id = $response->json()[1]['id']; + + $response = $this->deletePermittedUser($resource_type_id, $permitted_user_id); + $response->assertStatus(204); + } +} diff --git a/tests/Feature/Http/Controllers/ResourceManageTest.php b/tests/Feature/Http/Controllers/ResourceManageTest.php index 0d5e011a..a904bfe2 100644 --- a/tests/Feature/Http/Controllers/ResourceManageTest.php +++ b/tests/Feature/Http/Controllers/ResourceManageTest.php @@ -3,7 +3,6 @@ namespace Tests\Feature\Http\Controllers; use App\User; -use Illuminate\Support\Facades\Auth; use Tests\TestCase; class ResourceManageTest extends TestCase @@ -13,7 +12,7 @@ public function create_resource_fails_data_field_not_valid_json(): void { $this->actingAs(User::find(1)); - $id = $this->helperCreateResourceType(); + $id = $this->createAndReturnResourceTypeId(); $response = $this->postResource( $id, @@ -33,7 +32,7 @@ public function create_resource_fails_item_subtype_invalid(): void { $this->actingAs(User::find(1)); - $id = $this->helperCreateResourceType(); + $id = $this->createAndReturnResourceTypeId(); $response = $this->postResource( $id, @@ -53,7 +52,7 @@ public function create_resource_fails_no_description_in_payload(): void { $this->actingAs(User::find(1)); - $id = $this->helperCreateResourceType(); + $id = $this->createAndReturnResourceTypeId(); $response = $this->postResource( $id, @@ -72,7 +71,7 @@ public function create_resource_fails_no_name_in_payload(): void { $this->actingAs(User::find(1)); - $id = $this->helperCreateResourceType(); + $id = $this->createAndReturnResourceTypeId(); $response = $this->postResource( $id, @@ -91,7 +90,7 @@ public function create_resource_fails_no_payload(): void { $this->actingAs(User::find(1)); - $id = $this->helperCreateResourceType(); + $id = $this->createAndReturnResourceTypeId(); $response = $this->postResource( $id, @@ -106,7 +105,7 @@ public function create_resource_fails_non_unique_name(): void { $this->actingAs(User::find(1)); - $id = $this->helperCreateResourceType(); + $id = $this->createAndReturnResourceTypeId(); $name = $this->faker->text(200); @@ -139,7 +138,7 @@ public function create_resource_success(): void { $this->actingAs(User::find(1)); - $id = $this->helperCreateResourceType(); + $id = $this->createAndReturnResourceTypeId(); $response = $this->postResource( $id, @@ -159,7 +158,7 @@ public function create_resource_success_include_data_field(): void { $this->actingAs(User::find(1)); - $id = $this->helperCreateResourceType(); + $id = $this->createAndReturnResourceTypeId(); $response = $this->postResource( $id, @@ -180,8 +179,8 @@ public function delete_resource_success(): void { $this->actingAs(User::find(1)); - $resource_type_id = $this->helperCreateResourceType(); - $id = $this->helperCreateResource($resource_type_id); + $resource_type_id = $this->createAndReturnResourceTypeId(); + $id = $this->createAndReturnResourceId($resource_type_id); $response = $this->deleteResource($resource_type_id, $id); @@ -193,8 +192,8 @@ public function update_resource_fails_extra_fields_in_payload(): void { $this->actingAs(User::find(1)); - $resource_type_id = $this->helperCreateResourceType(); - $resource_id = $this->helperCreateResource($resource_type_id); + $resource_type_id = $this->createAndReturnResourceTypeId(); + $resource_id = $this->createAndReturnResourceId($resource_type_id); $response = $this->patchResource( $resource_type_id, @@ -212,8 +211,8 @@ public function update_resource_fails_non_payload(): void { $this->actingAs(User::find(1)); - $resource_type_id = $this->helperCreateResourceType(); - $resource_id = $this->helperCreateResource($resource_type_id); + $resource_type_id = $this->createAndReturnResourceTypeId(); + $resource_id = $this->createAndReturnResourceId($resource_type_id); $response = $this->patchResource( $resource_type_id, @@ -229,7 +228,7 @@ public function update_resource_fails_non_unique_name(): void { $this->actingAs(User::find(1)); - $resource_type_id = $this->helperCreateResourceType(); + $resource_type_id = $this->createAndReturnResourceTypeId(); // Create first resource $name = $this->faker->text(200); @@ -274,8 +273,8 @@ public function update_resource_success(): void { $this->actingAs(User::find(1)); - $resource_type_id = $this->helperCreateResourceType(); - $resource_id = $this->helperCreateResource($resource_type_id); + $resource_type_id = $this->createAndReturnResourceTypeId(); + $resource_id = $this->createAndReturnResourceId($resource_type_id); $response = $this->patchResource( $resource_type_id, diff --git a/tests/TestCase.php b/tests/TestCase.php index a58a3211..3bb9dc54 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -18,21 +18,9 @@ abstract class TestCase extends BaseTestCase protected string $test_user_email = 'test-account-email@email.com'; protected string $test_user_password = 'test-account-secret-password'; - protected function setUp(): void + protected function assertJsonIsPermittedUser($content): void { - parent::setUp(); - - $result = DB::select(DB::raw("SHOW TABLES LIKE 'users';")); - - if (!count($result)) { - $this->artisan('migrate:fresh'); - - $user = new User(); - $user->name = $this->faker->name; - $user->email = $this->test_user_email; - $user->password = Hash::make($this->test_user_password); - $user->save(); - } + $this->assertJsonMatchesSchema($content, 'api/schema/permitted-user.json'); } protected function assertJsonIsResource($content): void @@ -54,6 +42,50 @@ protected function assertJsonMatchesSchema($content, $schema_file): void self::assertTrue($result->isValid()); } + protected function createAndReturnResourceId(string $resource_type_id): string + { + $response = $this->postResource( + $resource_type_id, + [ + 'name' => $this->faker->text(200), + 'description' => $this->faker->text(200), + 'item_subtype_id' => 'a56kbWV82n' + ] + ); + + if ($response->assertStatus(201)) { + return $response->json('id'); + } + + $this->fail('Unable to create the resource'); + } + + protected function createAndReturnResourceTypeId(): string + { + $response = $this->postResourceType( + [ + 'name' => $this->faker->text(255), + 'description' => $this->faker->text, + 'data' => '{"field":true}', + 'item_type_id' => 'OqZwKX16bW', + 'public' => false + ] + ); + + if ($response->assertStatus(201)) { + return $response->json('id'); + } + + $this->fail('Unable to create the resource type'); + } + + protected function deletePermittedUser(string $resource_type_id, string $permitted_user_id): TestResponse + { + return $this->delete( + route('permitted-user.delete', ['resource_type_id' => $resource_type_id, 'permitted_user_id' => $permitted_user_id]), [] + ); + } + protected function deleteResource(string $resource_type_id, $resource_id): TestResponse { return $this->delete( @@ -68,12 +100,34 @@ protected function deleteResourceType(string $resource_type_id): TestResponse ); } - protected function patchResourceType(string $resource_type_id, array $payload): TestResponse + protected function getARandomUser() { - return $this->patch( - route('resource-type.update', ['resource_type_id' => $resource_type_id]), - $payload - ); + return User::query()->where('id', '!=', 1)->inRandomOrder()->first(); + } + + protected function getRoute(string $route, array $parameters = []): TestResponse + { + return $this->get(route($route, $parameters)); + } + + protected function getPermittedUser(array $parameters = []): TestResponse + { + return $this->getRoute('permitted-user.show', $parameters); + } + + protected function getPermittedUsers(array $parameters = []): TestResponse + { + return $this->getRoute('permitted-user.list', $parameters); + } + + protected function getResourceType(array $parameters = []): TestResponse + { + return $this->getRoute('resource-type.show', $parameters); + } + + protected function getResourceTypes(array $parameters = []): TestResponse + { + return $this->getRoute('resource-type.list', $parameters); } protected function patchResource(string $resource_type_id, string $resource_id, array $payload): TestResponse @@ -90,63 +144,49 @@ protected function patchResource(string $resource_type_id, string $resource_id, ); } - protected function postResource(string $resource_type_id, array $payload): TestResponse + protected function patchResourceType(string $resource_type_id, array $payload): TestResponse { - return $this->post( - route('resource.create', ['resource_type_id' => $resource_type_id]), + return $this->patch( + route('resource-type.update', ['resource_type_id' => $resource_type_id]), $payload ); } - protected function postResourceType(array $payload): TestResponse + protected function postPermittedUser(string $resource_type_id, array $payload): TestResponse { - return $this->post(route('resource-type.create'), $payload); + return $this->post( + route('permitted-user.create', ['resource_type_id' => $resource_type_id]), + $payload + ); } - protected function helperCreateResourceType(): string + protected function postResource(string $resource_type_id, array $payload): TestResponse { - $response = $this->postResourceType( - [ - 'name' => $this->faker->text(255), - 'description' => $this->faker->text, - 'data' => '{"field":true}', - 'item_type_id' => 'OqZwKX16bW', - 'public' => false - ] + return $this->post( + route('resource.create', ['resource_type_id' => $resource_type_id]), + $payload ); - - if ($response->assertStatus(201)) { - return $response->json('id'); - } - - $this->fail('Unable to create the resource type'); } - protected function fetchResourceType(array $parameters = []): TestResponse + protected function postResourceType(array $payload): TestResponse { - return $this->get(route('resource-type.show', $parameters)); + return $this->post(route('resource-type.create'), $payload); } - protected function fetchResourceTypes(array $parameters = []): TestResponse + protected function setUp(): void { - return $this->get(route('resource-type.list', $parameters)); - } + parent::setUp(); - protected function helperCreateResource(string $resource_type_id): string - { - $response = $this->postResource( - $resource_type_id, - [ - 'name' => $this->faker->text(200), - 'description' => $this->faker->text(200), - 'item_subtype_id' => 'a56kbWV82n' - ] - ); + $result = DB::select(DB::raw("SHOW TABLES LIKE 'users';")); - if ($response->assertStatus(201)) { - return $response->json('id'); - } + if (!count($result)) { + $this->artisan('migrate:fresh'); - $this->fail('Unable to create the resource'); + $user = new User(); + $user->name = $this->faker->name; + $user->email = $this->test_user_email; + $user->password = Hash::make($this->test_user_password); + $user->save(); + } } } diff --git a/tests/View/Http/Controllers/PermittedUserViewTest.php b/tests/View/Http/Controllers/PermittedUserViewTest.php new file mode 100644 index 00000000..128c00f3 --- /dev/null +++ b/tests/View/Http/Controllers/PermittedUserViewTest.php @@ -0,0 +1,55 @@ +actingAs(User::find(1)); + + $response = $this->getResourceTypes(); + $response->assertStatus(200); + + foreach ($response->json() as $resource_type) { + + $permitted_user_response = $this->getPermittedUsers(['resource_type_id'=> $resource_type['id']]); + $permitted_user_response->assertStatus(200); + + foreach ($permitted_user_response->json() as $permitted_user) { + try { + $json = json_encode($permitted_user, JSON_THROW_ON_ERROR); + } catch (\JsonException $e) { + $this->fail('Unable to encode the JSON string'); + } + + $this->assertJsonIsPermittedUser($json); + } + } + } + + /** @test */ + public function item(): void + { + $this->actingAs(User::find(1)); + + $response = $this->getResourceTypes(['offset'=>0, 'limit'=> 1]); + $response->assertStatus(200); + + $resource_type_id = $response->json()[0]['id']; + + $response = $this->getPermittedUsers(['resource_type_id'=> $resource_type_id]); + $response->assertStatus(200); + + $permitted_user_id = $response->json()[0]['id']; + + $response = $this->getPermittedUser(['resource_type_id'=> $resource_type_id, 'permitted_user_id' => $permitted_user_id]); + $response->assertStatus(200); + + $this->assertJsonIsPermittedUser($response->content()); + } +} diff --git a/tests/View/Http/Controllers/ResourceTypeViewTest.php b/tests/View/Http/Controllers/ResourceTypeViewTest.php index 71b21a16..aeed0597 100644 --- a/tests/View/Http/Controllers/ResourceTypeViewTest.php +++ b/tests/View/Http/Controllers/ResourceTypeViewTest.php @@ -12,7 +12,7 @@ public function collection(): void { $this->actingAs(User::find(1)); - $response = $this->fetchResourceTypes(); + $response = $this->getResourceTypes(); $response->assertStatus(200); @@ -32,7 +32,7 @@ public function collection_pagination(): void { $this->actingAs(User::find(1)); - $response = $this->fetchResourceTypes(['offset'=>1, 'limit'=> 1]); + $response = $this->getResourceTypes(['offset'=>1, 'limit'=> 1]); $response->assertStatus(200); $response->assertHeader('X-Offset', 1); @@ -54,12 +54,12 @@ public function item(): void { $this->actingAs(User::find(1)); - $response = $this->fetchResourceTypes(['offset'=>0, 'limit'=> 1]); + $response = $this->getResourceTypes(['offset'=>0, 'limit'=> 1]); $response->assertStatus(200); $resource_type_id = $response->json()[0]['id']; - $response = $this->fetchResourceType(['resource_type_id'=> $resource_type_id]); + $response = $this->getResourceType(['resource_type_id'=> $resource_type_id]); $response->assertStatus(200); $this->assertJsonIsResourceType($response->content());