Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.26% covered (success)
93.26%
83 / 89
33.33% covered (danger)
33.33%
1 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
CrudTransactionMWAssociativeArrayMarshaller
93.26% covered (success)
93.26%
83 / 89
33.33% covered (danger)
33.33%
1 / 3
45.62
0.00% covered (danger)
0.00%
0 / 1
 deserializeRequest
88.10% covered (warning)
88.10%
37 / 42
0.00% covered (danger)
0.00%
0 / 1
23.89
 serializeResponse
100.00% covered (success)
100.00%
37 / 37
100.00% covered (success)
100.00%
1 / 1
18
 instancesArrayToInstancesByClassName
90.00% covered (success)
90.00%
9 / 10
0.00% covered (danger)
0.00%
0 / 1
4.02
1<?php
2
3namespace MediaWiki\WikispeechSpeechDataCollector\Crud\Transaction;
4
5/**
6 * @file
7 * @ingroup Extensions
8 * @license GPL-2.0-or-later
9 */
10
11use MediaWiki\WikispeechSpeechDataCollector\Domain\GetPersistentClassName;
12use MediaWiki\WikispeechSpeechDataCollector\Domain\Persistent;
13use MediaWiki\WikispeechSpeechDataCollector\Domain\PersistentMWAssociateArrayDeserializer;
14use MediaWiki\WikispeechSpeechDataCollector\Domain\PersistentMWAssociativeArraySerializer;
15use MediaWiki\WikispeechSpeechDataCollector\Uuid;
16use MWException;
17
18/**
19 * @todo This is part of an internal development API. It should be removed before deployment.
20 *
21 * Associative array serialization and deserialization for CRUD transaction API.
22 *
23 * @since 0.1.0
24 */
25class CrudTransactionMWAssociativeArrayMarshaller implements CrudTransactionMarshaller {
26
27    private const PERSISTENT_NAMESPACE = '\\MediaWiki\\WikispeechSpeechDataCollector\\Domain\\';
28
29    /**
30     * @param array $transaction Associative array representation
31     * @return CrudTransactionRequest
32     * @throws CrudTransactionException|MWException
33     */
34    public function deserializeRequest( $transaction ): CrudTransactionRequest {
35        $request = new CrudTransactionRequest();
36
37        // CREATE
38
39        if ( array_key_exists( 'create', $transaction ) && $transaction['create'] ) {
40            if ( !is_array( $transaction['create'] ) ) {
41                throw new CrudTransactionException( 'Field "create" must be an array or a falsy value.' );
42            }
43            foreach ( $transaction[ 'create' ] as $persistentClass => $serializedInstances ) {
44                $absolutePersistentClass = self::PERSISTENT_NAMESPACE . $persistentClass;
45                foreach ( $serializedInstances as $serializedInstance ) {
46                    /** @var Persistent $instance */
47                    $instance = new $absolutePersistentClass();
48                    $instance = $instance->accept( new PersistentMWAssociateArrayDeserializer( $serializedInstance ) );
49                    $request->addCreate( $instance );
50                }
51            }
52        }
53
54        // READ
55
56        if ( array_key_exists( 'read', $transaction ) && $transaction['read'] ) {
57            if ( !is_array( $transaction['read'] ) ) {
58                throw new CrudTransactionException( 'Field "read" must be an array or a falsy value.' );
59            }
60            foreach ( $transaction[ 'read' ] as $persistentClass => $identities ) {
61                $absolutePersistentClass = self::PERSISTENT_NAMESPACE . $persistentClass;
62                foreach ( $identities as $identity ) {
63                    /** @var Persistent $instance */
64                    $instance = new $absolutePersistentClass();
65                    $instance->setIdentity( Uuid::asBytes( $identity ) );
66                    $request->addRead( $instance );
67                }
68            }
69        }
70
71        // UPDATE
72
73        if ( array_key_exists( 'update', $transaction ) && $transaction['update'] ) {
74            if ( !is_array( $transaction['update'] ) ) {
75                throw new CrudTransactionException( 'Field "update" must be an array or a falsy value.' );
76            }
77            foreach ( $transaction[ 'update' ] as $persistentClass => $serializedInstances ) {
78                $absolutePersistentClass = self::PERSISTENT_NAMESPACE . $persistentClass;
79                foreach ( $serializedInstances as $serializedInstance ) {
80                    /** @var Persistent $instance */
81                    $instance = new $absolutePersistentClass();
82                    $instance = $instance->accept( new PersistentMWAssociateArrayDeserializer( $serializedInstance ) );
83                    $request->addUpdate( $instance );
84                }
85            }
86        }
87
88        // DELETE
89
90        if ( array_key_exists( 'delete', $transaction ) && $transaction['delete'] ) {
91            if ( !is_array( $transaction['delete'] ) ) {
92                throw new CrudTransactionException( 'Field "delete" must be an array or a falsy value.' );
93            }
94            foreach ( $transaction[ 'delete' ] as $persistentClass => $identities ) {
95                $absolutePersistentClass = self::PERSISTENT_NAMESPACE . $persistentClass;
96                foreach ( $identities as $identity ) {
97                    /** @var Persistent $instance */
98                    $instance = new $absolutePersistentClass();
99                    $instance->setIdentity( Uuid::asBytes( $identity ) );
100                    $request->addDelete( $instance );
101                }
102            }
103        }
104
105        if ( array_key_exists( 'reference', $transaction ) ) {
106            $request->setReference( $transaction['reference'] );
107        }
108
109        if ( array_key_exists( 'readGraph', $transaction ) ) {
110            $request->setReadGraph( $transaction['readGraph'] );
111        }
112
113        return $request;
114    }
115
116    /**
117     * @param CrudTransactionResponse $response
118     * @return array Associative array
119     */
120    public function serializeResponse( CrudTransactionResponse $response ) {
121        $array = [];
122        $serializer = new PersistentMWAssociativeArraySerializer();
123
124        if ( $response->getReference() !== null ) {
125            $array['reference'] = $response->getReference();
126        }
127
128        // CREATED
129
130        if ( $response->getCreated() ) {
131            $createdByClass = $this->instancesArrayToInstancesByClassName( $response->getCreated() );
132            if ( $createdByClass ) {
133                $array['created'] = [];
134                foreach ( $createdByClass as $class => $instances ) {
135                    $array['created'][$class] = [];
136                    foreach ( $instances as /** @var Persistent */ $instance ) {
137                        array_push( $array['created'][$class], $instance->accept( $serializer ) );
138                    }
139                }
140            }
141        }
142
143        // READ
144
145        if ( $response->getRead() ) {
146            $readByClass = $this->instancesArrayToInstancesByClassName( $response->getRead() );
147            if ( $readByClass ) {
148                $array['read'] = [];
149                foreach ( $readByClass as $class => $instances ) {
150                    $array['read'][$class] = [];
151                    foreach ( $instances as /** @var Persistent */ $instance ) {
152                        array_push( $array['read'][$class], $instance->accept( $serializer ) );
153                    }
154                }
155            }
156        }
157
158        // UPDATED
159
160        if ( $response->getUpdated() ) {
161            $updatedByClass = $this->instancesArrayToInstancesByClassName( $response->getUpdated() );
162            if ( $updatedByClass ) {
163                $array['updated'] = [];
164                foreach ( $updatedByClass as $class => $instances ) {
165                    $array['updated'][$class] = [];
166                    foreach ( $instances as /** @var Persistent */ $instance ) {
167                        // @todo only identity?
168                        array_push( $array['updated'][$class], $instance->accept( $serializer ) );
169                    }
170                }
171            }
172        }
173
174        // DELETED
175
176        if ( $response->getDeleted() ) {
177            $deletedByClass = $this->instancesArrayToInstancesByClassName( $response->getDeleted() );
178            if ( $deletedByClass ) {
179                $array['deleted'] = [];
180                foreach ( $deletedByClass as $class => $instances ) {
181                    $array['deleted'][$class] = [];
182                    foreach ( $instances as /** @var Persistent */ $instance ) {
183                        array_push( $array['deleted'][$class], Uuid::asHex( $instance->getIdentity(), true ) );
184                    }
185                }
186            }
187        }
188
189        return $array;
190    }
191
192    /**
193     * Converts an array of persistent instances
194     * to an associative array with one field per persistent class name
195     * containing each an array with the persistent instances.
196     *
197     * @param array|null $instances
198     * @return array|null Associative array class name => array instances, or null.
199     */
200    private function instancesArrayToInstancesByClassName(
201        ?array $instances
202    ): ?array {
203        if ( $instances === null ) {
204            return null;
205        }
206        $getPersistentClassName = new GetPersistentClassName();
207        $instancesByClassName = [];
208        foreach ( $instances as /** @var Persistent */ $instance ) {
209            $className = $instance->accept( $getPersistentClassName );
210            if ( !array_key_exists( $className, $instancesByClassName ) ) {
211                $instancesByClassName[$className] = [];
212            }
213            array_push( $instancesByClassName[$className], $instance );
214        }
215        return $instancesByClassName;
216    }
217}