├── .docker
├── otel
│ └── otel-collector-config.yml
├── php
│ └── Dockerfile
└── temporalio
│ ├── development-cass.yaml
│ ├── development-sql.yaml
│ └── docker.yaml
├── .env
├── .idea
├── inspectionProfiles
│ └── Project_Default.xml
├── microservices.iml
├── php.xml
├── phpunit.xml
├── shelf
│ ├── Changes.xml
│ └── Changes
│ │ ├── favicon.ico
│ │ └── shelved.patch
├── vcs.xml
└── workspace.xml
├── Makefile
├── README.md
├── docker-compose.yaml
├── lib
├── grpc-shared
│ ├── .gitignore
│ ├── bin
│ │ ├── console
│ │ └── protoc-gen-php-grpc
│ ├── composer.json
│ ├── composer.lock
│ ├── generated
│ │ └── GRPC
│ │ │ ├── ProtobufMetadata
│ │ │ ├── Auth
│ │ │ │ └── v1
│ │ │ │ │ ├── Message.php
│ │ │ │ │ ├── Request.php
│ │ │ │ │ ├── Response.php
│ │ │ │ │ └── Service.php
│ │ │ ├── Common
│ │ │ │ └── v1
│ │ │ │ │ └── Message.php
│ │ │ ├── Payment
│ │ │ │ └── v1
│ │ │ │ │ ├── Message.php
│ │ │ │ │ ├── Request.php
│ │ │ │ │ ├── Response.php
│ │ │ │ │ └── Service.php
│ │ │ └── Users
│ │ │ │ └── v1
│ │ │ │ ├── Message.php
│ │ │ │ ├── Request.php
│ │ │ │ ├── Response.php
│ │ │ │ └── Service.php
│ │ │ └── Services
│ │ │ ├── Auth
│ │ │ └── v1
│ │ │ │ ├── AuthServiceClient.php
│ │ │ │ ├── AuthServiceInterface.php
│ │ │ │ ├── LoginRequest.php
│ │ │ │ ├── LoginResponse.php
│ │ │ │ ├── LogoutRequest.php
│ │ │ │ ├── MeRequest.php
│ │ │ │ ├── MeResponse.php
│ │ │ │ ├── RegisterRequest.php
│ │ │ │ ├── RegisterResponse.php
│ │ │ │ └── Token.php
│ │ │ ├── Common
│ │ │ └── v1
│ │ │ │ ├── Exception.php
│ │ │ │ └── PBEmpty.php
│ │ │ ├── Payment
│ │ │ └── v1
│ │ │ │ ├── ChargeRequest.php
│ │ │ │ ├── ChargeResponse.php
│ │ │ │ ├── Money.php
│ │ │ │ ├── Payment.php
│ │ │ │ ├── PaymentServiceClient.php
│ │ │ │ ├── PaymentServiceInterface.php
│ │ │ │ └── Receipt.php
│ │ │ └── Users
│ │ │ └── v1
│ │ │ ├── CreateRequest.php
│ │ │ ├── CreateRequest
│ │ │ └── User.php
│ │ │ ├── CreateRequest_User.php
│ │ │ ├── CreateResponse.php
│ │ │ ├── GetRequest.php
│ │ │ ├── GetResponse.php
│ │ │ ├── UpdateRequest.php
│ │ │ ├── UpdateRequest
│ │ │ └── User.php
│ │ │ ├── UpdateRequest_User.php
│ │ │ ├── UpdateResponse.php
│ │ │ ├── User.php
│ │ │ ├── UsersServiceClient.php
│ │ │ └── UsersServiceInterface.php
│ ├── generator
│ │ ├── CommandExecutor.php
│ │ ├── Console
│ │ │ └── GeneratorCommand.php
│ │ ├── Exception
│ │ │ └── CompileException.php
│ │ ├── Generators
│ │ │ ├── BootloaderGenerator.php
│ │ │ ├── Command
│ │ │ │ ├── Annotation
│ │ │ │ │ ├── DefaultValue.php
│ │ │ │ │ ├── DocType.php
│ │ │ │ │ ├── Event.php
│ │ │ │ │ ├── Guarded.php
│ │ │ │ │ ├── Internal.php
│ │ │ │ │ ├── Optional.php
│ │ │ │ │ └── Type.php
│ │ │ │ ├── ClassPropertiesGenerator.php
│ │ │ │ ├── JsonSerializationGenerator.php
│ │ │ │ └── PropertyType.php
│ │ │ ├── CommandClassGenerator.php
│ │ │ ├── ConfigGenerator.php
│ │ │ ├── EnumClassGenerator.php
│ │ │ ├── GeneratedMessagesFixer.php
│ │ │ ├── GeneratorInterface.php
│ │ │ ├── Message
│ │ │ │ ├── MessageClass.php
│ │ │ │ ├── MessageClassParser.php
│ │ │ │ └── MessageCommentsParser.php
│ │ │ ├── ServiceClientGenerator.php
│ │ │ ├── ServiceClientTestsGenerator.php
│ │ │ └── ServiceInterfaceAttributesGenerator.php
│ │ ├── PHP
│ │ │ ├── AnnotationsParser.php
│ │ │ ├── AttributesParser.php
│ │ │ ├── ClassDeclaration.php
│ │ │ ├── ClassDeclarationFactory.php
│ │ │ ├── ClassTransformer.php
│ │ │ └── Property
│ │ │ │ ├── BuiltInType.php
│ │ │ │ ├── ClassType.php
│ │ │ │ ├── DateTime.php
│ │ │ │ ├── EnumClassType.php
│ │ │ │ ├── Interval.php
│ │ │ │ ├── RepeatableType.php
│ │ │ │ ├── Timestamp.php
│ │ │ │ ├── Type.php
│ │ │ │ └── TypeFactory.php
│ │ ├── ProtoCompiler.php
│ │ └── ProtocCommandBuilder.php
│ └── src
│ │ ├── Attribute
│ │ ├── Guarded.php
│ │ └── Internal.php
│ │ ├── Bootloader
│ │ └── ServiceBootloader.php
│ │ ├── Config
│ │ └── GRPCServicesConfig.php
│ │ ├── Interceptors
│ │ ├── Incoming
│ │ │ ├── ContextInterceptor.php
│ │ │ ├── ExceptionHandlerInterceptor.php
│ │ │ └── OpenTelemetryInterceptor.php
│ │ └── Outgoing
│ │ │ └── SendTraceContextInterceptor.php
│ │ └── Request
│ │ └── RequestContext.php
└── temporal-shared
│ ├── .gitignore
│ ├── composer.json
│ ├── composer.lock
│ └── src
│ ├── Activity
│ └── NotificationsActivity.php
│ ├── Bootloader
│ └── TemporalSharedBootloader.php
│ ├── OptionsTrait.php
│ ├── TaskQueue.php
│ └── Workflow
│ ├── EmailVerificationWorkflow.php
│ ├── ExceptionHelper.php
│ ├── KYCWorkflow.php
│ ├── RegisterUserWorkflow.php
│ └── SubscriptionWorkflow.php
├── notifications
├── .editorconfig
├── .env.sample
├── .gitignore
├── .rr-prod.yaml
├── .rr.yaml
├── .styleci.yml
├── README.md
├── app.php
├── app
│ ├── config
│ │ ├── cycle.php
│ │ ├── database.php
│ │ ├── migration.php
│ │ ├── scaffolder.php
│ │ └── sentry.php
│ ├── migrations
│ │ └── .gitignore
│ └── src
│ │ └── Application
│ │ ├── Assert.php
│ │ ├── Bootloader
│ │ ├── AppBootloader.php
│ │ ├── ExceptionHandlerBootloader.php
│ │ └── PersistenceBootloader.php
│ │ ├── Exception
│ │ └── InvalidArgumentException.php
│ │ └── Kernel.php
├── composer.json
├── composer.lock
├── phpunit.xml
├── psalm.xml
└── tests
│ ├── App
│ └── TestKernel.php
│ ├── Feature
│ └── .gitignore
│ ├── TestCase.php
│ └── Unit
│ └── DemoTest.php
├── proto
├── auth
│ └── v1
│ │ ├── message.proto
│ │ ├── request.proto
│ │ ├── response.proto
│ │ └── service.proto
├── common
│ └── v1
│ │ └── message.proto
├── payment
│ └── v1
│ │ ├── message.proto
│ │ ├── request.proto
│ │ ├── response.proto
│ │ └── service.proto
└── users
│ └── v1
│ ├── message.proto
│ ├── request.proto
│ ├── response.proto
│ └── service.proto
├── subscriptions
├── .editorconfig
├── .env.sample
├── .gitignore
├── .rr-prod.yaml
├── .rr.yaml
├── .styleci.yml
├── README.md
├── app.php
├── app
│ ├── config
│ │ ├── cycle.php
│ │ ├── database.php
│ │ ├── migration.php
│ │ ├── scaffolder.php
│ │ └── sentry.php
│ ├── migrations
│ │ ├── .gitignore
│ │ └── 20240708.163856_0_0_default_create_subscriptions.php
│ └── src
│ │ ├── Application
│ │ ├── Assert.php
│ │ ├── Bootloader
│ │ │ ├── AppBootloader.php
│ │ │ ├── ExceptionHandlerBootloader.php
│ │ │ └── PersistenceBootloader.php
│ │ ├── Exception
│ │ │ └── InvalidArgumentException.php
│ │ └── Kernel.php
│ │ ├── Domain
│ │ └── Subscription
│ │ │ ├── Subscription.php
│ │ │ ├── SubscriptionRepositoryInterface.php
│ │ │ └── ValueObject
│ │ │ └── Uuid.php
│ │ └── Endpoint
│ │ ├── Console
│ │ └── SeedSubscriptionsCommand.php
│ │ └── Temporal
│ │ └── SubscriptionWorkflow.php
├── composer.json
├── composer.lock
├── phpunit.xml
├── psalm.xml
└── tests
│ ├── App
│ └── TestKernel.php
│ ├── Feature
│ └── .gitignore
│ ├── TestCase.php
│ └── Unit
│ └── DemoTest.php
├── users
├── .editorconfig
├── .env.sample
├── .gitignore
├── .rr-prod.yaml
├── .rr.otel.yaml
├── .rr.yaml
├── .rr
│ ├── .rr.grpc.yaml
│ └── .rr.otel.yaml
├── .styleci.yml
├── README.md
├── app.php
├── app
│ ├── config
│ │ ├── cycle.php
│ │ ├── database.php
│ │ ├── grpc.php
│ │ ├── grpcServices.php
│ │ ├── migration.php
│ │ ├── scaffolder.php
│ │ └── sentry.php
│ ├── migrations
│ │ ├── .gitignore
│ │ ├── 20240529.182720_0_0_default_create_users.php
│ │ ├── 20240529.183200_0_0_default_change_users_add_fist_name_add_last_name_add_language.php
│ │ ├── 20240529.191748_0_0_default_change_users_add_created_at_add_updated_at.php
│ │ ├── 20240604.183640_0_0_default_create_auth_tokens.php
│ │ └── 20240708.150058_0_0_default_change_users_add_email_verified_at_add_kyc_verified_at_add_subscription_uuid_alter_updated_at.php
│ └── src
│ │ ├── Application
│ │ ├── Assert.php
│ │ ├── Bootloader
│ │ │ ├── AppBootloader.php
│ │ │ ├── AuthBootloader.php
│ │ │ ├── ExceptionHandlerBootloader.php
│ │ │ ├── PersistenceBootloader.php
│ │ │ └── UserBootloader.php
│ │ ├── Exception
│ │ │ ├── AuthException.php
│ │ │ ├── EmailAlreadyExistsException.php
│ │ │ ├── InvalidArgumentException.php
│ │ │ ├── PasswordIncorrectException.php
│ │ │ └── UserNotFoundException.php
│ │ ├── Kernel.php
│ │ ├── PasswordHasher.php
│ │ ├── Security
│ │ │ └── OrmActorProvider.php
│ │ └── UserService.php
│ │ ├── Domain
│ │ └── User
│ │ │ ├── PasswordHasherInterface.php
│ │ │ ├── Profile.php
│ │ │ ├── Specification
│ │ │ └── UniqueEmailSpecificationInterface.php
│ │ │ ├── User.php
│ │ │ ├── UserFactoryInterface.php
│ │ │ ├── UserRepositoryInterface.php
│ │ │ ├── UserServiceInterface.php
│ │ │ └── ValueObject
│ │ │ ├── Email.php
│ │ │ ├── Password.php
│ │ │ ├── PasswordHash.php
│ │ │ └── Uuid.php
│ │ ├── Endpoint
│ │ ├── Console
│ │ │ └── RegisterUserCommand.php
│ │ ├── GRPC
│ │ │ ├── Exception
│ │ │ │ └── UnauthorizedException.php
│ │ │ ├── Interceptor
│ │ │ │ ├── GuardInterceptor.php
│ │ │ │ └── HandleExceptionsInterceptor.php
│ │ │ ├── Mapper
│ │ │ │ ├── TimestampMapper.php
│ │ │ │ └── UserService
│ │ │ │ │ └── UserMapper.php
│ │ │ └── Service
│ │ │ │ ├── AuthService.php
│ │ │ │ └── UserService.php
│ │ └── Temporal
│ │ │ └── User
│ │ │ ├── CreditApplicationWorkflow.php
│ │ │ ├── EmailVerificationWorkflow.php
│ │ │ ├── KYCWorkflow.php
│ │ │ ├── RegisterUserWorkflow.php
│ │ │ ├── RegisterWorkflow.php
│ │ │ ├── SubscriptionWorkflow.php
│ │ │ └── UserServiceActivity.php
│ │ └── Infrastructure
│ │ └── CycleOrm
│ │ ├── Factory
│ │ └── UserFactory.php
│ │ ├── Repository
│ │ └── UserRepository.php
│ │ └── Specification
│ │ └── UniqueEmailSpecification.php
├── composer.json
├── composer.lock
├── phpunit.xml
├── psalm.xml
└── tests
│ ├── App
│ └── TestKernel.php
│ ├── Feature
│ └── .gitignore
│ ├── TestCase.php
│ └── Unit
│ └── DemoTest.php
└── web
├── .editorconfig
├── .env.sample
├── .gitignore
├── .rr-prod.yaml
├── .rr.yaml
├── .styleci.yml
├── README.md
├── app.php
├── app
├── config
│ ├── grpc-services.php
│ ├── grpc.php
│ ├── scaffolder.php
│ └── session.php
├── src
│ ├── Application
│ │ ├── Auth
│ │ │ ├── AuthKey.php
│ │ │ └── AuthKeyInterface.php
│ │ ├── Bootloader
│ │ │ ├── AppBootloader.php
│ │ │ ├── ExceptionHandlerBootloader.php
│ │ │ ├── LoggingBootloader.php
│ │ │ └── RoutesBootloader.php
│ │ ├── Exception
│ │ │ └── NotFoundException.php
│ │ ├── GRPC
│ │ │ └── Interceptor
│ │ │ │ └── AuthInterceptor.php
│ │ └── Kernel.php
│ └── Endpoint
│ │ ├── Console
│ │ └── TestCommand.php
│ │ └── Http
│ │ ├── Controller
│ │ ├── Auth
│ │ │ └── LoginAction.php
│ │ └── User
│ │ │ ├── CreateAction.php
│ │ │ └── ShowAction.php
│ │ ├── Middleware
│ │ ├── ErrorHandlerMiddleware.php
│ │ └── SimpleAuthMiddleware.php
│ │ └── Request
│ │ ├── Auth
│ │ └── LoginRequest.php
│ │ └── User
│ │ └── CreateRequest.php
└── views
│ └── home.php
├── composer.json
├── composer.lock
├── functions.php
├── phpunit.xml
├── psalm.xml
├── public
├── favicon.ico
├── images
│ └── logo.svg
└── styles
│ └── welcome.css
└── tests
├── App
└── TestKernel.php
├── Feature
└── .gitignore
├── TestCase.php
└── Unit
└── DemoTest.php
/.docker/otel/otel-collector-config.yml:
--------------------------------------------------------------------------------
1 | receivers:
2 | otlp:
3 | protocols:
4 | grpc:
5 | http:
6 |
7 | processors:
8 | batch:
9 | timeout: 1s
10 |
11 | exporters:
12 | zipkin:
13 | endpoint: "http://ms-zipkin:9411/api/v2/spans"
14 |
15 | service:
16 | pipelines:
17 | traces:
18 | receivers: [ otlp ]
19 | processors: [ batch ]
20 | exporters: [ zipkin ]
21 |
--------------------------------------------------------------------------------
/.docker/php/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ghcr.io/roadrunner-server/roadrunner:2023.3.12 as rr
2 |
3 | FROM ghcr.io/spiral/php-grpc:8.2 as backend
4 |
5 | RUN apk add --no-cache \
6 | openssh-client \
7 | ca-certificates \
8 | postgresql-dev
9 |
10 | RUN docker-php-ext-install \
11 | pgsql pdo_pgsql
12 |
13 | RUN curl -s https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin/ --filename=composer
14 |
15 | ARG SERVICE_NAME
16 | ARG APP_VERSION=v1.0
17 | ENV COMPOSER_ALLOW_SUPERUSER=1
18 | ENV SERVICE_NAME=php-$SERVICE_NAME
19 |
20 | COPY --from=rr /usr/bin/rr /bin
21 |
22 | WORKDIR /app
23 |
24 | #RUN composer config --no-plugins allow-plugins.spiral/composer-publish-plugin false
25 | #RUN composer install --no-dev --no-interaction --no-progress --no-suggest --optimize-autoloader
26 |
27 | CMD ["/bin/rr", "serve", "-e", "-c", "/app/.rr-prod.yaml"]
28 |
--------------------------------------------------------------------------------
/.docker/temporalio/development-cass.yaml:
--------------------------------------------------------------------------------
1 | system.forceSearchAttributesCacheRefreshOnRead:
2 | - value: true # Dev setup only. Please don't turn this on in production.
3 | constraints: {}
4 |
--------------------------------------------------------------------------------
/.docker/temporalio/development-sql.yaml:
--------------------------------------------------------------------------------
1 | limit.maxIDLength:
2 | - value: 255
3 | constraints: {}
4 | system.forceSearchAttributesCacheRefreshOnRead:
5 | - value: true # Dev setup only. Please don't turn this on in production.
6 | constraints: {}
7 |
--------------------------------------------------------------------------------
/.docker/temporalio/docker.yaml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/php-fart/grpc-microservices/df91325a22df18aea3ed00b7de7c88c019f8f7ef/.docker/temporalio/docker.yaml
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | APP_ENV=local
2 | DEBUG=true
3 | VERBOSITY_LEVEL=verbose # basic, verbose, debug
4 | ENCRYPTER_KEY=def000000cc7dc8fccb7c6aa28497907c91cf6790df0c7db92d125c4bdf9567f3bf8cad0a1911f966eff2ec01fde754ab92ad7efb3136fa8660261d9665c0095f5316354
5 |
6 | MONOLOG_DEFAULT_CHANNEL=default # Use "roadrunner" channel if you want to use RoadRunner logger
7 | MONOLOG_DEFAULT_LEVEL=DEBUG # DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL, ALERT, EMERGENCY
8 |
9 | # Telemetry
10 | TELEMETRY_DRIVER=otel
11 | OTEL_TRACES_EXPORTER=otlp
12 | OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
13 | OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318
14 | OTEL_PHP_TRACES_PROCESSOR=simple
15 |
16 | USERS_SERVICE_HOST=users:9001
17 | AUTH_SERVICE_HOST=users:9001
18 | PAYMENT_SERVICE_HOST=payment:9002
19 |
20 | DB_CONNECTION: pgsql
21 | DB_HOST: db
22 | DB_PORT: 5432
23 | DB_USERNAME: homestead
24 | DB_PASSWORD: secret
25 |
26 | TEMPORAL_ADDRESS=temporal:7233
27 |
28 | VAR_DUMPER_FORMAT=server
29 | VAR_DUMPER_SERVER=buggregator:9912
30 |
31 | SENTRY_DSN=http://sentry@buggregator:8000/1
32 |
33 | TOKENIZER_CACHE_TARGETS=true
34 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/shelf/Changes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/shelf/Changes/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/php-fart/grpc-microservices/df91325a22df18aea3ed00b7de7c88c019f8f7ef/.idea/shelf/Changes/favicon.ico
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | build:
2 | docker compose up --no-start;
3 |
4 | start:
5 | docker compose up --remove-orphans -d;
6 |
7 | up: build start
8 |
9 | stop:
10 | docker compose stop;
11 |
12 | down:
13 | docker compose down;
14 |
15 | restart:
16 | docker compose restart;
17 |
18 | list:
19 | docker compose ps;
20 |
21 | log-tail:
22 | docker compose logs --tail=50 -f;
23 |
24 | # =========================
25 |
26 | install:
27 | for service in web users; do \
28 | cd $$service; \
29 | composer install; \
30 | cd ..; \
31 | done
32 |
33 |
34 | reinstall-temporal-shared:
35 | for service in web users; do \
36 | cd $$service; \
37 | rm -rf vendor/ms/temporal-shared; \
38 | composer require ms/temporal-shared; \
39 | cd ..; \
40 | done
41 |
42 | reinstall-grpc-shared:
43 | for service in web users; do \
44 | cd $$service; \
45 | rm -rf vendor/ms/grpc-shared; \
46 | composer require ms/grpc-shared; \
47 | cd ..; \
48 | done
49 |
50 | clear-cache:
51 | for service in web users; do \
52 | cd $$service; \
53 | rm -rf runtime/cache; \
54 | cd ..; \
55 | done
56 |
57 | compile-proto:
58 | chmod +x lib/grpc-shared/bin/console
59 | chmod +x lib/grpc-shared/bin/protoc-gen-php-grpc
60 | php lib/grpc-shared/bin/console generate;
61 |
62 | update-proto: compile-proto reinstall-grpc-shared;
63 |
64 | composer-du:
65 | for service in web users; do \
66 | cd $$service; \
67 | composer du; \
68 | cd ..; \
69 | done
70 |
71 | # =========================
72 |
73 | bash-web:
74 | docker compose exec web /bin/sh;
75 |
76 | bash-users:
77 | docker compose exec users /bin/sh;
78 |
79 | reset-web:
80 | docker compose exec web ./rr reset;
81 |
82 | reset-users:
83 | docker compose exec users ./rr reset;
84 |
85 | reset: reset-web reset-users
86 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # This repository is a part of streams about developing microservices.
2 |
3 | In a video we develop microservices using PHP, Spiral Framework, RoadRunner, Docker and gRPC.
4 |
5 | Link to the [YouTube playlist](https://www.youtube.com/playlist?list=PLq5yMPNm4NaCBXMGlIeIDA5SLYRIDSK5e).
6 |
7 | ## Installation
8 |
9 | 1. Clone the repository
10 | 2. Install make and docker
11 | 3. Run `make install` to install composer dependencies
12 | 4. Run `make up` to start docker containers
13 |
14 |
15 | ## TODO:
16 | 1. Add bash scripts for db initialization
17 |
--------------------------------------------------------------------------------
/lib/grpc-shared/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/
--------------------------------------------------------------------------------
/lib/grpc-shared/bin/console:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | isDir() && !\str_starts_with($fileInfo->getFilename(), '.')) {
17 | $protoDirs[] = $fileInfo->getPathname();
18 | }
19 | }
20 |
21 | $application->add(
22 | new GeneratorCommand(
23 | files: new \Spiral\Files\Files(),
24 | rootDir: __DIR__ . '/../',
25 | protoFileDirs: $protoDirs,
26 | ),
27 | );
28 |
29 | $application->run();
30 |
--------------------------------------------------------------------------------
/lib/grpc-shared/bin/protoc-gen-php-grpc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/php-fart/grpc-microservices/df91325a22df18aea3ed00b7de7c88c019f8f7ef/lib/grpc-shared/bin/protoc-gen-php-grpc
--------------------------------------------------------------------------------
/lib/grpc-shared/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ms/grpc-shared",
3 | "description": "This is shared package",
4 | "keywords": [],
5 | "homepage": "https://github.com/spiral/framework",
6 | "license": "MIT",
7 | "authors": [
8 | {
9 | "name": "butschster",
10 | "email": "butschster@gmail.com",
11 | "role": "Developer"
12 | }
13 | ],
14 | "require": {
15 | "php": "^8.2",
16 | "ext-grpc": "*",
17 | "grpc/grpc": "^1.42",
18 | "spiral/boot": "^3.7",
19 | "spiral/hmvc": "^3.7",
20 | "spiral/files": "^3.7",
21 | "spiral/console": "^3.7",
22 | "spiral/auth": "^3.7",
23 | "spiral/telemetry": "^3.7",
24 | "spiral-packages/cqrs": "^2.0",
25 | "spiral/roadrunner-grpc": "^3.0",
26 | "spiral/roadrunner-bridge": "^3.6"
27 | },
28 | "require-dev": {
29 | "spiral/reactor": "^3.13",
30 | "nikic/php-parser": "^v4.19",
31 | "symfony/console": "^7.0",
32 | "spiral/roadrunner-cli": "^2.5",
33 | "doctrine/annotations": "^2.0"
34 | },
35 | "autoload": {
36 | "psr-4": {
37 | "Internal\\Shared\\gRPC\\": "src",
38 | "GRPC\\": "generated/GRPC",
39 | "Generator\\": "generator"
40 | }
41 | },
42 | "config": {
43 | "sort-packages": true
44 | },
45 | "minimum-stability": "dev",
46 | "prefer-stable": true
47 | }
48 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Auth/v1/Message.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/php-fart/grpc-microservices/df91325a22df18aea3ed00b7de7c88c019f8f7ef/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Auth/v1/Message.php
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Auth/v1/Request.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/php-fart/grpc-microservices/df91325a22df18aea3ed00b7de7c88c019f8f7ef/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Auth/v1/Request.php
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Auth/v1/Response.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/php-fart/grpc-microservices/df91325a22df18aea3ed00b7de7c88c019f8f7ef/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Auth/v1/Response.php
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Auth/v1/Service.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/php-fart/grpc-microservices/df91325a22df18aea3ed00b7de7c88c019f8f7ef/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Auth/v1/Service.php
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Common/v1/Message.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/php-fart/grpc-microservices/df91325a22df18aea3ed00b7de7c88c019f8f7ef/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Common/v1/Message.php
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Payment/v1/Message.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/php-fart/grpc-microservices/df91325a22df18aea3ed00b7de7c88c019f8f7ef/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Payment/v1/Message.php
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Payment/v1/Request.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/php-fart/grpc-microservices/df91325a22df18aea3ed00b7de7c88c019f8f7ef/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Payment/v1/Request.php
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Payment/v1/Response.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/php-fart/grpc-microservices/df91325a22df18aea3ed00b7de7c88c019f8f7ef/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Payment/v1/Response.php
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Payment/v1/Service.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/php-fart/grpc-microservices/df91325a22df18aea3ed00b7de7c88c019f8f7ef/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Payment/v1/Service.php
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Users/v1/Message.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/php-fart/grpc-microservices/df91325a22df18aea3ed00b7de7c88c019f8f7ef/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Users/v1/Message.php
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Users/v1/Request.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/php-fart/grpc-microservices/df91325a22df18aea3ed00b7de7c88c019f8f7ef/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Users/v1/Request.php
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Users/v1/Response.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/php-fart/grpc-microservices/df91325a22df18aea3ed00b7de7c88c019f8f7ef/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Users/v1/Response.php
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Users/v1/Service.php:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/php-fart/grpc-microservices/df91325a22df18aea3ed00b7de7c88c019f8f7ef/lib/grpc-shared/generated/GRPC/ProtobufMetadata/Users/v1/Service.php
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/Services/Auth/v1/AuthServiceClient.php:
--------------------------------------------------------------------------------
1 | core->callAction(AuthServiceInterface::class, '/'.self::NAME.'/Login', [
20 | 'in' => $in,
21 | 'ctx' => $ctx,
22 | 'responseClass' => \GRPC\Services\Auth\v1\LoginResponse::class,
23 | ]);
24 |
25 | return $response;
26 | }
27 |
28 | public function Logout(ContextInterface $ctx, LogoutRequest $in): \GRPC\Services\Common\v1\PBEmpty
29 | {
30 | [$response, $status] = $this->core->callAction(AuthServiceInterface::class, '/'.self::NAME.'/Logout', [
31 | 'in' => $in,
32 | 'ctx' => $ctx,
33 | 'responseClass' => \GRPC\Services\Common\v1\PBEmpty::class,
34 | ]);
35 |
36 | return $response;
37 | }
38 |
39 | public function Register(ContextInterface $ctx, RegisterRequest $in): RegisterResponse
40 | {
41 | [$response, $status] = $this->core->callAction(AuthServiceInterface::class, '/'.self::NAME.'/Register', [
42 | 'in' => $in,
43 | 'ctx' => $ctx,
44 | 'responseClass' => \GRPC\Services\Auth\v1\RegisterResponse::class,
45 | ]);
46 |
47 | return $response;
48 | }
49 |
50 | public function Me(ContextInterface $ctx, MeRequest $in): MeResponse
51 | {
52 | [$response, $status] = $this->core->callAction(AuthServiceInterface::class, '/'.self::NAME.'/Me', [
53 | 'in' => $in,
54 | 'ctx' => $ctx,
55 | 'responseClass' => \GRPC\Services\Auth\v1\MeResponse::class,
56 | ]);
57 |
58 | return $response;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/Services/Auth/v1/AuthServiceInterface.php:
--------------------------------------------------------------------------------
1 | auth.v1.request.LoginRequest
11 | */
12 | class LoginRequest extends \Google\Protobuf\Internal\Message
13 | {
14 | /** Generated from protobuf field string email = 1;
*/
15 | protected $email = '';
16 |
17 | /** Generated from protobuf field string password = 2;
*/
18 | protected $password = '';
19 |
20 | /**
21 | * Constructor.
22 | *
23 | * @param array $data {
24 | * Optional. Data for populating the Message object.
25 | *
26 | * @type string $email
27 | * @type string $password
28 | * }
29 | */
30 | public function __construct($data = null)
31 | {
32 | \GRPC\ProtobufMetadata\Auth\v1\Request::initOnce();
33 | parent::__construct($data);
34 | }
35 |
36 | /**
37 | * Generated from protobuf field string email = 1;
38 | * @return string
39 | */
40 | public function getEmail()
41 | {
42 | return $this->email;
43 | }
44 |
45 | /**
46 | * Generated from protobuf field string email = 1;
47 | * @param string $var
48 | * @return $this
49 | */
50 | public function setEmail($var)
51 | {
52 | GPBUtil::checkString($var, True);
53 | $this->email = $var;
54 |
55 | return $this;
56 | }
57 |
58 | /**
59 | * Generated from protobuf field string password = 2;
60 | * @return string
61 | */
62 | public function getPassword()
63 | {
64 | return $this->password;
65 | }
66 |
67 | /**
68 | * Generated from protobuf field string password = 2;
69 | * @param string $var
70 | * @return $this
71 | */
72 | public function setPassword($var)
73 | {
74 | GPBUtil::checkString($var, True);
75 | $this->password = $var;
76 |
77 | return $this;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/Services/Auth/v1/LoginResponse.php:
--------------------------------------------------------------------------------
1 | auth.v1.response.LoginResponse
11 | */
12 | class LoginResponse extends \Google\Protobuf\Internal\Message
13 | {
14 | /** Generated from protobuf field .auth.v1.dto.Token token = 1;
*/
15 | protected $token = null;
16 |
17 | /**
18 | * Constructor.
19 | *
20 | * @param array $data {
21 | * Optional. Data for populating the Message object.
22 | *
23 | * @type \GRPC\Services\Auth\v1\Token $token
24 | * }
25 | */
26 | public function __construct($data = null)
27 | {
28 | \GRPC\ProtobufMetadata\Auth\v1\Response::initOnce();
29 | parent::__construct($data);
30 | }
31 |
32 | /**
33 | * Generated from protobuf field .auth.v1.dto.Token token = 1;
34 | * @return \GRPC\Services\Auth\v1\Token|null
35 | */
36 | public function getToken()
37 | {
38 | return isset($this->token) ? $this->token : null;
39 | }
40 |
41 | public function hasToken()
42 | {
43 | return isset($this->token);
44 | }
45 |
46 | public function clearToken()
47 | {
48 | unset($this->token);
49 | }
50 |
51 | /**
52 | * Generated from protobuf field .auth.v1.dto.Token token = 1;
53 | * @param \GRPC\Services\Auth\v1\Token $var
54 | * @return $this
55 | */
56 | public function setToken($var)
57 | {
58 | GPBUtil::checkMessage($var, Token::class);
59 | $this->token = $var;
60 |
61 | return $this;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/Services/Auth/v1/LogoutRequest.php:
--------------------------------------------------------------------------------
1 | auth.v1.request.LogoutRequest
11 | */
12 | class LogoutRequest extends \Google\Protobuf\Internal\Message
13 | {
14 | /** Generated from protobuf field string token = 1;
*/
15 | protected $token = '';
16 |
17 | /**
18 | * Constructor.
19 | *
20 | * @param array $data {
21 | * Optional. Data for populating the Message object.
22 | *
23 | * @type string $token
24 | * }
25 | */
26 | public function __construct($data = null)
27 | {
28 | \GRPC\ProtobufMetadata\Auth\v1\Request::initOnce();
29 | parent::__construct($data);
30 | }
31 |
32 | /**
33 | * Generated from protobuf field string token = 1;
34 | * @return string
35 | */
36 | public function getToken()
37 | {
38 | return $this->token;
39 | }
40 |
41 | /**
42 | * Generated from protobuf field string token = 1;
43 | * @param string $var
44 | * @return $this
45 | */
46 | public function setToken($var)
47 | {
48 | GPBUtil::checkString($var, True);
49 | $this->token = $var;
50 |
51 | return $this;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/Services/Auth/v1/MeRequest.php:
--------------------------------------------------------------------------------
1 | auth.v1.request.MeRequest
11 | */
12 | class MeRequest extends \Google\Protobuf\Internal\Message
13 | {
14 | /** Generated from protobuf field string token = 1;
*/
15 | protected $token = '';
16 |
17 | /**
18 | * Constructor.
19 | *
20 | * @param array $data {
21 | * Optional. Data for populating the Message object.
22 | *
23 | * @type string $token
24 | * }
25 | */
26 | public function __construct($data = null)
27 | {
28 | \GRPC\ProtobufMetadata\Auth\v1\Request::initOnce();
29 | parent::__construct($data);
30 | }
31 |
32 | /**
33 | * Generated from protobuf field string token = 1;
34 | * @return string
35 | */
36 | public function getToken()
37 | {
38 | return $this->token;
39 | }
40 |
41 | /**
42 | * Generated from protobuf field string token = 1;
43 | * @param string $var
44 | * @return $this
45 | */
46 | public function setToken($var)
47 | {
48 | GPBUtil::checkString($var, True);
49 | $this->token = $var;
50 |
51 | return $this;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/Services/Auth/v1/MeResponse.php:
--------------------------------------------------------------------------------
1 | auth.v1.response.MeResponse
11 | */
12 | class MeResponse extends \Google\Protobuf\Internal\Message
13 | {
14 | /** Generated from protobuf field .users.v1.dto.User user = 1;
*/
15 | protected $user = null;
16 |
17 | /**
18 | * Constructor.
19 | *
20 | * @param array $data {
21 | * Optional. Data for populating the Message object.
22 | *
23 | * @type \GRPC\Services\Users\v1\User $user
24 | * }
25 | */
26 | public function __construct($data = null)
27 | {
28 | \GRPC\ProtobufMetadata\Auth\v1\Response::initOnce();
29 | parent::__construct($data);
30 | }
31 |
32 | /**
33 | * Generated from protobuf field .users.v1.dto.User user = 1;
34 | * @return \GRPC\Services\Users\v1\User|null
35 | */
36 | public function getUser()
37 | {
38 | return isset($this->user) ? $this->user : null;
39 | }
40 |
41 | public function hasUser()
42 | {
43 | return isset($this->user);
44 | }
45 |
46 | public function clearUser()
47 | {
48 | unset($this->user);
49 | }
50 |
51 | /**
52 | * Generated from protobuf field .users.v1.dto.User user = 1;
53 | * @param \GRPC\Services\Users\v1\User $var
54 | * @return $this
55 | */
56 | public function setUser($var)
57 | {
58 | GPBUtil::checkMessage($var, \GRPC\Services\Users\v1\User::class);
59 | $this->user = $var;
60 |
61 | return $this;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/Services/Auth/v1/RegisterResponse.php:
--------------------------------------------------------------------------------
1 | auth.v1.response.RegisterResponse
11 | */
12 | class RegisterResponse extends \Google\Protobuf\Internal\Message
13 | {
14 | /** Generated from protobuf field .auth.v1.dto.Token token = 1;
*/
15 | protected $token = null;
16 |
17 | /**
18 | * Constructor.
19 | *
20 | * @param array $data {
21 | * Optional. Data for populating the Message object.
22 | *
23 | * @type \GRPC\Services\Auth\v1\Token $token
24 | * }
25 | */
26 | public function __construct($data = null)
27 | {
28 | \GRPC\ProtobufMetadata\Auth\v1\Response::initOnce();
29 | parent::__construct($data);
30 | }
31 |
32 | /**
33 | * Generated from protobuf field .auth.v1.dto.Token token = 1;
34 | * @return \GRPC\Services\Auth\v1\Token|null
35 | */
36 | public function getToken()
37 | {
38 | return isset($this->token) ? $this->token : null;
39 | }
40 |
41 | public function hasToken()
42 | {
43 | return isset($this->token);
44 | }
45 |
46 | public function clearToken()
47 | {
48 | unset($this->token);
49 | }
50 |
51 | /**
52 | * Generated from protobuf field .auth.v1.dto.Token token = 1;
53 | * @param \GRPC\Services\Auth\v1\Token $var
54 | * @return $this
55 | */
56 | public function setToken($var)
57 | {
58 | GPBUtil::checkMessage($var, Token::class);
59 | $this->token = $var;
60 |
61 | return $this;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/Services/Common/v1/PBEmpty.php:
--------------------------------------------------------------------------------
1 | common.v1.dto.Empty
11 | */
12 | class PBEmpty extends \Google\Protobuf\Internal\Message
13 | {
14 | /**
15 | * Constructor.
16 | *
17 | * @param array $data {
18 | * Optional. Data for populating the Message object.
19 | *
20 | * }
21 | */
22 | public function __construct($data = null)
23 | {
24 | \GRPC\ProtobufMetadata\Common\v1\Message::initOnce();
25 | parent::__construct($data);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/Services/Payment/v1/ChargeRequest.php:
--------------------------------------------------------------------------------
1 | payment.v1.request.ChargeRequest
11 | */
12 | class ChargeRequest extends \Google\Protobuf\Internal\Message
13 | {
14 | /** Generated from protobuf field .payment.v1.dto.Payment payment = 1;
*/
15 | protected $payment = null;
16 |
17 | /**
18 | * Constructor.
19 | *
20 | * @param array $data {
21 | * Optional. Data for populating the Message object.
22 | *
23 | * @type \GRPC\Services\Payment\v1\Payment $payment
24 | * }
25 | */
26 | public function __construct($data = null)
27 | {
28 | \GRPC\ProtobufMetadata\Payment\v1\Request::initOnce();
29 | parent::__construct($data);
30 | }
31 |
32 | /**
33 | * Generated from protobuf field .payment.v1.dto.Payment payment = 1;
34 | * @return \GRPC\Services\Payment\v1\Payment|null
35 | */
36 | public function getPayment()
37 | {
38 | return isset($this->payment) ? $this->payment : null;
39 | }
40 |
41 | public function hasPayment()
42 | {
43 | return isset($this->payment);
44 | }
45 |
46 | public function clearPayment()
47 | {
48 | unset($this->payment);
49 | }
50 |
51 | /**
52 | * Generated from protobuf field .payment.v1.dto.Payment payment = 1;
53 | * @param \GRPC\Services\Payment\v1\Payment $var
54 | * @return $this
55 | */
56 | public function setPayment($var)
57 | {
58 | GPBUtil::checkMessage($var, Payment::class);
59 | $this->payment = $var;
60 |
61 | return $this;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/Services/Payment/v1/ChargeResponse.php:
--------------------------------------------------------------------------------
1 | payment.v1.response.ChargeResponse
11 | */
12 | class ChargeResponse extends \Google\Protobuf\Internal\Message
13 | {
14 | /** Generated from protobuf field .payment.v1.dto.Receipt receipt = 1;
*/
15 | protected $receipt = null;
16 |
17 | /**
18 | * Constructor.
19 | *
20 | * @param array $data {
21 | * Optional. Data for populating the Message object.
22 | *
23 | * @type \GRPC\Services\Payment\v1\Receipt $receipt
24 | * }
25 | */
26 | public function __construct($data = null)
27 | {
28 | \GRPC\ProtobufMetadata\Payment\v1\Response::initOnce();
29 | parent::__construct($data);
30 | }
31 |
32 | /**
33 | * Generated from protobuf field .payment.v1.dto.Receipt receipt = 1;
34 | * @return \GRPC\Services\Payment\v1\Receipt|null
35 | */
36 | public function getReceipt()
37 | {
38 | return isset($this->receipt) ? $this->receipt : null;
39 | }
40 |
41 | public function hasReceipt()
42 | {
43 | return isset($this->receipt);
44 | }
45 |
46 | public function clearReceipt()
47 | {
48 | unset($this->receipt);
49 | }
50 |
51 | /**
52 | * Generated from protobuf field .payment.v1.dto.Receipt receipt = 1;
53 | * @param \GRPC\Services\Payment\v1\Receipt $var
54 | * @return $this
55 | */
56 | public function setReceipt($var)
57 | {
58 | GPBUtil::checkMessage($var, Receipt::class);
59 | $this->receipt = $var;
60 |
61 | return $this;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/Services/Payment/v1/Money.php:
--------------------------------------------------------------------------------
1 | payment.v1.dto.Money
11 | */
12 | class Money extends \Google\Protobuf\Internal\Message
13 | {
14 | /** Generated from protobuf field string currency_code = 1;
*/
15 | protected $currency_code = '';
16 |
17 | /** Generated from protobuf field int64 amount = 2;
*/
18 | protected $amount = 0;
19 |
20 | /**
21 | * Constructor.
22 | *
23 | * @param array $data {
24 | * Optional. Data for populating the Message object.
25 | *
26 | * @type string $currency_code
27 | * @type int|string $amount
28 | * }
29 | */
30 | public function __construct($data = null)
31 | {
32 | \GRPC\ProtobufMetadata\Payment\v1\Message::initOnce();
33 | parent::__construct($data);
34 | }
35 |
36 | /**
37 | * Generated from protobuf field string currency_code = 1;
38 | * @return string
39 | */
40 | public function getCurrencyCode()
41 | {
42 | return $this->currency_code;
43 | }
44 |
45 | /**
46 | * Generated from protobuf field string currency_code = 1;
47 | * @param string $var
48 | * @return $this
49 | */
50 | public function setCurrencyCode($var)
51 | {
52 | GPBUtil::checkString($var, True);
53 | $this->currency_code = $var;
54 |
55 | return $this;
56 | }
57 |
58 | /**
59 | * Generated from protobuf field int64 amount = 2;
60 | * @return int|string
61 | */
62 | public function getAmount()
63 | {
64 | return $this->amount;
65 | }
66 |
67 | /**
68 | * Generated from protobuf field int64 amount = 2;
69 | * @param int|string $var
70 | * @return $this
71 | */
72 | public function setAmount($var)
73 | {
74 | GPBUtil::checkInt64($var);
75 | $this->amount = $var;
76 |
77 | return $this;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/Services/Payment/v1/PaymentServiceClient.php:
--------------------------------------------------------------------------------
1 | core->callAction(PaymentServiceInterface::class, '/'.self::NAME.'/Charge', [
20 | 'in' => $in,
21 | 'ctx' => $ctx,
22 | 'responseClass' => \GRPC\Services\Payment\v1\ChargeResponse::class,
23 | ]);
24 |
25 | return $response;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/Services/Payment/v1/PaymentServiceInterface.php:
--------------------------------------------------------------------------------
1 | users.v1.request.CreateRequest
11 | */
12 | class CreateRequest extends \Google\Protobuf\Internal\Message
13 | {
14 | /** Generated from protobuf field .users.v1.request.CreateRequest.User user = 1;
*/
15 | protected $user = null;
16 |
17 | /**
18 | * Constructor.
19 | *
20 | * @param array $data {
21 | * Optional. Data for populating the Message object.
22 | *
23 | * @type \GRPC\Services\Users\v1\CreateRequest\User $user
24 | * }
25 | */
26 | public function __construct($data = null)
27 | {
28 | \GRPC\ProtobufMetadata\Users\v1\Request::initOnce();
29 | parent::__construct($data);
30 | }
31 |
32 | /**
33 | * Generated from protobuf field .users.v1.request.CreateRequest.User user = 1;
34 | * @return \GRPC\Services\Users\v1\CreateRequest\User|null
35 | */
36 | public function getUser()
37 | {
38 | return isset($this->user) ? $this->user : null;
39 | }
40 |
41 | public function hasUser()
42 | {
43 | return isset($this->user);
44 | }
45 |
46 | public function clearUser()
47 | {
48 | unset($this->user);
49 | }
50 |
51 | /**
52 | * Generated from protobuf field .users.v1.request.CreateRequest.User user = 1;
53 | * @param \GRPC\Services\Users\v1\CreateRequest\User $var
54 | * @return $this
55 | */
56 | public function setUser($var)
57 | {
58 | GPBUtil::checkMessage($var, CreateRequest\User::class);
59 | $this->user = $var;
60 |
61 | return $this;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/Services/Users/v1/CreateRequest_User.php:
--------------------------------------------------------------------------------
1 | users.v1.response.CreateResponse
11 | */
12 | class CreateResponse extends \Google\Protobuf\Internal\Message
13 | {
14 | /** Generated from protobuf field .users.v1.dto.User user = 1;
*/
15 | protected $user = null;
16 |
17 | /**
18 | * Constructor.
19 | *
20 | * @param array $data {
21 | * Optional. Data for populating the Message object.
22 | *
23 | * @type \GRPC\Services\Users\v1\User $user
24 | * }
25 | */
26 | public function __construct($data = null)
27 | {
28 | \GRPC\ProtobufMetadata\Users\v1\Response::initOnce();
29 | parent::__construct($data);
30 | }
31 |
32 | /**
33 | * Generated from protobuf field .users.v1.dto.User user = 1;
34 | * @return \GRPC\Services\Users\v1\User|null
35 | */
36 | public function getUser()
37 | {
38 | return isset($this->user) ? $this->user : null;
39 | }
40 |
41 | public function hasUser()
42 | {
43 | return isset($this->user);
44 | }
45 |
46 | public function clearUser()
47 | {
48 | unset($this->user);
49 | }
50 |
51 | /**
52 | * Generated from protobuf field .users.v1.dto.User user = 1;
53 | * @param \GRPC\Services\Users\v1\User $var
54 | * @return $this
55 | */
56 | public function setUser($var)
57 | {
58 | GPBUtil::checkMessage($var, User::class);
59 | $this->user = $var;
60 |
61 | return $this;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/Services/Users/v1/GetRequest.php:
--------------------------------------------------------------------------------
1 | users.v1.request.GetRequest
11 | */
12 | class GetRequest extends \Google\Protobuf\Internal\Message
13 | {
14 | /** Generated from protobuf field string uuid = 1;
*/
15 | protected $uuid = '';
16 |
17 | /**
18 | * Constructor.
19 | *
20 | * @param array $data {
21 | * Optional. Data for populating the Message object.
22 | *
23 | * @type string $uuid
24 | * }
25 | */
26 | public function __construct($data = null)
27 | {
28 | \GRPC\ProtobufMetadata\Users\v1\Request::initOnce();
29 | parent::__construct($data);
30 | }
31 |
32 | /**
33 | * Generated from protobuf field string uuid = 1;
34 | * @return string
35 | */
36 | public function getUuid()
37 | {
38 | return $this->uuid;
39 | }
40 |
41 | /**
42 | * Generated from protobuf field string uuid = 1;
43 | * @param string $var
44 | * @return $this
45 | */
46 | public function setUuid($var)
47 | {
48 | GPBUtil::checkString($var, True);
49 | $this->uuid = $var;
50 |
51 | return $this;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/Services/Users/v1/GetResponse.php:
--------------------------------------------------------------------------------
1 | users.v1.response.GetResponse
11 | */
12 | class GetResponse extends \Google\Protobuf\Internal\Message
13 | {
14 | /** Generated from protobuf field .users.v1.dto.User user = 1;
*/
15 | protected $user = null;
16 |
17 | /**
18 | * Constructor.
19 | *
20 | * @param array $data {
21 | * Optional. Data for populating the Message object.
22 | *
23 | * @type \GRPC\Services\Users\v1\User $user
24 | * }
25 | */
26 | public function __construct($data = null)
27 | {
28 | \GRPC\ProtobufMetadata\Users\v1\Response::initOnce();
29 | parent::__construct($data);
30 | }
31 |
32 | /**
33 | * Generated from protobuf field .users.v1.dto.User user = 1;
34 | * @return \GRPC\Services\Users\v1\User|null
35 | */
36 | public function getUser()
37 | {
38 | return isset($this->user) ? $this->user : null;
39 | }
40 |
41 | public function hasUser()
42 | {
43 | return isset($this->user);
44 | }
45 |
46 | public function clearUser()
47 | {
48 | unset($this->user);
49 | }
50 |
51 | /**
52 | * Generated from protobuf field .users.v1.dto.User user = 1;
53 | * @param \GRPC\Services\Users\v1\User $var
54 | * @return $this
55 | */
56 | public function setUser($var)
57 | {
58 | GPBUtil::checkMessage($var, User::class);
59 | $this->user = $var;
60 |
61 | return $this;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/Services/Users/v1/UpdateRequest.php:
--------------------------------------------------------------------------------
1 | users.v1.request.UpdateRequest
11 | */
12 | class UpdateRequest extends \Google\Protobuf\Internal\Message
13 | {
14 | /** Generated from protobuf field .users.v1.request.UpdateRequest.User user = 1;
*/
15 | protected $user = null;
16 |
17 | /**
18 | * Constructor.
19 | *
20 | * @param array $data {
21 | * Optional. Data for populating the Message object.
22 | *
23 | * @type \GRPC\Services\Users\v1\UpdateRequest\User $user
24 | * }
25 | */
26 | public function __construct($data = null)
27 | {
28 | \GRPC\ProtobufMetadata\Users\v1\Request::initOnce();
29 | parent::__construct($data);
30 | }
31 |
32 | /**
33 | * Generated from protobuf field .users.v1.request.UpdateRequest.User user = 1;
34 | * @return \GRPC\Services\Users\v1\UpdateRequest\User|null
35 | */
36 | public function getUser()
37 | {
38 | return isset($this->user) ? $this->user : null;
39 | }
40 |
41 | public function hasUser()
42 | {
43 | return isset($this->user);
44 | }
45 |
46 | public function clearUser()
47 | {
48 | unset($this->user);
49 | }
50 |
51 | /**
52 | * Generated from protobuf field .users.v1.request.UpdateRequest.User user = 1;
53 | * @param \GRPC\Services\Users\v1\UpdateRequest\User $var
54 | * @return $this
55 | */
56 | public function setUser($var)
57 | {
58 | GPBUtil::checkMessage($var, UpdateRequest\User::class);
59 | $this->user = $var;
60 |
61 | return $this;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/Services/Users/v1/UpdateRequest_User.php:
--------------------------------------------------------------------------------
1 | users.v1.response.UpdateResponse
11 | */
12 | class UpdateResponse extends \Google\Protobuf\Internal\Message
13 | {
14 | /** Generated from protobuf field .users.v1.dto.User user = 1;
*/
15 | protected $user = null;
16 |
17 | /**
18 | * Constructor.
19 | *
20 | * @param array $data {
21 | * Optional. Data for populating the Message object.
22 | *
23 | * @type \GRPC\Services\Users\v1\User $user
24 | * }
25 | */
26 | public function __construct($data = null)
27 | {
28 | \GRPC\ProtobufMetadata\Users\v1\Response::initOnce();
29 | parent::__construct($data);
30 | }
31 |
32 | /**
33 | * Generated from protobuf field .users.v1.dto.User user = 1;
34 | * @return \GRPC\Services\Users\v1\User|null
35 | */
36 | public function getUser()
37 | {
38 | return isset($this->user) ? $this->user : null;
39 | }
40 |
41 | public function hasUser()
42 | {
43 | return isset($this->user);
44 | }
45 |
46 | public function clearUser()
47 | {
48 | unset($this->user);
49 | }
50 |
51 | /**
52 | * Generated from protobuf field .users.v1.dto.User user = 1;
53 | * @param \GRPC\Services\Users\v1\User $var
54 | * @return $this
55 | */
56 | public function setUser($var)
57 | {
58 | GPBUtil::checkMessage($var, User::class);
59 | $this->user = $var;
60 |
61 | return $this;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/Services/Users/v1/UsersServiceClient.php:
--------------------------------------------------------------------------------
1 | core->callAction(UsersServiceInterface::class, '/'.self::NAME.'/Get', [
20 | 'in' => $in,
21 | 'ctx' => $ctx,
22 | 'responseClass' => \GRPC\Services\Users\v1\GetResponse::class,
23 | ]);
24 |
25 | return $response;
26 | }
27 |
28 | public function Create(ContextInterface $ctx, CreateRequest $in): CreateResponse
29 | {
30 | [$response, $status] = $this->core->callAction(UsersServiceInterface::class, '/'.self::NAME.'/Create', [
31 | 'in' => $in,
32 | 'ctx' => $ctx,
33 | 'responseClass' => \GRPC\Services\Users\v1\CreateResponse::class,
34 | ]);
35 |
36 | return $response;
37 | }
38 |
39 | public function Update(ContextInterface $ctx, UpdateRequest $in): UpdateResponse
40 | {
41 | [$response, $status] = $this->core->callAction(UsersServiceInterface::class, '/'.self::NAME.'/Update', [
42 | 'in' => $in,
43 | 'ctx' => $ctx,
44 | 'responseClass' => \GRPC\Services\Users\v1\UpdateResponse::class,
45 | ]);
46 |
47 | return $response;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generated/GRPC/Services/Users/v1/UsersServiceInterface.php:
--------------------------------------------------------------------------------
1 | $class
15 | * @param PropertyType[] $properties
16 | */
17 | public function __construct(
18 | public string $class,
19 | public array $properties,
20 | public array $attributes,
21 | ) {
22 | }
23 |
24 | public function isGuarded(): bool
25 | {
26 | foreach ($this->attributes as $attribute) {
27 | if ($attribute instanceof Annotation\Guarded) {
28 | return true;
29 | }
30 | }
31 |
32 | return false;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generator/Generators/Message/MessageClassParser.php:
--------------------------------------------------------------------------------
1 | $class
21 | * @throws \ReflectionException
22 | */
23 | public function parse(string $class): MessageClass
24 | {
25 | $reflection = new \ReflectionClass($class);
26 |
27 | if (!$reflection->isSubclassOf(Message::class)) {
28 | throw new \InvalidArgumentException(\sprintf('Class %s is not a subclass of %s', $class, Message::class));
29 | }
30 |
31 | new $class;
32 | $pool = DescriptorPool::getGeneratedPool();
33 | $descriptor = $pool->getDescriptorByClassName($class);
34 |
35 | $method = $reflection->getMethod('__construct');
36 |
37 | $parser = new MessageCommentsParser(new TypeFactory());
38 | $properties = $parser->parse($descriptor, $method);
39 |
40 | return new MessageClass(
41 | $class,
42 | $properties,
43 | $this->annotationsParser->parseFromClass($reflection),
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generator/PHP/ClassDeclaration.php:
--------------------------------------------------------------------------------
1 | class->getName();
26 | }
27 |
28 | public function markAsFinal(): void
29 | {
30 | $this->class->setFinal(true);
31 | }
32 |
33 | public function markAsReadonly(): void
34 | {
35 | $this->class->getElement()->setReadonly(true);
36 | }
37 |
38 | public function getNameWithNamespace(): string
39 | {
40 | return $this->getNamespace() . '\\' . $this->getName();
41 | }
42 |
43 | public function getNamespace(): string
44 | {
45 | return $this->namespace->getName();
46 | }
47 |
48 | public function isClassNameEndsWith(string $suffix): bool
49 | {
50 | return \str_ends_with($this->getName(), $suffix);
51 | }
52 |
53 | public function persist(): void
54 | {
55 | (new Writer($this->files))->write($this->filePath, $this->file);
56 | }
57 |
58 | public function getReflection(): \ReflectionClass
59 | {
60 | return new \ReflectionClass($this->getNameWithNamespace());
61 | }
62 |
63 | public function addImplement(string $interface): void
64 | {
65 | $this->class->addImplement($interface);
66 | }
67 |
68 | public function __toString()
69 | {
70 | return $this->filePath;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generator/PHP/ClassTransformer.php:
--------------------------------------------------------------------------------
1 | class);
17 |
18 | return \implode('\\', \array_slice($segments, 0, -1));
19 | }
20 |
21 | public function getShortName(): string
22 | {
23 | $segments = \explode('\\', $this->class);
24 |
25 | return \array_pop($segments);
26 | }
27 |
28 | public function cleanNamespace(?string $prefix = null): self
29 | {
30 | $namespace = $this->getNamespace();
31 |
32 | if (\str_starts_with($namespace, 'Internal\\Shared\\gRPC\\Services\\')) {
33 | $namespace = \str_replace('Internal\\Shared\\gRPC\\Services\\', '', $namespace);
34 |
35 | if ($prefix) {
36 | $prefix = \explode('\\', $prefix);
37 | $namespace = \explode('\\', $namespace);
38 | $namespace = \implode('\\', \array_filter([...$prefix, ...$namespace]));
39 | }
40 | }
41 |
42 | return new self($namespace . '\\' . $this->getShortName());
43 | }
44 |
45 | public function getDirectoryPath(): string
46 | {
47 | return \implode('/', \explode('\\', $this->getNamespace()));
48 | }
49 |
50 | public function getFilePath(?string $suffix = null): string
51 | {
52 | $filename = $this->getShortName();
53 | if ($suffix) {
54 | $filename .= \ucfirst($suffix);
55 | }
56 |
57 | return $this->getDirectoryPath() . '/' . $filename . '.php';
58 | }
59 |
60 | public function __toString()
61 | {
62 | return $this->class;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generator/PHP/Property/BuiltInType.php:
--------------------------------------------------------------------------------
1 | transformer = new ClassTransformer($type);
20 | }
21 |
22 | public function getShortName(): string
23 | {
24 | return $this->transformer->getShortName();
25 | }
26 |
27 | public function getName(): string
28 | {
29 | return $this->transformer->class;
30 | }
31 |
32 | public function getNamespace(): string
33 | {
34 | return $this->transformer->getNamespace();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generator/PHP/Property/DateTime.php:
--------------------------------------------------------------------------------
1 | iterableType = $type;
14 | parent::__construct('array', $type->type . '[]');
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generator/PHP/Property/Timestamp.php:
--------------------------------------------------------------------------------
1 | docType = $doctype ?? $type;
16 | }
17 |
18 | public function isEqual(string $type): bool
19 | {
20 | return $this->type === $type;
21 | }
22 |
23 | public function __toString(): string
24 | {
25 | return $this->type;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lib/grpc-shared/generator/ProtocCommandBuilder.php:
--------------------------------------------------------------------------------
1 | getProtoFiles($protoDir);
23 | if ($files === []) {
24 | return '';
25 | }
26 |
27 | return \sprintf(
28 | 'protoc %s --php_out=%s --php-grpc_out=%s%s %s 2>&1',
29 | $this->protocBinaryPath ? '--plugin=' . $this->protocBinaryPath : '',
30 | \escapeshellarg($tmpDir),
31 | \escapeshellarg($tmpDir),
32 | $this->buildDirs($protoDir),
33 | \implode(' ', \array_map('escapeshellarg', $files)),
34 | );
35 | }
36 |
37 | /**
38 | * Include all proto files from the directory.
39 | */
40 | private function getProtoFiles(string $protoDir): array
41 | {
42 | return \array_filter(
43 | $this->files->getFiles($protoDir),
44 | static fn(string $file) => \str_ends_with($file, '.proto') && !\str_contains($file, 'google'),
45 | );
46 | }
47 |
48 | private function buildDirs(string $protoDir): string
49 | {
50 | $dirs = \array_filter([
51 | $this->basePath,
52 | $protoDir,
53 | ]);
54 |
55 | if ($dirs === []) {
56 | return '';
57 | }
58 |
59 | return ' -I=' . \implode(' -I=', \array_map('escapeshellarg', $dirs));
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/lib/grpc-shared/src/Attribute/Guarded.php:
--------------------------------------------------------------------------------
1 | []
19 | * }
20 | */
21 | protected array $config = ['services' => [], 'interceptors' => []];
22 |
23 | public function getDefaultCredentials(): ChannelCredentials|null
24 | {
25 | return ChannelCredentials::createInsecure();
26 | }
27 |
28 | public function getInterceptors(): array
29 | {
30 | return $this->config['interceptors'];
31 | }
32 |
33 | /**
34 | * Get service definition.
35 | * @return array{host: string, credentials?: mixed}
36 | */
37 | public function getService(string $name): array
38 | {
39 | return $this->config['services'][$name] ?? [
40 | 'host' => 'localhost',
41 | ];
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/lib/grpc-shared/src/Interceptors/Incoming/ContextInterceptor.php:
--------------------------------------------------------------------------------
1 | container->runScope([
24 | ContextInterface::class => $parameters['ctx'],
25 | ], static fn() => $core->callAction($controller, $action, $parameters));
26 | }
27 | }
--------------------------------------------------------------------------------
/lib/grpc-shared/src/Interceptors/Incoming/ExceptionHandlerInterceptor.php:
--------------------------------------------------------------------------------
1 | callAction($controller, $action, $parameters);
23 |
24 | $statusCode = (int) ($response[1]?->code ?? StatusCode::UNKNOWN);
25 |
26 | if ($statusCode === StatusCode::OK) {
27 | return $response;
28 | }
29 |
30 | Message::initOnce();
31 |
32 | $status = new Status();
33 | $status->mergeFromString($response[1]->metadata['grpc-status-details-bin'][0]);
34 |
35 | // TODO: use exception DTO
36 | match ($response[1]->details) {
37 | 'users.user_not_found' => throw new NotFoundException(),
38 | default => throw new \RuntimeException($status->getMessage()),
39 | };
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/lib/grpc-shared/src/Interceptors/Incoming/OpenTelemetryInterceptor.php:
--------------------------------------------------------------------------------
1 | getTelemetry();
25 |
26 | return $this->tracerFactory
27 | ->make($telemetryContext)
28 | ->trace(
29 | name: \sprintf('Incoming GRPC %s::%s', $controller, $action),
30 | callback: static fn() => $core->callAction($controller, $action, $parameters),
31 | attributes: [
32 | 'controller' => $controller,
33 | 'action' => $action,
34 | ],
35 | scoped: true,
36 | traceKind: TraceKind::SERVER,
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib/grpc-shared/src/Interceptors/Outgoing/SendTraceContextInterceptor.php:
--------------------------------------------------------------------------------
1 | withTelemetryContext($this->tracer->getContext());
26 |
27 | return $this->tracer->trace(
28 | name: \sprintf('GRPC request %s', $action),
29 | callback: static fn() => $core->callAction($controller, $action, $parameters),
30 | attributes: [
31 | 'controller' => $controller,
32 | 'action' => $action,
33 | ],
34 | traceKind: TraceKind::CLIENT,
35 | );
36 | }
37 | }
--------------------------------------------------------------------------------
/lib/temporal-shared/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/
--------------------------------------------------------------------------------
/lib/temporal-shared/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ms/temporal-shared",
3 | "description": "This is shared package",
4 | "keywords": [],
5 | "homepage": "https://github.com/spiral/framework",
6 | "license": "MIT",
7 | "authors": [
8 | {
9 | "name": "butschster",
10 | "email": "butschster@gmail.com",
11 | "role": "Developer"
12 | }
13 | ],
14 | "require": {
15 | "php": "^8.2",
16 | "ext-grpc": "*",
17 | "spiral/boot": "^3.13",
18 | "ramsey/uuid": "^4.7",
19 | "temporal/sdk": "^2.10",
20 | "temporal-php/support": "^1.0"
21 | },
22 | "require-dev": {
23 | "spiral/roadrunner-cli": "^2.5"
24 | },
25 | "autoload": {
26 | "psr-4": {
27 | "Internal\\Shared\\Temporal\\": "src"
28 | }
29 | },
30 | "config": {
31 | "sort-packages": true
32 | },
33 | "minimum-stability": "dev",
34 | "prefer-stable": true
35 | }
36 |
--------------------------------------------------------------------------------
/lib/temporal-shared/src/Activity/NotificationsActivity.php:
--------------------------------------------------------------------------------
1 | addDirectory($dirs->get('root') . 'vendor/ms/temporal-shared/src');
18 | }
19 | }
--------------------------------------------------------------------------------
/lib/temporal-shared/src/OptionsTrait.php:
--------------------------------------------------------------------------------
1 | withTaskQueue($taskQueue)
28 | ->withStartToCloseTimeout($timeout ?? self::TIMEOUT)
29 | ->withRetryOptions(
30 | RetryOptions::new()
31 | ->withMaximumAttempts($retryAttempts ?? self::RETRY_ATTEMPTS)
32 | ->withBackoffCoefficient(2.5),
33 | );
34 | }
35 |
36 | /**
37 | * Must be used for each workflow activity
38 | *
39 | * @param string $taskQueue used task queue from TaskQueue
40 | *
41 | * @return ChildWorkflowOptions
42 | */
43 | private function workflowOptions(
44 | string $taskQueue,
45 | ): ChildWorkflowOptions {
46 | return ChildWorkflowOptions::new()->withTaskQueue($taskQueue);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/lib/temporal-shared/src/TaskQueue.php:
--------------------------------------------------------------------------------
1 | getPrevious()) {
20 | if (\method_exists($e, 'getType') && \in_array($e->getType(), $exception, true)) {
21 | return true;
22 | }
23 | }
24 |
25 | return false;
26 | }
27 |
28 | public static function getApplicationFailure(\Throwable $e): ?ApplicationFailure
29 | {
30 | if (!$e instanceof WorkflowException) {
31 | return null;
32 | }
33 |
34 | while ($e = $e->getPrevious()) {
35 | if ($e instanceof ApplicationFailure) {
36 | return $e;
37 | }
38 | }
39 |
40 | return new ApplicationFailure('Unknown error');
41 | }
42 |
43 | public static function convertToRealException(\Throwable $e): \Throwable
44 | {
45 | $failure = self::getApplicationFailure($e);
46 |
47 | if ($failure === null) {
48 | return $e;
49 | }
50 |
51 | $type = $failure->getType();
52 |
53 | return new $type($failure->getOriginalMessage(), $failure->getCode(), $e);
54 | }
55 | }
--------------------------------------------------------------------------------
/lib/temporal-shared/src/Workflow/KYCWorkflow.php:
--------------------------------------------------------------------------------
1 |
22 | */
23 | #[WorkflowMethod]
24 | public function process(UuidInterface $userUuid, string $verificationId);
25 |
26 | #[SignalMethod]
27 | public function updateStatus(bool $status): void;
28 |
29 | #[QueryMethod]
30 | public function currentStatus(): bool;
31 | }
--------------------------------------------------------------------------------
/lib/temporal-shared/src/Workflow/RegisterUserWorkflow.php:
--------------------------------------------------------------------------------
1 |
29 | */
30 | #[UpdateMethod(name: 'emailVerified')]
31 | public function emailVerified();
32 |
33 | /**
34 | * @return VirtualPromise
35 | */
36 | #[UpdateMethod(name: 'kycVerified')]
37 | public function kycVerified(bool $status);
38 |
39 | /**
40 | * @return VirtualPromise
41 | */
42 | #[UpdateMethod(name: 'subscriptionPaid')]
43 | public function subscriptionPaid(string $transactionId);
44 |
45 | #[UpdateValidatorMethod(forUpdate: 'subscriptionPaid')]
46 | public function validateSubscriptionTransaction(string $transactionId): void;
47 | }
--------------------------------------------------------------------------------
/lib/temporal-shared/src/Workflow/SubscriptionWorkflow.php:
--------------------------------------------------------------------------------
1 | __DIR__],
22 | )->run();
23 |
24 | if ($app === null) {
25 | exit(255);
26 | }
27 |
28 | $code = (int)$app->serve();
29 | exit($code);
30 |
--------------------------------------------------------------------------------
/notifications/app/config/database.php:
--------------------------------------------------------------------------------
1 | [
9 | 'default' => null,
10 | 'drivers' => [
11 | // 'runtime' => 'stdout'
12 | ],
13 | ],
14 |
15 | 'default' => 'default',
16 |
17 | 'databases' => [
18 | 'default' => [
19 | 'driver' => env('DB_CONNECTION', 'pgsql'),
20 | ],
21 | ],
22 |
23 | 'drivers' => [
24 | 'pgsql' => new Config\PostgresDriverConfig(
25 | connection: new Config\Postgres\TcpConnectionConfig(
26 | database: env('DB_DATABASE', 'spiral'),
27 | host: env('DB_HOST', '127.0.0.1'),
28 | port: (int) env('DB_PORT', 5432),
29 | user: env('DB_USERNAME', 'postgres'),
30 | password: env('DB_PASSWORD', ''),
31 | ),
32 | schema: 'public',
33 | queryCache: true,
34 | ),
35 | ],
36 | ];
37 |
--------------------------------------------------------------------------------
/notifications/app/config/migration.php:
--------------------------------------------------------------------------------
1 | directory('app') . 'migrations/',
9 |
10 | 'table' => 'migrations',
11 |
12 | 'strategy' => MultipleFilesStrategy::class,
13 |
14 | 'safe' => env('APP_ENV') === 'local',
15 | ];
16 |
--------------------------------------------------------------------------------
/notifications/app/config/scaffolder.php:
--------------------------------------------------------------------------------
1 | 'App',
15 |
16 | 'declarations' => [
17 | Declaration\BootloaderDeclaration::TYPE => [
18 | 'namespace' => 'Application\\Bootloader',
19 | ],
20 | Declaration\ConfigDeclaration::TYPE => [
21 | 'namespace' => 'Application\\Config',
22 | ],
23 | Declaration\ControllerDeclaration::TYPE => [
24 | 'namespace' => 'Endpoint\\Web',
25 | ],
26 | Declaration\FilterDeclaration::TYPE => [
27 | 'namespace' => 'Endpoint\\Web\\Filter',
28 | ],
29 | Declaration\MiddlewareDeclaration::TYPE => [
30 | 'namespace' => 'Endpoint\\Web\\Middleware',
31 | ],
32 | Declaration\CommandDeclaration::TYPE => [
33 | 'namespace' => 'Endpoint\\Console',
34 | ],
35 | Declaration\JobHandlerDeclaration::TYPE => [
36 | 'namespace' => 'Endpoint\\Job',
37 | ],
38 | ],
39 | ];
40 |
--------------------------------------------------------------------------------
/notifications/app/config/sentry.php:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
17 | tests/Unit
18 |
19 |
20 | tests/Feature
21 |
22 |
23 |
24 |
25 | app/src
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/notifications/psalm.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/notifications/tests/App/TestKernel.php:
--------------------------------------------------------------------------------
1 | beforeBooting(static function (ConfiguratorInterface $config): void {
20 | if (!$config->exists('session')) {
21 | return;
22 | }
23 |
24 | $config->modify('session', new Set('handler', null));
25 | });
26 |
27 | parent::setUp();
28 |
29 | $container = $this->getContainer();
30 |
31 | if ($container->has(TranslatorInterface::class)) {
32 | $container->get(TranslatorInterface::class)->setLocale('en');
33 | }
34 | }
35 |
36 | public function createAppInstance(Container $container = new Container()): TestableKernelInterface
37 | {
38 | return TestKernel::create(
39 | directories: $this->defineDirectories(
40 | $this->rootDirectory(),
41 | ),
42 | container: $container,
43 | );
44 | }
45 |
46 | protected function tearDown(): void
47 | {
48 | // Uncomment this line if you want to clean up runtime directory.
49 | // $this->cleanUpRuntimeDirectory();
50 | }
51 |
52 | public function rootDirectory(): string
53 | {
54 | return __DIR__ . '/..';
55 | }
56 |
57 | public function defineDirectories(string $root): array
58 | {
59 | return [
60 | 'root' => $root,
61 | ];
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/notifications/tests/Unit/DemoTest.php:
--------------------------------------------------------------------------------
1 | assertTrue($expected);
17 | $this->assertFalse($actual);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/proto/auth/v1/message.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package auth.v1.dto;
4 |
5 | option php_namespace = "GRPC\\Services\\Auth\\v1";
6 | option php_metadata_namespace = "GRPC\\ProtobufMetadata\\Auth\\v1";
7 |
8 | import "google/protobuf/timestamp.proto";
9 |
10 | message Token {
11 | string token = 1;
12 | string type = 2; // auth, refresh, 2fa, password_reset
13 | google.protobuf.Timestamp expires_at = 3;
14 | }
--------------------------------------------------------------------------------
/proto/auth/v1/request.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package auth.v1.request;
4 |
5 | option php_namespace = "GRPC\\Services\\Auth\\v1";
6 | option php_metadata_namespace = "GRPC\\ProtobufMetadata\\Auth\\v1";
7 |
8 | message LoginRequest {
9 | string email = 1;
10 | string password = 2;
11 | }
12 |
13 | message MeRequest {
14 | string token = 1;
15 | }
16 |
17 | message LogoutRequest {
18 | string token = 1;
19 | }
20 |
21 | message RegisterRequest {
22 | string email = 1;
23 | string name = 2;
24 | string password = 3;
25 | }
--------------------------------------------------------------------------------
/proto/auth/v1/response.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package auth.v1.response;
4 |
5 | option php_namespace = "GRPC\\Services\\Auth\\v1";
6 | option php_metadata_namespace = "GRPC\\ProtobufMetadata\\Auth\\v1";
7 |
8 | import "auth/v1/message.proto";
9 | import "users/v1/message.proto";
10 |
11 | message LoginResponse {
12 | auth.v1.dto.Token token = 1;
13 | }
14 |
15 | message RegisterResponse {
16 | auth.v1.dto.Token token = 1;
17 | }
18 |
19 | message MeResponse {
20 | users.v1.dto.User user = 1;
21 | }
--------------------------------------------------------------------------------
/proto/auth/v1/service.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package auth.v1;
4 |
5 | option php_namespace = "GRPC\\Services\\Auth\\v1";
6 | option php_metadata_namespace = "GRPC\\ProtobufMetadata\\Auth\\v1";
7 |
8 | import "auth/v1/request.proto";
9 | import "auth/v1/response.proto";
10 | import "common/v1/message.proto";
11 |
12 | service AuthService {
13 | rpc Login (auth.v1.request.LoginRequest) returns (auth.v1.response.LoginResponse) {
14 | }
15 |
16 | rpc Logout (auth.v1.request.LogoutRequest) returns (common.v1.dto.Empty) {
17 | }
18 |
19 | rpc Register (auth.v1.request.RegisterRequest) returns (auth.v1.response.RegisterResponse) {
20 | }
21 |
22 | rpc Me (auth.v1.request.MeRequest) returns (auth.v1.response.MeResponse) {
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/proto/common/v1/message.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package common.v1.dto;
4 |
5 | option php_namespace = "GRPC\\Services\\Common\\v1";
6 | option php_metadata_namespace = "GRPC\\ProtobufMetadata\\Common\\v1";
7 |
8 | message Exception {
9 | string message = 1;
10 | string code = 2;
11 | string class = 3;
12 | }
13 |
14 | message Empty {}
--------------------------------------------------------------------------------
/proto/payment/v1/message.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package payment.v1.dto;
4 |
5 | option php_namespace = "GRPC\\Services\\Payment\\v1";
6 | option php_metadata_namespace = "GRPC\\ProtobufMetadata\\Payment\\v1";
7 |
8 | import "google/protobuf/timestamp.proto";
9 |
10 | message Money {
11 | string currency_code = 1;
12 | int64 amount = 2;
13 | }
14 |
15 | message Payment {
16 | string description = 1;
17 | string email = 2;
18 | Money amount = 3;
19 | string payment_method = 4;
20 | google.protobuf.Timestamp created_at = 5;
21 | }
22 |
23 | message Receipt {
24 | string id = 1;
25 | string transaction_id = 2;
26 | Money amount = 3;
27 | Money tax = 4;
28 | google.protobuf.Timestamp paid_at = 5;
29 | }
--------------------------------------------------------------------------------
/proto/payment/v1/request.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package payment.v1.request;
4 |
5 | option php_namespace = "GRPC\\Services\\Payment\\v1";
6 | option php_metadata_namespace = "GRPC\\ProtobufMetadata\\Payment\\v1";
7 |
8 | import "payment/v1/message.proto";
9 |
10 | message ChargeRequest {
11 | payment.v1.dto.Payment payment = 1;
12 | }
--------------------------------------------------------------------------------
/proto/payment/v1/response.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package payment.v1.response;
4 |
5 | option php_namespace = "GRPC\\Services\\Payment\\v1";
6 | option php_metadata_namespace = "GRPC\\ProtobufMetadata\\Payment\\v1";
7 |
8 | import "payment/v1/message.proto";
9 |
10 | message ChargeResponse {
11 | payment.v1.dto.Receipt receipt = 1;
12 | }
--------------------------------------------------------------------------------
/proto/payment/v1/service.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package payment.v1;
4 |
5 | option php_namespace = "GRPC\\Services\\Payment\\v1";
6 | option php_metadata_namespace = "GRPC\\ProtobufMetadata\\Payment\\v1";
7 |
8 | import "payment/v1/request.proto";
9 | import "payment/v1/response.proto";
10 |
11 | service PaymentService {
12 | rpc Charge(payment.v1.request.ChargeRequest) returns (payment.v1.response.ChargeResponse) {
13 |
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/proto/users/v1/message.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package users.v1.dto;
4 |
5 | option php_namespace = "GRPC\\Services\\Users\\v1";
6 | option php_metadata_namespace = "GRPC\\ProtobufMetadata\\Users\\v1";
7 |
8 | import "google/protobuf/timestamp.proto";
9 |
10 | message User {
11 | string uuid = 1;
12 | string name = 2;
13 | string email = 3;
14 | google.protobuf.Timestamp created_at = 4;
15 | google.protobuf.Timestamp updated_at = 5;
16 | }
--------------------------------------------------------------------------------
/proto/users/v1/request.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package users.v1.request;
4 |
5 | option php_namespace = "GRPC\\Services\\Users\\v1";
6 | option php_metadata_namespace = "GRPC\\ProtobufMetadata\\Users\\v1";
7 |
8 | message GetRequest {
9 | string uuid = 1;
10 | }
11 |
12 | message CreateRequest {
13 | message User {
14 | string name = 1;
15 | string email = 2;
16 | string password = 3;
17 | }
18 |
19 | User user = 1;
20 | }
21 |
22 | message UpdateRequest {
23 | message User {
24 | string uuid = 1;
25 | string name = 2;
26 | string password = 4;
27 | }
28 |
29 | User user = 1;
30 | }
--------------------------------------------------------------------------------
/proto/users/v1/response.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package users.v1.response;
4 |
5 | option php_namespace = "GRPC\\Services\\Users\\v1";
6 | option php_metadata_namespace = "GRPC\\ProtobufMetadata\\Users\\v1";
7 |
8 | import "users/v1/message.proto";
9 |
10 | message GetResponse {
11 | users.v1.dto.User user = 1;
12 | }
13 |
14 | message CreateResponse {
15 | users.v1.dto.User user = 1;
16 | }
17 |
18 | message UpdateResponse {
19 | users.v1.dto.User user = 1;
20 | }
--------------------------------------------------------------------------------
/proto/users/v1/service.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package users.v1;
4 |
5 | option php_namespace = "GRPC\\Services\\Users\\v1";
6 | option php_metadata_namespace = "GRPC\\ProtobufMetadata\\Users\\v1";
7 |
8 | import "users/v1/request.proto";
9 | import "users/v1/response.proto";
10 |
11 | service UsersService {
12 | rpc Get (users.v1.request.GetRequest) returns (users.v1.response.GetResponse) {
13 | }
14 |
15 | rpc Create (users.v1.request.CreateRequest) returns (users.v1.response.CreateResponse) {
16 | }
17 |
18 | rpc Update (users.v1.request.UpdateRequest) returns (users.v1.response.UpdateResponse) {
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/subscriptions/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | indent_style = space
8 | indent_size = 4
9 | trim_trailing_whitespace = true
10 |
11 | [*.yml]
12 | indent_size = 2
13 |
14 | [*.yaml]
15 | indent_size = 2
16 |
--------------------------------------------------------------------------------
/subscriptions/.env.sample:
--------------------------------------------------------------------------------
1 | # Environment (prod or local)
2 | APP_ENV=local
3 |
4 | # Debug mode set to TRUE disables view caching and enables higher verbosity
5 | DEBUG=true
6 |
7 | # Verbosity level
8 | VERBOSITY_LEVEL=verbose # basic, verbose, debug
9 |
10 | # Set to an application specific value, used to encrypt/decrypt cookies etc
11 | ENCRYPTER_KEY={encrypt-key}
12 |
13 | # Monolog
14 | MONOLOG_DEFAULT_CHANNEL=default # Use "roadrunner" channel if you want to use RoadRunner logger
15 | MONOLOG_DEFAULT_LEVEL=DEBUG # DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL, ALERT, EMERGENCY
16 |
17 | # Telemetry
18 | TELEMETRY_DRIVER=null
19 |
20 | # Set to TRUE to disable confirmation in `migrate` commands
21 | SAFE_MIGRATIONS=true
22 |
23 | # Database
24 | DB_CONNECTION=sqlite
25 |
26 | # Cycle Bridge (Don't forget to set `CYCLE_SCHEMA_CACHE` to `true` in production)
27 | CYCLE_SCHEMA_CACHE=false
28 | CYCLE_SCHEMA_WARMUP=false
29 |
--------------------------------------------------------------------------------
/subscriptions/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | vendor
3 | runtime
4 | rr*
5 | protoc-gen-php-grpc*
6 | .env
7 | .phpunit.result.cache
8 | .phpunit.cache
9 | .deptrac.cache
10 | .phpunit.cache/
11 |
--------------------------------------------------------------------------------
/subscriptions/.rr-prod.yaml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | rpc:
4 | listen: 'tcp://127.0.0.1:6001'
5 |
6 | server:
7 | command: 'php app.php'
8 | relay: pipes
9 |
10 | logs:
11 | level: ${RR_LOG_LEVEL:-warn}
12 |
13 | temporal:
14 | address: 'temporal:7233'
15 |
--------------------------------------------------------------------------------
/subscriptions/.rr.yaml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | rpc:
4 | listen: 'tcp://127.0.0.1:6001'
5 |
6 | server:
7 | command: 'php app.php'
8 | relay: pipes
9 |
10 | logs:
11 | level: ${RR_LOG_LEVEL:-debug}
12 |
13 | temporal:
14 | address: 'temporal:7233'
15 |
--------------------------------------------------------------------------------
/subscriptions/.styleci.yml:
--------------------------------------------------------------------------------
1 | risky: false
2 | preset: psr12
3 | enabled:
4 | # Risky Fixers
5 | # - declare_strict_types
6 | # - void_return
7 | - ordered_class_elements
8 | - linebreak_after_opening_tag
9 | - single_quote
10 | - no_blank_lines_after_phpdoc
11 | - unary_operator_spaces
12 | - no_useless_else
13 | - no_useless_return
14 | - trailing_comma_in_multiline_array
15 | finder:
16 | exclude:
17 | - "Tests"
18 | - "installer/Application/Common/resources/tests"
19 | - "installer/Module/RoadRunnerBridge/resources/config"
20 |
--------------------------------------------------------------------------------
/subscriptions/app.php:
--------------------------------------------------------------------------------
1 | __DIR__],
22 | )->run();
23 |
24 | if ($app === null) {
25 | exit(255);
26 | }
27 |
28 | $code = (int)$app->serve();
29 | exit($code);
30 |
--------------------------------------------------------------------------------
/subscriptions/app/config/database.php:
--------------------------------------------------------------------------------
1 | [
9 | 'default' => null,
10 | 'drivers' => [
11 | // 'runtime' => 'stdout'
12 | ],
13 | ],
14 |
15 | 'default' => 'default',
16 |
17 | 'databases' => [
18 | 'default' => [
19 | 'driver' => env('DB_CONNECTION', 'pgsql'),
20 | ],
21 | ],
22 |
23 | 'drivers' => [
24 | 'pgsql' => new Config\PostgresDriverConfig(
25 | connection: new Config\Postgres\TcpConnectionConfig(
26 | database: env('DB_DATABASE', 'spiral'),
27 | host: env('DB_HOST', '127.0.0.1'),
28 | port: (int) env('DB_PORT', 5432),
29 | user: env('DB_USERNAME', 'postgres'),
30 | password: env('DB_PASSWORD', ''),
31 | ),
32 | schema: 'public',
33 | queryCache: true,
34 | ),
35 | ],
36 | ];
37 |
--------------------------------------------------------------------------------
/subscriptions/app/config/migration.php:
--------------------------------------------------------------------------------
1 | directory('app') . 'migrations/',
9 |
10 | 'table' => 'migrations',
11 |
12 | 'strategy' => MultipleFilesStrategy::class,
13 |
14 | 'safe' => env('APP_ENV') === 'local',
15 | ];
16 |
--------------------------------------------------------------------------------
/subscriptions/app/config/scaffolder.php:
--------------------------------------------------------------------------------
1 | 'App',
15 |
16 | 'declarations' => [
17 | Declaration\BootloaderDeclaration::TYPE => [
18 | 'namespace' => 'Application\\Bootloader',
19 | ],
20 | Declaration\ConfigDeclaration::TYPE => [
21 | 'namespace' => 'Application\\Config',
22 | ],
23 | Declaration\ControllerDeclaration::TYPE => [
24 | 'namespace' => 'Endpoint\\Web',
25 | ],
26 | Declaration\FilterDeclaration::TYPE => [
27 | 'namespace' => 'Endpoint\\Web\\Filter',
28 | ],
29 | Declaration\MiddlewareDeclaration::TYPE => [
30 | 'namespace' => 'Endpoint\\Web\\Middleware',
31 | ],
32 | Declaration\CommandDeclaration::TYPE => [
33 | 'namespace' => 'Endpoint\\Console',
34 | ],
35 | Declaration\JobHandlerDeclaration::TYPE => [
36 | 'namespace' => 'Endpoint\\Job',
37 | ],
38 | ],
39 | ];
40 |
--------------------------------------------------------------------------------
/subscriptions/app/config/sentry.php:
--------------------------------------------------------------------------------
1 | table('subscriptions')
16 | ->addColumn('created_at', 'datetime', [
17 | 'nullable' => false,
18 | 'defaultValue' => 'CURRENT_TIMESTAMP',
19 | 'withTimezone' => false,
20 | ])
21 | ->addColumn('updated_at', 'datetime', ['nullable' => false, 'defaultValue' => null, 'withTimezone' => false],
22 | )
23 | ->addColumn('uuid', 'uuid', ['nullable' => false, 'defaultValue' => null])
24 | ->addColumn('name', 'string', ['nullable' => false, 'defaultValue' => null, 'unique' => true, 'size' => 255],
25 | )
26 | ->addColumn('trial_days', 'integer', ['nullable' => false, 'defaultValue' => null])
27 | ->addColumn('price', 'double', ['nullable' => false, 'defaultValue' => null])
28 | ->setPrimaryKeys(['uuid'])
29 | ->create();
30 | }
31 |
32 | public function down(): void
33 | {
34 | $this->table('subscriptions')->drop();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/subscriptions/app/src/Application/Assert.php:
--------------------------------------------------------------------------------
1 | createdAt = $now;
39 | $this->updatedAt = $now;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/subscriptions/app/src/Domain/Subscription/SubscriptionRepositoryInterface.php:
--------------------------------------------------------------------------------
1 | uuid;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/subscriptions/app/src/Endpoint/Console/SeedSubscriptionsCommand.php:
--------------------------------------------------------------------------------
1 | persist(
23 | new Subscription(
24 | uuid: Uuid::generate(),
25 | name: 'Basic',
26 | price: 5.0,
27 | trialDays: 7,
28 | ),
29 | )
30 | ->persist(
31 | new Subscription(
32 | uuid: Uuid::generate(),
33 | name: 'Pro',
34 | price: 20.0,
35 | trialDays: 0,
36 | ),
37 | )
38 | ->run();
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/subscriptions/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
17 | tests/Unit
18 |
19 |
20 | tests/Feature
21 |
22 |
23 |
24 |
25 | app/src
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/subscriptions/psalm.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/subscriptions/tests/App/TestKernel.php:
--------------------------------------------------------------------------------
1 | beforeBooting(static function (ConfiguratorInterface $config): void {
20 | if (!$config->exists('session')) {
21 | return;
22 | }
23 |
24 | $config->modify('session', new Set('handler', null));
25 | });
26 |
27 | parent::setUp();
28 |
29 | $container = $this->getContainer();
30 |
31 | if ($container->has(TranslatorInterface::class)) {
32 | $container->get(TranslatorInterface::class)->setLocale('en');
33 | }
34 | }
35 |
36 | public function createAppInstance(Container $container = new Container()): TestableKernelInterface
37 | {
38 | return TestKernel::create(
39 | directories: $this->defineDirectories(
40 | $this->rootDirectory(),
41 | ),
42 | container: $container,
43 | );
44 | }
45 |
46 | protected function tearDown(): void
47 | {
48 | // Uncomment this line if you want to clean up runtime directory.
49 | // $this->cleanUpRuntimeDirectory();
50 | }
51 |
52 | public function rootDirectory(): string
53 | {
54 | return __DIR__ . '/..';
55 | }
56 |
57 | public function defineDirectories(string $root): array
58 | {
59 | return [
60 | 'root' => $root,
61 | ];
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/subscriptions/tests/Unit/DemoTest.php:
--------------------------------------------------------------------------------
1 | assertTrue($expected);
17 | $this->assertFalse($actual);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/users/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | indent_style = space
8 | indent_size = 4
9 | trim_trailing_whitespace = true
10 |
11 | [*.yml]
12 | indent_size = 2
13 |
14 | [*.yaml]
15 | indent_size = 2
16 |
--------------------------------------------------------------------------------
/users/.env.sample:
--------------------------------------------------------------------------------
1 | # Environment (prod or local)
2 | APP_ENV=local
3 |
4 | # Debug mode set to TRUE disables view caching and enables higher verbosity
5 | DEBUG=true
6 |
7 | # Verbosity level
8 | VERBOSITY_LEVEL=verbose # basic, verbose, debug
9 |
10 | # Set to an application specific value, used to encrypt/decrypt cookies etc
11 | ENCRYPTER_KEY={encrypt-key}
12 |
13 | # Monolog
14 | MONOLOG_DEFAULT_CHANNEL=default # Use "roadrunner" channel if you want to use RoadRunner logger
15 | MONOLOG_DEFAULT_LEVEL=DEBUG # DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL, ALERT, EMERGENCY
16 |
17 | # Telemetry
18 | TELEMETRY_DRIVER=null
19 |
20 | # Set to TRUE to disable confirmation in `migrate` commands
21 | SAFE_MIGRATIONS=true
22 |
23 | # Database
24 | DB_CONNECTION=sqlite
25 |
26 | # Cycle Bridge (Don't forget to set `CYCLE_SCHEMA_CACHE` to `true` in production)
27 | CYCLE_SCHEMA_CACHE=false
28 | CYCLE_SCHEMA_WARMUP=false
29 |
--------------------------------------------------------------------------------
/users/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | vendor
3 | runtime
4 | rr*
5 | protoc-gen-php-grpc*
6 | .env
7 | .phpunit.result.cache
8 | .phpunit.cache
9 | .deptrac.cache
10 | .phpunit.cache/
11 |
--------------------------------------------------------------------------------
/users/.rr-prod.yaml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | rpc:
4 | listen: 'tcp://127.0.0.1:6001'
5 |
6 | include:
7 | - ./.rr/.rr.grpc.yaml
8 | - ./.rr/.rr.otel.yaml
9 |
10 | server:
11 | command: 'php app.php'
12 | relay: pipes
13 |
14 | logs:
15 | level: ${RR_LOG_LEVEL:-debug}
16 |
17 | temporal:
18 | address: 'temporal:7233'
19 |
--------------------------------------------------------------------------------
/users/.rr.otel.yaml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | otel:
4 | insecure: true
5 | compress: false
6 | client: http
7 | exporter: otlp
8 | service_name: ${OTEL_SERVICE_NAME:-php-user}
9 | service_version: ${APP_VERSION:-1.0.0}
10 | endpoint: ms-collector:4318
11 |
--------------------------------------------------------------------------------
/users/.rr.yaml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | rpc:
4 | listen: 'tcp://127.0.0.1:6001'
5 |
6 | include:
7 | - ./.rr/.rr.grpc.yaml
8 |
9 | server:
10 | command: 'php app.php'
11 | relay: pipes
12 |
--------------------------------------------------------------------------------
/users/.rr/.rr.grpc.yaml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | grpc:
4 | listen: 'tcp://0.0.0.0:9001'
5 | proto:
6 | - ../../proto/users/v1/service.proto
7 |
--------------------------------------------------------------------------------
/users/.rr/.rr.otel.yaml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | #otel:
4 | # insecure: true
5 | # compress: false
6 | # client: http
7 | # exporter: otlp
8 | # service_name: ${OTEL_SERVICE_NAME:-php-user}
9 | # service_version: ${APP_VERSION:-1.0.0}
10 | # endpoint: ms-collector:4318
11 |
--------------------------------------------------------------------------------
/users/.styleci.yml:
--------------------------------------------------------------------------------
1 | risky: false
2 | preset: psr12
3 | enabled:
4 | # Risky Fixers
5 | # - declare_strict_types
6 | # - void_return
7 | - ordered_class_elements
8 | - linebreak_after_opening_tag
9 | - single_quote
10 | - no_blank_lines_after_phpdoc
11 | - unary_operator_spaces
12 | - no_useless_else
13 | - no_useless_return
14 | - trailing_comma_in_multiline_array
15 | finder:
16 | exclude:
17 | - "Tests"
18 | - "installer/Application/Common/resources/tests"
19 | - "installer/Module/RoadRunnerBridge/resources/config"
20 |
--------------------------------------------------------------------------------
/users/app.php:
--------------------------------------------------------------------------------
1 | __DIR__],
22 | )->run();
23 |
24 | if ($app === null) {
25 | exit(255);
26 | }
27 |
28 | $code = (int)$app->serve();
29 | exit($code);
30 |
--------------------------------------------------------------------------------
/users/app/config/database.php:
--------------------------------------------------------------------------------
1 | [
9 | 'default' => null,
10 | 'drivers' => [
11 | // 'runtime' => 'stdout'
12 | ],
13 | ],
14 |
15 | 'default' => 'default',
16 |
17 | 'databases' => [
18 | 'default' => [
19 | 'driver' => env('DB_CONNECTION', 'pgsql'),
20 | ],
21 | ],
22 |
23 | 'drivers' => [
24 | 'pgsql' => new Config\PostgresDriverConfig(
25 | connection: new Config\Postgres\TcpConnectionConfig(
26 | database: env('DB_DATABASE', 'spiral'),
27 | host: env('DB_HOST', '127.0.0.1'),
28 | port: (int) env('DB_PORT', 5432),
29 | user: env('DB_USERNAME', 'postgres'),
30 | password: env('DB_PASSWORD', ''),
31 | ),
32 | schema: 'public',
33 | queryCache: true,
34 | ),
35 | ],
36 | ];
37 |
--------------------------------------------------------------------------------
/users/app/config/grpc.php:
--------------------------------------------------------------------------------
1 | directory('root') . 'protoc-gen-php-grpc',
7 | 'generatedPath' => directory('root') . '/generated',
8 | 'namespace' => 'GRPC',
9 | 'services' => [
10 | directory('root') . '/../proto/users/v1/service.proto',
11 | directory('root') . '/../proto/auth/v1/service.proto',
12 | directory('root') . '/../proto/payment/v1/service.proto',
13 | directory('root') . '/../proto/common/v1/message.proto',
14 | ],
15 | 'servicesBasePath' => directory('root') . '/../proto',
16 | 'interceptors' => [
17 | \App\Endpoint\GRPC\Interceptor\HandleExceptionsInterceptor::class,
18 | \Internal\Shared\gRPC\Interceptors\Incoming\ContextInterceptor::class,
19 | //\App\Endpoint\GRPC\Interceptor\GuardInterceptor::class,
20 | //\Internal\Shared\gRPC\Interceptors\Incoming\OpenTelemetryInterceptor::class,
21 | ],
22 | ];
23 |
--------------------------------------------------------------------------------
/users/app/config/grpcServices.php:
--------------------------------------------------------------------------------
1 | [
7 | \Internal\Shared\gRPC\Interceptors\Outgoing\SendTraceContextInterceptor::class,
8 | ],
9 | ];
10 |
--------------------------------------------------------------------------------
/users/app/config/migration.php:
--------------------------------------------------------------------------------
1 | directory('app') . 'migrations/',
10 |
11 | 'table' => 'migrations',
12 |
13 | 'strategy' => MultipleFilesStrategy::class,
14 |
15 | 'safe' => env('APP_ENV') === 'local',
16 | ];
17 |
--------------------------------------------------------------------------------
/users/app/config/scaffolder.php:
--------------------------------------------------------------------------------
1 | 'App',
15 |
16 | 'declarations' => [
17 | Declaration\BootloaderDeclaration::TYPE => [
18 | 'namespace' => 'Application\\Bootloader',
19 | ],
20 | Declaration\ConfigDeclaration::TYPE => [
21 | 'namespace' => 'Application\\Config',
22 | ],
23 | Declaration\ControllerDeclaration::TYPE => [
24 | 'namespace' => 'Endpoint\\Web',
25 | ],
26 | Declaration\FilterDeclaration::TYPE => [
27 | 'namespace' => 'Endpoint\\Web\\Filter',
28 | ],
29 | Declaration\MiddlewareDeclaration::TYPE => [
30 | 'namespace' => 'Endpoint\\Web\\Middleware',
31 | ],
32 | Declaration\CommandDeclaration::TYPE => [
33 | 'namespace' => 'Endpoint\\Console',
34 | ],
35 | Declaration\JobHandlerDeclaration::TYPE => [
36 | 'namespace' => 'Endpoint\\Job',
37 | ],
38 | ],
39 | ];
40 |
--------------------------------------------------------------------------------
/users/app/config/sentry.php:
--------------------------------------------------------------------------------
1 | table('users')
16 | ->addColumn('uuid', 'uuid', ['nullable' => false, 'defaultValue' => null])
17 | ->addColumn(
18 | 'email',
19 | 'string',
20 | ['nullable' => false, 'defaultValue' => null, 'unique' => true, 'size' => 255],
21 | )
22 | ->addColumn('password', 'string', ['nullable' => false, 'defaultValue' => null, 'size' => 64])
23 | ->setPrimaryKeys(['uuid'])
24 | ->create();
25 | }
26 |
27 | public function down(): void
28 | {
29 | $this->table('users')->drop();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/users/app/migrations/20240529.183200_0_0_default_change_users_add_fist_name_add_last_name_add_language.php:
--------------------------------------------------------------------------------
1 | table('users')
16 | ->addColumn('fist_name', 'string', ['nullable' => true, 'defaultValue' => null, 'size' => 255])
17 | ->addColumn('last_name', 'string', ['nullable' => true, 'defaultValue' => null, 'size' => 255])
18 | ->addColumn('language', 'string', ['nullable' => false, 'defaultValue' => 'ru', 'size' => 255])
19 | ->update();
20 | }
21 |
22 | public function down(): void
23 | {
24 | $this->table('users')
25 | ->dropColumn('fist_name')
26 | ->dropColumn('last_name')
27 | ->dropColumn('language')
28 | ->update();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/users/app/migrations/20240529.191748_0_0_default_change_users_add_created_at_add_updated_at.php:
--------------------------------------------------------------------------------
1 | table('users')
16 | ->addColumn('created_at', 'datetime', [
17 | 'nullable' => false,
18 | 'defaultValue' => 'CURRENT_TIMESTAMP',
19 | 'withTimezone' => false,
20 | ])
21 | ->addColumn(
22 | 'updated_at',
23 | 'datetime',
24 | ['nullable' => true, 'defaultValue' => null, 'withTimezone' => false],
25 | )
26 | ->update();
27 | }
28 |
29 | public function down(): void
30 | {
31 | $this->table('users')
32 | ->dropColumn('created_at')
33 | ->dropColumn('updated_at')
34 | ->update();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/users/app/migrations/20240604.183640_0_0_default_create_auth_tokens.php:
--------------------------------------------------------------------------------
1 | table('auth_tokens')
16 | ->addColumn('id', 'string', ['nullable' => false, 'defaultValue' => null, 'size' => 64])
17 | ->addColumn('hashed_value', 'string', ['nullable' => false, 'defaultValue' => null, 'size' => 128])
18 | ->addColumn('created_at', 'datetime', ['nullable' => false, 'defaultValue' => null, 'withTimezone' => false])
19 | ->addColumn('expires_at', 'datetime', ['nullable' => true, 'defaultValue' => null, 'withTimezone' => false])
20 | ->addColumn('payload', 'binary', ['nullable' => false, 'defaultValue' => null])
21 | ->setPrimaryKeys(['id'])
22 | ->create();
23 | }
24 |
25 | public function down(): void
26 | {
27 | $this->table('auth_tokens')->drop();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/users/app/migrations/20240708.150058_0_0_default_change_users_add_email_verified_at_add_kyc_verified_at_add_subscription_uuid_alter_updated_at.php:
--------------------------------------------------------------------------------
1 | table('users')
16 | ->addColumn(
17 | 'email_verified_at',
18 | 'datetime',
19 | ['nullable' => true, 'defaultValue' => null, 'withTimezone' => false],
20 | )
21 | ->addColumn(
22 | 'kyc_verified_at',
23 | 'datetime',
24 | ['nullable' => true, 'defaultValue' => null, 'withTimezone' => false],
25 | )
26 | ->addColumn('subscription_uuid', 'uuid', ['nullable' => true, 'defaultValue' => null])
27 | ->alterColumn(
28 | 'updated_at',
29 | 'datetime',
30 | ['nullable' => false, 'defaultValue' => null, 'withTimezone' => false],
31 | )
32 | ->update();
33 | }
34 |
35 | public function down(): void
36 | {
37 | $this->table('users')
38 | ->alterColumn(
39 | 'updated_at',
40 | 'timestamp',
41 | ['nullable' => true, 'defaultValue' => null, 'withTimezone' => false],
42 | )
43 | ->dropColumn('email_verified_at')
44 | ->dropColumn('kyc_verified_at')
45 | ->dropColumn('subscription_uuid')
46 | ->update();
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/users/app/src/Application/Assert.php:
--------------------------------------------------------------------------------
1 | bind(AuthServiceInterface::class, AuthService::class);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/users/app/src/Application/Bootloader/AuthBootloader.php:
--------------------------------------------------------------------------------
1 | OrmActorProvider::class
17 | ];
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/users/app/src/Application/Bootloader/PersistenceBootloader.php:
--------------------------------------------------------------------------------
1 | static fn(
24 | ORMInterface $orm,
25 | ): UserRepositoryInterface => new UserRepository(new Select($orm, User::ROLE)),
26 |
27 | UserFactoryInterface::class => UserFactory::class,
28 |
29 | UniqueEmailSpecificationInterface::class => UniqueEmailSpecification::class,
30 | ];
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/users/app/src/Application/Bootloader/UserBootloader.php:
--------------------------------------------------------------------------------
1 | PasswordHasher::class,
19 | UserServiceInterface::class => UserService::class,
20 | ];
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/users/app/src/Application/Exception/AuthException.php:
--------------------------------------------------------------------------------
1 | getPayload()['userId'] ?? null;
21 |
22 | if ($userId === null) {
23 | return null;
24 | }
25 |
26 | return $this->users->getByUuid(Uuid::fromString($userId));
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/users/app/src/Domain/User/PasswordHasherInterface.php:
--------------------------------------------------------------------------------
1 | fistName === null || $this->lastName === null) {
25 | return null;
26 | }
27 |
28 | return \trim($this->fistName . ' ' . $this->lastName);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/users/app/src/Domain/User/Specification/UniqueEmailSpecificationInterface.php:
--------------------------------------------------------------------------------
1 | createdAt = $now;
51 | $this->updatedAt = $now;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/users/app/src/Domain/User/UserFactoryInterface.php:
--------------------------------------------------------------------------------
1 | email;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/users/app/src/Domain/User/ValueObject/Password.php:
--------------------------------------------------------------------------------
1 | password;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/users/app/src/Domain/User/ValueObject/PasswordHash.php:
--------------------------------------------------------------------------------
1 | hash;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/users/app/src/Domain/User/ValueObject/Uuid.php:
--------------------------------------------------------------------------------
1 | uuid;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/users/app/src/Endpoint/Console/RegisterUserCommand.php:
--------------------------------------------------------------------------------
1 | newWorkflowStub(
37 | class: RegisterUserWorkflow::class, // user.register.workflow
38 | options: WorkflowOptions::new()
39 | ->withTaskQueue(TaskQueue::UserRegistration)
40 | ->withWorkflowId((string) $userUuid)
41 | ->withWorkflowIdReusePolicy(IdReusePolicy::RejectDuplicate),
42 | );
43 |
44 | // try {
45 | $result = $workflows->start($wf, new RegisterRequest([
46 | 'email' => $this->email,
47 | 'password' => $this->password,
48 | 'name' => 'John Doe',
49 | ]))->getResult(User::class);
50 | // } catch (\Throwable $e) {
51 | // dump(ExceptionHelper::convertToRealException($e));
52 | // return;
53 | // }
54 |
55 | $runWf = $workflows->newRunningWorkflowStub(RegisterUserWorkflow::class, (string) $userUuid);
56 | $runWf->subscriptionPaid('transaction-id-1234');
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/users/app/src/Endpoint/GRPC/Exception/UnauthorizedException.php:
--------------------------------------------------------------------------------
1 | callAction($controller, $action, $parameters);
28 | } catch (\Throwable $e) {
29 | $this->reporter->report($e);
30 | throw new GRPCException(
31 | message: $e->getMessage(),
32 | code: StatusCode::NOT_FOUND,
33 | details: [
34 | new Exception([
35 | 'message' => $e->getMessage(),
36 | 'code' => $e->getCode(),
37 | 'class' => $e::class,
38 | ]),
39 | ],
40 | previous: $e,
41 | );
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/users/app/src/Endpoint/GRPC/Mapper/TimestampMapper.php:
--------------------------------------------------------------------------------
1 | fromDateTime(\DateTime::createFromInterface($dateTime));
17 |
18 | return $timestamp;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/users/app/src/Endpoint/GRPC/Mapper/UserService/UserMapper.php:
--------------------------------------------------------------------------------
1 | (string) $user->uuid,
20 | 'email' => (string) $user->email,
21 | 'name' => (string) $user->profile->fullName(),
22 | 'created_at' => $this->timestamps->toMessage($user->createdAt),
23 | // 'updated_at' => $this->timestamps->toMessage($user->updatedAt),
24 | ];
25 |
26 | $updatedAt = $this->timestamps->toMessage($user->updatedAt);
27 |
28 | if ($updatedAt !== null) {
29 | $data['updated_at'] = $updatedAt;
30 | }
31 |
32 | return new \GRPC\Services\Users\v1\User($data);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/users/app/src/Endpoint/GRPC/Service/UserService.php:
--------------------------------------------------------------------------------
1 | usersService->getUser(Uuid::fromString($in->getUuid()));
34 |
35 | return new GetResponse();
36 | }
37 |
38 | #[Guarded]
39 | public function Create(GRPC\ContextInterface $ctx, CreateRequest $in): CreateResponse
40 | {
41 | // TODO: Implement Create() method.
42 | }
43 |
44 | #[Guarded]
45 | public function Update(GRPC\ContextInterface $ctx, UpdateRequest $in): UpdateResponse
46 | {
47 | // TODO: Implement Update() method.
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/users/app/src/Endpoint/Temporal/User/EmailVerificationWorkflow.php:
--------------------------------------------------------------------------------
1 | notifications = Workflow::newActivityStub(
25 | NotificationsActivity::class,
26 | $this->activityOptions(
27 | taskQueue: TaskQueue::Notifications,
28 | timeout: CarbonInterval::minute(),
29 | retryAttempts: 3,
30 | ),
31 | );
32 | }
33 |
34 | public function updateStatus(bool $status): void
35 | {
36 | $this->isVerified = $status;
37 | }
38 |
39 | public function currentStatus(): bool
40 | {
41 | return $this->isVerified;
42 | }
43 |
44 | public function process(UuidInterface $userUuid)
45 | {
46 | // 1. Send email verification link
47 | yield $this->notifications->sendVerificationLink($userUuid);
48 |
49 | // 2. Wait for verification
50 | yield Workflow::await(fn() => $this->isVerified);
51 |
52 | // 3. Send welcome email
53 | yield $this->notifications->emailVerified($userUuid);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/users/app/src/Endpoint/Temporal/User/RegisterWorkflow.php:
--------------------------------------------------------------------------------
1 | users = Workflow::newActivityStub(
30 | UserServiceActivity::class,
31 | $this->activityOptions(
32 | taskQueue: 'user.service',
33 | timeout: CarbonInterval::minute(),
34 | retryAttempts: 1,
35 | ),
36 | );
37 |
38 | $this->notifications = Workflow::newActivityStub(
39 | NotificationsActivity::class,
40 | $this->activityOptions(
41 | taskQueue: 'notifications.service',
42 | timeout: CarbonInterval::minutes(5),
43 | retryAttempts: 3,
44 | ),
45 | );
46 | }
47 |
48 | #[WorkflowMethod(name: 'RegisterUser')]
49 | public function register(RegisterRequest $request)
50 | {
51 | // 1. Create user in the database
52 | /** @var User $user */
53 | // user.service.register
54 | $user = yield $this->users->register($request);
55 |
56 | // 2. Create auth token
57 | // $token = yield $this->auth->createToken($user->uuid);
58 |
59 | // 3. Create user notification settings
60 | // yield $this->notifications->createSettings($user);
61 |
62 | // 2. Send email to the user
63 | // yield $this->notifications->sendWelcomeEmail($user);
64 |
65 | return $user;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/users/app/src/Endpoint/Temporal/User/UserServiceActivity.php:
--------------------------------------------------------------------------------
1 | service->register(
30 | Email::create($request->getEmail()),
31 | Password::create($request->getPassword()),
32 | );
33 |
34 | $createdAt = new Timestamp();
35 | $createdAt->fromDateTime(\DateTime::createFromInterface($user->createdAt));
36 |
37 | $updatedAt = new Timestamp();
38 | if ($user->updatedAt !== null) {
39 | $updatedAt->fromDateTime(\DateTime::createFromInterface($user->updatedAt));
40 | }
41 |
42 | return new User([
43 | 'uuid' => (string) $user->uuid,
44 | 'name' => (string) $user->profile->fullName(),
45 | 'email' => (string) $user->email,
46 | 'created_at' => $createdAt,
47 | 'updated_at' => $updatedAt,
48 | ]);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/users/app/src/Infrastructure/CycleOrm/Factory/UserFactory.php:
--------------------------------------------------------------------------------
1 | orm->make(User::class, [
25 | 'uuid' => Uuid::generate(),
26 | 'email' => $email,
27 | 'password' => $this->passwords->hash($password),
28 | ]);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/users/app/src/Infrastructure/CycleOrm/Repository/UserRepository.php:
--------------------------------------------------------------------------------
1 | select()->where('email', $email)->fetchOne();
19 | }
20 |
21 | public function findByUuid(Uuid $uuid): ?User
22 | {
23 | return $this->select()->where('uuid', $uuid)->fetchOne();
24 | }
25 |
26 | public function getByEmail(Email $email): User
27 | {
28 | return $this->findByEmail($email) ?? throw new UserNotFoundException();
29 | }
30 |
31 | public function getByUuid(Uuid $uuid): User
32 | {
33 | return $this->findByUuid($uuid) ?? throw new UserNotFoundException();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/users/app/src/Infrastructure/CycleOrm/Specification/UniqueEmailSpecification.php:
--------------------------------------------------------------------------------
1 | users->findByEmail($email) === null;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/users/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
17 | tests/Unit
18 |
19 |
20 | tests/Feature
21 |
22 |
23 |
24 |
25 | app/src
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/users/psalm.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/users/tests/App/TestKernel.php:
--------------------------------------------------------------------------------
1 | beforeBooting(static function (ConfiguratorInterface $config): void {
20 | if (!$config->exists('session')) {
21 | return;
22 | }
23 |
24 | $config->modify('session', new Set('handler', null));
25 | });
26 |
27 | parent::setUp();
28 |
29 | $container = $this->getContainer();
30 |
31 | if ($container->has(TranslatorInterface::class)) {
32 | $container->get(TranslatorInterface::class)->setLocale('en');
33 | }
34 | }
35 |
36 | public function createAppInstance(Container $container = new Container()): TestableKernelInterface
37 | {
38 | return TestKernel::create(
39 | directories: $this->defineDirectories(
40 | $this->rootDirectory(),
41 | ),
42 | container: $container,
43 | );
44 | }
45 |
46 | protected function tearDown(): void
47 | {
48 | // Uncomment this line if you want to clean up runtime directory.
49 | // $this->cleanUpRuntimeDirectory();
50 | }
51 |
52 | public function rootDirectory(): string
53 | {
54 | return __DIR__ . '/..';
55 | }
56 |
57 | public function defineDirectories(string $root): array
58 | {
59 | return [
60 | 'root' => $root,
61 | ];
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/users/tests/Unit/DemoTest.php:
--------------------------------------------------------------------------------
1 | assertTrue($expected);
17 | $this->assertFalse($actual);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/web/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | indent_style = space
8 | indent_size = 4
9 | trim_trailing_whitespace = true
10 |
11 | [*.yml]
12 | indent_size = 2
13 |
14 | [*.yaml]
15 | indent_size = 2
16 |
--------------------------------------------------------------------------------
/web/.env.sample:
--------------------------------------------------------------------------------
1 | # Environment (prod or local)
2 | APP_ENV=local
3 |
4 | # Debug mode set to TRUE disables view caching and enables higher verbosity
5 | DEBUG=true
6 |
7 | # Verbosity level
8 | VERBOSITY_LEVEL=verbose # basic, verbose, debug
9 |
10 | # Set to an application specific value, used to encrypt/decrypt cookies etc
11 | ENCRYPTER_KEY={encrypt-key}
12 |
13 | # Monolog
14 | MONOLOG_DEFAULT_CHANNEL=default # Use "roadrunner" channel if you want to use RoadRunner logger
15 | MONOLOG_DEFAULT_LEVEL=DEBUG # DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL, ALERT, EMERGENCY
16 |
17 | # Telemetry
18 | TELEMETRY_DRIVER=null
19 |
20 | # Session
21 | SESSION_LIFETIME=86400
22 | SESSION_COOKIE=sid
23 |
24 | # Authorization
25 | AUTH_TOKEN_TRANSPORT=cookie
26 | AUTH_TOKEN_STORAGE=session
27 |
--------------------------------------------------------------------------------
/web/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | vendor
3 | runtime
4 | rr*
5 | protoc-gen-php-grpc*
6 | .env
7 | .phpunit.result.cache
8 | .phpunit.cache
9 | .deptrac.cache
10 | .phpunit.cache/
11 |
--------------------------------------------------------------------------------
/web/.rr-prod.yaml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | rpc:
4 | listen: 'tcp://127.0.0.1:6001'
5 |
6 | http:
7 | address: '0.0.0.0:8080'
8 | middleware:
9 | - gzip
10 | - static
11 | static:
12 | dir: public
13 | forbid:
14 | - .php
15 | - .htaccess
16 | pool:
17 | num_workers: 1
18 | supervisor:
19 | max_worker_memory: 100
20 |
21 | server:
22 | command: 'php app.php'
23 | relay: pipes
24 |
25 | logs:
26 | level: ${RR_LOG_LEVEL:-debug}
27 |
28 | otel:
29 | insecure: true
30 | compress: false
31 | client: http
32 | exporter: otlp
33 | service_name: ${OTEL_SERVICE_NAME:-php-web}
34 | service_version: ${APP_VERSION:-1.0.0}
35 | endpoint: ms-collector:4318
36 |
--------------------------------------------------------------------------------
/web/.rr.yaml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | rpc:
4 | listen: 'tcp://127.0.0.1:6001'
5 |
6 | http:
7 | address: '0.0.0.0:8080'
8 | middleware:
9 | - gzip
10 | - static
11 | static:
12 | dir: public
13 | forbid:
14 | - .php
15 | - .htaccess
16 | pool:
17 | num_workers: 1
18 | debug: true
19 | supervisor:
20 | max_worker_memory: 100
21 |
22 | server:
23 | command: 'php app.php'
24 | relay: pipes
25 |
--------------------------------------------------------------------------------
/web/.styleci.yml:
--------------------------------------------------------------------------------
1 | risky: false
2 | preset: psr12
3 | enabled:
4 | # Risky Fixers
5 | # - declare_strict_types
6 | # - void_return
7 | - ordered_class_elements
8 | - linebreak_after_opening_tag
9 | - single_quote
10 | - no_blank_lines_after_phpdoc
11 | - unary_operator_spaces
12 | - no_useless_else
13 | - no_useless_return
14 | - trailing_comma_in_multiline_array
15 | finder:
16 | exclude:
17 | - "Tests"
18 | - "installer/Application/Common/resources/tests"
19 | - "installer/Module/RoadRunnerBridge/resources/config"
20 |
--------------------------------------------------------------------------------
/web/app.php:
--------------------------------------------------------------------------------
1 | __DIR__],
25 | )->run();
26 |
27 | if ($app === null) {
28 | exit(255);
29 | }
30 |
31 | $code = (int)$app->serve();
32 | exit($code);
33 |
--------------------------------------------------------------------------------
/web/app/config/grpc-services.php:
--------------------------------------------------------------------------------
1 | [
7 | \Internal\Shared\gRPC\Interceptors\Outgoing\SendTraceContextInterceptor::class,
8 | \App\Application\GRPC\Interceptor\AuthInterceptor::class,
9 | \Internal\Shared\gRPC\Interceptors\Incoming\ExceptionHandlerInterceptor::class,
10 | ],
11 | ];
12 |
--------------------------------------------------------------------------------
/web/app/config/grpc.php:
--------------------------------------------------------------------------------
1 | directory('root') . 'protoc-gen-php-grpc',
7 | 'generatedPath' => directory('root') . '/generated',
8 | 'namespace' => 'GRPC',
9 | 'services' => [
10 | directory('root') . '/../proto/users/v1/service.proto',
11 | directory('root') . '/../proto/auth/v1/service.proto',
12 | directory('root') . '/../proto/payment/v1/service.proto',
13 | directory('root') . '/../proto/common/v1/message.proto',
14 | ],
15 | 'servicesBasePath' => directory('root') . '/../proto',
16 | ];
17 |
--------------------------------------------------------------------------------
/web/app/config/scaffolder.php:
--------------------------------------------------------------------------------
1 | 'App',
15 |
16 | 'declarations' => [
17 | Declaration\BootloaderDeclaration::TYPE => [
18 | 'namespace' => 'Application\\Bootloader',
19 | ],
20 | Declaration\ConfigDeclaration::TYPE => [
21 | 'namespace' => 'Application\\Config',
22 | ],
23 | Declaration\ControllerDeclaration::TYPE => [
24 | 'namespace' => 'Endpoint\\Web',
25 | ],
26 | Declaration\FilterDeclaration::TYPE => [
27 | 'namespace' => 'Endpoint\\Web\\Filter',
28 | ],
29 | Declaration\MiddlewareDeclaration::TYPE => [
30 | 'namespace' => 'Endpoint\\Web\\Middleware',
31 | ],
32 | Declaration\CommandDeclaration::TYPE => [
33 | 'namespace' => 'Endpoint\\Console',
34 | ],
35 | Declaration\JobHandlerDeclaration::TYPE => [
36 | 'namespace' => 'Endpoint\\Job',
37 | ],
38 | ],
39 | ];
40 |
--------------------------------------------------------------------------------
/web/app/config/session.php:
--------------------------------------------------------------------------------
1 | (int)env('SESSION_LIFETIME', 86400),
14 | 'cookie' => env('SESSION_COOKIE', 'sid'),
15 | 'secure' => true,
16 | 'sameSite' => null,
17 | 'handler' => new Autowire(
18 | FileHandler::class,
19 | [
20 | 'directory' => directory('runtime') . 'session',
21 | 'lifetime' => (int)env('SESSION_LIFETIME', 86400),
22 | ]
23 | ),
24 | ];
25 |
--------------------------------------------------------------------------------
/web/app/src/Application/Auth/AuthKey.php:
--------------------------------------------------------------------------------
1 | key;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/web/app/src/Application/Auth/AuthKeyInterface.php:
--------------------------------------------------------------------------------
1 | [self::class, 'domainCore']];
17 | protected const INTERCEPTORS = [
18 | GuardInterceptor::class
19 | ];
20 | }
21 |
--------------------------------------------------------------------------------
/web/app/src/Application/Bootloader/LoggingBootloader.php:
--------------------------------------------------------------------------------
1 | addHandler(
30 | channel: ErrorHandlerMiddleware::class,
31 | handler: $monolog->logRotate(
32 | directory('runtime') . 'logs/http.log',
33 | ),
34 | );
35 |
36 | // app level errors
37 | $monolog->addHandler(
38 | channel: MonologConfig::DEFAULT_CHANNEL,
39 | handler: $monolog->logRotate(
40 | filename: directory('runtime') . 'logs/error.log',
41 | level: Logger::ERROR,
42 | maxFiles: 25,
43 | bubble: false,
44 | ),
45 | );
46 |
47 | // debug and info messages via global LoggerInterface
48 | $monolog->addHandler(
49 | channel: MonologConfig::DEFAULT_CHANNEL,
50 | handler: $monolog->logRotate(
51 | filename: directory('runtime') . 'logs/debug.log',
52 | ),
53 | );
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/web/app/src/Application/Bootloader/RoutesBootloader.php:
--------------------------------------------------------------------------------
1 | [
38 | SimpleAuthMiddleware::class,
39 | ],
40 | ];
41 | }
42 |
43 | protected function defineRoutes(RoutingConfigurator $routes): void
44 | {
45 | // Fallback route if no other route matched
46 | // Will show 404 page
47 | // $routes->default('/')
48 | // ->callable(function (ServerRequestInterface $r, ResponseInterface $response) {
49 | // return $response->withStatus(404)->withBody('Not found');
50 | // });
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/web/app/src/Application/Exception/NotFoundException.php:
--------------------------------------------------------------------------------
1 | withAuthToken($this->key->getKey());
24 |
25 | return $core->callAction($controller, $action, $parameters);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/web/app/src/Endpoint/Console/TestCommand.php:
--------------------------------------------------------------------------------
1 | Get(
22 | new Context([]),
23 | new \GRPC\Services\Users\v1\GetRequest([
24 | 'uuid' => Uuid::uuid4()->toString()
25 | ])
26 | );
27 |
28 | trap($response);
29 |
30 | return 0;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/web/app/src/Endpoint/Http/Controller/Auth/LoginAction.php:
--------------------------------------------------------------------------------
1 | authService->Login(
22 | RequestContext::create(),
23 | new \GRPC\Services\Auth\v1\LoginRequest([
24 | 'email' => $request->email,
25 | 'password' => $request->password,
26 | ]),
27 | );
28 |
29 | return \json_decode(
30 | $response->getToken()->serializeToJsonString(),
31 | true,
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/web/app/src/Endpoint/Http/Controller/User/CreateAction.php:
--------------------------------------------------------------------------------
1 | Create(
21 | new Context([]),
22 | new \GRPC\Services\Users\v1\CreateRequest([
23 | 'user' => new User([
24 | 'name' => $request->name,
25 | 'email' => $request->email,
26 | 'password' => $request->password,
27 | ]),
28 | ]),
29 | );
30 |
31 | return \json_decode(
32 | $response->getUser()->serializeToJsonString(),
33 | true,
34 | );
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/web/app/src/Endpoint/Http/Controller/User/ShowAction.php:
--------------------------------------------------------------------------------
1 | ', methods: ['GET'])]
16 | public function __invoke(
17 | UsersServiceInterface $users,
18 | string $uuid,
19 | ): array {
20 | $response = $users->Get(
21 | RequestContext::create(),
22 | new \GRPC\Services\Users\v1\GetRequest([
23 | 'uuid' => Uuid::fromString($uuid)->toString(),
24 | ]),
25 | );
26 |
27 | return \json_decode(
28 | $response->getUser()->serializeToJsonString(),
29 | true,
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/web/app/src/Endpoint/Http/Middleware/ErrorHandlerMiddleware.php:
--------------------------------------------------------------------------------
1 | handle($request);
21 | } catch (NotFoundException $e) {
22 | throw new \Spiral\Http\Exception\ClientException\NotFoundException();
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/web/app/src/Endpoint/Http/Middleware/SimpleAuthMiddleware.php:
--------------------------------------------------------------------------------
1 | getHeaderLine('X-Auth-Token') ?? null;
26 |
27 | return $this->container->runScope([
28 | AuthKeyInterface::class => new AuthKey($authKey),
29 | ], function () use ($request, $handler) {
30 | return $handler->handle($request);
31 | });
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/web/app/src/Endpoint/Http/Request/Auth/LoginRequest.php:
--------------------------------------------------------------------------------
1 | ['required', 'email'],
25 | 'password' => ['required', 'string'],
26 | ]);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/web/app/src/Endpoint/Http/Request/User/CreateRequest.php:
--------------------------------------------------------------------------------
1 | ['required', 'string'],
28 | 'email' => ['required', 'email'],
29 | 'password' => ['required', 'string'],
30 | ]);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/web/functions.php:
--------------------------------------------------------------------------------
1 | addDump(
17 | $dumper->dump((new VarCloner())->cloneVar($var), true)
18 | );
19 | }
20 |
21 | throw $throwable;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/web/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
17 | tests/Unit
18 |
19 |
20 | tests/Feature
21 |
22 |
23 |
24 |
25 | app/src
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/web/psalm.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/web/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/php-fart/grpc-microservices/df91325a22df18aea3ed00b7de7c88c019f8f7ef/web/public/favicon.ico
--------------------------------------------------------------------------------
/web/public/styles/welcome.css:
--------------------------------------------------------------------------------
1 | .box {
2 | margin-bottom: 17px;
3 | padding: 46px 49px;
4 | background: rgba(255, 255, 255, 0.56);
5 | }
6 | .box .items {
7 | display: flex;
8 | justify-content: space-between;
9 | flex-wrap: wrap;
10 | row-gap: 33px;
11 | border-radius: 10px;
12 | }
13 |
14 | .item {
15 | width: calc(50% - 33px);
16 | }
17 |
18 | .item__title {
19 | margin: 0 0 11px;
20 | font-weight: 900;
21 | font-size: 12px;
22 | line-height: 150%;
23 | }
24 |
25 | .item__title a {
26 | color: inherit;
27 | text-decoration: none;
28 | transition: opacity .25s ease-out;
29 | }
30 |
31 | .item__title a:hover {
32 | opacity: .8;
33 | }
34 |
35 | .item__text {
36 | margin: 0;
37 | font-size: 12px;
38 | line-height: 150%;
39 | }
40 |
41 | .links {
42 | display: flex;
43 | justify-content: center;
44 | margin: 30px 0;
45 | }
46 |
47 | .links-item {
48 | display: flex;
49 | align-items: center;
50 | justify-content: center;
51 | width: 40px;
52 | height: 40px;
53 | color: #4d5460;
54 | background-color: transparent;
55 | box-sizing: border-box;
56 | transition: all .25s ease-out
57 | }
58 |
59 | .links-item:hover {
60 | color: #161b22;
61 | }
62 |
63 | .links-item:not(:last-child) {
64 | margin-right: 6px
65 | }
66 |
67 | .discourse-path-1,.discourse-path-2 {
68 | fill: #fff;
69 | }
70 |
71 | .discourse-path-3 {
72 | fill: #4d5460;
73 | }
74 |
--------------------------------------------------------------------------------
/web/tests/App/TestKernel.php:
--------------------------------------------------------------------------------
1 | beforeBooting(static function (ConfiguratorInterface $config): void {
20 | if (!$config->exists('session')) {
21 | return;
22 | }
23 |
24 | $config->modify('session', new Set('handler', null));
25 | });
26 |
27 | parent::setUp();
28 |
29 | $container = $this->getContainer();
30 |
31 | if ($container->has(TranslatorInterface::class)) {
32 | $container->get(TranslatorInterface::class)->setLocale('en');
33 | }
34 | }
35 |
36 | public function createAppInstance(Container $container = new Container()): TestableKernelInterface
37 | {
38 | return TestKernel::create(
39 | directories: $this->defineDirectories(
40 | $this->rootDirectory(),
41 | ),
42 | container: $container,
43 | );
44 | }
45 |
46 | protected function tearDown(): void
47 | {
48 | // Uncomment this line if you want to clean up runtime directory.
49 | // $this->cleanUpRuntimeDirectory();
50 | }
51 |
52 | public function rootDirectory(): string
53 | {
54 | return __DIR__ . '/..';
55 | }
56 |
57 | public function defineDirectories(string $root): array
58 | {
59 | return [
60 | 'root' => $root,
61 | ];
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/web/tests/Unit/DemoTest.php:
--------------------------------------------------------------------------------
1 | assertTrue($expected);
17 | $this->assertFalse($actual);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------