Skip to content

Commit

Permalink
Merge pull request #2 from misantron/fixtures-refactoring
Browse files Browse the repository at this point in the history
Library refactoring
  • Loading branch information
misantron authored Jun 26, 2022
2 parents b060eaa + 08b09a1 commit c9450c4
Show file tree
Hide file tree
Showing 48 changed files with 1,383 additions and 1,469 deletions.
102 changes: 87 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# Dynamite
# Dynamite - AWS DynamoDB fixtures

[![Build Status](https://img.shields.io/github/workflow/status/misantron/dynamite/build.svg?style=flat-square)](https://github.com/misantron/dynamite/actions)
[![Code Coverage](https://img.shields.io/codacy/coverage/14793b443be444dbb19c02ddca1b0118.svg?style=flat-square)](https://app.codacy.com/gh/misantron/dynamite/files)
[![Code Quality](https://img.shields.io/codacy/grade/14793b443be444dbb19c02ddca1b0118.svg?style=flat-square)](https://app.codacy.com/gh/misantron/dynamite)
[![Packagist](https://img.shields.io/packagist/v/misantron/dynamite.svg?style=flat-square)](https://packagist.org/packages/misantron/dynamite)

AWS DynamoDB migrations and seeding tool
Provide a simple way to manage and execute the loading of data fixtures for AWS DynamoDB storage.
Library code design is heavily inspired by [doctrine/data-fixtures](https://github.com/doctrine/data-fixtures).

## Install

Expand All @@ -16,46 +17,58 @@ Run this command to install the latest stable version:
composer require --dev misantron/dynamite
```

## Examples
## Loading fixtures

### Create table
### Create table creation class

This feature is optional.
Fixture classes must implement `Dynamite\TableInterface` interface to be visible for a loader.

```php
<?php

declare(strict_types=1);

namespace Migrations;
namespace Fixtures;

use Dynamite\AbstractTable;
use Dynamite\TableInterface;

final class CreateUsersTable extends \Dynamite\AbstractMigration
final class UsersTable extends AbstractTable implements TableInterface
{
public function up(): void
public function configure(): void
{
$this
->setTableName('Users')
->addAttribute('Id', 'S')
->addAttribute('Email', 'S')
->addAttributes([
['Id', 'S'],
['Email', 'S'],
])
->addHashKey('Id')
->addGlobalSecondaryIndex('Emails', 'KEYS_ONLY', 'Email')
->setProvisionedThroughput(1, 1)
->create()
;
}
}
```

### Create seeder
### Create a fixture loading class

Fixture classes must implement `Dynamite\FixtureInterface` interface to be visible for a loader.

```php
<?php

declare(strict_types=1);

namespace Seeders;
namespace Fixtures;

use Dynamite\AbstractFixture;
use Dynamite\FixtureInterface;

final class UsersTableSeeder extends \Dynamite\AbstractSeeder
final class UserFixtures extends AbstractFixture implements FixtureInterface
{
public function seed(): void
public function configure(): void
{
$this
->setTableName('Users')
Expand All @@ -69,8 +82,67 @@ final class UsersTableSeeder extends \Dynamite\AbstractSeeder
'Email' => ['S' => '[email protected]'],
],
])
->save()
;
}
}
```

### Tables and fixtures loading

It's possible to provide fixtures loading path:

```php
<?php

declare(strict_types=1);

use Dynamite\Loader;
use Symfony\Component\Validator\Validation;
use Symfony\Component\Serializer\Serializer;

$validator = Validation::createValidator();
$serializer = new Serializer();

$loader = new Loader($validator, $serializer);
$loader->loadFromDirectory('/path/to/YourFixtures');
```

or loading each fixture or table class manually:

```php
<?php

declare(strict_types=1);

use App\Fixtures;

$loader->addTable(new UsersTable());
$loader->addFixture(new UserFixtures());
```

### Create tables and executing fixtures

To create database schema and load the fixtures in storage you should do the following:

```php
<?php

declare(strict_types=1);

use AsyncAws\DynamoDb\DynamoDbClient;
use Dynamite\Executor;
use Symfony\Component\Validator\Validation;
use Symfony\Component\Serializer\Serializer;

$validator = Validation::createValidator();
$serializer = new Serializer();
$dynamoDbClient = new DynamoDbClient();

$loader = new Loader($validator, $serializer);
$loader->loadFromDirectory('/path/to/YourFixtures');

$executor = new Executor($dynamoDbClient);
$executor->execute($loader->getFixtures(), $loader->getTables());
```

**Important!** Each executor class comes with a purger class which executed before, drop tables and truncate data.
5 changes: 1 addition & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
"require": {
"php": "^8.1",
"async-aws/dynamo-db": "^1.2",
"symfony/console": "^5.4 || ^6.0",
"symfony/property-access": "^5.4 || ^6.0",
"symfony/serializer": "^5.4 || ^6.0",
"symfony/validator": "^5.4 || ^6.0"
Expand All @@ -25,9 +24,7 @@
},
"autoload-dev": {
"psr-4": {
"Dynamite\\Tests\\": "tests",
"Dynamite\\Tests\\Seeders\\": "tests/resources/seeders",
"Dynamite\\Tests\\Migrations\\": "tests/resources/migrations"
"Dynamite\\Tests\\": "tests"
}
},
"config": {
Expand Down
48 changes: 21 additions & 27 deletions src/AbstractSeeder.php → src/AbstractFixture.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,67 +9,62 @@
use AsyncAws\DynamoDb\Input\PutItemInput;
use AsyncAws\DynamoDb\ValueObject\PutRequest;
use AsyncAws\DynamoDb\ValueObject\WriteRequest;
use Dynamite\Exception\TableException;
use Dynamite\Exception\ValidationException;
use Dynamite\Schema\Records;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Dynamite\Validator\ValidatorAwareTrait;

abstract class AbstractSeeder implements SeederInterface
abstract class AbstractFixture
{
use TableTrait;
use ValidatorAwareTrait;

private Records $schema;

public function __construct(
private readonly DynamoDbClient $dynamoDbClient,
private readonly ValidatorInterface $validator
) {
public function __construct()
{
$this->schema = new Records();
}

/**
* @param array<int, array> $items
* @param array<string, array<string, string>> $item
*/
protected function addItems(array $items): self
protected function addItem(array $item): self
{
foreach ($items as $item) {
$this->schema->addRecord($item);
}
$this->schema->addRecord($item);

return $this;
}

/**
* @param array<string, array<string, string>> $item
* @param array<int, array> $items
*/
protected function addItem(array $item): self
protected function addItems(array $items): self
{
$this->schema->addRecord($item);
foreach ($items as $item) {
$this->schema->addRecord($item);
}

return $this;
}

protected function save(): array
final public function load(DynamoDbClient $client): void
{
$this->initialize();

$violations = $this->validator->validate($this->schema);
if ($violations->count() > 0) {
throw new ValidationException($violations);
}

if (!$this->isTableExists()) {
throw TableException::notExists($this->schema->getTableName());
}

if ($this->schema->isSingleRecord()) {
$input = new PutItemInput([
'TableName' => $this->schema->getTableName(),
'Item' => current($this->schema->getRecords()),
]);

$response = $this->dynamoDbClient->putItem($input);
$response->resolve();
$client->putItem($input)->resolve();

return $response->info();
return;
}

$input = new BatchWriteItemInput([
Expand All @@ -85,9 +80,8 @@ protected function save(): array
],
]);

$response = $this->dynamoDbClient->batchWriteItem($input);
$response->resolve();

return $response->info();
$client->batchWriteItem($input)->resolve();
}

abstract protected function configure(): void;
}
Loading

0 comments on commit c9450c4

Please sign in to comment.