Passed
Push — master ( 71f500...b67e96 )
by Russell
03:25
created

UpdateProofController::index()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 13
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @author  Russell Michell 2018 <[email protected]>
5
 * @package silverstripe-verifiable
6
 */
7
8
namespace PhpTek\Verifiable\Controller;
9
10
use SilverStripe\ORM\DataObject;
11
use SilverStripe\Control\HTTPRequest;
12
use SilverStripe\ORM\DB;
13
use SilverStripe\Core\ClassInfo;
14
use SilverStripe\Control\Controller;
15
use SilverStripe\Core\Injector\Injector;
16
use SilverStripe\Control\Director;
17
use SilverStripe\Versioned\Versioned;
18
use PhpTek\Verifiable\ORM\FieldType\ChainpointProof;
19
use PhpTek\Verifiable\Verify\VerifiableExtension;
20
21
/**
22
 * Controller available to CLI or AJAX for updating all or selected versions.
23
 *
24
 * @todo Check with Tierion API docs: How many nodes should be submitted to? And why if I submit to only one, does the network not synchronise it?
25
 * @todo Update logic to write hashes to all 3 nodes, not just the first as-is the case in the client() method.
26
 */
27
class UpdateProofController extends Controller
28
{
29
    /**
30
     * @param  HTTPRequest $request
31
     * @throws Exception
32
     */
33
    public function index(HTTPRequest $request = null)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

33
    public function index(/** @scrutinizer ignore-unused */ HTTPRequest $request = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
34
    {
35
        if (!$backend = $this->verifiableService->getBackend()->name() === 'chainpoint') {
0 ignored issues
show
Bug Best Practice introduced by
The property verifiableService does not exist on PhpTek\Verifiable\Controller\UpdateProofController. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug introduced by
The method getBackend() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

35
        if (!$backend = $this->verifiableService->/** @scrutinizer ignore-call */ getBackend()->name() === 'chainpoint') {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
36
            throw new \Exception(sprintf('Cannot use %s backend with %s!', $backend, __CLASS__));
0 ignored issues
show
Bug introduced by
$backend of type false is incompatible with the type string expected by parameter $args of sprintf(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

36
            throw new \Exception(sprintf('Cannot use %s backend with %s!', /** @scrutinizer ignore-type */ $backend, __CLASS__));
Loading history...
37
        }
38
39
        $this->log('NOTICE', 'Start: Processing proofs...', 2);
40
41
        // Get all records with partial proofs. Attempt to fetch their full proofs
42
        // from Tierion, then write them back to local DB
43
        $this->updateVersions();
44
45
        $this->log('NOTICE', 'Finish: Processing proofs.');
46
    }
47
48
    /**
49
     * Fetch all partial proofs, ready to make them whole again by re-writing to
50
     * each xxx_Versions table's "Proof" field.
51
     *
52
     * @return array
53
     */
54
    protected function updateVersions()
55
    {
56
        // Get decorated classes
57
        $dataObjectSublasses = ClassInfo::getValidSubClasses(DataObject::class);
58
        $candidates = [];
59
60
        foreach ($dataObjectSublasses as $class) {
61
            $obj = Injector::inst()->create($class);
62
63
            if (!$obj->hasExtension(VerifiableExtension::class)) {
64
                continue;
65
            }
66
67
            $this->log('NOTICE', "Processing class: $class");
68
69
            foreach ($class::get() as $item) {
70
                // TODO Get all versions that have non-duplicated proof-hashes
71
                $versions = Versioned::get_all_versions($class, $item->ID)->sort('Version ASC');
72
73
                foreach ($versions as $record) {
74
                    $proof = $record->dbObject('Proof');
75
76
                    if ($proof->isInitial()) {
77
                        $this->log('NOTICE', "\tInitial proof found for ID #{$record->RecordID} and version {$record->Version}");
78
                        $this->log('NOTICE', "\tRequesting proof via UUID {$proof->getHashIdNode()[0]}");
79
80
                        // I don't understand the Tierion network... if we submit a hash to one IP
81
                        // the rest of the network is not updated. So we need to pre-seed the service
82
                        // with the saved nodes...unless it's only verified proofs that get propagated..??
83
                        $nodes = $record->dbObject('Extra')->getStoreAsArray();
84
                        $uuid = $proof->getHashIdNode()[0];
85
                        $this->verifiableService->setExtra($nodes);
0 ignored issues
show
Bug Best Practice introduced by
The property verifiableService does not exist on PhpTek\Verifiable\Controller\UpdateProofController. Since you implemented __get, consider adding a @property annotation.
Loading history...
86
87
                        $this->log('NOTICE', sprintf('Calling %s/proofs/%s', $nodes[0], $uuid));
88
89
                        // Don't attempt to write anything that isn't a full proof
90
                        $response = $this->verifiableService->call('read', $uuid);
91
                        $isFull = ChainpointProof::create()
92
                                ->setValue($response)
93
                                ->isFull();
94
95
                        if ($isFull) {
96
                            $this->log('NOTICE', "Full proof fetched. Updating record with ID #{$record->RecordID} and version $version");
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $version does not exist. Did you maybe mean $versions?
Loading history...
97
                            $this->updateQuery($record, $version, $response);
98
                        } else {
99
                            $this->log('WARN', "No full proof found for record with ID #{$record->RecordID} and version $version");
100
                        }
101
                    }
102
                }
103
            }
104
        }
105
106
        return $candidates;
107
    }
108
109
    /**
110
     * Use the lowest level of the ORM to update the xxx_Versions table directly
111
     * with a proof.
112
     *
113
     * @param type $id
114
     * @param type $version
0 ignored issues
show
Bug introduced by
The type PhpTek\Verifiable\Controller\type was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
115
     */
116
    protected function updateQuery($object, $version, $proof)
117
    {
118
        $table = sprintf('%s_Versions', $object->config()->get('table_name'));
119
        $sql = sprintf(
120
            'UPDATE "%s" SET "Proof" = \'%s\' WHERE "RecordID" = %d AND "Version" = %d',
121
            $table,
122
            $proof,
123
            $object->ID,
124
            $version
125
        );
126
127
        DB::query($sql);
128
129
        $this->log('NOTICE', "Version #$version of record #{$object->ID} updated.");
130
    }
131
132
    /**
133
     * @param  string $type
134
     * @param  string $msg
135
     * @param  int    $newLine
136
     * @return void
137
     * @todo   Declare a custom Monolog\Formatter\FormatterInterface
138
     */
139
    private function log(string $type, string $msg, int $newLines = 1)
140
    {
141
        $lb = Director::is_cli() ? PHP_EOL : '<br/>';
142
143
        echo sprintf('[%s] %s%s', $type, $msg, str_repeat($lb, $newLines));
144
    }
145
146
}
147