Skip to content

Commit

Permalink
Added functions for validation of oneof/anyof params by type (#27)
Browse files Browse the repository at this point in the history
* added methods to checkForType and getType for any value in JsonMapper

* style fixes

* added tests for newly added function

* added more tests for edge cases and fix a bug

* done changes recommended by sufyan

* addition of type checking for anyof/oneof types in typeGroups along with test cases

* addition of apply factory methods in library along with unit tests

* fix a test case failure in PHP 7.0

* fix a test case failure in PHP 7.2
  • Loading branch information
asadali214 authored Jun 13, 2022
1 parent 5b8386c commit 99fc537
Show file tree
Hide file tree
Showing 8 changed files with 1,066 additions and 76 deletions.
306 changes: 287 additions & 19 deletions src/JsonMapper.php

Large diffs are not rendered by default.

25 changes: 21 additions & 4 deletions src/JsonMapperException.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,26 +79,43 @@ static function missingTypePropertyException($key, $strClassName)

/**
* Exception for an unCallable Factory Method.
*
*
* @param string $factoryMethod The concerned factory method.
* @param string $strClassName Related class name.
*
*
* @return JsonMapperException
*/
static function unCallableFactoryMethodException($factoryMethod, $strClassName)
{
return new self(
"Factory method '$factoryMethod' referenced by ".
"Factory method '$factoryMethod' referenced by " .
"'$strClassName' is not callable."
);
}

/**
* Exception for not able to call factory method with the given value.
*
* @param string $argType Type of the argument passed in method.
* @param string $reasons Exception message received from factory method.
*
* @return JsonMapperException
*/
static function invalidArgumentFactoryMethodException($argType, $reasons)
{
return new self(
"Provided factory methods are not callable with " .
"the value of Type: $argType\n$reasons"
);
}

/**
* Exception when it is not possible to map an object to a specific type.
*
* @param string $typeName Name of type to map json object on.
* @param string $typeGroup Group name of the type provided.
* @param string $value JSON string.
* @param string $value Value that should be mapped by typeGroup
* i.e. JSON string.
*
* @return JsonMapperException
*/
Expand Down
92 changes: 88 additions & 4 deletions src/TypeCombination.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,59 @@ public function getDeserializers()
return $this->_deserializers;
}

/**
* Extract innermost oneof/anyof group hidden inside array/map
* type group
*
* @return TypeCombination
*/
public function extractOneOfAnyOfGroup()
{
$innerType = $this->getTypes()[0];
if (in_array($this->getGroupName(), ["array", "map"])
&& $innerType instanceof TypeCombination
) {
return $innerType->extractOneOfAnyOfGroup();
}
return $this;
}

/**
* Extract all internal groups similar to the given group as a list of
* TypeCombination objects, it will only return similar array/map groups
*
* @param TypeCombination $group All inner groups similar to this array/map
* type group will be extracted
*
* @return TypeCombination[] A list of similar TypeCombination objects
*/
public function extractSimilar($group)
{
$result = [];
if (!in_array($this->getGroupName(), ["array", "map"])) {
// if group is neither array nor map then call extractSimilar for
// each of the internal groups
foreach ($this->getTypes() as $typ) {
if ($typ instanceof TypeCombination) {
$result = array_merge($result, $typ->extractSimilar($group));
}
}
} elseif ($group->getGroupName() == $this->getGroupName()) {
// if groupName is same then check inner group type
$internal = $this->getTypes()[0];
$group = $group->getTypes()[0];
if (in_array($group->getGroupName(), ["array", "map"])) {
// if inner group is array/map then return result after
// extraction of groups similar to innerGroup
$result = $internal->extractSimilar($group);
} else {
// if inner group is oneof/anyof then only extract $internal
$result = [$internal];
}
}
return $result;
}

/**
* Extract type info like: isMap, isArray, and inner type for maps/arrays.
*
Expand All @@ -130,6 +183,35 @@ public static function extractTypeInfo($type)
return [$isMap, $isArray, $innerType];
}

/**
* Create an oneof/anyof TypeCombination instance, by specifying inner types
*
* @param array $types types array: (TypeCombination,string)[]
* @param string $gName group name value (anyof, oneof),
* Default: anyof
* @param string[] $deserializers deserializers array, Default: []
*
* @return TypeCombination
*/
public static function with($types, $gName = 'anyof', array $deserializers = [])
{
$format = join(
',',
array_map(
function ($t) {
return is_string($t) ? $t : $t->getFormat();
},
$types
)
);
return new self(
"$gName($format)",
$gName,
$types,
$deserializers
);
}

/**
* Wrap the given typeGroup string in the TypeCombination class,
* i.e. getTypes() method will return all the grouped types,
Expand All @@ -142,11 +224,12 @@ public static function extractTypeInfo($type)
* represents array types, and array<string,T>
* represents map types, oneOf/anyOf are group
* names, while default group name is anyOf.
* @param string[] $deserializers Callable factory methods for the property
* @param string[] $deserializers Callable factory methods for the property,
* Default: []
*
* @return TypeCombination
*/
public static function generateTypeCombination($typeGroup, $deserializers)
public static function withFormat($typeGroup, $deserializers = [])
{
$groupName = 'anyOf';
$start = strpos($typeGroup, '(');
Expand Down Expand Up @@ -203,7 +286,7 @@ private static function _createTypeGroup($name, $innerGroup, $deserializers)
return new self(
$format,
$name,
[self::generateTypeCombination($innerGroup, $deserializers)],
[self::withFormat($innerGroup, $deserializers)],
$deserializers
);
}
Expand All @@ -220,9 +303,10 @@ private static function _createTypeGroup($name, $innerGroup, $deserializers)
*/
private static function _insertType(&$types, $type, $deserializers)
{
$type = trim($type);
if (strpos($type, '(') !== false && strrpos($type, ')') !== false) {
// If type is Grouped, creating TypeCombination instance for it
$type = self::generateTypeCombination($type, $deserializers);
$type = self::withFormat($type, $deserializers);
}
if (!empty($type)) {
array_push($types, $type);
Expand Down
19 changes: 19 additions & 0 deletions tests/multitypetest/MultiTypeJsonMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace multitypetest;

use apimatic\jsonmapper\JsonMapper;

class MultiTypeJsonMapper extends JsonMapper
{
public function getType(&$value, $factoryMethods = [], $start = '', $end = '')
{
return parent::getType($value, $factoryMethods, $start, $end);
}

public function checkForType($typeGroup, $type, $start = '', $end = '')
{
return parent::checkForType($typeGroup, $type, $start, $end);
}

}
Loading

0 comments on commit 99fc537

Please sign in to comment.