Skip to content

Commit 72b09f5

Browse files
committed
SDK-766: Handle MultiValue attributes and Document Images
1 parent 83390e9 commit 72b09f5

File tree

7 files changed

+547
-30
lines changed

7 files changed

+547
-30
lines changed

src/Yoti/Entity/MultiValue.php

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
<?php
2+
namespace Yoti\Entity;
3+
4+
class MultiValue extends \ArrayObject
5+
{
6+
/**
7+
* @var array Original unfiltered Items.
8+
*/
9+
private $originalItems = [];
10+
11+
/**
12+
* @var callable[] Filter callbacks.
13+
*/
14+
private $filters = [];
15+
16+
/**
17+
* MultiValue Constructor.
18+
*
19+
* @param array $items
20+
*/
21+
public function __construct($items)
22+
{
23+
$this->originalItems = array_values($items);
24+
parent::__construct($items);
25+
}
26+
27+
/**
28+
* Filters Items using callback.
29+
*
30+
* @param callable $callback
31+
* @return MultiValue
32+
*/
33+
public function filter($callback)
34+
{
35+
$this->filters[] = $callback;
36+
$this->applyFilters();
37+
return $this;
38+
}
39+
40+
/**
41+
* Apply all filters.
42+
*/
43+
private function applyFilters()
44+
{
45+
// Reset to original items before filtering.
46+
$this->exchangeArray($this->originalItems);
47+
48+
// Apply filter to items.
49+
if (count($this->filters) > 0) {
50+
$filtered_array = array_filter(
51+
$this->getArrayCopy(),
52+
[$this, 'filterItem']
53+
);
54+
$this->exchangeArray(array_values($filtered_array));
55+
}
56+
57+
// Filter nested items.
58+
foreach ($this->getArrayCopy() as $item) {
59+
if ($item instanceof MultiValue) {
60+
$item->resetFilters();
61+
foreach ($this->filters as $callback) {
62+
$item->filter($callback);
63+
}
64+
}
65+
}
66+
}
67+
68+
/**
69+
* Callback for array_filter().
70+
*
71+
* @param mixed $item
72+
* @return boolean
73+
*/
74+
private function filterItem($item)
75+
{
76+
foreach ($this->filters as $callback) {
77+
if (call_user_func($callback, $item) === true) {
78+
return true;
79+
}
80+
}
81+
return false;
82+
}
83+
84+
/**
85+
* Filter items by instance.
86+
*
87+
* @param string $type
88+
*
89+
* @return MultiValue
90+
*/
91+
public function filterInstance($type)
92+
{
93+
return $this->filter(function ($item) use ($type) {
94+
return $item instanceof $type;
95+
});
96+
return $this;
97+
}
98+
99+
/**
100+
* Filter items by type.
101+
*
102+
* @param string $type
103+
*
104+
* @return MultiValue
105+
*/
106+
public function filterType($type)
107+
{
108+
return $this->filter(function ($item) use ($type) {
109+
return gettype($item) === $type;
110+
});
111+
return $this;
112+
}
113+
114+
/**
115+
* Resets items to original values.
116+
*
117+
* @return MultiValue
118+
*/
119+
public function resetFilters()
120+
{
121+
$this->filters = [];
122+
$this->applyFilters();
123+
return $this;
124+
}
125+
}

src/Yoti/Entity/Profile.php

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class Profile extends BaseProfile
1818
const ATTR_EMAIL_ADDRESS = 'email_address';
1919
const ATTR_POSTAL_ADDRESS = 'postal_address';
2020
const ATTR_DOCUMENT_DETAILS = "document_details";
21+
const ATTR_DOCUMENT_IMAGES = 'document_images';
2122
const ATTR_STRUCTURED_POSTAL_ADDRESS = 'structured_postal_address';
2223

