Skip to content

Commit 7ea3a03

Browse files
committed
add local clients
Signed-off-by: Robert Landers <landers.robert@gmail.com>
1 parent 4e25be3 commit 7ea3a03

2 files changed

Lines changed: 327 additions & 0 deletions

File tree

src/LocalEntityClient.php

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
<?php
2+
3+
/*
4+
* Copyright ©2024 Robert Landers
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20+
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
21+
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
22+
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23+
*/
24+
25+
namespace Bottledcode\DurablePhp;
26+
27+
use Bottledcode\DurablePhp\Events\EventDescription;
28+
use Bottledcode\DurablePhp\Events\RaiseEvent;
29+
use Bottledcode\DurablePhp\Events\Shares\Operation;
30+
use Bottledcode\DurablePhp\Events\WithEntity;
31+
use Bottledcode\DurablePhp\Glue\Provenance;
32+
use Bottledcode\DurablePhp\Proxy\SpyException;
33+
use Bottledcode\DurablePhp\Proxy\SpyProxy;
34+
use Bottledcode\DurablePhp\Search\EntityFilter;
35+
use Bottledcode\DurablePhp\State\EntityId;
36+
use Bottledcode\DurablePhp\State\EntityState;
37+
use Bottledcode\DurablePhp\State\Ids\StateId;
38+
use Bottledcode\DurablePhp\State\Serializer;
39+
use Closure;
40+
use DateTimeImmutable;
41+
use Exception;
42+
use Generator;
43+
use Override;
44+
use ReflectionFunction;
45+
46+
use function Bottledcode\DurablePhp\Ext\emit_event;
47+
48+
class LocalEntityClient implements EntityClientInterface
49+
{
50+
private ?Provenance $userContext = null;
51+
52+
public function __construct(
53+
private SpyProxy $spyProxy = new SpyProxy(),
54+
) {}
55+
56+
#[Override]
57+
public function cleanEntityStorage(): void {}
58+
59+
#[Override]
60+
public function listEntities(EntityFilter $filter, int $page): Generator
61+
{
62+
throw new Exception('listEntities not supported in LocalEntityClient - use RemoteEntityClient for this operation');
63+
}
64+
65+
#[Override]
66+
public function signal(EntityId|string $entityId, Closure $signal): void
67+
{
68+
$interfaceReflector = new ReflectionFunction($signal);
69+
$interfaceName = $interfaceReflector->getParameters()[0]->getType()?->getName();
70+
if (interface_exists($interfaceName) === false) {
71+
throw new Exception("Interface {$interfaceName} does not exist");
72+
}
73+
$spy = $this->spyProxy->define($interfaceName);
74+
$operationName = null;
75+
$arguments = null;
76+
try {
77+
$class = new $spy($operationName, $arguments);
78+
$signal($class);
79+
} catch (SpyException) {
80+
}
81+
82+
if ($operationName === null || $arguments === null) {
83+
return;
84+
}
85+
86+
$this->signalEntity(
87+
is_string($entityId) ? EntityId($interfaceName, $entityId) : $entityId,
88+
$operationName,
89+
$arguments,
90+
);
91+
}
92+
93+
#[Override]
94+
public function signalEntity(
95+
EntityId $entityId,
96+
string $operationName,
97+
array $input = [],
98+
?DateTimeImmutable $scheduledTime = null,
99+
): void {
100+
$event = WithEntity::forInstance(
101+
StateId::fromEntityId($entityId),
102+
RaiseEvent::forSignal($operationName, SerializedArray::fromArray($input), $scheduledTime),
103+
);
104+
105+
$eventDescription = new EventDescription($event);
106+
$userArray = $this->userContext ? Serializer::serialize($this->userContext) : null;
107+
108+
emit_event($userArray, $eventDescription->toArray(), StateId::fromEntityId($entityId)->id);
109+
}
110+
111+
#[Override]
112+
public function getEntitySnapshot(EntityId $entityId): ?EntityState
113+
{
114+
throw new Exception('getEntitySnapshot not supported in LocalEntityClient - use RemoteEntityClient for this operation');
115+
}
116+
117+
#[Override]
118+
public function withAuth(string $token): void
119+
{
120+
throw new Exception('withAuth not implemented for LocalEntityClient - set user context directly using setUserContext');
121+
}
122+
123+
public function setUserContext(?Provenance $userContext): void
124+
{
125+
$this->userContext = $userContext;
126+
}
127+
128+
#[Override]
129+
public function deleteEntity(EntityId $entityId): void
130+
{
131+
throw new Exception('deleteEntity not supported in LocalEntityClient - use RemoteEntityClient for this operation');
132+
}
133+
134+
public function shareEntityOwnership(EntityId $id, string $with): void
135+
{
136+
throw new Exception('shareEntityOwnership not supported in LocalEntityClient - use RemoteEntityClient for this operation');
137+
}
138+
139+
public function grantEntityAccessToUser(EntityId $id, string $user, Operation $operation): void
140+
{
141+
throw new Exception('grantEntityAccessToUser not supported in LocalEntityClient - use RemoteEntityClient for this operation');
142+
}
143+
144+
public function grantEntityAccessToRole(EntityId $id, string $role, Operation $operation): void
145+
{
146+
throw new Exception('grantEntityAccessToRole not supported in LocalEntityClient - use RemoteEntityClient for this operation');
147+
}
148+
149+
public function revokeEntityAccessToUser(EntityId $id, string $user): void
150+
{
151+
throw new Exception('revokeEntityAccessToUser not supported in LocalEntityClient - use RemoteEntityClient for this operation');
152+
}
153+
154+
public function revokeEntityAccessToRole(EntityId $id, string $role): void
155+
{
156+
throw new Exception('revokeEntityAccessToRole not supported in LocalEntityClient - use RemoteEntityClient for this operation');
157+
}
158+
}

