1 | package org.cardanofoundation.explorer.api.service.impl; | |
2 | ||
3 | import java.util.List; | |
4 | import java.util.Objects; | |
5 | import java.util.Optional; | |
6 | ||
7 | import lombok.RequiredArgsConstructor; | |
8 | ||
9 | import org.springframework.beans.factory.annotation.Value; | |
10 | import org.springframework.data.domain.Page; | |
11 | import org.springframework.data.domain.PageImpl; | |
12 | import org.springframework.data.domain.PageRequest; | |
13 | import org.springframework.data.domain.Pageable; | |
14 | import org.springframework.stereotype.Service; | |
15 | import org.springframework.util.CollectionUtils; | |
16 | ||
17 | import org.cardanofoundation.explorer.api.common.constant.CommonConstant; | |
18 | import org.cardanofoundation.explorer.api.common.constant.CommonConstant.NetworkType; | |
19 | import org.cardanofoundation.explorer.api.model.response.BaseFilterResponse; | |
20 | import org.cardanofoundation.explorer.api.model.response.pool.projection.PoolInfoProjection; | |
21 | import org.cardanofoundation.explorer.api.model.response.search.*; | |
22 | import org.cardanofoundation.explorer.api.repository.ledgersync.BlockRepository; | |
23 | import org.cardanofoundation.explorer.api.repository.ledgersync.DrepInfoRepository; | |
24 | import org.cardanofoundation.explorer.api.repository.ledgersync.EpochRepository; | |
25 | import org.cardanofoundation.explorer.api.repository.ledgersync.MultiAssetRepository; | |
26 | import org.cardanofoundation.explorer.api.repository.ledgersync.PoolHashRepository; | |
27 | import org.cardanofoundation.explorer.api.repository.ledgersync.ScriptRepository; | |
28 | import org.cardanofoundation.explorer.api.repository.ledgersync.StakeAddressRepository; | |
29 | import org.cardanofoundation.explorer.api.repository.ledgersync.TxRepository; | |
30 | import org.cardanofoundation.explorer.api.service.SearchService; | |
31 | import org.cardanofoundation.explorer.api.util.AddressUtils; | |
32 | import org.cardanofoundation.explorer.common.entity.enumeration.ScriptType; | |
33 | import org.cardanofoundation.explorer.common.entity.ledgersync.Block; | |
34 | import org.cardanofoundation.explorer.common.entity.ledgersync.DRepInfo; | |
35 | import org.cardanofoundation.explorer.common.entity.ledgersync.MultiAsset; | |
36 | import org.cardanofoundation.explorer.common.entity.ledgersync.Script; | |
37 | ||
38 | @Service | |
39 | @RequiredArgsConstructor | |
40 | public class SearchServiceImpl implements SearchService { | |
41 | ||
42 | private final EpochRepository epochRepository; | |
43 | private final BlockRepository blockRepository; | |
44 | private final TxRepository txRepository; | |
45 | private final MultiAssetRepository multiAssetRepository; | |
46 | private final PoolHashRepository poolHashRepository; | |
47 | private final StakeAddressRepository stakeAddressRepository; | |
48 | private final ScriptRepository scriptRepository; | |
49 | private final DrepInfoRepository drepInfoRepository; | |
50 | ||
51 | @Value("${application.network}") | |
52 | private String network; | |
53 | ||
54 | @Override | |
55 | public SearchResponse search(String query) { | |
56 | String rawQuery = query; | |
57 | query = query.trim().toLowerCase(); | |
58 | SearchResponse searchResponse = new SearchResponse(); | |
59 |
1
1. search : removed call to org/cardanofoundation/explorer/api/service/impl/SearchServiceImpl::searchEpoch → KILLED |
searchEpoch(query, searchResponse); |
60 |
1
1. search : removed call to org/cardanofoundation/explorer/api/service/impl/SearchServiceImpl::searchBlock → KILLED |
searchBlock(query, searchResponse); |
61 |
1
1. search : removed call to org/cardanofoundation/explorer/api/service/impl/SearchServiceImpl::searchTx → KILLED |
searchTx(query, searchResponse); |
62 |
1
1. search : removed call to org/cardanofoundation/explorer/api/service/impl/SearchServiceImpl::searchToken → KILLED |
searchToken(query, searchResponse); |
63 |
1
1. search : removed call to org/cardanofoundation/explorer/api/service/impl/SearchServiceImpl::searchAddress → KILLED |
searchAddress(rawQuery, searchResponse); |
64 |
1
1. search : removed call to org/cardanofoundation/explorer/api/service/impl/SearchServiceImpl::searchPool → KILLED |
searchPool(query, searchResponse); |
65 |
1
1. search : removed call to org/cardanofoundation/explorer/api/service/impl/SearchServiceImpl::searchScriptHash → KILLED |
searchScriptHash(query, searchResponse); |
66 |
1
1. search : removed call to org/cardanofoundation/explorer/api/service/impl/SearchServiceImpl::searchDRep → SURVIVED |
searchDRep(query, searchResponse); |
67 |
1
1. search : replaced return value with null for org/cardanofoundation/explorer/api/service/impl/SearchServiceImpl::search → KILLED |
return searchResponse; |
68 | } | |
69 | ||
70 | @Override | |
71 | public SearchStakingLifecycle searchForStakingLifecycle(String query, Pageable pageable) { | |
72 | String rawQuery = query; | |
73 | query = query.trim().toLowerCase(); | |
74 | SearchStakingLifecycle response = new SearchStakingLifecycle(); | |
75 |
2
1. searchForStakingLifecycle : negated conditional → KILLED 2. searchForStakingLifecycle : negated conditional → KILLED |
if (query.isBlank() || query.isEmpty()) { |
76 |
1
1. searchForStakingLifecycle : replaced return value with null for org/cardanofoundation/explorer/api/service/impl/SearchServiceImpl::searchForStakingLifecycle → NO_COVERAGE |
return response; |
77 | } | |
78 |
1
1. searchForStakingLifecycle : removed call to org/cardanofoundation/explorer/api/service/impl/SearchServiceImpl::searchPoolIdAndHash → KILLED |
searchPoolIdAndHash(query, response, pageable); |
79 |
1
1. searchForStakingLifecycle : removed call to org/cardanofoundation/explorer/api/service/impl/SearchServiceImpl::searchAddress → KILLED |
searchAddress(rawQuery, response); |
80 |
1
1. searchForStakingLifecycle : replaced return value with null for org/cardanofoundation/explorer/api/service/impl/SearchServiceImpl::searchForStakingLifecycle → KILLED |
return response; |
81 | } | |
82 | ||
83 | private void searchDRep(String query, SearchResponse searchResponse) { | |
84 | Optional<DRepInfo> dRepInfo = drepInfoRepository.findByDRepHashOrDRepId(query); | |
85 |
2
1. lambda$searchDRep$0 : removed call to org/cardanofoundation/explorer/api/model/response/search/SearchResponse::setDRep → NO_COVERAGE 2. searchDRep : removed call to java/util/Optional::ifPresent → SURVIVED |
dRepInfo.ifPresent(repInfo -> searchResponse.setDRep(repInfo.getDrepId())); |
86 | } | |
87 | ||
88 | private void searchEpoch(String query, SearchResponse searchResponse) { | |
89 | try { | |
90 | Integer epochNo = Integer.parseInt(query); | |
91 | var epoch = epochRepository.findFirstByNo(epochNo); | |
92 |
1
1. searchEpoch : negated conditional → KILLED |
if (epoch.isPresent()) { |
93 |
1
1. searchEpoch : removed call to org/cardanofoundation/explorer/api/model/response/search/SearchResponse::setEpoch → KILLED |
searchResponse.setEpoch(epochNo); |
94 | } | |
95 | } catch (NumberFormatException e) { | |
96 |
1
1. searchEpoch : removed call to org/cardanofoundation/explorer/api/model/response/search/SearchResponse::setEpoch → SURVIVED |
searchResponse.setEpoch(null); |
97 | } | |
98 | } | |
99 | ||
100 | private void searchBlock(String query, SearchResponse searchResponse) { | |
101 | Optional<Block> block; | |
102 | try { | |
103 | Long blockNo = Long.parseLong(query); | |
104 | block = blockRepository.findFirstByBlockNo(blockNo); | |
105 | } catch (NumberFormatException e) { | |
106 | block = blockRepository.findFirstByHash(query); | |
107 | } | |
108 |
1
1. searchBlock : negated conditional → KILLED |
if (block.isPresent()) { |
109 |
1
1. searchBlock : removed call to org/cardanofoundation/explorer/api/model/response/search/SearchResponse::setBlock → KILLED |
searchResponse.setBlock(query); |
110 | } | |
111 | } | |
112 | ||
113 | private void searchTx(String query, SearchResponse searchResponse) { | |
114 | var tx = txRepository.findByHash(query); | |
115 |
1
1. searchTx : negated conditional → KILLED |
if (tx.isPresent()) { |
116 |
1
1. searchTx : removed call to org/cardanofoundation/explorer/api/model/response/search/SearchResponse::setTx → KILLED |
searchResponse.setTx(query); |
117 | } | |
118 | } | |
119 | ||
120 | private void searchToken(String query, SearchResponse searchResponse) { | |
121 | var token = multiAssetRepository.findByFingerprint(query); | |
122 |
1
1. searchToken : negated conditional → KILLED |
if (token.isPresent()) { |
123 |
1
1. searchToken : removed call to org/cardanofoundation/explorer/api/model/response/search/SearchResponse::setToken → KILLED |
searchResponse.setToken( |
124 | new TokenSearchResponse(token.get().getNameView(), token.get().getFingerprint())); | |
125 | } else { | |
126 | Pageable pageable = PageRequest.of(0, 2); | |
127 | var tokenList = multiAssetRepository.findByNameViewLike(query, pageable); | |
128 |
1
1. searchToken : negated conditional → KILLED |
if (tokenList.size() == 1) { |
129 | MultiAsset multiAsset = tokenList.get(0); | |
130 |
1
1. searchToken : removed call to org/cardanofoundation/explorer/api/model/response/search/SearchResponse::setToken → KILLED |
searchResponse.setToken( |
131 | new TokenSearchResponse(multiAsset.getNameView(), multiAsset.getFingerprint())); | |
132 | } | |
133 |
1
1. searchToken : negated conditional → KILLED |
if (!CollectionUtils.isEmpty(tokenList)) { |
134 |
1
1. searchToken : removed call to org/cardanofoundation/explorer/api/model/response/search/SearchResponse::setValidTokenName → KILLED |
searchResponse.setValidTokenName(true); |
135 | } | |
136 | } | |
137 | } | |
138 | ||
139 | private void searchAddress(String query, SearchResponse searchResponse) { | |
140 |
1
1. searchAddress : negated conditional → KILLED |
if (query.startsWith(CommonConstant.STAKE_ADDRESS_PREFIX)) { |
141 | var stakeAddress = stakeAddressRepository.findByView(query); | |
142 |
1
1. searchAddress : removed call to java/util/Optional::ifPresent → KILLED |
stakeAddress.ifPresent( |
143 | address -> | |
144 |
1
1. lambda$searchAddress$1 : removed call to org/cardanofoundation/explorer/api/model/response/search/SearchResponse::setAddress → KILLED |
searchResponse.setAddress( |
145 | new AddressSearchResponse(address.getView(), address.getView(), false, true))); | |
146 | } else { | |
147 | final int ADDRESS_MIN_LENGTH = 56; | |
148 |
2
1. searchAddress : changed conditional boundary → SURVIVED 2. searchAddress : negated conditional → SURVIVED |
if (query.length() < ADDRESS_MIN_LENGTH) { |
149 | return; | |
150 | } | |
151 | try { | |
152 |
1
1. searchAddress : negated conditional → NO_COVERAGE |
if (checkNetworkAddress(query)) { |
153 | String stakeAddress = AddressUtils.checkStakeAddress(query); | |
154 |
1
1. searchAddress : negated conditional → NO_COVERAGE |
if (Objects.nonNull(stakeAddress)) { |
155 |
1
1. searchAddress : removed call to org/cardanofoundation/explorer/api/model/response/search/SearchResponse::setAddress → NO_COVERAGE |
searchResponse.setAddress(new AddressSearchResponse(query, stakeAddress, true, false)); |
156 | } else { | |
157 |
1
1. searchAddress : removed call to org/cardanofoundation/explorer/api/model/response/search/SearchResponse::setAddress → NO_COVERAGE |
searchResponse.setAddress(new AddressSearchResponse(query, null, true, false)); |
158 | } | |
159 | } | |
160 | } catch (Exception e) { | |
161 | // ignore | |
162 | } | |
163 | } | |
164 | } | |
165 | ||
166 | private void searchAddress(String query, SearchStakingLifecycle response) { | |
167 |
1
1. searchAddress : negated conditional → KILLED |
if (query.startsWith(CommonConstant.STAKE_ADDRESS_PREFIX)) { |
168 | var stakeAddress = stakeAddressRepository.findByView(query); | |
169 |
1
1. searchAddress : removed call to java/util/Optional::ifPresent → KILLED |
stakeAddress.ifPresent( |
170 | address -> | |
171 |
1
1. lambda$searchAddress$2 : removed call to org/cardanofoundation/explorer/api/model/response/search/SearchStakingLifecycle::setAddress → KILLED |
response.setAddress( |
172 | new AddressSearchResponse(address.getView(), address.getView(), false, true))); | |
173 | } else { | |
174 | final int ADDRESS_MIN_LENGTH = 56; | |
175 |
2
1. searchAddress : changed conditional boundary → SURVIVED 2. searchAddress : negated conditional → KILLED |
if (query.length() < ADDRESS_MIN_LENGTH) { |
176 | return; | |
177 | } | |
178 | try { | |
179 |
1
1. searchAddress : negated conditional → KILLED |
if (checkNetworkAddress(query)) { |
180 | String stakeAddress = AddressUtils.checkStakeAddress(query); | |
181 |
1
1. searchAddress : negated conditional → SURVIVED |
if (Objects.nonNull(stakeAddress)) { |
182 |
1
1. searchAddress : removed call to org/cardanofoundation/explorer/api/model/response/search/SearchStakingLifecycle::setAddress → KILLED |
response.setAddress(new AddressSearchResponse(query, stakeAddress, true, false)); |
183 | } else { | |
184 |
1
1. searchAddress : removed call to org/cardanofoundation/explorer/api/model/response/search/SearchStakingLifecycle::setAddress → NO_COVERAGE |
response.setAddress(new AddressSearchResponse(query, null, true, false)); |
185 | } | |
186 | } | |
187 | } catch (Exception e) { | |
188 | // ignore | |
189 | } | |
190 | } | |
191 | } | |
192 | ||
193 | private void searchPoolIdAndHash( | |
194 | String query, SearchStakingLifecycle response, Pageable pageable) { | |
195 | var pool = poolHashRepository.getPoolInfo(query); | |
196 |
1
1. searchPoolIdAndHash : negated conditional → KILLED |
if (Objects.nonNull(pool)) { |
197 | Page<PoolSearchResponse> poolPage = | |
198 | new PageImpl<>( | |
199 | List.of( | |
200 | new PoolSearchResponse(pool.getPoolName(), pool.getPoolView(), pool.getIcon())), | |
201 | pageable, | |
202 | 1); | |
203 |
1
1. searchPoolIdAndHash : removed call to org/cardanofoundation/explorer/api/model/response/search/SearchStakingLifecycle::setPoolList → NO_COVERAGE |
response.setPoolList(new BaseFilterResponse<>(poolPage)); |
204 | } | |
205 | } | |
206 | ||
207 | /** | |
208 | * Check address is valid in this network | |
209 | * | |
210 | * @param address address view value | |
211 | * @return true if valid and false if not | |
212 | */ | |
213 | private boolean checkNetworkAddress(String address) { | |
214 |
1
1. checkNetworkAddress : negated conditional → KILLED |
if (address.startsWith(CommonConstant.TESTNET_ADDRESS_PREFIX)) { |
215 |
2
1. checkNetworkAddress : negated conditional → NO_COVERAGE 2. checkNetworkAddress : replaced boolean return with true for org/cardanofoundation/explorer/api/service/impl/SearchServiceImpl::checkNetworkAddress → NO_COVERAGE |
return !network.equals(NetworkType.MAINNET); |
216 | } else { | |
217 |
2
1. checkNetworkAddress : replaced boolean return with true for org/cardanofoundation/explorer/api/service/impl/SearchServiceImpl::checkNetworkAddress → SURVIVED 2. checkNetworkAddress : replaced boolean return with false for org/cardanofoundation/explorer/api/service/impl/SearchServiceImpl::checkNetworkAddress → KILLED |
return network.equals(NetworkType.MAINNET); |
218 | } | |
219 | } | |
220 | ||
221 | private void searchPool(String query, SearchResponse searchResponse) { | |
222 | var pool = poolHashRepository.getPoolInfo(query); | |
223 |
1
1. searchPool : negated conditional → KILLED |
if (Objects.nonNull(pool)) { |
224 |
1
1. searchPool : removed call to org/cardanofoundation/explorer/api/model/response/search/SearchResponse::setPool → NO_COVERAGE |
searchResponse.setPool( |
225 | new PoolSearchResponse(pool.getPoolName(), pool.getPoolView(), pool.getIcon())); | |
226 | } else { | |
227 | Pageable pageable = PageRequest.of(0, 2); | |
228 | var poolList = poolHashRepository.findByPoolNameLike(query, pageable); | |
229 |
1
1. searchPool : negated conditional → KILLED |
if (poolList.getTotalElements() == 1) { |
230 | PoolInfoProjection poolInfo = poolList.getContent().get(0); | |
231 |
1
1. searchPool : removed call to org/cardanofoundation/explorer/api/model/response/search/SearchResponse::setPool → KILLED |
searchResponse.setPool( |
232 | new PoolSearchResponse( | |
233 | poolInfo.getPoolName(), poolInfo.getPoolView(), poolInfo.getIcon())); | |
234 | } | |
235 |
1
1. searchPool : negated conditional → KILLED |
if (!CollectionUtils.isEmpty(poolList.getContent())) { |
236 |
1
1. searchPool : removed call to org/cardanofoundation/explorer/api/model/response/search/SearchResponse::setValidPoolName → KILLED |
searchResponse.setValidPoolName(true); |
237 | } | |
238 | } | |
239 | } | |
240 | ||
241 | public void searchScriptHash(String query, SearchResponse searchResponse) { | |
242 | Script script = scriptRepository.findByHash(query).orElse(null); | |
243 | ||
244 |
1
1. searchScriptHash : negated conditional → KILLED |
if (Objects.nonNull(script)) { |
245 | boolean isSmartContract = | |
246 |
1
1. searchScriptHash : negated conditional → SURVIVED |
ScriptType.PLUTUSV1.equals(script.getType()) |
247 |
1
1. searchScriptHash : negated conditional → SURVIVED |
|| ScriptType.PLUTUSV2.equals(script.getType()) |
248 |
1
1. searchScriptHash : negated conditional → KILLED |
|| ScriptType.PLUTUSV3.equals(script.getType()); |
249 | ||
250 | ScriptSearchResponse scriptSearchResponse = | |
251 | ScriptSearchResponse.builder().scriptHash(script.getHash()).build(); | |
252 | ||
253 |
1
1. searchScriptHash : negated conditional → KILLED |
if (isSmartContract) { |
254 |
1
1. searchScriptHash : removed call to org/cardanofoundation/explorer/api/model/response/search/ScriptSearchResponse::setSmartContract → KILLED |
scriptSearchResponse.setSmartContract(true); |
255 | } else { | |
256 |
1
1. searchScriptHash : removed call to org/cardanofoundation/explorer/api/model/response/search/ScriptSearchResponse::setNativeScript → NO_COVERAGE |
scriptSearchResponse.setNativeScript(true); |
257 | } | |
258 |
1
1. searchScriptHash : removed call to org/cardanofoundation/explorer/api/model/response/search/SearchResponse::setScript → KILLED |
searchResponse.setScript(scriptSearchResponse); |
259 | } | |
260 | } | |
261 | } | |
Mutations | ||
59 |
1.1 |
|
60 |
1.1 |
|
61 |
1.1 |
|
62 |
1.1 |
|
63 |
1.1 |
|
64 |
1.1 |
|
65 |
1.1 |
|
66 |
1.1 |
|
67 |
1.1 |
|
75 |
1.1 2.2 |
|
76 |
1.1 |
|
78 |
1.1 |
|
79 |
1.1 |
|
80 |
1.1 |
|
85 |
1.1 2.2 |
|
92 |
1.1 |
|
93 |
1.1 |
|
96 |
1.1 |
|
108 |
1.1 |
|
109 |
1.1 |
|
115 |
1.1 |
|
116 |
1.1 |
|
122 |
1.1 |
|
123 |
1.1 |
|
128 |
1.1 |
|
130 |
1.1 |
|
133 |
1.1 |
|
134 |
1.1 |
|
140 |
1.1 |
|
142 |
1.1 |
|
144 |
1.1 |
|
148 |
1.1 2.2 |
|
152 |
1.1 |
|
154 |
1.1 |
|
155 |
1.1 |
|
157 |
1.1 |
|
167 |
1.1 |
|
169 |
1.1 |
|
171 |
1.1 |
|
175 |
1.1 2.2 |
|
179 |
1.1 |
|
181 |
1.1 |
|
182 |
1.1 |
|
184 |
1.1 |
|
196 |
1.1 |
|
203 |
1.1 |
|
214 |
1.1 |
|
215 |
1.1 2.2 |
|
217 |
1.1 2.2 |
|
223 |
1.1 |
|
224 |
1.1 |
|
229 |
1.1 |
|
231 |
1.1 |
|
235 |
1.1 |
|
236 |
1.1 |
|
244 |
1.1 |
|
246 |
1.1 |
|
247 |
1.1 |
|
248 |
1.1 |
|
253 |
1.1 |
|
254 |
1.1 |
|
256 |
1.1 |
|
258 |
1.1 |