Skip to content

Commit 4640c22

Browse files
authored
Merge pull request #4 from DirectoryTree/anonymizable-resources
Add ability to anonymize JSON resources
2 parents 0b56bb0 + c3f6d48 commit 4640c22

File tree

7 files changed

+192
-89
lines changed

7 files changed

+192
-89
lines changed

src/Anonymizable.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66

77
interface Anonymizable
88
{
9+
/**
10+
* Get the key value used to ensure consistent fake data generation.
11+
*/
12+
public function getAnonymizableKey(): ?string;
13+
914
/**
1015
* Get the seed value used to ensure consistent fake data generation.
1116
*/

src/Anonymized.php

Lines changed: 1 addition & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,13 @@
33
namespace DirectoryTree\Anonymize;
44

55
use Faker\Generator;
6-
use Illuminate\Support\Facades\App;
76

87
/**
98
* @mixin Anonymizable
109
*/
1110
trait Anonymized
1211
{
13-
/**
14-
* Whether to enable anonymization for the current model instance.
15-
*/
16-
protected bool $anonymizeEnabled = true;
17-
18-
/**
19-
* The anonymized attributes for the current model instance and seed.
20-
*/
21-
protected array $anonymizedAttributeCache;
22-
23-
/**
24-
* The seed for the cached anonymized attributes.
25-
*/
26-
protected string $anonymizedAttributeCacheSeed;
27-
28-
/**
29-
* Get the anonymize manager instance.
30-
*/
31-
protected static function getAnonymizeManager(): AnonymizeManager
32-
{
33-
return App::make(AnonymizeManager::class);
34-
}
12+
use AnonymizesAttributes;
3513

3614
/**
3715
* Get the anonymized attributes.
@@ -67,70 +45,4 @@ public function getAttributeValue($key): mixed
6745

6846
return $this->getCachedAnonymizedAttributes()[$key] ?? parent::getAttributeValue($key);
6947
}
70-
71-
/**
72-
* Get the seed for the anonymizable model.
73-
*/
74-
public function getAnonymizableSeed(): string
75-
{
76-
return get_class($this).':'.$this->getAttributeValue('id');
77-
}
78-
79-
/**
80-
* @template TReturn
81-
*
82-
* @param callable($this): TReturn $callback
83-
* @return TReturn
84-
*/
85-
public function withoutAnonymization(callable $callback): mixed
86-
{
87-
$previous = $this->anonymizeEnabled;
88-
89-
$this->anonymizeEnabled = false;
90-
91-
try {
92-
return $callback($this);
93-
} finally {
94-
$this->anonymizeEnabled = $previous;
95-
}
96-
}
97-
98-
/**
99-
* Make the anonymized attributes.
100-
*/
101-
protected function getCachedAnonymizedAttributes(): array
102-
{
103-
return $this->withoutAnonymization(function (): array {
104-
$seed = $this->getAnonymizableSeed();
105-
106-
if (! isset($this->anonymizedAttributeCache) || $this->anonymizedAttributeCacheSeed !== $seed) {
107-
$this->anonymizedAttributeCache = $this->getAnonymizedAttributes(
108-
static::getAnonymizeManager()->faker($seed)
109-
);
110-
111-
$this->anonymizedAttributeCacheSeed = $seed;
112-
}
113-
114-
return $this->anonymizedAttributeCache;
115-
});
116-
}
117-
118-
/**
119-
* Add the anonymized attributes to the attribute array.
120-
*
121-
* @param array<string, mixed> $attributes
122-
* @return array<string, mixed>
123-
*/
124-
protected function addAnonymizedAttributesToArray(array $attributes): array
125-
{
126-
foreach ($this->getCachedAnonymizedAttributes() as $key => $value) {
127-
if (! array_key_exists($key, $attributes)) {
128-
continue;
129-
}
130-
131-
$attributes[$key] = $value;
132-
}
133-
134-
return $attributes;
135-
}
13648
}

src/AnonymizedResource.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace DirectoryTree\Anonymize;
4+
5+
trait AnonymizedResource
6+
{
7+
use AnonymizesAttributes;
8+
9+
/**
10+
* Transform the resource into an array.
11+
*
12+
* @param \Illuminate\Http\Request|null $request
13+
* @return array<string, mixed>
14+
*/
15+
public function resolve($request = null): array
16+
{
17+
$attributes = parent::resolve($request);
18+
19+
if (! $this->anonymizeEnabled || ! static::getAnonymizeManager()->isEnabled()) {
20+
return $attributes;
21+
}
22+
23+
return $this->addAnonymizedAttributesToArray($attributes);
24+
}
25+
}

src/AnonymizesAttributes.php

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
<?php
2+
3+
namespace DirectoryTree\Anonymize;
4+
5+
use Illuminate\Support\Facades\App;
6+
7+
trait AnonymizesAttributes
8+
{
9+
/**
10+
* Whether anonymization is enabled for the current instance.
11+
*/
12+
protected bool $anonymizeEnabled = true;
13+
14+
/**
15+
* The anonymized attributes for the current instance and seed.
16+
*/
17+
protected array $anonymizedAttributeCache;
18+
19+
/**
20+
* The seed for the cached anonymized attributes.
21+
*/
22+
protected string $anonymizedAttributeCacheSeed;
23+
24+
/**
25+
* Execute a callback without anonymization.
26+
*
27+
* @template TReturn
28+
*
29+
* @param callable($this): TReturn $callback
30+
* @return TReturn
31+
*/
32+
public function withoutAnonymization(callable $callback): mixed
33+
{
34+
$previous = $this->anonymizeEnabled;
35+
36+
$this->anonymizeEnabled = false;
37+
38+
try {
39+
return $callback($this);
40+
} finally {
41+
$this->anonymizeEnabled = $previous;
42+
}
43+
}
44+
45+
/**
46+
* Get the key for the anonymizable instance.
47+
*/
48+
public function getAnonymizableKey(): ?string
49+
{
50+
return $this->getKey();
51+
}
52+
53+
/**
54+
* Get the seed for the anonymizable instance.
55+
*/
56+
public function getAnonymizableSeed(): string
57+
{
58+
return get_class($this).':'.$this->getAnonymizableKey();
59+
}
60+
61+
/**
62+
* Add the anonymized attributes to the attribute array.
63+
*
64+
* @param array<string, mixed> $attributes
65+
* @return array<string, mixed>
66+
*/
67+
protected function addAnonymizedAttributesToArray(array $attributes): array
68+
{
69+
foreach ($this->getCachedAnonymizedAttributes() as $key => $value) {
70+
if (! array_key_exists($key, $attributes)) {
71+
continue;
72+
}
73+
74+
$attributes[$key] = $value;
75+
}
76+
77+
return $attributes;
78+
}
79+
80+
/**
81+
* Make the anonymized attributes.
82+
*/
83+
protected function getCachedAnonymizedAttributes(): array
84+
{
85+
return $this->withoutAnonymization(function (): array {
86+
$seed = $this->getAnonymizableSeed();
87+
88+
if (! isset($this->anonymizedAttributeCache) || $this->anonymizedAttributeCacheSeed !== $seed) {
89+
$this->anonymizedAttributeCache = $this->getAnonymizedAttributes(
90+
static::getAnonymizeManager()->faker($seed)
91+
);
92+
93+
$this->anonymizedAttributeCacheSeed = $seed;
94+
}
95+
96+
return $this->anonymizedAttributeCache;
97+
});
98+
}
99+
100+
/**
101+
* Get the anonymize manager instance.
102+
*/
103+
protected static function getAnonymizeManager(): AnonymizeManager
104+
{
105+
return App::make(AnonymizeManager::class);
106+
}
107+
}

tests/AnonymizedJsonResource.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace DirectoryTree\Anonymize\Tests;
4+
5+
use DirectoryTree\Anonymize\Anonymizable;
6+
use DirectoryTree\Anonymize\AnonymizedResource;
7+
use Faker\Generator;
8+
use Illuminate\Http\Resources\Json\JsonResource;
9+
10+
class AnonymizedJsonResource extends JsonResource implements Anonymizable
11+
{
12+
use AnonymizedResource;
13+
14+
public function getAnonymizedAttributes(Generator $faker): array
15+
{
16+
return [
17+
'name' => $faker->name(),
18+
'address' => $faker->address(),
19+
];
20+
}
21+
22+
public function getAnonymizableKey(): string
23+
{
24+
return 1;
25+
}
26+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
use DirectoryTree\Anonymize\Facades\Anonymize;
4+
use DirectoryTree\Anonymize\Tests\AnonymizedJsonResource;
5+
6+
it('anonymizes json resource when anonymization is enabled', function () {
7+
Anonymize::enable();
8+
9+
$resource = new AnonymizedJsonResource([
10+
'name' => 'Foo Bar',
11+
'address' => '1600 Pennsylvania Avenue',
12+
]);
13+
14+
expect($resource->resolve())->not->toHaveKey('name', 'Foo Bar')
15+
->and($resource->resolve())->not->toHaveKey('address', '1600 Pennsylvania Avenue');
16+
});
17+
18+
it('does not anonymize json resource when anonymization is disabled', function () {
19+
Anonymize::disable();
20+
21+
$resource = new AnonymizedJsonResource([
22+
'name' => 'Foo Bar',
23+
'address' => '1600 Pennsylvania Avenue',
24+
]);
25+
26+
expect($resource->resolve())->toHaveKey('name', 'Foo Bar')
27+
->and($resource->resolve())->toHaveKey('address', '1600 Pennsylvania Avenue');
28+
});

0 commit comments

Comments
 (0)