212 lines
7.4 KiB
Go
212 lines
7.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"git.cin.su/m/Assistant/contracts/capability"
|
|
"git.cin.su/m/Assistant/contracts/continuity"
|
|
"git.cin.su/m/Assistant/contracts/identity"
|
|
"git.cin.su/m/Assistant/contracts/work"
|
|
)
|
|
|
|
var (
|
|
errCreatorSessionDenied = errors.New("creator session denied")
|
|
errCreatorOwnerDenied = errors.New("creator owner denied")
|
|
errCreatorSubjectMissing = errors.New("creator subject not found")
|
|
errCreatorQueryUnavailable = errors.New("creator query unavailable")
|
|
)
|
|
|
|
type creatorCoreClient struct {
|
|
baseURL *url.URL
|
|
client *http.Client
|
|
}
|
|
|
|
type creatorContinuityClient struct {
|
|
baseURL *url.URL
|
|
client *http.Client
|
|
}
|
|
|
|
func newCreatorCoreClient(rawURL string, client *http.Client) (creatorCoreClient, error) {
|
|
baseURL, err := parseCreatorRuntimeURL(rawURL)
|
|
return creatorCoreClient{baseURL: baseURL, client: client}, err
|
|
}
|
|
|
|
func newCreatorContinuityClient(rawURL string, client *http.Client) (creatorContinuityClient, error) {
|
|
baseURL, err := parseCreatorRuntimeURL(rawURL)
|
|
return creatorContinuityClient{baseURL: baseURL, client: client}, err
|
|
}
|
|
|
|
func parseCreatorRuntimeURL(rawURL string) (*url.URL, error) {
|
|
baseURL, err := url.Parse(rawURL)
|
|
if err != nil || baseURL.Scheme == "" || baseURL.Host == "" {
|
|
return nil, fmt.Errorf("invalid Creator runtime URL")
|
|
}
|
|
return baseURL, nil
|
|
}
|
|
|
|
func (c creatorCoreClient) Session(ctx context.Context, authorization string) (identity.CreatorSession, error) {
|
|
var session identity.CreatorSession
|
|
status, err := c.get(ctx, "/v1/creator/sessions/current", authorization, &session)
|
|
if err != nil {
|
|
return identity.CreatorSession{}, err
|
|
}
|
|
if status == http.StatusUnauthorized {
|
|
return identity.CreatorSession{}, errCreatorSessionDenied
|
|
}
|
|
if status != http.StatusOK || session.ActorID == "" || session.PrincipalID == "" {
|
|
return identity.CreatorSession{}, errCreatorQueryUnavailable
|
|
}
|
|
return session, nil
|
|
}
|
|
|
|
func (c creatorCoreClient) Task(ctx context.Context, id work.TaskID) (work.TaskTarget, error) {
|
|
var target work.TaskTarget
|
|
status, err := c.get(ctx, "/v1/tasks/"+url.PathEscape(string(id)), "", &target)
|
|
if err != nil {
|
|
return work.TaskTarget{}, err
|
|
}
|
|
if status == http.StatusNotFound {
|
|
return work.TaskTarget{}, errCreatorSubjectMissing
|
|
}
|
|
if status != http.StatusOK {
|
|
return work.TaskTarget{}, errCreatorQueryUnavailable
|
|
}
|
|
return target, nil
|
|
}
|
|
|
|
func (c creatorCoreClient) Binding(ctx context.Context, actor identity.ActorID, principal identity.PrincipalID) (identity.PrincipalBinding, error) {
|
|
var binding identity.PrincipalBinding
|
|
status, err := c.get(ctx, "/v1/principal-bindings/"+url.PathEscape(string(actor))+"/"+url.PathEscape(string(principal)), "", &binding)
|
|
if err != nil {
|
|
return identity.PrincipalBinding{}, err
|
|
}
|
|
if status == http.StatusNotFound {
|
|
return identity.PrincipalBinding{}, errCreatorOwnerDenied
|
|
}
|
|
if status != http.StatusOK {
|
|
return identity.PrincipalBinding{}, errCreatorQueryUnavailable
|
|
}
|
|
return binding, nil
|
|
}
|
|
|
|
func (c creatorCoreClient) ActiveCapabilityBinding(ctx context.Context, capabilityID capability.CapabilityID, owner identity.PrincipalID) (capability.Binding, error) {
|
|
var binding capability.Binding
|
|
target := *c.baseURL
|
|
target.Path = "/v1/capability-bindings/active"
|
|
query := target.Query()
|
|
query.Set("capability_id", string(capabilityID))
|
|
query.Set("owner_principal_id", string(owner))
|
|
target.RawQuery = query.Encode()
|
|
status, err := c.getURL(ctx, target, "", &binding)
|
|
if err != nil || status != http.StatusOK {
|
|
return capability.Binding{}, errCreatorQueryUnavailable
|
|
}
|
|
return binding, nil
|
|
}
|
|
|
|
func (c creatorCoreClient) CapabilityBinding(ctx context.Context, id capability.CapabilityBindingID, revision int64) (capability.Binding, error) {
|
|
var binding capability.Binding
|
|
status, err := c.get(ctx, "/v1/capability-bindings/"+url.PathEscape(string(id))+"/revisions/"+strconv.FormatInt(revision, 10), "", &binding)
|
|
if err != nil || status != http.StatusOK {
|
|
return capability.Binding{}, errCreatorQueryUnavailable
|
|
}
|
|
return binding, nil
|
|
}
|
|
|
|
func (c creatorCoreClient) get(ctx context.Context, path, authorization string, destination any) (int, error) {
|
|
target := *c.baseURL
|
|
target.Path = path
|
|
target.RawQuery = ""
|
|
return c.getURL(ctx, target, authorization, destination)
|
|
}
|
|
|
|
func (c creatorCoreClient) getURL(ctx context.Context, target url.URL, authorization string, destination any) (int, error) {
|
|
request, err := http.NewRequestWithContext(ctx, http.MethodGet, target.String(), nil)
|
|
if err != nil {
|
|
return 0, errCreatorQueryUnavailable
|
|
}
|
|
if authorization != "" {
|
|
request.Header.Set("Authorization", authorization)
|
|
}
|
|
response, err := c.client.Do(request)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("%w: %v", errCreatorQueryUnavailable, err)
|
|
}
|
|
defer response.Body.Close()
|
|
if response.StatusCode == http.StatusOK {
|
|
if err := json.NewDecoder(io.LimitReader(response.Body, 32<<10)).Decode(destination); err != nil {
|
|
return 0, errCreatorQueryUnavailable
|
|
}
|
|
}
|
|
return response.StatusCode, nil
|
|
}
|
|
|
|
func (c creatorContinuityClient) Item(ctx context.Context, authorization string, id continuity.ProspectiveItemID) (continuity.ProspectiveItem, error) {
|
|
if !strings.HasPrefix(authorization, "Bearer ") {
|
|
return continuity.ProspectiveItem{}, errCreatorSessionDenied
|
|
}
|
|
target := *c.baseURL
|
|
target.Path = "/v1/creator/prospective-items/" + url.PathEscape(string(id))
|
|
target.RawQuery = ""
|
|
request, err := http.NewRequestWithContext(ctx, http.MethodGet, target.String(), nil)
|
|
if err != nil {
|
|
return continuity.ProspectiveItem{}, errCreatorQueryUnavailable
|
|
}
|
|
request.Header.Set("Authorization", authorization)
|
|
response, err := c.client.Do(request)
|
|
if err != nil {
|
|
return continuity.ProspectiveItem{}, fmt.Errorf("%w: %v", errCreatorQueryUnavailable, err)
|
|
}
|
|
defer response.Body.Close()
|
|
switch response.StatusCode {
|
|
case http.StatusUnauthorized:
|
|
return continuity.ProspectiveItem{}, errCreatorSessionDenied
|
|
case http.StatusForbidden:
|
|
return continuity.ProspectiveItem{}, errCreatorOwnerDenied
|
|
case http.StatusNotFound:
|
|
return continuity.ProspectiveItem{}, errCreatorSubjectMissing
|
|
case http.StatusOK:
|
|
var item continuity.ProspectiveItem
|
|
if err := json.NewDecoder(io.LimitReader(response.Body, 32<<10)).Decode(&item); err != nil {
|
|
return continuity.ProspectiveItem{}, errCreatorQueryUnavailable
|
|
}
|
|
return item, nil
|
|
default:
|
|
return continuity.ProspectiveItem{}, errCreatorQueryUnavailable
|
|
}
|
|
}
|
|
|
|
func (c creatorContinuityClient) InternalItem(ctx context.Context, id continuity.ProspectiveItemID) (continuity.ProspectiveItem, error) {
|
|
target := *c.baseURL
|
|
target.Path = "/v1/prospective-items/" + url.PathEscape(string(id))
|
|
target.RawQuery = ""
|
|
request, err := http.NewRequestWithContext(ctx, http.MethodGet, target.String(), nil)
|
|
if err != nil {
|
|
return continuity.ProspectiveItem{}, errCreatorQueryUnavailable
|
|
}
|
|
response, err := c.client.Do(request)
|
|
if err != nil {
|
|
return continuity.ProspectiveItem{}, fmt.Errorf("%w: %v", errCreatorQueryUnavailable, err)
|
|
}
|
|
defer response.Body.Close()
|
|
if response.StatusCode == http.StatusNotFound {
|
|
return continuity.ProspectiveItem{}, errCreatorSubjectMissing
|
|
}
|
|
if response.StatusCode != http.StatusOK {
|
|
return continuity.ProspectiveItem{}, errCreatorQueryUnavailable
|
|
}
|
|
var item continuity.ProspectiveItem
|
|
if err := json.NewDecoder(io.LimitReader(response.Body, 32<<10)).Decode(&item); err != nil {
|
|
return continuity.ProspectiveItem{}, errCreatorQueryUnavailable
|
|
}
|
|
return item, nil
|
|
}
|