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 }