-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1419 from bstasyszyn/1418
fix: Discover actor from service endpoint in orb-cli
- Loading branch information
Showing
12 changed files
with
1,000 additions
and
292 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
/* | ||
Copyright SecureKey Technologies Inc. All Rights Reserved. | ||
SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package common | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
"strings" | ||
|
||
"github.com/spf13/cobra" | ||
cmdutils "github.com/trustbloc/edge-core/pkg/utils/cmd" | ||
|
||
"github.com/trustbloc/orb/pkg/activitypub/vocab" | ||
orberrors "github.com/trustbloc/orb/pkg/errors" | ||
) | ||
|
||
// ActivityPubClient reads ActivityPub actors and collections from the service endpoint. | ||
type ActivityPubClient struct { | ||
cmd *cobra.Command | ||
sendRequest requestSender | ||
overridesMap map[string]string | ||
} | ||
|
||
// NewActivityPubClient returns a new ActivityPub client. | ||
func NewActivityPubClient(cmd *cobra.Command) (*ActivityPubClient, error) { | ||
httpClient, err := NewHTTPClient(cmd) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
headers := newAuthTokenHeader(cmd) | ||
|
||
overridesMap := make(map[string]string) | ||
|
||
const mappingParts = 2 | ||
|
||
for _, mapping := range cmdutils.GetUserSetOptionalVarFromArrayString(cmd, TargetOverrideFlagName, | ||
TargetOverrideEnvKey) { | ||
pair := strings.Split(mapping, "->") | ||
if len(pair) != mappingParts { | ||
return nil, fmt.Errorf("invalid target override %s", mapping) | ||
} | ||
|
||
overridesMap[pair[0]] = pair[1] | ||
} | ||
|
||
return &ActivityPubClient{ | ||
cmd: cmd, | ||
overridesMap: overridesMap, | ||
sendRequest: func(req []byte, method, endpointURL string) ([]byte, error) { | ||
return SendRequest(httpClient, req, headers, method, endpointURL) | ||
}, | ||
}, nil | ||
} | ||
|
||
// ResolveActor returns the actor for the given endpoint. | ||
func (c *ActivityPubClient) ResolveActor(actorEndpoint string) (*vocab.ActorType, error) { | ||
resp, err := c.sendRequest(nil, http.MethodGet, c.overrideTarget(actorEndpoint)) | ||
if err != nil { | ||
return nil, fmt.Errorf("send http request: %w", err) | ||
} | ||
|
||
actor := &vocab.ActorType{} | ||
|
||
if err := json.Unmarshal(resp, actor); err != nil { | ||
return nil, fmt.Errorf("unmarshal actor: %w", err) | ||
} | ||
|
||
return actor, nil | ||
} | ||
|
||
// GetCollection returns an ActivityPub collection iterator for the given collection IRI. | ||
func (c *ActivityPubClient) GetCollection(collURI *url.URL) (*ActivityPubCollectionIterator, error) { | ||
return newAPCollIterator(c.cmd, collURI.String(), c.sendRequest, c.overrideTarget) | ||
} | ||
|
||
// CollectionContains returns true if the given ActivityPub collection contains the given IRI. | ||
func (c *ActivityPubClient) CollectionContains(collURI *url.URL, iri string) (bool, error) { | ||
it, err := c.GetCollection(collURI) | ||
if err != nil { | ||
return false, fmt.Errorf("get ActivityPub collection iterator: %w", err) | ||
} | ||
|
||
for { | ||
item, err := it.Next() | ||
if err != nil { | ||
if errors.Is(err, orberrors.ErrContentNotFound) { | ||
return false, nil | ||
} | ||
|
||
return false, err | ||
} | ||
|
||
if item.String() == iri { | ||
return true, nil | ||
} | ||
} | ||
} | ||
|
||
// overrideTarget checks if there is an override for the given target (specified with the --target-override flag). | ||
// If so, a new target URL is returned, otherwise the same target is returned. | ||
func (c *ActivityPubClient) overrideTarget(targetURI string) string { | ||
for oldTarget, newTarget := range c.overridesMap { | ||
if strings.Contains(targetURI, "//"+oldTarget) { | ||
return strings.Replace(targetURI, "//"+oldTarget, "//"+newTarget, 1) | ||
} | ||
} | ||
|
||
return targetURI | ||
} | ||
|
||
// ActivityPubCollectionIterator iterates over an ActivityPub collection. | ||
type ActivityPubCollectionIterator struct { | ||
cmd *cobra.Command | ||
sendRequest requestSender | ||
getTargetOverride targetOverrideFunc | ||
nextPage *url.URL | ||
items []*vocab.ObjectProperty | ||
index int | ||
} | ||
|
||
type requestSender func(req []byte, method, endpointURL string) ([]byte, error) | ||
|
||
type targetOverrideFunc func(targetURI string) string | ||
|
||
func newAPCollIterator(cmd *cobra.Command, collURI string, sendRequest requestSender, | ||
getTargetOverride targetOverrideFunc) (*ActivityPubCollectionIterator, error) { | ||
resp, err := sendRequest(nil, http.MethodGet, getTargetOverride(collURI)) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to send http request: %w", err) | ||
} | ||
|
||
coll := &vocab.CollectionType{} | ||
|
||
if err := json.Unmarshal(resp, coll); err != nil { | ||
return nil, err | ||
} | ||
|
||
return &ActivityPubCollectionIterator{ | ||
cmd: cmd, | ||
sendRequest: sendRequest, | ||
getTargetOverride: getTargetOverride, | ||
nextPage: coll.First(), | ||
}, nil | ||
} | ||
|
||
// Next returns the next item in the collection. | ||
func (it *ActivityPubCollectionIterator) Next() (*url.URL, error) { | ||
if it.index >= len(it.items) { | ||
if it.nextPage == nil { | ||
return nil, orberrors.ErrContentNotFound | ||
} | ||
|
||
resp, err := it.sendRequest(nil, http.MethodGet, it.getTargetOverride(it.nextPage.String())) | ||
if err != nil { | ||
return nil, fmt.Errorf("send http request: %w", err) | ||
} | ||
|
||
collPage := &vocab.CollectionPageType{} | ||
|
||
if err := json.Unmarshal(resp, collPage); err != nil { | ||
return nil, err | ||
} | ||
|
||
it.items = collPage.Items() | ||
it.index = 0 | ||
it.nextPage = collPage.Next() | ||
} | ||
|
||
i := it.index | ||
|
||
it.index++ | ||
|
||
if i >= len(it.items) { | ||
return nil, orberrors.ErrContentNotFound | ||
} | ||
|
||
return it.items[i].IRI(), nil | ||
} |
Oops, something went wrong.