From db88d6a50d558c3bd583436c68c2a44ae824e579 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 2 Mar 2020 21:22:37 +1100 Subject: [PATCH] Fixes for V2 (#3408) * Refactor for user * payment notifications * Fixes for contact request * Fix validation for contacts * Fixes for base repo * Fixes for Invoice Repo * hide password field on clientcontact --- app/Console/Commands/CreateTestData.php | 1 - app/Http/Controllers/SelfUpdateController.php | 11 ++ app/Http/Controllers/UserController.php | 15 +-- app/Http/Middleware/Cors.php | 4 +- .../Requests/Client/StoreClientRequest.php | 19 ++- .../Requests/Client/UpdateClientRequest.php | 28 +++-- .../Activity/PaymentCreatedActivity.php | 2 + app/Listeners/Payment/PaymentNotification.php | 50 ++++++++ app/Models/Account.php | 27 ++-- .../Payment/NewPaymentNotification.php | 109 ++++++++++++++++ app/Providers/EventServiceProvider.php | 2 + app/Repositories/BaseRepository.php | 29 +++-- app/Repositories/ClientContactRepository.php | 18 ++- app/Repositories/InvoiceRepository.php | 5 + app/Repositories/UserRepository.php | 26 +++- app/Transformers/CreditTransformer.php | 2 + app/Transformers/InvoiceTransformer.php | 1 + app/Transformers/QuoteTransformer.php | 2 + resources/lang/en/texts.php | 3 + routes/api.php | 2 + tests/Feature/ClientTest.php | 119 +++++++++++++++++- 21 files changed, 411 insertions(+), 64 deletions(-) create mode 100644 app/Listeners/Payment/PaymentNotification.php create mode 100644 app/Notifications/Payment/NewPaymentNotification.php diff --git a/app/Console/Commands/CreateTestData.php b/app/Console/Commands/CreateTestData.php index 38fb9db080..11e450de22 100644 --- a/app/Console/Commands/CreateTestData.php +++ b/app/Console/Commands/CreateTestData.php @@ -130,7 +130,6 @@ class CreateTestData extends Command $this->info('Creating '.$this->count. ' clients'); - for ($x=0; $x<$this->count; $x++) { $z = $x+1; $this->info("Creating client # ".$z); diff --git a/app/Http/Controllers/SelfUpdateController.php b/app/Http/Controllers/SelfUpdateController.php index 9a6446aac4..333d5fc8a7 100644 --- a/app/Http/Controllers/SelfUpdateController.php +++ b/app/Http/Controllers/SelfUpdateController.php @@ -62,4 +62,15 @@ class SelfUpdateController extends BaseController return response()->json(['message'=>$res], 200); } + + public function checkVersion(UpdaterManager $updater) + { + + //echo $updater->source()->getVersionInstalled(); + + //echo $updater->source()->isNewVersionAvailable(); + + //echo $updater->source()->getVersionAvailable(); + + } } diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index ade73a6f7f..1c131dea1e 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -439,20 +439,7 @@ class UserController extends BaseController public function destroy(DestroyUserRequest $request, User $user) { /* If the user passes the company user we archive the company user */ - if(array_key_exists('company_user', $request->all())) - { - $this->forced_includes = 'company_users'; - - $company = auth()->user()->company(); - - $cu = CompanyUser::whereUserId($user->id) - ->whereCompanyId($company->id) - ->first(); - - $cu->delete(); - } - else - $user->delete(); + $user = $this->user_repo->destroy($request->all(), $user); return $this->itemResponse($user->fresh()); } diff --git a/app/Http/Middleware/Cors.php b/app/Http/Middleware/Cors.php index cb26ca07df..4b5ec93750 100644 --- a/app/Http/Middleware/Cors.php +++ b/app/Http/Middleware/Cors.php @@ -37,7 +37,9 @@ class Cors $response->headers->set('Access-Control-Allow-Origin', '*'); $response->headers->set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); $response->headers->set('Access-Control-Allow-Headers', 'X-API-SECRET,X-API-TOKEN,X-API-PASSWORD,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'); - + $response->headers->set('X-APP-VERSION', config('ninja.app_version')); + $response->headers->set('X-API-VERSION', config('ninja.api_version')); + return $response; } } diff --git a/app/Http/Requests/Client/StoreClientRequest.php b/app/Http/Requests/Client/StoreClientRequest.php index bb76db4ae6..bf05d4b8f7 100644 --- a/app/Http/Requests/Client/StoreClientRequest.php +++ b/app/Http/Requests/Client/StoreClientRequest.php @@ -16,7 +16,6 @@ use App\Http\Requests\Request; use App\Http\ValidationRules\ValidClientGroupSettingsRule; use App\Models\Client; use App\Utils\Traits\MakesHash; -use Illuminate\Support\Facades\Log; use Illuminate\Validation\Rule; class StoreClientRequest extends Request @@ -43,6 +42,7 @@ class StoreClientRequest extends Request $rules['settings'] = new ValidClientGroupSettingsRule(); $rules['contacts.*.email'] = 'nullable|distinct'; $rules['contacts.*.password'] = [ + 'nullable', 'sometimes', 'string', 'min:7', // must be at least 10 characters in length @@ -72,7 +72,6 @@ class StoreClientRequest extends Request $input = $this->all(); //@todo implement feature permissions for > 100 clients - if (!isset($input['settings'])) { $input['settings'] = ClientSettings::defaults(); } @@ -94,16 +93,24 @@ class StoreClientRequest extends Request //Filter the client contact password - if it is sent with ***** we should ignore it! if(isset($contact['password'])) { - $contact['password'] = str_replace("*", "", $contact['password']); - if(strlen($contact['password']) == 0) - unset($input['contacts'][$key]['password']); + if(strlen($contact['password']) == 0){ + $input['contacts'][$key]['password'] = ''; + } + else { + $contact['password'] = str_replace("*", "", $contact['password']); + + if(strlen($contact['password']) == 0){ + unset($input['contacts'][$key]['password']); + } + + } + } } } - $this->replace($input); } diff --git a/app/Http/Requests/Client/UpdateClientRequest.php b/app/Http/Requests/Client/UpdateClientRequest.php index 8231905507..2b60e7dd06 100644 --- a/app/Http/Requests/Client/UpdateClientRequest.php +++ b/app/Http/Requests/Client/UpdateClientRequest.php @@ -18,7 +18,6 @@ use App\Http\ValidationRules\ValidClientGroupSettingsRule; use App\Http\ValidationRules\ValidSettingsRule; use App\Utils\Traits\ChecksEntityStatus; use App\Utils\Traits\MakesHash; -use Illuminate\Support\Facades\Log; use Illuminate\Validation\Rule; class UpdateClientRequest extends Request @@ -50,6 +49,7 @@ class UpdateClientRequest extends Request $rules['settings'] = new ValidClientGroupSettingsRule(); $rules['contacts.*.email'] = 'nullable|distinct'; $rules['contacts.*.password'] = [ + 'nullable', 'sometimes', 'string', 'min:7', // must be at least 10 characters in length @@ -88,18 +88,28 @@ class UpdateClientRequest extends Request unset($input['contacts'][$key]['id']); elseif(array_key_exists('id', $contact) && is_string($contact['id'])) $input['contacts'][$key]['id'] = $this->decodePrimaryKey($contact['id']); - } - //Filter the client contact password - if it is sent with ***** we should ignore it! - if(isset($contact['password'])) - { - $contact['password'] = str_replace("*", "", $contact['password']); - if(strlen($contact['password']) == 0) - unset($input['contacts'][$key]['password']); + //Filter the client contact password - if it is sent with ***** we should ignore it! + if(isset($contact['password'])) + { + + if(strlen($contact['password']) == 0){ + $input['contacts'][$key]['password'] = ''; + } + else { + $contact['password'] = str_replace("*", "", $contact['password']); + + if(strlen($contact['password']) == 0){ + unset($input['contacts'][$key]['password']); + } + + } + + } + } } - $this->replace($input); } } diff --git a/app/Listeners/Activity/PaymentCreatedActivity.php b/app/Listeners/Activity/PaymentCreatedActivity.php index 52e1434c64..9437f2dd6d 100644 --- a/app/Listeners/Activity/PaymentCreatedActivity.php +++ b/app/Listeners/Activity/PaymentCreatedActivity.php @@ -60,5 +60,7 @@ class PaymentCreatedActivity implements ShouldQueue if (count($invoices) == 0) { $this->activityRepo->save($fields, $payment); } + + } } diff --git a/app/Listeners/Payment/PaymentNotification.php b/app/Listeners/Payment/PaymentNotification.php new file mode 100644 index 0000000000..2f2ac27cfe --- /dev/null +++ b/app/Listeners/Payment/PaymentNotification.php @@ -0,0 +1,50 @@ +payment; + + //$invoices = $payment->invoices; + + foreach($payment->company->company_users as $company_user) + { + $company_user->user->notify(new NewPaymentNotification($payment, $payment->company)); + } + } +} diff --git a/app/Models/Account.php b/app/Models/Account.php index 37132c3dcc..4920e7f45c 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -143,31 +143,31 @@ class Account extends BaseModel return $self_host || ! empty($plan_details); // Pro; No trial allowed, unless they're trialing enterprise with an active pro plan - case FEATURE_MORE_CLIENTS: + case self::FEATURE_MORE_CLIENTS: return $self_host || ! empty($plan_details) && (! $plan_details['trial'] || ! empty($this->getPlanDetails(false, false))); // White Label - case FEATURE_WHITE_LABEL: + case self::FEATURE_WHITE_LABEL: if (! $self_host && $plan_details && ! $plan_details['expires']) { return false; } // Fallthrough - case FEATURE_REMOVE_CREATED_BY: + case self::FEATURE_REMOVE_CREATED_BY: return ! empty($plan_details); // A plan is required even for self-hosted users // Enterprise; No Trial allowed; grandfathered for old pro users - case FEATURE_USERS:// Grandfathered for old Pro users + case self::FEATURE_USERS:// Grandfathered for old Pro users if ($planDetails && $planDetails['trial']) { // Do they have a non-trial plan? $planDetails = $this->getPlanDetails(false, false); } - return $selfHost || ! empty($planDetails) && ($planDetails['plan'] == PLAN_ENTERPRISE || $planDetails['started'] <= date_create(PRO_USERS_GRANDFATHER_DEADLINE)); + return $selfHost || ! empty($planDetails) && ($planDetails['plan'] == self::PLAN_ENTERPRISE); // Enterprise; No Trial allowed - case FEATURE_DOCUMENTS: - case FEATURE_USER_PERMISSIONS: - return $selfHost || ! empty($planDetails) && $planDetails['plan'] == PLAN_ENTERPRISE && ! $planDetails['trial']; + case self::FEATURE_DOCUMENTS: + case self::FEATURE_USER_PERMISSIONS: + return $selfHost || ! empty($planDetails) && $planDetails['plan'] == self::PLAN_ENTERPRISE && ! $planDetails['trial']; default: return false; @@ -181,14 +181,16 @@ class Account extends BaseModel public function isPaidHostedClient() { + if (! Ninja::isNinja()) + return false; + return $this->plan == 'pro' || $this->plan == 'enterprise'; } public function isTrial() { - if (! Ninja::isNinja()) { + if (! Ninja::isNinja()) return false; - } $plan_details = $this->getPlanDetails(); @@ -197,10 +199,7 @@ class Account extends BaseModel public function getPlanDetails($include_inactive = false, $include_trial = true) { - if (!$this) { - return null; - } - + $plan = $this->plan; $price = $this->plan_price; $trial_plan = $this->trial_plan; diff --git a/app/Notifications/Payment/NewPaymentNotification.php b/app/Notifications/Payment/NewPaymentNotification.php new file mode 100644 index 0000000000..cf61ca3676 --- /dev/null +++ b/app/Notifications/Payment/NewPaymentNotification.php @@ -0,0 +1,109 @@ +payment = $payment; + $this->company = $company; + $this->settings = $payment->client->getMergedSettings(); + } + + /** + * Get the notification's delivery channels. + * + * @param mixed $notifiable + * @return array + */ + public function via($notifiable) + { + //return ['mail']; + return ['mail']; + } + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + $amount = Number::formatMoney($this->payment->amount, $this->payment->client); + + $invoice_texts = ctrans('texts.invoice_number_short'); + + foreach($this->payment->invoices as $invoice) + { + $invoice_texts .= $invoice->number . ','; + } + + $invoice_texts = rtrim($invoice_texts); + + $data = [ + 'title' => ctrans('texts.notification_payment_paid_subject', ['client' => $this->payment->client->present()->name()]), + 'message' => ctrans('texts.notification_paid_paid', ['amount' => $amount, 'client' => $this->payment->client->present()->name(), 'invoice' => $invoice_texts]), + 'url' => config('ninja.site_url') . '/payments/' . $this->payment->hashed_id, + 'button' => ctrans('texts.view_payment'), + 'signature' => $this->company->settings->email_signature, + 'logo' => $this->company->present()->logo(), + ]; + + + return (new MailMessage) + ->subject(ctrans('texts.notification_payment_paid_subject', ['client' => $this->payment->client->present()->name()])) + ->markdown('email.admin.generic', $data); + + + } + + /** + * Get the array representation of the notification. + * + * @param mixed $notifiable + * @return array + */ + public function toArray($notifiable) + { + return [ + // + ]; + } + + public function toSlack($notifiable) + { + $logo = $this->company->present()->logo(); + + return (new SlackMessage) + ->success() + ->to("#devv2") + ->from("System") + ->image($logo) + ->content("A new account has been created by {$user_name} - {$email} - from IP: {$ip}"); + } + +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index f2dc810303..bba9838c99 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -38,6 +38,7 @@ use App\Listeners\Invoice\InvoiceEmailActivity; use App\Listeners\Invoice\InvoiceEmailFailedActivity; use App\Listeners\Invoice\UpdateInvoiceActivity; use App\Listeners\Invoice\UpdateInvoiceInvitations; +use App\Listeners\Payment\PaymentNotification; use App\Listeners\SendVerificationNotification; use App\Listeners\SetDBListener; use App\Listeners\User\UpdateUserLastLogin; @@ -73,6 +74,7 @@ class EventServiceProvider extends ServiceProvider ], PaymentWasCreated::class => [ PaymentCreatedActivity::class, + PaymentNotification::class, ], PaymentWasDeleted::class => [ PaymentDeletedActivity::class, diff --git a/app/Repositories/BaseRepository.php b/app/Repositories/BaseRepository.php index 69284452ff..c9247dcdd0 100644 --- a/app/Repositories/BaseRepository.php +++ b/app/Repositories/BaseRepository.php @@ -16,8 +16,10 @@ use App\Factory\QuoteInvitationFactory; use App\Jobs\Product\UpdateOrCreateProduct; use App\Models\Client; use App\Models\ClientContact; +use App\Models\Credit; use App\Models\Invoice; use App\Models\InvoiceInvitation; +use App\Models\Quote; use App\Utils\Traits\MakesHash; use ReflectionClass; @@ -166,9 +168,17 @@ class BaseRepository return $this->getInstance()->scope($ids)->withTrashed()->get(); } - public function getInvitationByKey($key) + public function getInvitation($invitation, $resource) { - return InvoiceInvitation::whereRaw("BINARY `key`= ?", [$key])->first(); + + if(!array_key_exists('key', $invitation)) + return false; + + $invitation_class = sprintf("App\\Models\\%sInvitation", $resource); + + $invitation = $invitation_class::whereRaw("BINARY `key`= ?", [$invitation['key']])->first(); + + return $invitation; } /** @@ -177,6 +187,7 @@ class BaseRepository protected function alternativeSave($data, $model) { $class = new ReflectionClass($model); + $state = []; $resource = explode('\\', $class->name)[2]; /** This will extract 'Invoice' from App\Models\Invoice */ $lcfirst_resource_id = lcfirst($resource) . '_id'; @@ -209,21 +220,13 @@ class BaseRepository /* Get array of Keys which have been removed from the invitations array and soft delete each invitation */ $model->invitations->pluck('key')->diff($invitations->pluck('key'))->each(function ($invitation) { - $this->getInvitationByKey($invitation)->delete(); + $this->getInvitation($invitation, $resource)->delete(); }); foreach ($data['invitations'] as $invitation) { - $inv = false; - if (array_key_exists('key', $invitation)) { - $inv = $this->getInvitationByKey([$invitation['key']]); - - if($inv) - $inv->forceDelete(); - - } - - if (!$inv) { + //if no invitations are present - create one. + if (! $this->getInvitation($invitation, $resource) ) { if (isset($invitation['id'])) { unset($invitation['id']); diff --git a/app/Repositories/ClientContactRepository.php b/app/Repositories/ClientContactRepository.php index f557a9ba11..440ec0860c 100644 --- a/app/Repositories/ClientContactRepository.php +++ b/app/Repositories/ClientContactRepository.php @@ -15,6 +15,7 @@ use App\Factory\ClientContactFactory; use App\Models\Client; use App\Models\ClientContact; use Illuminate\Support\Str; +use Illuminate\Support\Facades\Hash; /** * ClientContactRepository @@ -54,8 +55,23 @@ class ClientContactRepository extends BaseRepository } $update_contact->fill($contact); + + if(array_key_exists('password', $contact)) { + + if(strlen($contact['password']) == 0){ + + $update_contact->password = ''; + + } + else{ + + $update_contact->password = Hash::make($contact['password']); + } + + } + $update_contact->save(); - + }); diff --git a/app/Repositories/InvoiceRepository.php b/app/Repositories/InvoiceRepository.php index cf033ffb57..6babdf7dea 100644 --- a/app/Repositories/InvoiceRepository.php +++ b/app/Repositories/InvoiceRepository.php @@ -58,4 +58,9 @@ class InvoiceRepository extends BaseRepository { public function markSent(Invoice $invoice):?Invoice { return $invoice->service()->markSent()->save(); } + + public function getInvitationByKey($key) :?InvoiceInvitation + { + return InvoiceInvitation::whereRaw("BINARY `key`= ?", [$key])->first(); + } } diff --git a/app/Repositories/UserRepository.php b/app/Repositories/UserRepository.php index 671409f46b..fe709f4f40 100644 --- a/app/Repositories/UserRepository.php +++ b/app/Repositories/UserRepository.php @@ -52,7 +52,7 @@ class UserRepository extends BaseRepository $company = auth()->user()->company(); $account_id = $company->account->id; - $cu = CompanyUser::whereUserId($user->id)->whereCompanyId($company->id)->first(); + $cu = CompanyUser::whereUserId($user->id)->whereCompanyId($company->id)->withTrashed()->first(); /*No company user exists - attach the user*/ if (!$cu) { @@ -60,10 +60,34 @@ class UserRepository extends BaseRepository $user->companies()->attach($company->id, $data['company_user']); } else { $cu->fill($data['company_user']); + $cu->restore(); + $cu->tokens()->restore(); $cu->save(); } } return $user; } + + public function destroy(array $data, User $user) + { + + if(array_key_exists('company_user', $data)) + { + $this->forced_includes = 'company_users'; + + $company = auth()->user()->company(); + + $cu = CompanyUser::whereUserId($user->id) + ->whereCompanyId($company->id) + ->first(); + + $cu->tokens()->delete(); + $cu->delete(); + } + else + $user->delete(); + + return $user->fresh(); + } } diff --git a/app/Transformers/CreditTransformer.php b/app/Transformers/CreditTransformer.php index a4b9e507fe..bbad19c492 100644 --- a/app/Transformers/CreditTransformer.php +++ b/app/Transformers/CreditTransformer.php @@ -121,6 +121,8 @@ class CreditTransformer extends EntityTransformer 'custom_surcharge_taxes' => (bool) $credit->custom_surcharge_taxes, 'line_items' => $credit->line_items ?: (array)[], 'backup' => $credit->backup ?: '', + 'entity_type' => 'credit', + ]; } } diff --git a/app/Transformers/InvoiceTransformer.php b/app/Transformers/InvoiceTransformer.php index 7c4489a54a..dec9999af6 100644 --- a/app/Transformers/InvoiceTransformer.php +++ b/app/Transformers/InvoiceTransformer.php @@ -130,6 +130,7 @@ class InvoiceTransformer extends EntityTransformer 'custom_surcharge_taxes' => (bool) $invoice->custom_surcharge_taxes, 'line_items' => $invoice->line_items ?: (array)[], 'backup' => $invoice->backup ?: '', + 'entity_type' => 'invoice', ]; } } diff --git a/app/Transformers/QuoteTransformer.php b/app/Transformers/QuoteTransformer.php index 2171550623..67019bcd38 100644 --- a/app/Transformers/QuoteTransformer.php +++ b/app/Transformers/QuoteTransformer.php @@ -121,6 +121,8 @@ class QuoteTransformer extends EntityTransformer 'custom_surcharge_taxes' => (bool) $quote->custom_surcharge_taxes, 'line_items' => $quote->line_items ?: (array)[], 'backup' => $quote->backup ?: '', + 'entity_type' => 'quote', + ]; } } diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 22f2f6f1d2..030852e5fd 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -3119,6 +3119,9 @@ $LANG = array( 'new_signup' => 'New Signup', 'new_signup_text' => 'A new account has been created by :user - :email - from IP address: :ip', + 'notification_payment_paid_subject' => 'Payment was made by :client', + 'notification_paid_paid' => 'A payment of :amount was made by client :client towards :invoice.', + 'email_link_not_working' => 'If button above isn\'t working for you, please click on the link', ); diff --git a/routes/api.php b/routes/api.php index 0b68bcaf47..9e141ed296 100644 --- a/routes/api.php +++ b/routes/api.php @@ -119,6 +119,8 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a Route::post('self-update', 'SelfUpdateController@update')->middleware('password_protected'); + Route::post('self-update/check_version', 'SelfUpdateController@checkVersion')->middleware('password_protected'); + /* Route::resource('tasks', 'TaskController'); // name = (tasks. index / create / show / update / destroy / edit diff --git a/tests/Feature/ClientTest.php b/tests/Feature/ClientTest.php index acd0230a23..a27e98298d 100644 --- a/tests/Feature/ClientTest.php +++ b/tests/Feature/ClientTest.php @@ -42,7 +42,8 @@ class ClientTest extends TestCase $this->withoutExceptionHandling(); - + Client::reguard(); + ClientContact::reguard(); } public function testClientList() @@ -362,7 +363,7 @@ class ClientTest extends TestCase catch(ValidationException $e) { $message = json_decode($e->validator->getMessageBag(),1); - \Log::error($message); + //\Log::error($message); $this->assertNotNull($message); } @@ -387,7 +388,7 @@ class ClientTest extends TestCase catch(ValidationException $e) { $message = json_decode($e->validator->getMessageBag(),1); - //\Log::error($message); + ////\Log::error($message); //$this->assertNotNull($message); } @@ -418,7 +419,7 @@ class ClientTest extends TestCase catch(ValidationException $e) { $message = json_decode($e->validator->getMessageBag(),1); - \Log::error($message); + //\Log::error($message); $this->assertNotNull($message); } @@ -437,8 +438,118 @@ class ClientTest extends TestCase $arr = $response->json(); //\Log::error($arr); + $safe_email = $this->faker->unique()->safeEmail; + $data = [ + 'name' => 'A loyal Client', + 'contacts' => [ + [ + 'email' => $safe_email, + 'password' => '' + ], + ] + ]; + $response = null; + + try{ + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $token, + ])->post('/api/v1/clients/', $data); + } + catch(ValidationException $e) { + + $message = json_decode($e->validator->getMessageBag(),1); + //\Log::error($message); + $this->assertNotNull($message); + } + + $response->assertStatus(200); + + $arr = $response->json(); + + $client = Client::find($this->decodePrimaryKey($arr['data']['id'])); + + $contact = $client->contacts()->whereEmail($safe_email)->first(); + + $this->assertEquals(0, strlen($contact->password)); + + $safe_email = $this->faker->unique()->safeEmail; + + $data = [ + 'name' => 'A loyal Client', + 'contacts' => [ + [ + 'email' => $safe_email, + 'password' => 'AFancyDancy191$Password' + ], + ] + ]; + + $response = null; + + try{ + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $token, + ])->post('/api/v1/clients/', $data); + } + catch(ValidationException $e) { + + $message = json_decode($e->validator->getMessageBag(),1); + //\Log::error($message); + $this->assertNotNull($message); + } + + $response->assertStatus(200); + + $arr = $response->json(); + + $client = Client::find($this->decodePrimaryKey($arr['data']['id'])); + + $contact = $client->contacts()->whereEmail($safe_email)->first(); + + $this->assertGreaterThan(1, strlen($contact->password)); + + $password = $contact->password; + + $data = [ + 'name' => 'A Stary eyed client', + 'contacts' => [ + [ + 'id' => $contact->hashed_id, + 'email' => $safe_email, + 'password' => '*****' + ], + ] + ]; + + $response = null; + + try{ + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $token, + ])->put('/api/v1/clients/' . $client->hashed_id, $data); + } + catch(ValidationException $e) { + + $message = json_decode($e->validator->getMessageBag(),1); + //\Log::error($message); + $this->assertNotNull($message); + } + + $response->assertStatus(200); + + $arr = $response->json(); + + $client = Client::find($this->decodePrimaryKey($arr['data']['id'])); + $client->fresh(); + + $contact = $client->contacts()->whereEmail($safe_email)->first(); + + $this->assertEquals($password, $contact->password); } /** @test */ // public function testMassivelyCreatingClients()