Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
78.57% covered (warning)
78.57%
44 / 56
28.57% covered (danger)
28.57%
2 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
CheckConstraintsRdf
78.57% covered (warning)
78.57%
44 / 56
28.57% covered (danger)
28.57%
2 / 7
17.21
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 newFromGlobalState
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 getName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 requiresWrite
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 requiresUnblock
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 cleanupGuid
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 onView
95.12% covered (success)
95.12%
39 / 41
0.00% covered (danger)
0.00%
0 / 1
9
1<?php
2
3namespace WikibaseQuality\ConstraintReport\Api;
4
5use Article;
6use FormlessAction;
7use IContextSource;
8use Wikibase\Lib\Store\EntityIdLookup;
9use Wikibase\Repo\Rdf\RdfVocabulary;
10use Wikibase\Repo\WikibaseRepo;
11use WikibaseQuality\ConstraintReport\ConstraintCheck\Result\CheckResult;
12use WikibaseQuality\ConstraintReport\ConstraintCheck\Result\NullResult;
13use WikibaseQuality\ConstraintReport\ConstraintsServices;
14use Wikimedia\Purtle\RdfWriterFactory;
15
16/**
17 * Produce constraint check results in RDF.
18 * Only returns cached constraint check results for now.
19 *
20 * @license GPL-2.0-or-later
21 */
22class CheckConstraintsRdf extends FormlessAction {
23
24    /**
25     * @var EntityIdLookup
26     */
27    private $entityIdLookup;
28    /**
29     * @var ResultsSource
30     */
31    private $resultsSource;
32    /**
33     * @var RdfVocabulary
34     */
35    private $rdfVocabulary;
36
37    /**
38     * @param Article $page
39     * @param IContextSource $context
40     * @param ResultsSource $resultsSource
41     * @param EntityIdLookup $entityIdLookup
42     * @param RdfVocabulary $rdfVocabulary
43     */
44    public function __construct(
45        object $page,
46        IContextSource $context,
47        ResultsSource $resultsSource,
48        EntityIdLookup $entityIdLookup,
49        RdfVocabulary $rdfVocabulary
50    ) {
51        parent::__construct( $page, $context );
52        $this->resultsSource = $resultsSource;
53        $this->entityIdLookup = $entityIdLookup;
54        $this->rdfVocabulary = $rdfVocabulary;
55    }
56
57    /**
58     * @param Article $page
59     * @param IContextSource $context
60     * @return CheckConstraintsRdf
61     */
62    public static function newFromGlobalState(
63        object $page,
64        IContextSource $context
65    ) {
66        return new static(
67            $page,
68            $context,
69            ConstraintsServices::getResultsSource(),
70            WikibaseRepo::getEntityIdLookup(),
71            WikibaseRepo::getRdfVocabulary()
72        );
73    }
74
75    /**
76     * Return the name of the action this object responds to
77     * @since 1.17
78     *
79     * @return string Lowercase name
80     */
81    public function getName() {
82        return 'constraintsrdf';
83    }
84
85    /**
86     * Whether this action requires the wiki not to be locked
87     * @since 1.17
88     *
89     * @return bool
90     */
91    public function requiresWrite() {
92        return false;
93    }
94
95    /**
96     * @see Action::requiresUnblock
97     *
98     * @return bool Always false.
99     */
100    public function requiresUnblock() {
101        return false;
102    }
103
104    /**
105     * Cleanup GUID string so it's OK for RDF.
106     * Should match what we're doing on RDF generation.
107     * @param string $guid
108     * @return string
109     */
110    private function cleanupGuid( $guid ) {
111        return preg_replace( '/[^\w-]/', '-', $guid );
112    }
113
114    /**
115     * Show something on GET request.
116     * @return string|null Will be added to the HTMLForm if present, or just added to the
117     *     output if not.  Return null to not add anything
118     */
119    public function onView() {
120        $response = $this->getRequest()->response();
121        $this->getOutput()->disable();
122
123        if ( !$this->resultsSource instanceof CachingResultsSource ) {
124            // TODO: make configurable whether only cached results are returned
125            $response->statusHeader( 501 ); // Not Implemented
126            return null;
127        }
128
129        $entityId = $this->entityIdLookup->getEntityIdForTitle( $this->getTitle() );
130        if ( $entityId === null ) {
131            $response->statusHeader( 404 ); // Not Found
132            return null;
133        }
134        $revId = $this->getRequest()->getInt( 'revision' );
135
136        $results = $this->resultsSource->getStoredResults( $entityId, $revId );
137        if ( $results === null ) {
138            $response->statusHeader( 204 ); // No Content
139            return null;
140        }
141
142        $format = 'ttl'; // TODO: make format an option
143
144        $writerFactory = new RdfWriterFactory();
145        $formatName = $writerFactory->getFormatName( $format );
146        $contentType = $writerFactory->getMimeTypes( $formatName )[0];
147
148        $writer = $writerFactory->getWriter( $formatName );
149        foreach ( [ RdfVocabulary::NS_STATEMENT, RdfVocabulary::NS_ONTOLOGY ] as $ns ) {
150            $writer->prefix( $ns, $this->rdfVocabulary->getNamespaceURI( $ns ) );
151        }
152        $writer->start();
153        $writtenAny = false;
154
155        foreach ( $results->getArray() as $checkResult ) {
156            if ( $checkResult instanceof NullResult ) {
157                continue;
158            }
159            if ( $checkResult->getStatus() === CheckResult::STATUS_BAD_PARAMETERS ) {
160                continue;
161            }
162            $writtenAny = true;
163            $writer->about( RdfVocabulary::NS_STATEMENT,
164                $this->cleanupGuid( $checkResult->getContextCursor()->getStatementGuid() ) )
165                ->say( RdfVocabulary::NS_ONTOLOGY, 'hasViolationForConstraint' )
166                ->is( RdfVocabulary::NS_STATEMENT,
167                    $this->cleanupGuid( $checkResult->getConstraint()->getConstraintId() ) );
168        }
169        $writer->finish();
170        if ( $writtenAny ) {
171            $response->header( "Content-Type: $contentType; charset=UTF-8" );
172            echo $writer->drain();
173        } else {
174            // Do not output RDF if we haven't written any actual statements. Output 204 instead
175            $writer->drain();
176            $response->statusHeader( 204 ); // No Content
177        }
178        return null;
179    }
180
181}