github.com/vchain-us/vcn@v0.9.11-0.20210921212052-a2484d23c0b3/pkg/api/artifact.go (about)

     1  /*
     2   * Copyright (c) 2018-2020 vChain, Inc. All Rights Reserved.
     3   * This software is released under GPL3.
     4   * The full license information can be found under:
     5   * https://www.gnu.org/licenses/gpl-3.0.en.html
     6   *
     7   */
     8  
     9  package api
    10  
    11  import (
    12  	"fmt"
    13  
    14  	"github.com/ethereum/go-ethereum/common"
    15  	"github.com/sirupsen/logrus"
    16  	"github.com/vchain-us/vcn/pkg/meta"
    17  )
    18  
    19  // Artifact represents the set of all relevant information gathered from a digital asset.
    20  type Artifact struct {
    21  	Kind        string
    22  	Name        string
    23  	Hash        string
    24  	Size        uint64
    25  	ContentType string
    26  	IncludedIn  []PackageDetails
    27  	Deps        []PackageDetails
    28  	Metadata
    29  }
    30  
    31  type PackageDetails struct {
    32  	Name    string      `json:"name" yaml:"name" vcn:"name"`
    33  	Version string      `json:"version,omitempty" yaml:"version,omitempty" vcn:"version"`
    34  	Hash    string      `json:"hash" yaml:"hash" vcn:"hash"`
    35  	Status  meta.Status `json:"status" yaml:"status" vcn:"status"`
    36  	License string      `json:"license,omitempty" yaml:"license"`
    37  }
    38  
    39  // Copy returns a deep copy of the artifact.
    40  func (a Artifact) Copy() Artifact {
    41  	c := a
    42  	if a.Metadata != nil {
    43  		c.Metadata = nil
    44  		c.Metadata.SetValues(a.Metadata)
    45  	}
    46  	return c
    47  }
    48  
    49  func (a Artifact) toRequest() *artifactRequest {
    50  	aR := &artifactRequest{
    51  		// root fields
    52  		Kind:        a.Kind,
    53  		Name:        a.Name,
    54  		Hash:        a.Hash,
    55  		Size:        a.Size,
    56  		ContentType: a.ContentType,
    57  
    58  		// custom metadata
    59  		Metadata: a.Metadata,
    60  	}
    61  
    62  	// promote url from custom metadata to root
    63  	aR.URL = a.Metadata.swipeString("url")
    64  
    65  	return aR
    66  }
    67  
    68  type artifactRequest struct {
    69  	// root fields
    70  	Kind        string `json:"kind"`
    71  	Name        string `json:"name"`
    72  	Hash        string `json:"hash"`
    73  	Size        uint64 `json:"size,omitempty"`
    74  	ContentType string `json:"contentType"`
    75  	URL         string `json:"url"`
    76  
    77  	// custom metadata
    78  	Metadata Metadata `json:"metadata"`
    79  
    80  	// artifactRequest specific
    81  	Visibility string `json:"visibility"`
    82  	Status     string `json:"status"`
    83  	MetaHash   string `json:"metaHash"`
    84  	TxHash     string `json:"txHash"`
    85  }
    86  
    87  // PagedArtifactResponse holds a page of ArtifactResponse(s) returned by the platform.
    88  type PagedArtifactResponse struct {
    89  	Content       []ArtifactResponse `json:"content"`
    90  	TotalElements uint64             `json:"totalElements"`
    91  	Pageable      struct {
    92  		PageSize   uint64 `json:"pageSize"`
    93  		PageNumber uint64 `json:"pageNumber"`
    94  	} `json:"pageable"`
    95  }
    96  
    97  // ArtifactResponse holds artifact values returned by the platform.
    98  type ArtifactResponse struct {
    99  	// root fields
   100  	Kind        string `json:"kind" yaml:"kind" vcn:"Kind"`
   101  	Name        string `json:"name" yaml:"name" vcn:"Name"`
   102  	Hash        string `json:"hash" yaml:"hash" vcn:"Hash"`
   103  	Size        uint64 `json:"size" yaml:"size" vcn:"Size"`
   104  	ContentType string `json:"contentType" yaml:"contentType" vcn:"ContentType"`
   105  	URL         string `json:"url" yaml:"url" vcn:"URL"`
   106  
   107  	// custom metadata
   108  	Metadata Metadata `json:"metadata" yaml:"metadata" vcn:"Metadata"`
   109  
   110  	// ArtifactResponse specific
   111  	Level             int64  `json:"level,omitempty" yaml:"level,omitempty"`
   112  	Status            string `json:"status,omitempty" yaml:"status,omitempty"`
   113  	Visibility        string `json:"visibility" yaml:"visibility" vcn:"Visibility"`
   114  	CreatedAt         string `json:"createdAt" yaml:"createdAt"`
   115  	VerificationCount uint64 `json:"verificationCount" yaml:"verificationCount"`
   116  	SignerCount       uint64 `json:"signerCount" yaml:"signerCount"`
   117  	Signer            string `json:"signer" yaml:"signer" vcn:"Signer"`
   118  	Company           string `json:"company" yaml:"company" vcn:"Company"`
   119  	Website           string `json:"website" yaml:"website" vcn:"Website"`
   120  }
   121  
   122  func (a ArtifactResponse) String() string {
   123  	return fmt.Sprintf("Name:\t%s\nHash:\t%s\nStatus:\t%s\n\n",
   124  		a.Name, a.Hash, a.Status)
   125  }
   126  
   127  // Artifact returns an new *Artifact from a
   128  func (a ArtifactResponse) Artifact() *Artifact {
   129  	return &Artifact{
   130  		// root fields
   131  		Kind:        a.Kind,
   132  		Name:        a.Name,
   133  		Hash:        a.Hash,
   134  		Size:        a.Size,
   135  		ContentType: a.ContentType,
   136  
   137  		// custom metadata
   138  		Metadata: a.Metadata,
   139  	}
   140  }
   141  
   142  func (u User) createArtifact(verification *BlockchainVerification, walletAddress string,
   143  	artifact Artifact, visibility meta.Visibility, status meta.Status, txHash common.Hash) error {
   144  
   145  	hasAuth, err := u.IsAuthenticated()
   146  	if err != nil {
   147  		return err
   148  	}
   149  	if !hasAuth {
   150  		return makeAuthRequiredError()
   151  	}
   152  
   153  	aR := artifact.toRequest()
   154  	aR.Visibility = visibility.String()
   155  	aR.Status = status.String()
   156  	aR.MetaHash = verification.MetaHash()
   157  	aR.TxHash = txHash.String()
   158  
   159  	restError := new(Error)
   160  	r, err := newSling(u.token()).
   161  		Post(meta.APIEndpoint("artifact")+"?wallet-address="+walletAddress).
   162  		BodyJSON(aR).Receive(nil, restError)
   163  	if err != nil {
   164  		return err
   165  	}
   166  	if r.StatusCode != 200 {
   167  		return fmt.Errorf("request failed: %s (%d)", restError.Message,
   168  			restError.Status)
   169  	}
   170  	return nil
   171  }
   172  
   173  // LoadArtifact fetches and returns an *ArtifactResponse for the given hash and current u, if any.
   174  func (u *User) LoadArtifact(hash string) (*ArtifactResponse, error) {
   175  	notFound := func() (*ArtifactResponse, error) {
   176  		return nil, fmt.Errorf("no asset matching hash %s found for %s", hash, u.Email())
   177  	}
   178  	response := new(PagedArtifactResponse)
   179  	restError := new(Error)
   180  	r, err := newSling(u.token()).
   181  		Get(meta.APIEndpoint("artifact")+"/"+hash+"?scope=CURRENT_USER&size=1&sort=createdAt,desc").
   182  		Receive(&response, restError)
   183  	if err != nil {
   184  		return nil, err
   185  	}
   186  
   187  	switch r.StatusCode {
   188  	case 200:
   189  		if len(response.Content) < 1 {
   190  			return notFound()
   191  		}
   192  	case 404:
   193  		return notFound()
   194  	default:
   195  		return nil, fmt.Errorf("request failed: %s (%d)",
   196  			restError.Message, restError.Status)
   197  	}
   198  
   199  	ar := &response.Content[0]
   200  	if ar.Hash != hash {
   201  		return notFound()
   202  	}
   203  
   204  	return ar, nil
   205  }
   206  
   207  // ListArtifacts fetches and returns a paged list of user's artifacts.
   208  func (u User) ListArtifacts(page uint) (*PagedArtifactResponse, error) {
   209  	response := new(PagedArtifactResponse)
   210  	restError := new(Error)
   211  	url := fmt.Sprintf(
   212  		"%s/search?limit=CURRENT_USER&sort=createdAt,desc&size=25&page=%d&group=true",
   213  		meta.APIEndpoint("artifact"),
   214  		page,
   215  	)
   216  	r, err := newSling(u.token()).
   217  		Get(url).
   218  		Receive(&response, restError)
   219  	if err != nil {
   220  		return nil, err
   221  	}
   222  
   223  	if r.StatusCode == 200 {
   224  		return response, nil
   225  	}
   226  
   227  	return nil, fmt.Errorf(
   228  		"request failed: %s (%d)",
   229  		restError.Message,
   230  		restError.Status,
   231  	)
   232  }
   233  
   234  // LoadArtifact fetches and returns an artifact matching the given hash and optionally a given metahash.
   235  // Returned values depends on user permissions on the artifact, if user is nil then only
   236  // publicly disclosable values are returned.
   237  func LoadArtifact(user *User, hash string, metahash string) (*ArtifactResponse, error) {
   238  	response := new(ArtifactResponse)
   239  	restError := new(Error)
   240  	r, err := newSling(user.token()).
   241  		Get(meta.APIEndpoint("artifact")+"/"+hash+"/"+metahash).
   242  		Receive(&response, restError)
   243  	logger().WithFields(logrus.Fields{
   244  		"response":  response,
   245  		"err":       err,
   246  		"restError": restError,
   247  	}).Trace("LoadArtifact")
   248  	if err != nil {
   249  		return nil, err
   250  	}
   251  	switch r.StatusCode {
   252  	case 200:
   253  		return response, nil
   254  	case 404:
   255  		return nil, fmt.Errorf("no artifact matching %s/%s found", hash, metahash)
   256  	}
   257  	return nil, fmt.Errorf("loading artifact failed: %+v", restError)
   258  }