Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove league/json-guard and replace with opis/json-schema for PHP 8.1 upgrade #723

Open
ThomasAFink opened this issue Dec 6, 2024 · 2 comments
Labels
dependencies Pull requests that update a dependency file php Pull requests that update Php code

Comments

@ThomasAFink
Copy link
Member

Task Description

In the ZMS system, the library league/json-guard needs to be removed and replaced with opis/json-schema. The focus is on the implementation of JsonSerializable::jsonSerialize().

Details

  • The migration should be performed in the zmsapi module.
  • Reference: GitHub Actions Run for zmsapi.
  • Testing: All unit tests must pass successfully after the migration.

Acceptance Criteria

  1. The library league/json-guard has been successfully replaced with opis/json-schema.
  2. The implementation of JsonSerializable::jsonSerialize() works correctly.
  3. All unit tests execute without errors.

Ask @coderabbitai on how to get started.

@ThomasAFink ThomasAFink pinned this issue Dec 6, 2024
@ThomasAFink
Copy link
Member Author

How to begin @coderabbitai?

Copy link
Contributor

coderabbitai bot commented Dec 6, 2024

Tip

For best results, initiate chat on the files or code changes.

@ThomasAFink, now I can provide a complete migration plan for the Schema Validator. Let's create a new implementation using Opis/json-schema that maintains the same functionality:

<?php

namespace BO\Zmsentities\Schema;

use Opis\JsonSchema\{Validator as OpisValidator, Schema as OpisSchema, ValidationResult};
use Opis\JsonSchema\Errors\ValidationError as OpisValidationError;

class Validator
{
    protected $schemaObject;
    protected $schemaData;
    protected $locale;
    protected $validator;
    protected $validationResult;

    public function __construct($data, Schema $schemaObject, $locale)
    {
        $this->schemaData = $data;
        $this->schemaObject = $schemaObject;
        $this->locale = $locale;
        
        $this->validator = new OpisValidator();
        
        // Register custom keywords
        $this->validator->resolver()->registerKeyword(
            'coerce-type',
            new Extensions\CoerceType()
        );
        
        // Perform validation
        $schema = OpisSchema::fromJsonString(json_encode($schemaObject->toJsonObject()));
        $this->validationResult = $this->validator->validate($data, $schema);
    }

    public function isValid()
    {
        return $this->validationResult->isValid();
    }

    public function getErrors()
    {
        if ($this->isValid()) {
            return [];
        }

        $errorsReducedList = [];
        $error = $this->validationResult->getFirstError();
        
        while ($error) {
            $errorsReducedList[] = new ValidationError(
                $this->getCustomMessage($error),
                $error->keyword(),
                $error->keywordArgs(),
                $error->data(),
                $this->getTranslatedPointer($error),
                $error->schema(),
                $error->schemaPath()
            );
            $error = $this->validationResult->getNextError();
        }
        
        return $errorsReducedList;
    }

    public function getCustomMessage(OpisValidationError $error)
    {
        $message = null;
        $property = new \BO\Zmsentities\Helper\Property($error->schema());
        $message = $property['x-locale'][$this->locale]->messages[$error->keyword()]->get();
        return $message ?: $error->message();
    }

    public static function getOriginPointer(OpisValidationError $error)
    {
        $pointer = explode('/', $error->schemaPath());
        $keys = array_keys($pointer, 'properties', true);
        if (0 < count($keys)) {
            $pointer = array_values(array_slice($pointer, end($keys) + 1, null, true));
        }
        return reset($pointer);
    }

    public function getTranslatedPointer(OpisValidationError $error)
    {
        $property = new \BO\Zmsentities\Helper\Property($error->schema());
        return $property['x-locale'][$this->locale]->pointer->get(static::getOriginPointer($error));
    }

    public function registerFormatExtension($name, $extension)
    {
        return $this->validator->resolver()->registerFormat($name, $extension);
    }
}

And here's the CoerceType extension implementation for Opis:

<?php

namespace BO\Zmsentities\Schema\Extensions;

use Opis\JsonSchema\Keyword;
use Opis\JsonSchema\ValidationContext;
use Opis\JsonSchema\Errors\ValidationError;

class CoerceType implements Keyword
{
    public function validate($data, ValidationContext $context, $value): ?ValidationError
    {
        $type = $value;
        
        // Perform type coercion
        switch ($type) {
            case 'string':
                if (is_numeric($data) || is_bool($data)) {
                    $context->setData((string)$data);
                }
                break;
            case 'number':
                if (is_string($data) && is_numeric($data)) {
                    $context->setData((float)$data);
                }
                break;
            case 'integer':
                if (is_string($data) && ctype_digit($data)) {
                    $context->setData((int)$data);
                }
                break;
            case 'boolean':
                if (is_string($data)) {
                    if ($data === 'true') {
                        $context->setData(true);
                    } elseif ($data === 'false') {
                        $context->setData(false);
                    }
                }
                break;
        }
        
        return null; // Return null if validation passes
    }
}

Let's create a unit test to ensure the migration works correctly:

<?php

namespace BO\Zmsentities\Tests\Schema;

use PHPUnit\Framework\TestCase;
use BO\Zmsentities\Schema\Validator;
use BO\Zmsentities\Schema\Schema;

