schmunk42/api-platform-utils-bundle

Generic utilities for API Platform projects: UUID resolver, credential encryption, OpenAPI enhancements, and Hydra operations

Installs: 2

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

Type:symfony-bundle

pkg:composer/schmunk42/api-platform-utils-bundle

0.1.0 2025-11-23 20:00 UTC

This package is auto-updated.

Last update: 2025-11-23 23:10:02 UTC


README

Generic utilities for API Platform projects: UUID resolver, credential encryption, OpenAPI enhancements, and Hydra operations.

Features

  • UUID Resolver: Find entities by full or partial UUID (supports both binary and string UUID storage)
  • Credential Encryption: Secure encryption/decryption using libsodium (XChaCha20-Poly1305)
  • Relation Field Schema Decorator: Automatically adds x-* metadata to OpenAPI schemas for admin UI autocomplete
  • Hydra Operations Subscriber: Enriches JSON-LD responses with operation metadata for better API discoverability

Installation

1. Add to composer.json

Since this is an internal bundle in the extensions/ directory, add autoloading:

{
    "autoload": {
        "psr-4": {
            "Schmunk42\\ApiPlatformUtils\\": "extensions/api-platform-utils-bundle/src/"
        }
    }
}

Run composer dump-autoload:

composer dump-autoload

2. Register Bundle

Add to config/bundles.php:

return [
    // ...
    Schmunk42\ApiPlatformUtils\Schmunk42ApiPlatformUtilsBundle::class => ['all' => true],
];

3. Configure

Create config/packages/schmunk42_api_platform_utils.yaml:

schmunk42_api_platform_utils:
    # Credential Encryption (required)
    credential_encryption:
        key: '%env(base64:CREDENTIALS_ENCRYPTION_KEY)%'

    # Relation Field Schema Decorator (optional)
    relation_field_decorator:
        api_prefix: '/api'
        decoration_priority: 10
        label_property_candidates:
            - name
            - title
            - label
            - displayName

    # Hydra Operations (optional)
    hydra_operations:
        api_prefix: '/api'
        event_priority: -10

4. Set Environment Variable

Add to .env:

# Generate with: docker compose exec php bin/console app:generate-encryption-key
CREDENTIALS_ENCRYPTION_KEY="base64-encoded-32-byte-key-here"

Usage

1. UUID Resolver

Find entities by full or partial UUID:

use Schmunk42\ApiPlatformUtils\Service\UuidResolver;

class MyCommand extends Command
{
    public function __construct(
        private readonly UuidResolver $uuidResolver
    ) {
        parent::__construct();
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        // Find by full UUID
        $entity = $this->uuidResolver->findByPartialUuid(
            Project::class,
            '550e8400-e29b-41d4-a716-446655440000'
        );

        // Find by partial UUID (first 8 characters)
        $entity = $this->uuidResolver->findByPartialUuid(
            Project::class,
            '550e8400'
        );

        if ($entity === null) {
            $output->writeln('Entity not found');
            return Command::FAILURE;
        }

        return Command::SUCCESS;
    }
}

Features:

  • Supports binary UUID storage (BINARY(16) with Symfony Uid)
  • Supports string UUID storage (CHAR(36) / GUID)
  • Throws exception if partial UUID is ambiguous
  • Automatically detects storage type from Doctrine metadata

2. Credential Encryption

Securely encrypt/decrypt API credentials:

use Schmunk42\ApiPlatformUtils\Service\CredentialEncryption;

class MyService
{
    public function __construct(
        private readonly CredentialEncryption $encryption
    ) {}

    public function store(array $credentials): string
    {
        // Encrypt credentials
        return $this->encryption->encrypt([
            'username' => 'user@example.com',
            'password' => 'secret123',
            'api_token' => 'token_abc123'
        ]);
    }

    public function retrieve(string $encryptedData): array
    {
        // Decrypt credentials
        try {
            return $this->encryption->decrypt($encryptedData);
        } catch (\RuntimeException $e) {
            // Decryption failed
            return [];
        }
    }
}

Generate Encryption Key:

use Schmunk42\ApiPlatformUtils\Service\CredentialEncryption;

// Generate a new base64-encoded 32-byte key
$key = CredentialEncryption::generateKey();
echo "CREDENTIALS_ENCRYPTION_KEY=" . $key;

Security Features:

  • Uses PHP's built-in libsodium (XChaCha20-Poly1305)
  • Automatic nonce generation
  • Memory cleanup with sodium_memzero
  • Base64 encoding for storage

3. Relation Field Schema Decorator

Automatically enhances OpenAPI schemas for Doctrine relations. No code required!

Example Entity:

use ApiPlatform\Metadata\ApiResource;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ApiResource]
class Project
{
    #[ORM\ManyToOne(targetEntity: Customer::class)]
    private ?Customer $customer = null;
}

