github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/core/discovery/repository.go (about) 1 /* 2 * Copyright (C) 2020 The "MysteriumNetwork/node" Authors. 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 package discovery 19 20 import ( 21 "sync" 22 23 "github.com/rs/zerolog/log" 24 25 "github.com/mysteriumnetwork/node/core/discovery/proposal" 26 "github.com/mysteriumnetwork/node/market" 27 "github.com/mysteriumnetwork/node/utils" 28 ) 29 30 // repository provides proposals from multiple other repositories. 31 type repository struct { 32 delegates []proposal.Repository 33 } 34 35 // NewRepository constructs a new composite repository. 36 func NewRepository() *repository { 37 return &repository{} 38 } 39 40 // Add adds a delegate repositories from which proposals can be acquired. 41 func (c *repository) Add(repository proposal.Repository) { 42 c.delegates = append(c.delegates, repository) 43 } 44 45 // Proposal returns a single proposal by its ID. 46 func (c *repository) Proposal(id market.ProposalID) (*market.ServiceProposal, error) { 47 allErrors := utils.ErrorCollection{} 48 49 for _, delegate := range c.delegates { 50 serviceProposal, err := delegate.Proposal(id) 51 if err == nil { 52 return serviceProposal, nil 53 } 54 allErrors.Add(err) 55 } 56 57 return nil, allErrors.Error() 58 } 59 60 // Proposals returns proposals matching the filter. 61 func (c *repository) Proposals(filter *proposal.Filter) ([]market.ServiceProposal, error) { 62 log.Debug(). 63 Interface("filter", filter). 64 Msgf("Retrieving proposals from %d repositories", len(c.delegates)) 65 proposals := make([][]market.ServiceProposal, len(c.delegates)) 66 errors := make([]error, len(c.delegates)) 67 68 var wg sync.WaitGroup 69 for i, delegate := range c.delegates { 70 wg.Add(1) 71 go func(idx int, repo proposal.Repository) { 72 defer wg.Done() 73 proposals[idx], errors[idx] = repo.Proposals(filter) 74 }(i, delegate) 75 } 76 wg.Wait() 77 78 uniqueProposals := make(map[market.ProposalID]market.ServiceProposal) 79 for i, repoProposals := range proposals { 80 log.Trace().Msgf("Retrieved %d proposals from repository %d", len(repoProposals), i) 81 for _, p := range repoProposals { 82 uniqueProposals[p.UniqueID()] = p 83 } 84 } 85 86 var result []market.ServiceProposal 87 for _, val := range uniqueProposals { 88 result = append(result, val) 89 } 90 91 allErrors := utils.ErrorCollection{} 92 allErrors.Add(errors...) 93 94 log.Debug().Err(allErrors.Error()).Msgf("Returning %d unique proposals", len(result)) 95 return result, allErrors.Error() 96 } 97 98 // Countries returns proposals per country matching the filter. 99 func (c *repository) Countries(filter *proposal.Filter) (map[string]int, error) { 100 countries := make(map[string]int, 0) 101 102 for _, repo := range c.delegates { 103 repoCountries, err := repo.Countries(filter) 104 if err != nil { 105 return nil, err 106 } 107 for k, v := range repoCountries { 108 countries[k] += v 109 } 110 } 111 112 return countries, nil 113 }