diff --git a/client/api_group.go b/client/api_group.go index e916c28d..06ec7c82 100644 --- a/client/api_group.go +++ b/client/api_group.go @@ -74,6 +74,7 @@ type Group interface { // ListGroupsByOwner returns a list of groups owned by the specified user, including those for which the user's expiration time has already elapsed // By default, the user is the sender. Other users can be set using the option ListGroupsByOwner(ctx context.Context, opts types.GroupsOwnerPaginationOptions) (*types.GroupsResult, error) + ListGroupsByGroupID(ctx context.Context, groupIDs []uint64, opts types.EndPointOptions) (types.ListGroupsByGroupIDResponse, error) } // CreateGroup create a new group on greenfield chain, the group members can be initialized or not @@ -568,3 +569,107 @@ func (c *client) ListGroupsByOwner(ctx context.Context, opts types.GroupsOwnerPa return groups, nil } + +type gfSpListGroupsByGroupIDsResponse map[uint64]*types.GroupMeta + +type groupEntry struct { + Id uint64 + Value *types.GroupMeta +} + +// UnmarshalXML unmarshal gfSpListGroupsByGroupIDsResponse xml response type +func (m *gfSpListGroupsByGroupIDsResponse) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + *m = gfSpListGroupsByGroupIDsResponse{} + for { + var e groupEntry + + err := d.Decode(&e) + if err == io.EOF { + break + } else if err != nil { + return err + } else { + (*m)[e.Id] = e.Value + } + } + return nil +} + +// ListGroupsByGroupID - List groups by group ids. +// +// By inputting a collection of group IDs, we can retrieve the corresponding object data. If the group is nonexistent or has been deleted, a null value will be returned +// +// - ctx: Context variables for the current API call. +// +// - groupIDs: The list of group ids. +// +// - opts: The options to set the meta to list groups by group id. +// +// - ret1: The result of group info map by given group ids. +// +// - ret2: Return error when the request failed, otherwise return nil. +func (c *client) ListGroupsByGroupID(ctx context.Context, groupIDs []uint64, opts types.EndPointOptions) (types.ListGroupsByGroupIDResponse, error) { + const MaximumListBucketsSize = 1000 + if len(groupIDs) == 0 || len(groupIDs) > MaximumListBucketsSize { + return types.ListGroupsByGroupIDResponse{}, nil + } + + groupIDMap := make(map[uint64]bool) + for _, id := range groupIDs { + if _, ok := groupIDMap[id]; ok { + // repeat id keys in request + return types.ListGroupsByGroupIDResponse{}, nil + } + groupIDMap[id] = true + } + + idStr := make([]string, len(groupIDs)) + for i, id := range groupIDs { + idStr[i] = strconv.FormatUint(id, 10) + } + IDs := strings.Join(idStr, ",") + + params := url.Values{} + params.Set("groups-query", "") + params.Set("ids", IDs) + + reqMeta := requestMeta{ + urlValues: params, + contentSHA256: types.EmptyStringSHA256, + } + + sendOpt := sendOptions{ + method: http.MethodGet, + disableCloseBody: true, + } + + endpoint, err := c.getEndpointByOpt(&opts) + if err != nil { + log.Error().Msg(fmt.Sprintf("get endpoint by option failed %s", err.Error())) + return types.ListGroupsByGroupIDResponse{}, err + + } + + resp, err := c.sendReq(ctx, reqMeta, &sendOpt, endpoint) + if err != nil { + return types.ListGroupsByGroupIDResponse{}, err + } + defer utils.CloseResponse(resp) + + buf := new(strings.Builder) + _, err = io.Copy(buf, resp.Body) + if err != nil { + log.Error().Msgf("the list of groups in group ids:%v failed: %s", groupIDs, err.Error()) + return types.ListGroupsByGroupIDResponse{}, err + } + + groups := types.ListGroupsByGroupIDResponse{} + bufStr := buf.String() + err = xml.Unmarshal([]byte(bufStr), (*gfSpListGroupsByGroupIDsResponse)(&groups.Groups)) + if err != nil && groups.Groups == nil { + log.Error().Msgf("the list of groups in group ids:%v failed: %s", groups, err.Error()) + return types.ListGroupsByGroupIDResponse{}, err + } + + return groups, nil +} diff --git a/examples/group.go b/examples/group.go index b4be4691..5c20044f 100644 --- a/examples/group.go +++ b/examples/group.go @@ -104,6 +104,19 @@ func main() { log.Printf("name: %s, source type: %s\n", group.Group.GroupName, group.Group.SourceType) } + ids := []uint64{1, 2, 333} + // list groups by group ids + groupList, err := cli.ListGroupsByGroupID(ctx, ids, types.EndPointOptions{ + Endpoint: httpsAddr, + SPAddress: "", + }) + log.Println("list groups result:") + for _, group := range groupList.Groups { + if group != nil { + log.Printf("name: %s, source type: %s\n", group.Group.GroupName, group.Group.SourceType) + } + } + // delete group delTx, err := cli.DeleteGroup(ctx, groupName, types.DeleteGroupOption{}) handleErr(err, "DeleteGroup") diff --git a/examples/payment.go b/examples/payment.go index 6c30b044..a3b80521 100644 --- a/examples/payment.go +++ b/examples/payment.go @@ -57,11 +57,11 @@ func main() { handleErr(err, "GetStreamRecord") log.Printf("stream record has balance %s", streamRecordAfterWithdraw.StaticBalance) streamRecords, err := cli.ListUserPaymentAccounts(ctx, types.ListUserPaymentAccountsOptions{ - Account: "0x4FEAA841B3436624C54B652695320830FCB1B309", + Account: "0x6bda8d05a24688f1f2ed05e503d19576aba7da2c", Endpoint: httpsAddr, SPAddress: "", }) - for _, record := range streamRecords.StreamRecords { - log.Printf("stream record %s", record.StreamRecord.OutFlowCount) + for _, record := range streamRecords.PaymentAccounts { + log.Printf("payment account %s", record.PaymentAccount.Address) } } diff --git a/types/list.go b/types/list.go index 0234a4fc..8c1d53a6 100644 --- a/types/list.go +++ b/types/list.go @@ -75,8 +75,8 @@ type ListBucketsByPaymentAccountResult struct { } type ListUserPaymentAccountsResult struct { - // StreamRecords defines the list of stream records - StreamRecords []*StreamRecordsMeta `xml:"StreamRecords"` + // PaymentAccount defines the list of payment accounts + PaymentAccounts []*PaymentAccounts `xml:"PaymentAccounts"` } type ListGroupsResult struct { @@ -274,6 +274,12 @@ type ListBucketsByBucketIDResponse struct { Buckets map[uint64]*BucketMeta `xml:"Buckets"` } +// ListGroupsByGroupIDResponse is response type for the ListGroupsByGroupID +type ListGroupsByGroupIDResponse struct { + // Groups defines the information of a group map + Groups map[uint64]*GroupMeta `xml:"Groups"` +} + // GroupMeta is the structure for group information type GroupMeta struct { // group defines the basic group info @@ -308,11 +314,11 @@ type GroupInfo struct { Extra string `xml:"Extra"` } -type StreamRecordsMeta struct { +type PaymentAccounts struct { + // refundable defines the payment account is refundable or not + PaymentAccount *PaymentAccount `xml:"PaymentAccount"` // stream_records defines stream payment records of a stream account StreamRecord *StreamRecord `xml:"StreamRecord"` - // refundable defines the payment account is refundable or not - Refundable bool `xml:"Refundable"` } // StreamRecord defines Record of a stream account @@ -341,6 +347,20 @@ type StreamRecord struct { FrozenNetflowRate int64 `xml:"FrozenNetflowRate"` } +// PaymentAccount defines payment account info +type PaymentAccount struct { + // Address defines the address of payment account + Address string `xml:"Address"` + // Owner defines the owner of this payment account + Owner string `xml:"Owner"` + // Refundable defines the payment account is refundable or not + Refundable bool `xml:"Refundable"` + // UpdateAt defines the update block height of this payment account + UpdateAt int64 `xml:"UpdateAt"` + // UpdateTime defines the update time of this payment account + UpdateTime int64 `xml:"UpdateTime"` +} + type ListObjectPoliciesResponse struct { Policies []*PolicyMeta `xml:"Policies"` }