wikimedia/mediawiki-extensions-Wikibase

View on GitHub
repo/rest-api/src/Infrastructure/DataValuesValueDeserializer.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php declare( strict_types=1 );

namespace Wikibase\Repo\RestApi\Infrastructure;

use DataValues\DataValue;
use DataValues\IllegalValueException;
use DataValues\TimeValue;
use Deserializers\Exceptions\DeserializationException;
use Wikibase\DataModel\Deserializers\SnakValueDeserializer;
use Wikibase\Repo\DataTypeValidatorFactory;
use Wikibase\Repo\RestApi\Application\Serialization\Exceptions\InvalidFieldException;
use Wikibase\Repo\RestApi\Application\Serialization\Exceptions\MissingFieldException;
use Wikibase\Repo\RestApi\Application\Serialization\ValueDeserializer;
use Wikibase\Repo\RestApi\Domain\Services\ValueTypeLookup;

/**
 * @license GPL-2.0-or-later
 */
class DataValuesValueDeserializer implements ValueDeserializer {

    private ValueTypeLookup $valueTypeLookup;
    private SnakValueDeserializer $snakValueDeserializer;
    private DataTypeValidatorFactory $validatorFactory;

    public function __construct(
        ValueTypeLookup $valueTypeLookup,
        SnakValueDeserializer $snakValueDeserializer,
        DataTypeValidatorFactory $validatorFactory
    ) {
        $this->valueTypeLookup = $valueTypeLookup;
        $this->snakValueDeserializer = $snakValueDeserializer;
        $this->validatorFactory = $validatorFactory;
    }

    public function deserialize( string $dataTypeId, array $valueSerialization, string $basePath = '' ): DataValue {
        $this->assertFieldExists( $valueSerialization, 'content', $basePath );

        // phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.Found
        switch ( $dataValueType = $this->valueTypeLookup->getValueType( $dataTypeId ) ) {
            case 'wikibase-entityid':
                $this->assertFieldIsString( $valueSerialization, 'content', $basePath );
                $dataValue = $this->deserializeEntityIdValue( $dataTypeId, $valueSerialization['content'], $basePath );
                break;
            case 'time':
                $this->assertFieldIsArray( $valueSerialization, 'content', $basePath );
                $dataValue = $this->deserializeTimeValue( $valueSerialization['content'], $basePath );
                break;
            default:
                try {
                    $dataValue = $this->snakValueDeserializer->deserialize( $dataTypeId, [
                        'type' => $dataValueType,
                        'value' => $valueSerialization['content'],
                    ] );
                } catch ( DeserializationException $e ) {
                    throw new InvalidFieldException( 'content', $valueSerialization['content'], "$basePath/content" );
                }
                break;
        }

        foreach ( $this->validatorFactory->getValidators( $dataTypeId ) as $validator ) {
            $result = $validator->validate( $dataValue );
            if ( !$result->isValid() ) {
                throw new InvalidFieldException( 'content', $valueSerialization['content'], "$basePath/content" );
            }
        }

        return $dataValue;
    }

    private function deserializeEntityIdValue( string $dataTypeId, string $content, string $basePath ): DataValue {
        try {
            return $this->snakValueDeserializer->deserialize( $dataTypeId, [
                'value' => [ 'id' => $content ],
                'type' => 'wikibase-entityid',
            ] );
        } catch ( DeserializationException $e ) {
            throw new InvalidFieldException( 'content', $content, "$basePath/content" );
        }
    }

    private function deserializeTimeValue( array $content, string $basePath ): TimeValue {
        $this->assertFieldExists( $content, 'time', $basePath );
        $this->assertFieldExists( $content, 'precision', $basePath );
        $this->assertFieldExists( $content, 'calendarmodel', $basePath );

        $this->assertFieldIsString( $content, 'time', $basePath );
        $this->assertFieldIsInt( $content, 'precision', $basePath );
        $this->assertFieldIsString( $content, 'calendarmodel', $basePath );

        $timestamp = $content['time'];
        $precision = $content['precision'];
        $calendarModel = $content['calendarmodel'];

        try {
            $timeValue = new TimeValue( $timestamp, 0, 0, 0, $precision, $calendarModel );
        } catch ( IllegalValueException $e ) {
            throw new InvalidFieldException( 'content', $content, "$basePath/content" );
        }

        return $timeValue;
    }

    private function assertFieldExists( array $serialization, string $field, string $basePath ): void {
        if ( !array_key_exists( $field, $serialization ) ) {
            throw new MissingFieldException( $field, $basePath );
        }
    }

    private function assertFieldIsArray( array $serialization, string $field, string $basePath ): void {
        if ( !is_array( $serialization[$field] ) ) {
            throw new InvalidFieldException( $field, $serialization[$field], "$basePath/$field" );
        }
    }

    private function assertFieldIsString( array $serialization, string $field, string $basePath ): void {
        if ( !is_string( $serialization[$field] ) ) {
            throw new InvalidFieldException( $field, $serialization[$field], "$basePath/$field" );
        }
    }

    private function assertFieldIsInt( array $serialization, string $field, string $basePath ): void {
        if ( !is_int( $serialization[$field] ) ) {
            throw new InvalidFieldException( $field, $serialization[$field], "$basePath/$field" );
        }
    }

}