Skip to content

Commit

Permalink
Merge pull request #97 from mettle/feature/api-subscriber-store-or-up…
Browse files Browse the repository at this point in the history
…date

[Feature] Add ability for Subscriber API store endpoint to also update
  • Loading branch information
JonoB authored Nov 27, 2020
2 parents 223b240 + f382363 commit 33a88b4
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 44 deletions.
2 changes: 1 addition & 1 deletion src/Http/Controllers/Api/SubscribersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public function index(int $workspaceId): AnonymousResourceCollection
*/
public function store(SubscriberStoreRequest $request, int $workspaceId): SubscriberResource
{
$subscriber = $this->apiService->store($workspaceId, collect($request->validated()));
$subscriber = $this->apiService->storeOrUpdate($workspaceId, collect($request->validated()));

$subscriber->load('segments');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ public function store($workspaceId, array $data)

$subscriber = $this->executeSave($workspaceId, $instance, Arr::except($data, ['segments']));

// only sync segments if its actually present. This means that users's must
// pass through an empty segments array if they want to delete all segments
// Only sync segments if its actually present. This means that users must
// pass through an empty segments array if they want to delete all segments.
if (isset($data['segments'])) {
$this->syncSegments($instance, Arr::get($data, 'segments', []));
}
Expand Down Expand Up @@ -60,8 +60,8 @@ public function update($workspaceId, $id, array $data)

$subscriber = $this->executeSave($workspaceId, $instance, Arr::except($data, ['segments', 'id']));

// only sync segments if its actually present. This means that users's must
// pass through an empty segments array if they want to delete all segments
// Only sync segments if its actually present. This means that users must
// pass through an empty segments array if they want to delete all segments.
if (isset($data['segments'])) {
$this->syncSegments($instance, Arr::get($data, 'segments', []));
}
Expand Down
31 changes: 13 additions & 18 deletions src/Services/Subscribers/ApiSubscriberService.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Sendportal\Base\Services\Subscribers;

use Exception;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Sendportal\Base\Events\SubscriberAddedEvent;
Expand All @@ -21,21 +22,24 @@ public function __construct(SubscriberTenantRepositoryInterface $subscribers)
}

/**
* @param int $workspaceId
* @param \Illuminate\Support\Collection $data
* The API provides the ability for the "store" endpoint to both create a new subscriber or update an existing
* subscriber, using their email as the key. This method allows us to handle both scenarios.
*
* @return \Sendportal\Base\Models\Subscriber
* @throws \Exception
* @throws Exception
*/
public function store(int $workspaceId, Collection $data): Subscriber
public function storeOrUpdate(int $workspaceId, Collection $data): Subscriber
{
$subscriber = $this->subscribers->store($workspaceId, $data->except(['segments'])->toArray());
$existingSubscriber = $this->subscribers->findBy($workspaceId, 'email', $data['email']);

event(new SubscriberAddedEvent($subscriber));
if (!$existingSubscriber) {
$subscriber = $this->subscribers->store($workspaceId, $data->toArray());

$this->handleSegments($data, $subscriber);
event(new SubscriberAddedEvent($subscriber));

return $subscriber;
return $subscriber;
}

return $this->subscribers->update($workspaceId, $existingSubscriber->id, $data->toArray());
}

public function delete(int $workspaceId, Subscriber $subscriber): bool
Expand All @@ -45,13 +49,4 @@ public function delete(int $workspaceId, Subscriber $subscriber): bool
return $this->subscribers->destroy($workspaceId, $subscriber->id);
});
}

protected function handleSegments(Collection $data, Subscriber $subscriber): Subscriber
{
if (!empty($data['segments'])) {
$subscriber->segments()->sync($data['segments']);
}

return $subscriber;
}
}
139 changes: 118 additions & 21 deletions tests/Feature/API/SubscribersControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Arr;
use Sendportal\Base\Models\Segment;
use Sendportal\Base\Models\Subscriber;
use Tests\TestCase;

class SubscribersControllerTest extends TestCase
Expand All @@ -18,57 +19,57 @@ class SubscribersControllerTest extends TestCase
/** @test */
public function the_subscribers_index_is_accessible_to_authorised_users()
{
// given
$user = $this->createUserWithWorkspace();

$subscriber = $this->createSubscriber($user);

$route = route('sendportal.api.subscribers.index', [
// when
$response = $this->get(route('sendportal.api.subscribers.index', [
'workspaceId' => $user->currentWorkspace()->id,
'api_token' => $user->api_token,
]);

$response = $this->get($route);
]));

// then
$response->assertStatus(200);

$expected = [
$response->assertJson([
'data' => [
Arr::only($subscriber->toArray(), ['first_name', 'last_name', 'email'])
],
];

$response->assertJson($expected);
]);
}

/** @test */
public function a_single_subscriber_is_accessible_to_authorised_users()
{
// given
$user = $this->createUserWithWorkspace();

$subscriber = $this->createSubscriber($user);

$route = route('sendportal.api.subscribers.show', [
// when
$response = $this->get(route('sendportal.api.subscribers.show', [
'workspaceId' => $user->currentWorkspace()->id,
'subscriber' => $subscriber->id,
'api_token' => $user->api_token,
]);

$response = $this->get($route);
]));

// then
$response->assertStatus(200);

$expected = [
$response->assertJson([
'data' => Arr::only($subscriber->toArray(), ['first_name', 'last_name', 'email']),
];

$response->assertJson($expected);
]);
}