2324
/**
@@ -100,7 +101,7 @@ public function getEmailAddress()
100101
public function getPostalAddress()
101102
{
102103
$postalAddress = $this->getProfileAttribute(self::ATTR_POSTAL_ADDRESS);
103-
if (NULL === $postalAddress) {
104+
if (null === $postalAddress) {
104105
// Get it from structured_postal_address.formatted_address
105106
$postalAddress = $this->getFormattedAddress();
106107
}
@@ -120,6 +121,16 @@ public function getDocumentDetails()
120121
return $this->getProfileAttribute(self::ATTR_DOCUMENT_DETAILS);
121122
}
122123

124+
/**
125+
* Return a list of document images.
126+
*
127+
* @return array
128+
*/
129+
public function getDocumentImages()
130+
{
131+
return $this->getProfileAttribute(self::ATTR_DOCUMENT_IMAGES);
132+
}
133+
123134
/**
124135
* Return all derived attributes from the DOB e.g 'Age Over', 'Age Under'
125136
* As a list of AgeVerification
@@ -173,22 +184,20 @@ public function findAgeUnderVerification($age)
173184
private function getAgeVerificationByAttribute($ageAttr)
174185
{
175186
$ageVerifications = $this->getAgeVerifications();
176-
return isset($ageVerifications[$ageAttr]) ? $ageVerifications[$ageAttr] : NULL;
187+
return isset($ageVerifications[$ageAttr]) ? $ageVerifications[$ageAttr] : null;
177188
}
178189

179190
/**
180191
* @return null|Attribute
181192
*/
182193
private function getFormattedAddress()
183194
{
184-
$postalAddress = NULL;
195+
$postalAddress = null;
185196
// Get it from structured_postal_address.formatted_address
186197
$structuredPostalAddress = $this->getStructuredPostalAddress();
187-
if (NULL !== $structuredPostalAddress)
188-
{
198+
if (null !== $structuredPostalAddress) {
189199
$valueArr = $structuredPostalAddress->getValue();
190-
if (
191-
is_array($valueArr)
200+
if (is_array($valueArr)
192201
&& isset($valueArr['formatted_address'])
193202
) {
194203
$postalAddressValue = $valueArr['formatted_address'];
@@ -203,4 +212,4 @@ private function getFormattedAddress()
203212
}
204213
return $postalAddress;
205214
}
206-
}
215+
}

src/Yoti/Util/Profile/AttributeConverter.php

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@
88
use Compubapi\EncryptedData;
99
use Attrpubapi\Attribute as ProtobufAttribute;
1010
use Yoti\Exception\AttributeException;
11+
use Yoti\Entity\MultiValue;
1112

1213
class AttributeConverter
1314
{
1415
const CONTENT_TYPE_JSON = 5;
1516
const CONTENT_TYPE_PNG = 4;
1617
const CONTENT_TYPE_JPEG = 2;
1718
const CONTENT_TYPE_DATE = 3;
19+
const CONTENT_TYPE_MULTI_VALUE = 6;
1820

1921
/**
2022
* @param ProtobufAttribute $attribute
@@ -25,13 +27,20 @@ class AttributeConverter
2527
*/
2628
private static function convertValueBasedOnAttributeName($value, $attrName)
2729
{
28-
self::validateInput($value, $attrName);
30+
self::validateInput($value);
2931

30-
switch($attrName)
31-
{
32+
switch ($attrName) {
3233
case Profile::ATTR_DOCUMENT_DETAILS:
3334
return new DocumentDetails($value);
3435

36+
case Profile::ATTR_DOCUMENT_IMAGES:
37+
if (!($value instanceof MultiValue)) {
38+
return null;
39+
}
40+
return $value
41+
->filterInstance(Image::class)
42+
->getArrayCopy();
43+
3544
default:
3645
return $value;
3746
}
@@ -45,16 +54,11 @@ private static function convertValueBasedOnAttributeName($value, $attrName)
4554
*
4655
* @throws AttributeException
4756
*/
48-
private static function convertValueBasedOnContentType(ProtobufAttribute $protobufAttribute)
57+
private static function convertValueBasedOnContentType($value, $contentType)
4958
{
50-
$value = $protobufAttribute->getValue();
51-
$contentType = $protobufAttribute->getContentType();
52-
$attrName = $protobufAttribute->getName();
59+
self::validateInput($value);
5360

54-
self::validateInput($value, $attrName);
55-
56-
switch($contentType)
57-
{
61+
switch ($contentType) {
5862
case self::CONTENT_TYPE_JPEG:
5963
case self::CONTENT_TYPE_PNG:
6064
$imageExtension = self::imageTypeToExtension($contentType);
@@ -65,18 +69,50 @@ private static function convertValueBasedOnContentType(ProtobufAttribute $protob
6569
// Convert JSON string to an array
6670
$value = json_decode($value, true);
6771
if (json_last_error()) {
68-
throw new AttributeException("Error converting attr {$attrName} to a JSON Object");
72+
throw new AttributeException("Error converting attr to a JSON Object");
6973
}
7074
break;
7175

7276
case self::CONTENT_TYPE_DATE:
7377
$value = self::convertTimestampToDate($value);
7478
break;
79+
80+
case self::CONTENT_TYPE_MULTI_VALUE:
81+
$value = self::convertMultiValue($value);
82+
break;
7583
}
7684

7785
return $value;
7886
}
7987

88+
/**
89+
* Convert attribute value to MultiValue.
90+
*
91+
* @param string $value
92+
* @return MultiValue
93+
*/
94+
private function convertMultiValue($value)
95+
{
96+
$protoMultiValue = new \Attrpubapi\MultiValue();
97+
$protoMultiValue->mergeFromString($value);
98+
$items = [];
99+
foreach ($protoMultiValue->getValues() as $protoValue) {
100+
$item = null;
101+
try {
102+
$item = self::convertValueBasedOnContentType(
103+
$protoValue->getData(),
104+
$protoValue->getContentType()
105+
);
106+
} catch (AttributeException $e) {
107+
error_log($e->getMessage() . " (MultiValue Value ContentType: {$protoValue->getContentType()})", 0);
108+
} catch (\Exception $e) {
109+
error_log($e->getMessage(), 0);
110+
}
111+
$items[] = $item;
112+
}
113+
return new MultiValue($items);
114+
}
115+
80116
/**
81117
* Convert Protobuf Image type to an image extension.
82118
*
@@ -88,8 +124,7 @@ private static function imageTypeToExtension($type)
88124
{
89125
$type = (int)$type;
90126

91-
switch($type)
92-
{
127+
switch ($type) {
93128
case 2:
94129
$format = 'JPEG';
95130
break;
@@ -129,14 +164,17 @@ public static function getEncryptedData($data)
129164
*/
130165
public static function convertToYotiAttribute(ProtobufAttribute $protobufAttribute)
131166
{
167+
$yotiAttribute = null;
168+
132169
try {
133170
$yotiAnchorsMap = AnchorListConverter::convert(
134171
$protobufAttribute->getAnchors()
135172
);
136-
$attrName = $protobufAttribute->getName();
137173
$attrValue = AttributeConverter::convertValueBasedOnContentType(
138-
$protobufAttribute
174+
$protobufAttribute->getValue(),
175+
$protobufAttribute->getContentType()
139176
);
177+
$attrName = $protobufAttribute->getName();
140178
$attrValue = AttributeConverter::convertValueBasedOnAttributeName(
141179
$attrValue,
142180
$attrName
@@ -146,8 +184,9 @@ public static function convertToYotiAttribute(ProtobufAttribute $protobufAttribu
146184
$attrValue,
147185
$yotiAnchorsMap
148186
);
187+
} catch (AttributeException $e) {
188+
error_log($e->getMessage() . " (Attribute: {$protobufAttribute->getName()})", 0);
149189
} catch (\Exception $e) {
150-
$yotiAttribute = NULL;
151190
error_log($e->getMessage(), 0);
152191
}
153192

@@ -166,14 +205,13 @@ public static function convertTimestampToDate($value)
166205

167206
/**
168207
* @param string $value
169-
* @param string $attrName
170208
*
171209
* @throws AttributeException
172210
*/
173-
private static function validateInput($value, $attrName)
211+
private static function validateInput($value)
174212
{
175213
if (empty($value)) {
176-
throw new AttributeException("Warning: {$attrName} value is NULL");
214+
throw new AttributeException("Warning: Value is NULL");
177215
}
178216
}
179-
}
217+
}

0 commit comments

Comments
 (0)