Passed
Pull Request — master (#11)
by Jeroen De
19:11
created

ServiceFactory::getEntityHandlers()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 0
1
<?php
2
3
namespace Queryr\Replicator;
4
5
use DataValues\BooleanValue;
6
use DataValues\Deserializers\DataValueDeserializer;
7
use DataValues\Geo\Values\GlobeCoordinateValue;
8
use DataValues\MonolingualTextValue;
9
use DataValues\MultilingualTextValue;
10
use DataValues\NumberValue;
11
use DataValues\QuantityValue;
12
use DataValues\Serializers\DataValueSerializer;
13
use DataValues\StringValue;
14
use DataValues\TimeValue;
15
use DataValues\UnknownValue;
16
use Doctrine\DBAL\Connection;
17
use Doctrine\DBAL\DBALException;
18
use Doctrine\DBAL\DriverManager;
19
use Monolog\Formatter\JsonFormatter;
20
use Monolog\Formatter\LineFormatter;
21
use Monolog\Handler\BufferHandler;
22
use Monolog\Handler\StreamHandler;
23
use Monolog\Logger;
24
use Psr\Log\LoggerInterface;
25
use Psr\Log\NullLogger;
26
use Queryr\EntityStore\EntityStoreConfig;
27
use Queryr\EntityStore\EntityStoreFactory;
28
use Queryr\EntityStore\EntityStoreInstaller;
29
use Queryr\Replicator\Importer\CompositeReporter;
30
use Queryr\Replicator\Importer\EntityHandlers\EntityStoreEntityHandler;
31
use Queryr\Replicator\Importer\EntityHandlers\QueryEngineEntityHandler;
32
use Queryr\Replicator\Importer\EntityHandlers\TermStoreEntityHandler;
33
use Queryr\Replicator\Importer\LoggingReporter;
34
use Queryr\Replicator\Importer\PageImporter;
35
use Queryr\Replicator\Importer\PageImportReporter;
36
use Queryr\Replicator\Importer\PagesImporter;
37
use Queryr\Replicator\Importer\StatsReporter;
38
use Queryr\Replicator\Model\EntityPage;
39
use Queryr\TermStore\TermStoreConfig;
40
use Queryr\TermStore\TermStoreInstaller;
41
use Queryr\TermStore\TermStoreWriter;
42
use RuntimeException;
43
use Wikibase\DataModel\Entity\BasicEntityIdParser;
44
use Wikibase\DataModel\Entity\EntityIdValue;
45
use Wikibase\DataModel\SerializerFactory;
46
use Wikibase\InternalSerialization\DeserializerFactory;
47
use Wikibase\JsonDumpReader\DumpReader;
48
use Wikibase\JsonDumpReader\JsonDumpFactory;
49
use Wikibase\QueryEngine\SQLStore\DataValueHandlersBuilder;
50
use Wikibase\QueryEngine\SQLStore\SQLStore;
51
use Wikibase\QueryEngine\SQLStore\StoreConfig;
52
use Wikibase\QueryEngine\SQLStore\StoreSchema;
53
54
/**
55
 * @licence GNU GPL v2+
56
 * @author Jeroen De Dauw < [email protected] >
57
 *
58
 * @SuppressWarnings(PHPMD)
59
 */
60
class ServiceFactory {
61
62
	const QUERY_ENGINE_PREFIX = 'qe_';
63
	const ENTITY_STORE_PREFIX = 'es_';
64
	const TERMS_STORE_PREFIX = 'ts_';
65
66
	public static function newFromConnection( Connection $connection ) {
67
		return new self( $connection );
68
	}
69
70
	/**
71
	 * @throws RuntimeException
72
	 */
73
	public static function newFromConfig(): self {
74
		$config = DatabaseConfigFile::newInstance()->read();
75
76
		try {
77
			$connection = DriverManager::getConnection( $config );
78
		}
79
		catch ( DBALException $ex ) {
80
			throw new RuntimeException(
81
				'Could not establish database connection: ' . $ex->getMessage()
82
			);
83
		}
84
85
		$factory = new self( $connection );
86
		$factory->setLogger( $factory->newProductionLogger() );
87
88
		return $factory;
89
	}
90
91
	/**
92
	 * @var Connection
93
	 */
94
	private $connection;
95
96
	/**
97
	 * @var LoggerInterface
98
	 */
99
	private $logger;
100
101
	private function __construct( Connection $connection ) {
102
		$this->connection = $connection;
103
		$this->logger = new NullLogger();
104
	}
105
106
	public function newProductionLogger() {
107
		$logger = new Logger( 'Replicator logger' );
108
109
		$streamHandler = new StreamHandler( $this->newLoggerPath( ( new \DateTime() )->format( 'Y-m-d\TH:i:s\Z' ) ) );
110
		$bufferHandler = new BufferHandler( $streamHandler, 500, Logger::INFO, true, true );
111
		$streamHandler->setFormatter( new LineFormatter( "%message%\n" ) );
112
		$logger->pushHandler( $bufferHandler );
113
114
		$errorHandler = new StreamHandler( $this->newLoggerPath( 'error' ), Logger::ERROR );
115
		$errorHandler->setFormatter( new JsonFormatter() );
116
		$logger->pushHandler( $errorHandler );
117
118
		return $logger;
119
	}
120
121
	private function newLoggerPath( $fileName ) {
122
		return __DIR__ . '/../var/log/' . $fileName . '.log';
123
	}
124
125
	public function setLogger( LoggerInterface $logger ) {
126
		$this->logger = $logger;
127
	}
128
129
	public function newQueryEngineInstaller() {
130
		$sqlStore = $this->newSqlStore();
131
		return $sqlStore->newInstaller( $this->connection->getSchemaManager() );
132
	}
133
134
	public function newQueryEngineUninstaller() {
135
		$sqlStore = $this->newSqlStore();
136
		return $sqlStore->newUninstaller( $this->connection->getSchemaManager() );
137
	}
138
139
	private function newSqlStore() {
140
		$handlers = new DataValueHandlersBuilder();
141
142
		$schema = new StoreSchema(
143
			self::QUERY_ENGINE_PREFIX,
144
			$handlers->withSimpleMainSnakHandlers()->getHandlers()
145
		);
146
147
		$config = new StoreConfig( 'QueryR Replicator QueryEngine' );
148
149
		return new SQLStore( $schema, $config );
150
	}
151
152
	public function newEntityStoreInstaller() {
153
		return new EntityStoreInstaller(
154
			$this->connection->getSchemaManager(),
155
			new EntityStoreConfig( self::ENTITY_STORE_PREFIX )
156
		);
157
	}
158
159
	public function newPagesImporter( PageImportReporter $reporter,
160
		StatsReporter $statsReporter, callable $onAborted = null ) {
161
162
		return new PagesImporter(
163
			$this->newPageImporter( $reporter ),
164
			$statsReporter,
165
			$onAborted
166
		);
167
	}
168
169
	public function newJsonEntityPageIterator( DumpReader $reader ) {
170
		$entityPageIterator = function( \Iterator $dumpIterator ) {
171
			foreach ( $dumpIterator as $entity ) {
172
				yield new EntityPage(
173
					json_encode( $entity ),
174
					$entity['type'] === 'property' ? 'Property:' . $entity['id'] : $entity['id'],
175
					0,
176
					0,
177
					( new \DateTime() )->format( 'Y-m-d\TH:i:s\Z' )
178
				);
179
			}
180
		};
181
182
		$dumpIterator = ( new JsonDumpFactory() )->newObjectDumpIterator( $reader );
183
		return $entityPageIterator( $dumpIterator );
184
	}
185
186
	public function newPageImporter( PageImportReporter $reporter ) {
187
		$loggingReporter = new LoggingReporter( $this->logger );
188
		$compositeReporter = new CompositeReporter( $loggingReporter, $reporter );
189
190
		return new PageImporter(
191
			$this->newLegacyEntityDeserializer(),
192
			$this->getEntityHandlers(),
193
			[
194
				new EntityStoreEntityHandler( $this->newEntityStore() )
195
			],
196
			$compositeReporter
197
		);
198
	}
199
200
	private function getEntityHandlers() {
201
		$handlers = [
202
			new TermStoreEntityHandler( $this->newTermStoreWriter() )
203
		];
204
205
		if ( defined( 'WIKIBASE_QUERYENGINE_VERSION' ) ) {
206
			$handlers[] = new QueryEngineEntityHandler( $this->newQueryStoreWriter() );
207
		}
208
209
		return $handlers;
210
	}
211
212
	public function newEntityStore() {
213
		return $this->newEntityStoreFactory()->newEntityStore();
214
	}
215
216
	public function newItemStore() {
217
		return $this->newEntityStoreFactory()->newItemStore();
218
	}
219
220
	private function newEntityStoreFactory() {
221
		return new EntityStoreFactory(
222
			$this->connection,
223
			new EntityStoreConfig( self::ENTITY_STORE_PREFIX )
224
		);
225
	}
226
227
	public function newTermStoreWriter() {
228
		return new TermStoreWriter(
229
			$this->connection,
230
			new TermStoreConfig( self::TERMS_STORE_PREFIX )
231
		);
232
	}
233
234
	private function newDataValueDeserializer() {
235
		return new DataValueDeserializer( [
236
			'boolean' => BooleanValue::class,
237
			'number' => NumberValue::class,
238
			'string' => StringValue::class,
239
			'unknown' => UnknownValue::class,
240
			'globecoordinate' => GlobeCoordinateValue::class,
241
			'monolingualtext' => MonolingualTextValue::class,
242
			'multilingualtext' => MultilingualTextValue::class,
243
			'quantity' => QuantityValue::class,
244
			'time' => TimeValue::class,
245
			'wikibase-entityid' => EntityIdValue::class,
246
		] );
247
	}
248
249
	private function newEntityIdParser() {
250
		return new BasicEntityIdParser();
251
	}
252
253
	public function newLegacyEntityDeserializer() {
254
		$factory = new DeserializerFactory(
255
			$this->newDataValueDeserializer(),
256
			$this->newEntityIdParser()
257
		);
258
259
		return $factory->newEntityDeserializer();
260
	}
261
262
	public function newCurrentEntityDeserializer() {
263
		$factory = new \Wikibase\DataModel\DeserializerFactory(
264
			$this->newDataValueDeserializer(),
265
			$this->newEntityIdParser()
266
		);
267
268
		return $factory->newEntityDeserializer();
269
	}
270
271
	public function newCurrentEntitySerializer() {
272
		$factory = new SerializerFactory(
273
			new DataValueSerializer()
274
		);
275
276
		return $factory->newEntitySerializer();
277
	}
278
279
	public function newQueryStoreWriter() {
280
		return $this->newSqlStore()->newWriter( $this->connection );
281
	}
282
283
	public function newTermStoreInstaller() {
284
		return new TermStoreInstaller(
285
			$this->connection->getSchemaManager(),
286
			new TermStoreConfig( self::TERMS_STORE_PREFIX )
287
		);
288
	}
289
290
}
291