/** @test */
public function a_subscriber_can_be_created_by_authorised_users()
{
// given
$user = $this->createUserWithWorkspace();

// when
$route = route('sendportal.api.subscribers.store', $user->currentWorkspace()->id);

$request = [
Expand All @@ -79,6 +80,7 @@ public function a_subscriber_can_be_created_by_authorised_users()

$response = $this->post($route, array_merge($request, ['api_token' => $user->api_token]));

// then
$response->assertStatus(201);
$this->assertDatabaseHas('subscribers', $request);
$response->assertJson(['data' => $request]);
Expand All @@ -87,10 +89,12 @@ public function a_subscriber_can_be_created_by_authorised_users()
/** @test */
public function a_subscriber_can_be_updated_by_authorised_users()
{
// given
$user = $this->createUserWithWorkspace();

$subscriber = $this->createSubscriber($user);

// when
$route = route('sendportal.api.subscribers.update', [
'workspaceId' => $user->currentWorkspace()->id,
'subscriber' => $subscriber->id,
Expand All @@ -105,6 +109,7 @@ public function a_subscriber_can_be_updated_by_authorised_users()

$response = $this->put($route, $request);

// then
$response->assertStatus(200);
$this->assertDatabaseMissing('subscribers', $subscriber->toArray());
$this->assertDatabaseHas('subscribers', $request);
Expand All @@ -114,18 +119,19 @@ public function a_subscriber_can_be_updated_by_authorised_users()
/** @test */
public function a_subscriber_can_be_deleted_by_authorised_users()
{
// given
$user = $this->createUserWithWorkspace();

$subscriber = $this->createSubscriber($user);

$route = route('sendportal.api.subscribers.destroy', [
// when
$response = $this->delete(route('sendportal.api.subscribers.destroy', [
'workspaceId' => $user->currentWorkspace()->id,
'subscriber' => $subscriber->id,
'api_token' => $user->api_token,
]);

$response = $this->delete($route);
]));

// then
$response->assertStatus(204);
}

Expand All @@ -140,7 +146,6 @@ public function a_subscriber_in_a_segment_can_be_deleted()
$subscriber->segments()->attach($segment->id);

// when
$this->withoutExceptionHandling();
$response = $this->delete(route('sendportal.api.subscribers.destroy', [
'workspaceId' => $user->currentWorkspace()->id,
'subscriber' => $subscriber->id,
Expand All @@ -154,4 +159,96 @@ public function a_subscriber_in_a_segment_can_be_deleted()
'subscriber_id' => $subscriber->id
]);
}

/** @test */
public function the_store_endpoint_can_update_subscriber_based_on_email_address()
{
// given
$user = $this->createUserWithWorkspace();

$subscriber = $this->createSubscriber($user);

// when
$route = route('sendportal.api.subscribers.store', $user->currentWorkspace()->id);

$updateData = [
'first_name' => $this->faker->firstName,
'last_name' => $this->faker->lastName,
'email' => $subscriber->email,
];

$response = $this->post($route, array_merge($updateData, ['api_token' => $user->api_token]));

// then
$response->assertStatus(200);

$this->assertDatabaseHas('subscribers', array_merge($updateData, ['id' => $subscriber->id]));
$this->assertDatabaseCount('subscribers', 1);

$response->assertJson(['data' => $updateData]);
}

/** @test */
public function the_store_endpoint_allows_segments_to_be_added_with_the_subscriber()
{
// given
$user = $this->createUserWithWorkspace();

$segment = $this->createSegment($user);

// when
$route = route('sendportal.api.subscribers.store', $user->currentWorkspace()->id);

$request = [
'first_name' => $this->faker->firstName,
'last_name' => $this->faker->lastName,
'email' => $this->faker->email,
'segments' => [$segment->id]
];

$response = $this->post($route, array_merge($request, ['api_token' => $user->api_token]));

// then
$response->assertStatus(201);

$this->assertDatabaseHas('subscribers', ['email' => $request['email']]);

$subscriber = Subscriber::with('segments')->where('email', $request['email'])->first();

self::assertContains($segment->id, $subscriber->segments->pluck('id'));
}

/** @test */
public function the_store_endpoint_allows_subscriber_segments_to_be_updated()
{
// given
$user = $this->createUserWithWorkspace();

$segment1 = $this->createSegment($user);
$segment2 = $this->createSegment($user);

$subscriber = $this->createSubscriber($user);
$subscriber->segments()->save($segment1);

// when
$route = route('sendportal.api.subscribers.store', $user->currentWorkspace()->id);

$request = [
'first_name' => $this->faker->firstName,
'last_name' => $this->faker->lastName,
'email' => $subscriber->email,
'segments' => [$segment2->id]
];

$response = $this->post($route, array_merge($request, ['api_token' => $user->api_token]));

// then
$response->assertStatus(200);

$subscriber = $subscriber->fresh();
$subscriber->load('segments');

self::assertContains($segment2->id, $subscriber->segments->pluck('id'));
self::assertNotContains($segment1->id, $subscriber->segments->pluck('id'));
}
}

0 comments on commit 33a88b4

Please sign in to comment.