Status public API

This commit is contained in:
Andrea Maria Piana 2021-08-05 15:27:47 +02:00
parent 07e46714f0
commit e63f6461ed
8 changed files with 227 additions and 15 deletions

View File

@ -1 +1 @@
0.86.0 0.86.1

View File

@ -1121,6 +1121,10 @@ func (b *GethStatusBackend) injectAccountsIntoServices() error {
} }
// Set initial connection state // Set initial connection state
st.ConnectionChanged(b.connectionState) st.ConnectionChanged(b.connectionState)
messenger := st.Messenger()
// Init public status api
b.statusNode.StatusPublicService().Init(messenger)
} }
} }

View File

@ -39,6 +39,7 @@ import (
"github.com/status-im/status-go/services/personal" "github.com/status-im/status-go/services/personal"
"github.com/status-im/status-go/services/rpcfilters" "github.com/status-im/status-go/services/rpcfilters"
"github.com/status-im/status-go/services/rpcstats" "github.com/status-im/status-go/services/rpcstats"
"github.com/status-im/status-go/services/status"
"github.com/status-im/status-go/services/subscriptions" "github.com/status-im/status-go/services/subscriptions"
"github.com/status-im/status-go/services/wakuext" "github.com/status-im/status-go/services/wakuext"
"github.com/status-im/status-go/services/wakuv2ext" "github.com/status-im/status-go/services/wakuv2ext"
@ -89,6 +90,7 @@ type StatusNode struct {
rpcFiltersSrvc *rpcfilters.Service rpcFiltersSrvc *rpcfilters.Service
subscriptionsSrvc *subscriptions.Service subscriptionsSrvc *subscriptions.Service
rpcStatsSrvc *rpcstats.Service rpcStatsSrvc *rpcstats.Service
statusPublicSrvc *status.Service
accountsSrvc *accountssvc.Service accountsSrvc *accountssvc.Service
browsersSrvc *browsers.Service browsersSrvc *browsers.Service
permissionsSrvc *permissions.Service permissionsSrvc *permissions.Service

View File

@ -32,6 +32,7 @@ import (
"github.com/status-im/status-go/services/personal" "github.com/status-im/status-go/services/personal"
"github.com/status-im/status-go/services/rpcfilters" "github.com/status-im/status-go/services/rpcfilters"
"github.com/status-im/status-go/services/rpcstats" "github.com/status-im/status-go/services/rpcstats"
"github.com/status-im/status-go/services/status"
"github.com/status-im/status-go/services/subscriptions" "github.com/status-im/status-go/services/subscriptions"
"github.com/status-im/status-go/services/wakuext" "github.com/status-im/status-go/services/wakuext"
"github.com/status-im/status-go/services/wakuv2ext" "github.com/status-im/status-go/services/wakuv2ext"
@ -60,6 +61,7 @@ func (b *StatusNode) initServices(config *params.NodeConfig) error {
services = append(services, b.appmetricsService()) services = append(services, b.appmetricsService())
services = append(services, b.peerService()) services = append(services, b.peerService())
services = append(services, b.personalService()) services = append(services, b.personalService())
services = append(services, b.statusPublicService())
services = appendIf(config.EnableNTPSync, services, b.timeSource()) services = appendIf(config.EnableNTPSync, services, b.timeSource())
services = appendIf(b.appDB != nil && b.multiaccountsDB != nil, services, b.accountsService(accountsFeed)) services = appendIf(b.appDB != nil && b.multiaccountsDB != nil, services, b.accountsService(accountsFeed))
services = appendIf(config.BrowsersConfig.Enabled, services, b.browsersService()) services = appendIf(config.BrowsersConfig.Enabled, services, b.browsersService())
@ -166,6 +168,17 @@ func (b *StatusNode) wakuV2ExtService(config *params.NodeConfig) (*wakuv2ext.Ser
return b.wakuV2ExtSrvc, nil return b.wakuV2ExtSrvc, nil
} }
func (b *StatusNode) statusPublicService() *status.Service {
if b.statusPublicSrvc == nil {
b.statusPublicSrvc = status.New()
}
return b.statusPublicSrvc
}
func (b *StatusNode) StatusPublicService() *status.Service {
return b.statusPublicSrvc
}
func (b *StatusNode) WakuService() *waku.Waku { func (b *StatusNode) WakuService() *waku.Waku {
return b.wakuSrvc return b.wakuSrvc
} }

View File

@ -5,6 +5,7 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"sync" "sync"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
@ -76,6 +77,73 @@ type CommunityCategory struct {
Position int `json:"position"` // Position is used to sort the categories Position int `json:"position"` // Position is used to sort the categories
} }
func (o *Community) MarshalPublicAPIJSON() ([]byte, error) {
if o.config.MemberIdentity == nil {
return nil, errors.New("member identity not set")
}
communityItem := struct {
ID types.HexBytes `json:"id"`
Verified bool `json:"verified"`
Chats map[string]CommunityChat `json:"chats"`
Categories map[string]CommunityCategory `json:"categories"`
Name string `json:"name"`
Description string `json:"description"`
Images map[string]images.IdentityImage `json:"images"`
Color string `json:"color"`
MembersCount int `json:"membersCount"`
EnsName string `json:"ensName"`
Link string `json:"link"`
}{
ID: o.ID(),
Verified: o.config.Verified,
Chats: make(map[string]CommunityChat),
Categories: make(map[string]CommunityCategory),
}
if o.config.CommunityDescription != nil {
for id, c := range o.config.CommunityDescription.Categories {
category := CommunityCategory{
ID: id,
Name: c.Name,
Position: int(c.Position),
}
communityItem.Categories[id] = category
}
for id, c := range o.config.CommunityDescription.Chats {
canPost, err := o.CanPost(o.config.MemberIdentity, id, nil)
if err != nil {
return nil, err
}
chat := CommunityChat{
ID: id,
Name: c.Identity.DisplayName,
Description: c.Identity.Description,
Permissions: c.Permissions,
Members: c.Members,
CanPost: canPost,
CategoryID: c.CategoryId,
Position: int(c.Position),
}
communityItem.Chats[id] = chat
}
communityItem.MembersCount = len(o.config.CommunityDescription.Members)
communityItem.Link = fmt.Sprintf("https://join.status.im/c/0x%x", o.ID())
if o.config.CommunityDescription.Identity != nil {
communityItem.Name = o.Name()
communityItem.Color = o.config.CommunityDescription.Identity.Color
communityItem.Description = o.config.CommunityDescription.Identity.Description
for t, i := range o.config.CommunityDescription.Identity.Images {
if communityItem.Images == nil {
communityItem.Images = make(map[string]images.IdentityImage)
}
communityItem.Images[t] = images.IdentityImage{Name: t, Payload: i.Payload}
}
}
}
return json.Marshal(communityItem)
}
func (o *Community) MarshalJSON() ([]byte, error) { func (o *Community) MarshalJSON() ([]byte, error) {
if o.config.MemberIdentity == nil { if o.config.MemberIdentity == nil {
return nil, errors.New("member identity not set") return nil, errors.New("member identity not set")

View File

@ -729,9 +729,22 @@ func (m *Messenger) BanUserFromCommunity(request *requests.BanUserFromCommunity)
// RequestCommunityInfoFromMailserver installs filter for community and requests its details // RequestCommunityInfoFromMailserver installs filter for community and requests its details
// from mailserver. When response received it will be passed through signals handler // from mailserver. When response received it will be passed through signals handler
func (m *Messenger) RequestCommunityInfoFromMailserver(communityID string) error { func (m *Messenger) RequestCommunityInfoFromMailserver(communityID string) error {
_, err := m.requestCommunityInfoFromMailserver(communityID, true)
return err
}
// RequestCommunityInfoFromMailserverSync installs filter for community and requests its details
// from mailserver. It will wait for a response and return it if any community is found
func (m *Messenger) RequestCommunityInfoFromMailserverSync(communityID string) (*communities.Community, error) {
return m.requestCommunityInfoFromMailserver(communityID, false)
}
// RequestCommunityInfoFromMailserver installs filter for community and requests its details
// from mailserver. When response received it will be passed through signals handler
func (m *Messenger) requestCommunityInfoFromMailserver(communityID string, async bool) (*communities.Community, error) {
if _, ok := m.requestedCommunities[communityID]; ok { if _, ok := m.requestedCommunities[communityID]; ok {
return nil return nil, nil
} }
//If filter wasn't installed we create it and remember for deinstalling after //If filter wasn't installed we create it and remember for deinstalling after
@ -740,10 +753,10 @@ func (m *Messenger) RequestCommunityInfoFromMailserver(communityID string) error
if filter == nil { if filter == nil {
filters, err := m.transport.InitPublicFilters([]string{communityID}) filters, err := m.transport.InitPublicFilters([]string{communityID})
if err != nil { if err != nil {
return fmt.Errorf("Can't install filter for community: %v", err) return nil, fmt.Errorf("Can't install filter for community: %v", err)
} }
if len(filters) != 1 { if len(filters) != 1 {
return fmt.Errorf("Unexpected amount of filters created") return nil, fmt.Errorf("Unexpected amount of filters created")
} }
filter = filters[0] filter = filters[0]
m.requestedCommunities[communityID] = filter m.requestedCommunities[communityID] = filter
@ -763,19 +776,44 @@ func (m *Messenger) RequestCommunityInfoFromMailserver(communityID string) error
filter, filter,
false) false)
//It is possible that we already processed last existing message for community if err != nil {
//and won't get any updates, so send stored info in this case after timeout return nil, err
go func() { }
time.Sleep(15 * time.Second)
m.mutex.Lock()
defer m.mutex.Unlock()
if _, ok := m.requestedCommunities[communityID]; ok { if async {
m.passStoredCommunityInfoToSignalHandler(communityID) //It is possible that we already processed last existing message for community
} //and won't get any updates, so send stored info in this case after timeout
}() go func() {
time.Sleep(15 * time.Second)
m.mutex.Lock()
defer m.mutex.Unlock()
return err if _, ok := m.requestedCommunities[communityID]; ok {
m.passStoredCommunityInfoToSignalHandler(communityID)
}
}()
return nil, nil
}
time.Sleep(15 * time.Second)
//send signal to client that message status updated
community, err := m.communitiesManager.GetByIDString(communityID)
if err != nil {
return nil, err
}
if community == nil {
return nil, nil
}
//if there is no info helpful for client, we don't post it
if community.Name() == "" && community.DescriptionText() == "" && community.MembersCount() == 0 {
return nil, nil
}
m.forgetCommunityRequest(communityID)
return community, nil
} }
// forgetCommunityRequest removes community from requested ones and removes filter // forgetCommunityRequest removes community from requested ones and removes filter

View File

@ -482,3 +482,7 @@ func (s *Service) ConnectionChanged(state connection.State) {
s.messenger.ConnectionChanged(state) s.messenger.ConnectionChanged(state)
} }
} }
func (s *Service) Messenger() *protocol.Messenger {
return s.messenger
}

View File

@ -0,0 +1,83 @@
package status
import (
"encoding/json"
"errors"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rpc"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol"
)
// Make sure that Service implements node.Lifecycle interface.
var _ node.Lifecycle = (*Service)(nil)
var ErrNotInitialized = errors.New("status public api not initialized")
// Service represents out own implementation of personal sign operations.
type Service struct {
messenger *protocol.Messenger
}
// New returns a new Service.
func New() *Service {
return &Service{}
}
func (s *Service) Init(messenger *protocol.Messenger) {
s.messenger = messenger
}
// Protocols returns a new protocols list. In this case, there are none.
func (s *Service) Protocols() []p2p.Protocol {
return []p2p.Protocol{}
}
// APIs returns a list of new APIs.
func (s *Service) APIs() []rpc.API {
return []rpc.API{
{
Namespace: "status",
Version: "1.0",
Service: NewPublicAPI(s),
Public: true,
},
}
}
// NewPublicAPI returns a reference to the PublicAPI object
func NewPublicAPI(s *Service) *PublicAPI {
api := &PublicAPI{
service: s,
}
return api
}
// Start is run when a service is started.
func (s *Service) Start() error {
return nil
}
// Stop is run when a service is stopped.
func (s *Service) Stop() error {
return nil
}
type PublicAPI struct {
service *Service
}
func (p *PublicAPI) CommunityInfo(communityID types.HexBytes) (json.RawMessage, error) {
if p.service.messenger == nil {
return nil, ErrNotInitialized
}
community, err := p.service.messenger.RequestCommunityInfoFromMailserverSync(communityID.String())
if err != nil {
return nil, err
}
return community.MarshalPublicAPIJSON()
}