src/LocalOrchestrationClient.php

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
<?php
2+
3+
/*
4+
* Copyright ©2024 Robert Landers
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20+
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
21+
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
22+
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23+
*/
24+
25+
namespace Bottledcode\DurablePhp;
26+
27+
use Bottledcode\DurablePhp\Events\EventDescription;
28+
use Bottledcode\DurablePhp\Events\RaiseEvent;
29+
use Bottledcode\DurablePhp\Events\Shares\Operation;
30+
use Bottledcode\DurablePhp\Events\StartOrchestration;
31+
use Bottledcode\DurablePhp\Events\WithOrchestration;
32+
use Bottledcode\DurablePhp\Glue\Provenance;
33+
use Bottledcode\DurablePhp\Proxy\SpyProxy;
34+
use Bottledcode\DurablePhp\State\Ids\StateId;
35+
use Bottledcode\DurablePhp\State\OrchestrationInstance;
36+
use Bottledcode\DurablePhp\State\Serializer;
37+
use Bottledcode\DurablePhp\State\Status;
38+
use Exception;
39+
use Generator;
40+
use Override;
41+
use Ramsey\Uuid\Uuid;
42+
43+
use function Bottledcode\DurablePhp\Ext\emit_event;
44+
45+
final class LocalOrchestrationClient implements OrchestrationClientInterface
46+
{
47+
private ?Provenance $userContext = null;
48+
49+
public function __construct(
50+
private SpyProxy $spyProxy = new SpyProxy(),
51+
) {}
52+
53+
#[Override]
54+
public function listInstances(): Generator
55+
{
56+
throw new Exception('listInstances not supported in LocalOrchestrationClient - use RemoteOrchestrationClient for this operation');
57+
}
58+
59+
#[Override]
60+
public function purge(OrchestrationInstance $instance): void
61+
{
62+
throw new Exception('purge not supported in LocalOrchestrationClient - use RemoteOrchestrationClient for this operation');
63+
}
64+
65+
#[Override]
66+
public function getStatus(OrchestrationInstance $instance): Status
67+
{
68+
throw new Exception('getStatus not supported in LocalOrchestrationClient - use RemoteOrchestrationClient for this operation');
69+
}
70+
71+
#[Override]
72+
public function raiseEvent(OrchestrationInstance $instance, string $eventName, array $eventData): void
73+
{
74+
$event = WithOrchestration::forInstance(
75+
StateId::fromOrchestrationInstance($instance),
76+
RaiseEvent::forSignal($eventName, SerializedArray::fromArray($eventData)),
77+
);
78+
79+
$eventDescription = new EventDescription($event);
80+
$userArray = $this->userContext ? Serializer::serialize($this->userContext) : null;
81+
82+
emit_event($userArray, $eventDescription->toArray(), StateId::fromOrchestrationInstance($instance)->id);
83+
}
84+
85+
#[Override]
86+
public function restart(OrchestrationInstance $instance): void
87+
{
88+
throw new Exception('restart not implemented in LocalOrchestrationClient');
89+
}
90+
91+
#[Override]
92+
public function resume(OrchestrationInstance $instance, string $reason): void
93+
{
94+
throw new Exception('resume not implemented in LocalOrchestrationClient');
95+
}
96+
97+
#[Override]
98+
public function startNew(string $name, array $args = [], ?string $id = null): OrchestrationInstance
99+
{
100+
$orchestrationId = OrchestrationId($name, $id ?? Uuid::uuid4()->toString());
101+
$stateId = StateId::fromOrchestrationId($orchestrationId);
102+
103+
$event = WithOrchestration::forInstance(
104+
$stateId,
105+
StartOrchestration::fromArray(SerializedArray::fromArray($args)),
106+
);
107+
108+
$eventDescription = new EventDescription($event);
109+
$userArray = $this->userContext ? Serializer::serialize($this->userContext) : null;
110+
111+
$sequence = emit_event($userArray, $eventDescription->toArray(), $stateId->id);
112+
113+
return $orchestrationId->toOrchestrationInstance();
114+
}
115+
116+
#[Override]
117+
public function suspend(OrchestrationInstance $instance, string $reason): void
118+
{
119+
throw new Exception('suspend not implemented in LocalOrchestrationClient');
120+
}
121+
122+
#[Override]
123+
public function terminate(OrchestrationInstance $instance, string $reason): void
124+
{
125+
throw new Exception('terminate not implemented in LocalOrchestrationClient');
126+
}
127+
128+
#[Override]
129+
public function waitForCompletion(OrchestrationInstance $instance): void
130+
{
131+
throw new Exception('waitForCompletion not supported in LocalOrchestrationClient - use RemoteOrchestrationClient for this operation');
132+
}
133+
134+
#[Override]
135+
public function withAuth(string $token): void
136+
{
137+
throw new Exception('withAuth not implemented for LocalOrchestrationClient - set user context directly using setUserContext');
138+
}
139+
140+
public function setUserContext(?Provenance $userContext): void
141+
{
142+
$this->userContext = $userContext;
143+
}
144+
145+
public function shareOrchestrationOwnership(OrchestrationInstance $id, string $with): void
146+
{
147+
throw new Exception('shareOrchestrationOwnership not supported in LocalOrchestrationClient - use RemoteOrchestrationClient for this operation');
148+
}
149+
150+
public function grantOrchestrationAccessToUser(OrchestrationInstance $id, string $user, Operation $operation): void
151+
{
152+
throw new Exception('grantOrchestrationAccessToUser not supported in LocalOrchestrationClient - use RemoteOrchestrationClient for this operation');
153+
}
154+
155+
public function grantOrchestrationAccessToRole(OrchestrationInstance $id, string $role, Operation $operation): void
156+
{
157+
throw new Exception('grantOrchestrationAccessToRole not supported in LocalOrchestrationClient - use RemoteOrchestrationClient for this operation');
158+
}
159+
160+
public function revokeOrchestrationAccessToUser(OrchestrationInstance $id, string $user): void
161+
{
162+
throw new Exception('revokeOrchestrationAccessToUser not supported in LocalOrchestrationClient - use RemoteOrchestrationClient for this operation');
163+
}
164+
165+
public function revokeOrchestrationAccessToRole(OrchestrationInstance $id, string $role): void
166+
{
167+
throw new Exception('revokeOrchestrationAccessToRole not supported in LocalOrchestrationClient - use RemoteOrchestrationClient for this operation');
168+
}
169+
}

0 commit comments

Comments
 (0)