class ValidatorTest extends TestCase
{
    public function testBasicValidation()
    {
        $schema = new Schema([
            'type' => 'object',
            'properties' => [
                'name' => [
                    'type' => 'string',
                    'x-locale' => [
                        'en' => [
                            'messages' => [
                                'type' => 'Must be a string'
                            ],
                            'pointer' => 'Name'
                        ]
                    ]
                ],
                'age' => [
                    'type' => 'integer',
                    'coerce-type' => 'integer'
                ]
            ],
            'required' => ['name', 'age']
        ]);

        // Test valid data
        $validator = new Validator(['name' => 'John', 'age' => '25'], $schema, 'en');
        $this->assertTrue($validator->isValid());
        $this->assertEmpty($validator->getErrors());

        // Test invalid data
        $validator = new Validator(['name' => 123, 'age' => 'invalid'], $schema, 'en');
        $this->assertFalse($validator->isValid());
        $errors = $validator->getErrors();
        $this->assertCount(2, $errors);
    }
}

Next steps:

  1. Update the composer.json in the zmsentities module
  2. Replace the existing files with the new implementations
  3. Run the unit tests to ensure everything works
  4. Update any dependent modules if needed

Would you like me to:

  1. Generate more unit tests?
  2. Help with updating dependent modules?
  3. Create a migration guide for other custom extensions?

Let me know which aspect you'd like to focus on next!

(_/)/
(•.•)
/ >🔄 Ready to help with the next steps of the migration!

🔗 Analysis chain

Let me help you get started with replacing league/json-guard with opis/json-schema. First, I'll analyze the codebase to understand the current implementation and dependencies.

Let me analyze the results and provide you with a structured plan to begin the migration.

@ThomasAFink, here's a step-by-step plan to begin the migration:

  1. Remove Old Dependency
    First, we need to remove the league/json-guard package:

    composer remove league/json-guard
  2. Add New Dependency
    Install the opis/json-schema package:

    composer require opis/json-schema
  3. Migration Steps

    • Identify all files using league/json-guard validators
    • Replace the validation logic using Opis's syntax
    • Update any JSON schema definitions if needed
    • Test each component after migration

Here's a basic example of how the migration would look:

// Old code with league/json-guard
use League\JsonGuard\Validator;

$validator = new Validator($data, $schema);
$isValid = $validator->passes();

// New code with opis/json-schema
use Opis\JsonSchema\{Validator, Schema};

$validator = new Validator();
$schema = Schema::fromJsonString($schemaString);
$result = $validator->validate($data, $schema);
$isValid = $result->isValid();

Would you like me to:

  1. Analyze specific files to provide more detailed migration steps?
  2. Help with migrating a particular component first?
  3. Generate test cases for the new implementation?

Please let me know which aspect you'd like to focus on first, and I'll provide more specific guidance.

(_/)/
(•.•)
/ >📋 Let's tackle this migration systematically!


Based on the analysis, I can see that league/json-guard is primarily used in the zmsentities module, which is where we need to focus our migration efforts. Let me provide you with a more detailed plan.

@ThomasAFink, here's a detailed migration plan:

  1. Main Components to Update

    • zmsentities/src/Zmsentities/Schema/Validator.php
    • zmsentities/src/Zmsentities/Schema/Entity.php
    • zmsentities/src/Zmsentities/Schema/Extensions/SameValues.php
    • zmsentities/src/Zmsentities/Schema/Extensions/CoerceType.php
  2. Update Dependencies
    First, in the zmsentities directory:

    composer remove league/json-guard
    composer require opis/json-schema
  3. Migration Steps
    a. Update the Schema Validator:

    // Old (league/json-guard):
    use League\JsonGuard\Validator as JsonGuardValidator;
    
    // New (opis/json-schema):
    use Opis\JsonSchema\{Validator, Schema};
    use Opis\JsonSchema\Errors\ValidationError;

    b. Basic validation pattern:

    // Old validation:
    $validator = new JsonGuardValidator($data, $schema);
    if ($validator->fails()) {
        $errors = $validator->errors();
    }
    
    // New validation:
    $validator = new Validator();
    $schema = Schema::fromJsonString($schemaString);
    $result = $validator->validate($data, $schema);
    if (!$result->isValid()) {
        $errors = $result->getFirstError();
    }
  4. Custom Extensions Migration

    • For SameValues.php and CoerceType.php, we'll need to rewrite them using Opis's keyword validation system:
    use Opis\JsonSchema\Schemas\KeywordValidator;
    use Opis\JsonSchema\ValidationContext;

Let's start with updating the Schema Validator first. Would you like me to:

  1. Analyze the current implementation of the Schema Validator to provide a more detailed migration plan?
  2. Generate a complete implementation of the new Schema Validator using Opis?
  3. Create unit tests for the new implementation?

Please let me know which aspect you'd like to focus on first, and I'll provide more specific guidance.

(_/)/
(•.•)
/ >📝 Ready to help with the implementation details once you decide which aspect to tackle first!

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for composer.json files to locate the dependency
fd composer.json --exec cat {}

# Find files using league/json-guard
rg -l "league/json-guard"

# Find JSON schema validation related code
rg -l "JsonGuard|JsonSchema|validate.*schema"

# Look for JsonSerializable implementations
ast-grep --pattern 'class $_ implements JsonSerializable'

Length of output: 20541


Script:

#!/bin/bash
# Let's look at the current Schema Validator implementation
cat zmsentities/src/Zmsentities/Schema/Validator.php

Length of output: 2569

@ThomasAFink ThomasAFink added dependencies Pull requests that update a dependency file php Pull requests that update Php code labels Dec 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dependencies Pull requests that update a dependency file php Pull requests that update Php code
Projects
None yet
Development

No branches or pull requests

1 participant