Generated OpenAPI Schema:

{
  "Project": {
    "properties": {
      "customer": {
        "type": ["string", "null"],
        "format": "iri-reference",
        "x-collection": "/api/admin/customers",
        "x-label-property": "name",
        "x-value-property": "@id",
        "x-search-property": "name",
        "x-resource-class": "Customer"
      }
    }
  }
}

What it does:

  • Detects all Doctrine relations (ManyToOne, OneToOne, ManyToMany)
  • Adds x-collection URL for autocomplete endpoints
  • Infers label property from entity (checks: name, title, label, displayName)
  • Only applies to INPUT schemas (forms, not responses)
  • Configurable API prefix and label property candidates

Use in Admin UI: The x-* extensions are used by admin UIs (like API Platform Admin) to render autocomplete dropdowns for relation fields.

4. Hydra Operations Subscriber

Automatically adds operation metadata to JSON-LD item responses. No code required!

Before (Standard JSON-LD response):

{
  "@id": "/api/admin/projects/550e8400",
  "@type": "Project",
  "name": "My Project"
}

After (With hydra:operation):

{
  "@id": "/api/admin/projects/550e8400",
  "@type": "Project",
  "name": "My Project",
  "hydra:operation": [
    {
      "@id": "/api/admin/projects/550e8400",
      "@type": "hydra:Operation",
      "method": "GET",
      "title": "Retrieves a Project resource",
      "returns": "Project"
    },
    {
      "@id": "/api/admin/projects/550e8400",
      "@type": "hydra:Operation",
      "method": "PUT",
      "title": "Replaces the Project resource",
      "expects": "Project",
      "returns": "Project"
    },
    {
      "@id": "/api/admin/projects/550e8400/health",
      "@type": "hydra:Operation",
      "method": "GET",
      "title": "Health",
      "returns": "Project"
    }
  ]
}

What it does:

  • Adds complete list of available operations to each item response
  • Includes standard CRUD operations and custom operations
  • Generates human-readable titles for operations
  • Adds expects/returns schema information
  • Makes API self-documenting and discoverable

Configuration Reference

schmunk42_api_platform_utils:
    credential_encryption:
        enabled: true                    # Enable/disable credential encryption service
        key: '%env(base64:CREDENTIALS_ENCRYPTION_KEY)%'  # Required: 32-byte base64-encoded key

    relation_field_decorator:
        enabled: true                    # Enable/disable OpenAPI decorator
        api_prefix: '/api'               # API prefix for collection paths
        decoration_priority: 10          # Decorator priority (higher = runs first)
        label_property_candidates:       # Property names to check for labels (in order)
            - name
            - title
            - label
            - displayName

    hydra_operations:
        enabled: true                    # Enable/disable Hydra operations subscriber
        api_prefix: '/api'               # API prefix for operation paths
        event_priority: -10              # Event priority (negative = after API Platform)

Requirements

  • PHP 8.2+
  • ext-sodium (built into PHP 7.2+)
  • Symfony 7.0+
  • API Platform 3.0+
  • Doctrine ORM 2.0+ / 3.0+

Architecture

Bundle Structure

api-platform-utils-bundle/
├── src/
│   ├── Schmunk42ApiPlatformUtilsBundle.php      # Main bundle class
│   ├── DependencyInjection/
│   │   ├── Configuration.php                 # Configuration tree
│   │   └── Schmunk42ApiPlatformUtilsExtension.php  # DI extension
│   ├── Service/
│   │   ├── UuidResolver.php                  # UUID resolver service
│   │   └── CredentialEncryption.php          # Encryption service
│   ├── OpenApi/
│   │   └── RelationFieldSchemaDecorator.php  # OpenAPI decorator
│   └── EventSubscriber/
│       └── AddHydraOperationsSubscriber.php  # Event subscriber
├── config/
│   └── services.yaml                         # Service definitions
├── composer.json
├── README.md
└── CHANGELOG.md

How It Works

  1. UuidResolver: Uses Doctrine metadata to detect UUID storage type (binary vs string), then performs partial matching via SQL LIKE queries

  2. CredentialEncryption: Wraps PHP's libsodium crypto_secretbox functions for symmetric encryption with automatic nonce handling

  3. RelationFieldSchemaDecorator: Decorates API Platform's SchemaFactory, processes INPUT schemas, detects Doctrine associations, and adds x-* extensions

  4. AddHydraOperationsSubscriber: Listens to ResponseEvent, checks for JSON-LD item responses, extracts all operations from resource metadata, and enriches response

License

Proprietary - herzog kommunikation GmbH

Credits

Developed for the ZA7 (Zentrales Agentursystem Version 7) project.