Skip to content
Snippets Groups Projects
Commit 722c0e9b authored by Tobias Gross-Vogt's avatar Tobias Gross-Vogt
Browse files

initial commit

parents
No related branches found
No related tags found
No related merge requests found
Pipeline #193651 failed
Showing
with 791 additions and 0 deletions
/vendor
/var
/.php_cs
/.idea
/*.cache
/_coverage
\ No newline at end of file
image: registry.gitlab.tugraz.at/dbp/relay/dbp-relay-core-bundle/main:v1
before_script:
- 'git config --global url."https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.tugraz.at/".insteadOf "git@gitlab.tugraz.at:"'
variables:
COMPOSER_CACHE_DIR: "$CI_PROJECT_DIR/_composer_cache"
cache:
key: ${CI_PROJECT_PATH}
paths:
- _composer_cache
stages:
- test
.test_defaults: &test_defaults
script:
- sudo update-alternatives --set php "/usr/bin/${PHP}"
- composer validate
- composer install
- composer test
test-php7.3:
stage: test
variables:
PHP: "php7.3"
<<: *test_defaults
test-php7.4:
stage: test
variables:
PHP: "php7.4"
<<: *test_defaults
test-php8.0:
stage: test
variables:
PHP: "php8.0"
<<: *test_defaults
test-php8.1:
stage: test
variables:
PHP: "php8.1"
<<: *test_defaults
test-rename:
stage: test
rules:
- if: $CI_PROJECT_PATH == "dbp/relay/dbp-relay-proxy-bundle"
script:
- sudo update-alternatives --set php "/usr/bin/php7.4"
- composer install
# remove the cache, otherwise the rename will break it
- rm -rf _composer_cache
- ./.bundle-rename --unique-name=greenlight --friendly-name="Electronic Covid Access Permits" --example-entity=Permit
- composer update --lock
- composer validate
- composer install
- composer test
- composer lint
linting:
stage: test
script:
- sudo update-alternatives --set php /usr/bin/php7.4
- composer install
- result=0
- composer run cs || result=1
- composer run phpstan || result=1
- composer run psalm || result=1
- exit $result
\ No newline at end of file
<?php
$finder = PhpCsFixer\Finder::create()
->in(__DIR__)
->exclude('var')
;
$config = new PhpCsFixer\Config();
$config->setRules([
'@Symfony' => true,
'@PHP70Migration' => true,
'@PHP71Migration' => true,
'@PHP73Migration' => true,
'@DoctrineAnnotation' => true,
'doctrine_annotation_array_assignment' => ['operator' => '='],
'yoda_style' => false,
'strict_comparison' => true,
'strict_param' => true,
'declare_strict_types' => true,
'method_argument_space' => ['on_multiline' => 'ignore'],
])
->setRiskyAllowed(true)
->setFinder($finder);
return $config;
\ No newline at end of file
{
"extends": [
"config:base",
"group:allNonMajor",
"schedule:weekends",
":automergePatch"
],
"ignorePresets": [":prHourlyLimit2"],
"rangeStrategy": "update-lockfile",
"lockFileMaintenance": {
"enabled": true
},
"packageRules": [
{
"matchPackagePrefixes": [
"symfony/"
],
"allowedVersions": "<6"
}
]
}
\ No newline at end of file
This diff is collapsed.
README.md 0 → 100644
Relay-API Bundle README Template
================================
<!--
This should act as a template README.md for a new Relay-API Bundle.
Just remove the parts that are not relevant to your bundle and
replace placeholders like "{{Name}}" with your bundle name and so on.
List of placeholders:
- {{name}}: Name of the bundle in lowercase, like "formalize"
- {{Name}}: Name of the bundle in camel case, like "Formalize"
- {{NAME}}: Name of the bundle in uppercase, like "FORMALIZE"
- {{bundle-path}}: GitLab bundle repository path, like "dbp/formalize/dbp-relay-formalize-bundle"
- {{package-name}}: Name of the bundle for packagist, like "dbp/relay-formalize-bundle"
- {{app-path}}: GitLab repository path of the frontend application, like "dbp/formalize/formalize"
-->
# DbpRelay{{Name}}Bundle
[GitLab](https://gitlab.tugraz.at/{{bundle-path}}) |
[Packagist](https://packagist.org/packages/{{package-name}}) |
[Frontend Application](https://gitlab.tugraz.at/{{app-path}}) |
[{{Name}} Website](https://dbp-demo.tugraz.at/site/software/{{name}}.html)
The {{name}} bundle provides an API for interacting with ...
There is a corresponding frontend application that uses this API at [{{Name}} Frontend Application](https://gitlab.tugraz.at/{{app-path}}).
## Bundle installation
You can install the bundle directly from [packagist.org](https://packagist.org/packages/{{package-name}}).
```bash
composer require {{package-name}}
```
## Integration into the Relay API Server
* Add the bundle to your `config/bundles.php` in front of `DbpRelayCoreBundle`:
```php
...
Dbp\Relay\{{Name}}Bundle\DbpRelay{{Name}}Bundle::class => ['all' => true],
Dbp\Relay\CoreBundle\DbpRelayCoreBundle::class => ['all' => true],
];
```
If you were using the [DBP API Server Template](https://gitlab.tugraz.at/dbp/relay/dbp-relay-server-template)
as template for your Symfony application, then this should have already been generated for you.
* Run `composer install` to clear caches
## Configuration
The bundle has a `database_url` configuration value that you can specify in your
app, either by hard-coding it, or by referencing an environment variable.
For this create `config/packages/dbp_relay_{{name}}.yaml` in the app with the following
content:
```yaml
dbp_relay_{{name}}:
database_url: 'mysql://db:secret@mariadb:3306/db?serverVersion=mariadb-10.3.30'
# database_url: %env({{NAME}}_DATABASE_URL)%
```
If you were using the [DBP API Server Template](https://gitlab.tugraz.at/dbp/relay/dbp-relay-server-template)
as template for your Symfony application, then the configuration file should have already been generated for you.
For more info on bundle configuration see <https://symfony.com/doc/current/bundles/configuration.html>.
## Development & Testing
* Install dependencies: `composer install`
* Run tests: `composer test`
* Run linters: `composer run lint`
* Run cs-fixer: `composer run cs-fix`
## Bundle dependencies
Don't forget you need to pull down your dependencies in your main application if you are installing packages in a bundle.
```bash
# updates and installs dependencies of {{package-name}}
composer update {{package-name}}
```
## Scripts
### Database migration
Run this script to migrate the database. Run this script after installation of the bundle and
after every update to adapt the database to the new source code.
```bash
php bin/console doctrine:migrations:migrate --em=dbp_relay_{{name}}_bundle
```
## Error codes
### `/{{name}}/submissions`
#### POST
| relay:errorId | Status code | Description | relay:errorDetails | Example |
|-------------------------------------|-------------|-------------------------------------------------| ------------------ |----------------------------------|
| `{{name}}:submission-not-created` | 500 | The submission could not be created. | `message` | `['message' => 'Error message']` |
| `{{name}}:submission-invalid-json` | 422 | The dataFeedElement doesn't contain valid json. | `message` | |
### `/{{name}}/submissions/{identifier}`
#### GET
| relay:errorId | Status code | Description | relay:errorDetails | Example |
| -------------------------------- | ----------- | ------------------------- | ------------------ | ------- |
| `{{name}}:submission-not-found` | 404 | Submission was not found. | | |
## Roles
This bundle needs the role `ROLE_SCOPE_{{NAME}}` assigned to the user to get permissions to fetch data.
To create a new submission entry the Symfony role `ROLE_SCOPE_{{NAME}}-POST` is required.
## Events
To extend the behavior of the bundle the following event is registered:
### CreateSubmissionPostEvent
This event allows you to react on submission creations.
You can use this for example to email the submitter of the submission.
An event subscriber receives a `Dbp\Relay\{{Name}}Bundle\Event\CreateSubmissionPostEvent` instance
in a service for example in `src/EventSubscriber/CreateSubmissionSubscriber.php`:
```php
<?php
namespace App\EventSubscriber;
use Dbp\Relay\{{Name}}Bundle\Event\CreateSubmissionPostEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class CreateSubmissionSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
CreateSubmissionPostEvent::NAME => 'onPost',
];
}
public function onPost(CreateSubmissionPostEvent $event)
{
$submission = $event->getSubmission();
$dataFeedElement = $submission->getDataFeedElementDecoded();
// TODO: extract email address and send email
$email = $dataFeedElement['email'];
}
}
```
{
"name": "dbp/relay-proxy-bundle",
"description": "A template bundle for the Relay API gateway",
"type": "symfony-bundle",
"license": "AGPL-3.0-or-later",
"require": {
"php": ">=7.3",
"ext-json": "*",
"api-platform/core": "^2.6",
"dbp/relay-core-bundle": "^0.1.35",
"symfony/event-dispatcher": "^5.4",
"symfony/framework-bundle": "^5.4"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.0",
"phpstan/phpstan": "^1.0.0",
"phpstan/phpstan-phpunit": "^1.0.0",
"phpstan/phpstan-symfony": "^1.2",
"phpunit/phpunit": "^9",
"symfony/browser-kit": "^5.4",
"symfony/http-client": "^5.4",
"symfony/monolog-bundle": "^3.7",
"symfony/phpunit-bridge": "^5.4",
"vimeo/psalm": "^4.2.1"
},
"autoload": {
"psr-4": {
"Dbp\\Relay\\ProxyBundle\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Dbp\\Relay\\ProxyBundle\\Tests\\": "tests/"
}
},
"config": {
"sort-packages": true,
"platform": {
"php": "7.3"
},
"allow-plugins": {
"composer/package-versions-deprecated": true
}
},
"scripts": {
"test": [
"@php vendor/bin/phpunit"
],
"phpstan": [
"@php vendor/bin/phpstan analyze --ansi"
],
"psalm": [
"@php vendor/bin/psalm"
],
"lint": [
"@composer run cs",
"@composer run phpstan",
"@composer run psalm"
],
"cs-fix": [
"@php vendor/bin/php-cs-fixer --ansi fix"
],
"cs": [
"@php vendor/bin/php-cs-fixer --ansi fix --dry-run --diff"
],
"coverage": [
"@php -dxdebug.mode=coverage vendor/bin/phpunit --coverage-html _coverage"
]
}
}
This diff is collapsed.
includes:
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-symfony/extension.neon
parameters:
inferPrivatePropertyTypeFromConstructor: true
level: 3
paths:
- src
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!-- https://phpunit.de/manual/current/en/appendixes.configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" backupGlobals="false" colors="true" bootstrap="tests/bootstrap.php">
<coverage>
<include>
<directory>src</directory>
<directory>tests</directory>
</include>
</coverage>
<php>
<ini name="error_reporting" value="-1"/>
<server name="APP_ENV" value="test" force="true"/>
<server name="SHELL_VERBOSITY" value="-1"/>
<server name="SYMFONY_DEPRECATIONS_HELPER" value='max[direct]=0&amp;quiet[]=indirect'/>
<server name="KERNEL_CLASS" value="Dbp\Relay\ProxyBundle\Tests\Kernel"/>
</php>
<testsuites>
<testsuite name="Project Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<listeners>
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener"/>
</listeners>
</phpunit>
<?xml version="1.0"?>
<psalm
errorLevel="5"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
<issueHandlers>
<UndefinedDocblockClass>
<errorLevel type="suppress">
<referencedClass name="UnitEnum" />
</errorLevel>
</UndefinedDocblockClass>
</issueHandlers>
</psalm>
<?php
declare(strict_types=1);
namespace Dbp\Relay\ProxyBundle\DataPersister;
use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
use Dbp\Relay\ProxyBundle\Entity\ProxyData;
use Dbp\Relay\ProxyBundle\Event\ProxyDataEvent;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class ProxyDataPersister extends AbstractController implements ContextAwareDataPersisterInterface
{
/** @var EventDispatcherInterface */
private $eventDispatcher;
public function __construct(EventDispatcherInterface $eventDispatcher)
{
$this->eventDispatcher = $eventDispatcher;
}
public function supports($data, array $context = []): bool
{
return $data instanceof ProxyData;
}
/**
* @param mixed $data
*/
public function persist($data, array $context = []): ProxyData
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$proxyDataEvent = new ProxyDataEvent($data);
$this->eventDispatcher->dispatch($proxyDataEvent, ProxyDataEvent::NAME.$data->getNamespace());
$data->setIdentifier($data->getNamespace().'::'.$data->getFunctionName());
return $data;
}
public function remove($data, array $context = []): void
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
}
}
<?php
declare(strict_types=1);
namespace Dbp\Relay\ProxyBundle\DataProvider;
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
use Dbp\Relay\ProxyBundle\Entity\ProxyData;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
final class ProxyDataCollectionDataProvider extends AbstractController implements CollectionDataProviderInterface, RestrictedDataProviderInterface
{
public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
{
return ProxyData::class === $resourceClass;
}
public function getCollection(string $resourceClass, string $operationName = null, array $context = []): iterable
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
return [];
}
}
<?php
declare(strict_types=1);
namespace Dbp\Relay\ProxyBundle\DataProvider;
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
use Dbp\Relay\ProxyBundle\Entity\ProxyData;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
final class ProxyDataItemDataProvider extends AbstractController implements ItemDataProviderInterface, RestrictedDataProviderInterface
{
public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
{
return ProxyData::class === $resourceClass;
}
public function getItem(string $resourceClass, $id, string $operationName = null, array $context = []): ?ProxyData
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
return null;
}
}
<?php
declare(strict_types=1);
namespace Dbp\Relay\ProxyBundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class DbpRelayProxyBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
}
}
<?php
declare(strict_types=1);
namespace Dbp\Relay\ProxyBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder(): TreeBuilder
{
$treeBuilder = new TreeBuilder('dbp_relay_proxy');
return $treeBuilder;
}
}
<?php
declare(strict_types=1);
namespace Dbp\Relay\ProxyBundle\DependencyInjection;
use Dbp\Relay\CoreBundle\Extension\ExtensionTrait;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension;
class DbpRelayProxyExtension extends ConfigurableExtension
{
use ExtensionTrait;
public function loadInternal(array $mergedConfig, ContainerBuilder $container)
{
$this->addResourceClassDirectory($container, __DIR__.'/../Entity');
$loader = new YamlFileLoader(
$container,
new FileLocator(__DIR__.'/../Resources/config')
);
$loader->load('services.yaml');
}
}
<?php
declare(strict_types=1);
namespace Dbp\Relay\ProxyBundle\Entity;
use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* @ApiResource(
* collectionOperations={
* "post" = {
* "security" = "is_granted('IS_AUTHENTICATED_FULLY')",
* "path" = "/proxy/proxydata",
* "openapi_context" = {
* "tags" = {"Proxy"},
* "requestBody" = {
* "content" = {
* "application/json" = {
* "schema" = {"type" = "object"},
* }
* }
* }
* }
* },
* "get" = {
* "security" = "is_granted('IS_AUTHENTICATED_FULLY')",
* "path" = "/proxy/proxydata",
* "openapi_context" = {
* "tags" = {"Proxy"},
* },
* }
* },
* itemOperations={
* "get" = {
* "path" = "/proxy/proxydata/{identifier}",
* "openapi_context" = {
* "tags" = {"Proxy"}
* },
* },
* },
* shortName="ProxyData",
* normalizationContext={
* "groups" = {"ProxyData:output"},
* "jsonld_embed_context" = true
* },
* denormalizationContext={
* "groups" = {"ProxyData:input"},
* "jsonld_embed_context" = true
* }
* )
*/
class ProxyData
{
/**
* @ApiProperty(identifier=true)
* @Groups({"ProxyData:output"})
*
* @var string
*/
private $identifier;
/**
* @ApiProperty
* @Groups({"ProxyData:input"})
*
* @var array
*/
private $arguments;
/**
* @ApiProperty
* @Groups({"ProxyData:output"})
*
* @var mixed|null
*/
private $data;
/**
* @ApiProperty
* @Groups({"ProxyData:output"})
*
* @var array|null
*/
private $errors;
/**
* @ApiProperty
* @Groups({"ProxyData:input"})
*
* @var string
*/
private $functionName;
/**
* @ApiProperty
* @Groups({"ProxyData:input"})
*
* @var string
*/
private $namespace;
public function __construct()
{
$this->arguments = [];
}
public function getIdentifier(): string
{
return $this->identifier;
}
public function setIdentifier(string $identifier): void
{
$this->identifier = $identifier;
}
public function getArguments(): array
{
return $this->arguments;
}
public function setArguments(array $arguments): void
{
$this->arguments = $arguments;
}
public function getData()
{
return $this->data;
}
public function setData($data)
{
$this->data = $data;
}
public function getErrors(): ?array
{
return $this->errors;
}
public function setErrors(?array $errors): void
{
$this->errors = $errors;
}
public function getFunctionName(): string
{
return $this->functionName;
}
public function setFunctionName(string $functionName): void
{
$this->functionName = $functionName;
}
public function getNamespace(): string
{
return $this->namespace;
}
public function setNamespace(string $namespace): void
{
$this->namespace = $namespace;
}
public function setErrorsFromException(\Exception $exception): void
{
$this->errors = [];
do {
$this->errors[] = [
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
];
$exception = $exception->getPrevious();
} while ($exception !== null);
}
}
<?php
declare(strict_types=1);
namespace Dbp\Relay\ProxyBundle\Event;
use Dbp\Relay\ProxyBundle\Entity\ProxyData;
use Symfony\Contracts\EventDispatcher\Event;
class ProxyDataEvent extends Event
{
public const NAME = 'dbp.relay.proxy_bundle.proxy_data';
/** @var ProxyData */
private $proxyData;
public function __construct(ProxyData $proxyData)
{
$this->proxyData = $proxyData;
}
public function getProxyData(): ProxyData
{
return $this->proxyData;
}
}
services:
Dbp\Relay\ProxyBundle\DataProvider\:
resource: '../../DataProvider'
autowire: true
autoconfigure: true
Dbp\Relay\ProxyBundle\DataPersister\:
resource: '../../DataPersister'
autowire: true
autoconfigure: true
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment