Client::getChannel()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 3
nop 1
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
ccs 6
cts 6
cp 1
crap 3
1
<?php
2
3
/**
4
 * Copyright 2017 American Express Travel Related Services Company, Inc.
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
15
 * or implied. See the License for the specific language governing
16
 * permissions and limitations under the License.
17
 */
18
19
declare(strict_types=1);
20
21
namespace AmericanExpress\HyperledgerFabricClient;
22
23
use AmericanExpress\HyperledgerFabricClient\Channel\ChannelFactoryInterface;
24
use AmericanExpress\HyperledgerFabricClient\Channel\ChannelInterface;
25
use AmericanExpress\HyperledgerFabricClient\Channel\ChannelProviderInterface;
26
use AmericanExpress\HyperledgerFabricClient\Exception\ExceptionInterface;
27
use AmericanExpress\HyperledgerFabricClient\Exception\RuntimeException;
28
use AmericanExpress\HyperledgerFabricClient\Peer\PeerInterface;
29
use AmericanExpress\HyperledgerFabricClient\Peer\UnaryCallResolver;
30
use AmericanExpress\HyperledgerFabricClient\Proposal\ProposalProcessorInterface;
31
use AmericanExpress\HyperledgerFabricClient\Peer\UnaryCallResolverInterface;
32
use AmericanExpress\HyperledgerFabricClient\Proposal\ResponseCollection;
33
use AmericanExpress\HyperledgerFabricClient\Signatory\SignatoryInterface;
34
use AmericanExpress\HyperledgerFabricClient\Transaction\TransactionOptions;
35
use AmericanExpress\HyperledgerFabricClient\User\UserContextInterface;
36
use Hyperledger\Fabric\Protos\Peer\Proposal;
37
use Hyperledger\Fabric\Protos\Peer\SignedProposal;
38
39
/**
40
 * #### Example Usage
41
 *
42
 * ```php
43
 * $client = ClientFactory::fromConfig(new ClientConfig([]));
44
 *
45
 * $proposal = new Proposal();
46
 * $options = new TransactionOptions();
47
 *
48
 * // Set up and configure `Proposal` and `TransactionOptions`
49
 *
50
 * $proposalResponseCollection = $client->processProposal($proposal, $options);
51
 * ```
52
 */
53
final class Client implements ChannelProviderInterface, ProposalProcessorInterface
54
{
55
    /**
56
     * @var UserContextInterface
57
     */
58
    private $user;
59
60
    /**
61
     * @var ChannelFactoryInterface
62
     */
63
    private $channelFactory;
64
65
    /**
66
     * @var SignatoryInterface
67
     */
68
    private $signatory;
69
70
    /**
71
     * @var ChannelInterface[]
72
     */
73
    private $channels = [];
74
75
    /**
76
     * @var UnaryCallResolverInterface
77
     */
78
    private $unaryCallResolver;
79
80
    /**
81
     * Client constructor.
82
     * @param UserContextInterface $user
83
     * @param SignatoryInterface $signatory
84
     * @param ChannelFactoryInterface $channelFactory
85
     * @param UnaryCallResolverInterface|null $unaryCallResolver
86
     */
87 5
    public function __construct(
88
        UserContextInterface $user,
89
        SignatoryInterface $signatory,
90
        ChannelFactoryInterface $channelFactory,
91
        UnaryCallResolverInterface $unaryCallResolver = null
92
    ) {
93 5
        $this->user = $user;
94 5
        $this->signatory = $signatory;
95 5
        $this->channelFactory = $channelFactory;
96 5
        $this->unaryCallResolver = $unaryCallResolver ?: new UnaryCallResolver();
97 5
    }
98
99
    /**
100
     * Creates a configured `Channel` object
101
     *
102
     * @param string $name
103
     * @return ChannelInterface
104
     * @throws RuntimeException
105
     */
106 2
    public function getChannel(string $name): ChannelInterface
107
    {
108 2
        if (!\array_key_exists($name, $this->channels)) {
109
            try {
110 2
                $this->channels[$name] = $this->channelFactory->create($name, $this, $this->user);
111 1
            } catch (ExceptionInterface $e) {
112 1
                throw new RuntimeException('Cannot create requested Channel', 0, $e);
113
            }
114
        }
115
116 1
        return $this->channels[$name];
117
    }
118
119
    /**
120
     * Signs and Submits a `Proposal` to the network based on provided `TransactionOptions`
121
     *
122
     * @param Proposal $proposal
123
     * @param TransactionOptions|null $options
124
     * @return ResponseCollection
125
     * @throws ExceptionInterface
126
     */
127 3
    public function processProposal(
128
        Proposal $proposal,
129
        TransactionOptions $options
130
    ): ResponseCollection {
131 3
        $privateKey = $this->user->getOrganization()->getPrivateKey();
132
133 3
        $signedProposal = $this->signatory->signProposal($proposal, new \SplFileObject($privateKey));
134
135 3
        return $this->processSignedProposal($signedProposal, $options);
136
    }
137
138
    /**
139
     * The SignedProposal instances is asynchronously transmitted to Peers. This method
140
     * waits until all Responses are collected and returns the ResponseCollection.
141
     *
142
     * Each Response in the Collection wraps a ProposalResponse upon success, or an Exception upon failure.
143
     *
144
     * @param SignedProposal $proposal
145
     * @param TransactionOptions|null $options
146
     * @return ResponseCollection
147
     * @throws RuntimeException
148
     */
149 3
    private function processSignedProposal(
150
        SignedProposal $proposal,
151
        TransactionOptions $options
152
    ): ResponseCollection {
153 3
        if (!$options->hasPeers()) {
154 1
            throw new RuntimeException('Could not determine peers for this transaction');
155
        }
156
157
        // Convert peers into asynchronous calls.
158 2
        $calls = \array_map(function (PeerInterface $peer) use ($proposal) {
159 2
            return $peer->processSignedProposal($proposal);
160 2
        }, $options->getPeers());
161
162
        // Resolve calls to responses.
163 2
        return $this->unaryCallResolver->resolveMany(...$calls);
164
    }
165
}
166