Skip to content
This repository has been archived by the owner on Jun 27, 2023. It is now read-only.

Commit

Permalink
Merge pull request #3 from ipfs/feat/update-spec
Browse files Browse the repository at this point in the history
feat: update to the latest pinning service api spec
  • Loading branch information
aschmahmann authored Nov 30, 2020
2 parents c86fa75 + 422b6d3 commit 1be53f5
Show file tree
Hide file tree
Showing 28 changed files with 1,003 additions and 707 deletions.
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
language: go

go:
- 1.14.x

install:
- go get -d -v .

Expand Down
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,27 @@ An IPFS Pinning Service HTTP Client

[Adin Schmahmann](https://github.com/aschmahmann)

## Updating Pinning Service Spec

Download the openapi-generator from https://github.com/OpenAPITools/openapi-generator and generate the code using:

Current code generated with: openapi-generator 5.0.0-beta

```
openapi-generator generate -g go-experimental -i https://raw.githubusercontent.com/ipfs/pinning-services-api-spec/master/ipfs-pinning-service.yaml -o openapi
rm openapi/go.mod openapi/go.sum
```

Notes:
Due to https://github.com/OpenAPITools/openapi-generator/issues/7473 the code generator the http error codes processing
may need some manual editing.

`go-experimental` is becoming mainstream and so in later versions will be replaced with `go`

## Contributing

Contributions are welcome! This repository is part of the IPFS project and therefore governed by our [contributing guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md).

## License

[SPDX-License-Identifier: Apache-2.0 OR MIT](LICENSE.md)
[SPDX-License-Identifier: Apache-2.0 OR MIT](LICENSE.md)
48 changes: 11 additions & 37 deletions client.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package go_pinning_service_http_client

import (
"bytes"
"context"
"encoding/json"
"fmt"
"github.com/pkg/errors"
"net/http"
"time"

Expand Down Expand Up @@ -38,10 +37,6 @@ func NewClient(url, bearerToken string) *Client {
return &Client{client: openapi.NewAPIClient(config)}
}

func getError(e *openapi.Error) error {
return fmt.Errorf("request error: %d - %s", e.Code, e.Message)
}

// TODO: We should probably make sure there are no duplicates sent
type lsSettings struct {
cids []string
Expand Down Expand Up @@ -311,7 +306,7 @@ func (c *Client) Add(ctx context.Context, cid cid.Cid, opts ...AddOption) (PinSt
}

func (c *Client) GetStatusByID(ctx context.Context, pinID string) (PinStatusGetter, error) {
getter := c.client.PinsApi.PinsIdGet(ctx, pinID)
getter := c.client.PinsApi.PinsRequestidGet(ctx, pinID)
result, httpresp, err := getter.Execute()
if err != nil {
err := httperr(httpresp, err)
Expand All @@ -322,7 +317,7 @@ func (c *Client) GetStatusByID(ctx context.Context, pinID string) (PinStatusGett
}

func (c *Client) DeleteByID(ctx context.Context, pinID string) error {
deleter := c.client.PinsApi.PinsIdDelete(ctx, pinID)
deleter := c.client.PinsApi.PinsRequestidDelete(ctx, pinID)
httpresp, err := deleter.Execute()
if err != nil {
err := httperr(httpresp, err)
Expand All @@ -339,7 +334,7 @@ func (c *Client) Modify(ctx context.Context, pinID string, cid cid.Cid, opts ...
}
}

adder := c.client.PinsApi.PinsIdPost(ctx, pinID)
adder := c.client.PinsApi.PinsRequestidPost(ctx, pinID)
p := openapi.Pin{
Cid: cid.Encode(getCIDEncoder()),
}
Expand Down Expand Up @@ -373,37 +368,16 @@ func getCIDEncoder() multibase.Encoder {

func httperr(resp *http.Response, e error) error {
oerr, ok := e.(openapi.GenericOpenAPIError)
if !ok {
panic("wrong error type")
}
var buf bytes.Buffer
var err error

var reqStr string
if resp.Request.GetBody != nil {
resp.Request.Body, err = resp.Request.GetBody()
if err != nil {
reqStr = err.Error()
} else if err := resp.Request.Write(&buf); err != nil {
reqStr = err.Error()
} else {
reqStr = buf.String()
if ok {
ferr, ok := oerr.Model().(openapi.Failure)
if ok {
return errors.Wrapf(e,"statusCode: %d, reason : %q, details : %q", resp.StatusCode, ferr.Error.GetReason(), ferr.Error.GetDetails())
}
} else {
reqStr = resp.Request.URL.String()
}

bodystr := string(oerr.Body())
//body, err := ioutil.ReadAll(resp.Body)
//var bodystr string
//if err == nil {
// bodystr = string(body)
//}
relevantErr := fmt.Sprintf("{ httpcode: %d, httpresp: %s, httpbody: %s, reqstr: %s }", resp.StatusCode, resp.Status, bodystr, reqStr)
relevantErrBytes, err := json.MarshalIndent(relevantErr, "", "\t")
if err != nil {
return fmt.Errorf("RelevantInfo : %s, MarshalErr: %w, Err: %w", relevantErr, err, e)
if resp == nil {
return errors.Wrapf(e,"empty response from remote pinning service")
}

return fmt.Errorf("relevantErr: %s, err: %w", relevantErrBytes, e)
return errors.Wrapf(e, "remote pinning service error. statusCode: %d", resp.StatusCode)
}
4 changes: 2 additions & 2 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func main() {

var pinned bool
for !pinned {
status, err := c.GetStatusByID(ctx, ps.GetId())
status, err := c.GetStatusByID(ctx, ps.GetRequestId())
if err == nil {
fmt.Println(status.GetStatus())
pinned = status.GetStatus() == pinclient.StatusPinned
Expand All @@ -68,7 +68,7 @@ func main() {
listPins(ctx, c)

fmt.Println("Delete pin")
err = c.DeleteByID(ctx, ps.GetId())
err = c.DeleteByID(ctx, ps.GetRequestId())
if err == nil {
fmt.Println("Successfully deleted pin")
} else {
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ require (
github.com/ipfs/go-log/v2 v2.1.1
github.com/multiformats/go-multiaddr v0.3.1
github.com/multiformats/go-multibase v0.0.3
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
github.com/pkg/errors v0.8.1
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
)
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down Expand Up @@ -78,8 +80,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2eP
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
Expand Down
10 changes: 7 additions & 3 deletions model.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ type PinStatusGetter interface {
fmt.Stringer
json.Marshaler
// Globally unique ID of the pin request; can be used to check the status of ongoing pinning, modification of pin object, or pin removal
GetId() string
GetRequestId() string
GetStatus() Status
// Immutable timestamp indicating when a pin request entered a pinning service; can be used for filtering results and pagination
GetCreated() time.Time
Expand Down Expand Up @@ -129,6 +129,10 @@ func (p *pinStatusObject) GetStatus() Status {
return Status(p.PinStatus.GetStatus())
}

func (p *pinStatusObject) GetRequestId() string {
return p.GetRequestid()
}

func (p *pinStatusObject) MarshalJSON() ([]byte, error) {
var delegatesStr string
if d := p.GetDelegates(); d != nil {
Expand All @@ -146,8 +150,8 @@ func (p *pinStatusObject) MarshalJSON() ([]byte, error) {
}
}

str := fmt.Sprintf("{\"Pin\" : %v, \"ID\" : \"%s\", \"Status\" : \"%s\", \"Created\" : \"%v\", \"Delegates\" : %v, \"Info\" : %v }",
p.GetPin(), p.GetId(), p.GetStatus(), p.GetCreated(), delegatesStr, infoStr)
str := fmt.Sprintf("{\"Pin\" : %v, \"RequestID\" : \"%s\", \"Status\" : \"%s\", \"Created\" : \"%v\", \"Delegates\" : %v, \"Info\" : %v }",
p.GetPin(), p.GetRequestId(), p.GetStatus(), p.GetCreated(), delegatesStr, infoStr)

return []byte(str), nil
}
Expand Down
6 changes: 4 additions & 2 deletions openapi/.openapi-generator/FILES
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ api/openapi.yaml
api_pins.go
client.go
configuration.go
docs/Error.md
docs/Failure.md
docs/FailureError.md
docs/Pin.md
docs/PinResults.md
docs/PinStatus.md
Expand All @@ -15,7 +16,8 @@ docs/Status.md
git_push.sh
go.mod
go.sum
model_error.go
model_failure.go
model_failure_error.go
model_pin.go
model_pin_results.go
model_pin_status.go
Expand Down
40 changes: 26 additions & 14 deletions openapi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,41 +13,52 @@ The IPFS Pinning Service API is intended to be an implementation-agnostic API&#x
This section describes the most important object types and conventions.

A full list of fields and schemas can be found in the `schemas` section of the [YAML file](https://github.com/ipfs/pinning-services-api-spec/blob/master/ipfs-pinning-service.yaml).

## Identifiers
### cid
[Content Identifier (CID)](https://docs.ipfs.io/concepts/content-addressing/) points at the root of a DAG that is pinned recursively.
### requestid
Unique identifier of a pin request.

When a pin is created, the service responds with unique `requestid` that can be later used for pin removal. When the same `cid` is pinned again, a different `requestid` is returned to differentiate between those pin requests.

Service implementation should use UUID, `hash(accessToken,Pin,PinStatus.created)`, or any other opaque identifier that provides equally strong protection against race conditions.

## Objects
### Pin object

![pinning-services-api-pin-object.png](https://bafybeidcoyxd723nakggdayy6qg25bl7mls3ilwwiyxmys5qpek7y6jwc4.ipfs.dweb.link/?filename=pinning-services-api-pin-object.png)
![pin object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pin.png)

The `Pin` object is a representation of a pin request.

It includes the `cid` of data to be pinned, as well as optional metadata in `name`, `origins`, and `meta`.

### Pin status response

![pinning-services-api-pin-status-response.png](https://bafybeiec3c3gzsus4rksddsuxcybilex3odq5cm2cyrzrb7m3suwspl6uy.ipfs.dweb.link/?filename=pinning-services-api-pin-status-response.png)
![pin status response object](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/pinstatus.png)

The `PinStatus` object is a representation of the current state of a pinning operation.
It includes the original `pin` object, along with the current `status` and globally unique `id` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`.
It includes the original `pin` object, along with the current `status` and globally unique `requestid` of the entire pinning request, which can be used for future status checks and management. Addresses in the `delegates` array are peers delegated by the pinning service for facilitating direct file transfers (more details in the provider hints section). Any additional vendor-specific information is returned in optional `info`.

## The pin lifecycle

![pinning-services-api-objects.png](https://bafybeigyefq6vwfcsi7dfgqunf4uei426lvia3w73ylg4kgdwwg6txivpe.ipfs.dweb.link/?filename=pinning-services-api-objects.png)
![pinning service objects and lifecycle](https://bafybeideck2fchyxna4wqwc2mo67yriokehw3yujboc5redjdaajrk2fjq.ipfs.dweb.link/lifecycle.png)

### Creating a new pin object
The user sends a `Pin` object to `POST /pins` and receives a `PinStatus` response:
- `id` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, modifying the pin, and/or removing the pin in the future
- `requestid` in `PinStatus` is the identifier of the pin operation, which can can be used for checking status, and removing the pin in the future
- `status` in `PinStatus` indicates the current state of a pin

### Checking status of in-progress pinning
`status` (in `PinStatus`) may indicate a pending state (`queued` or `pinning`). This means the data behind `Pin.cid` was not found on the pinning service and is being fetched from the IPFS network at large, which may take time.

In this case, the user can periodically check pinning progress via `GET /pins/{id}` until pinning is successful, or the user decides to remove the pending pin.
In this case, the user can periodically check pinning progress via `GET /pins/{requestid}` until pinning is successful, or the user decides to remove the pending pin.

### Modifying an existing pin object
The user can modify an existing pin object via `POST /pins/{id}`. The new pin object `id` is returned in the `PinStatus` response. The old pin object is deleted automatically.
### Replacing an existing pin object
The user can replace an existing pin object via `POST /pins/{requestid}`. This is a shortcut for removing a pin object identified by `requestid` and creating a new one in a single API call that protects against undesired garbage collection of blocks common to both pins. Useful when updating a pin representing a huge dataset where most of blocks did not change. The new pin object `requestid` is returned in the `PinStatus` response. The old pin object is deleted automatically.

### Removing a pin object
A pin object can be removed via `DELETE /pins/{id}`.
A pin object can be removed via `DELETE /pins/{requestid}`.


## Provider hints
Expand Down Expand Up @@ -93,7 +104,7 @@ Pin objects can be listed by executing `GET /pins` with optional parameters:
## Overview
This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the [OpenAPI-spec](https://www.openapis.org/) from a remote server, you can easily generate an API client.

- API version: 0.0.5
- API version: 0.1.1
- Package version: 1.0.0
- Build package: org.openapitools.codegen.languages.GoClientExperimentalCodegen

Expand Down Expand Up @@ -161,15 +172,16 @@ All URIs are relative to *https://pinning-service.example.com*
Class | Method | HTTP request | Description
------------ | ------------- | ------------- | -------------
*PinsApi* | [**PinsGet**](docs/PinsApi.md#pinsget) | **Get** /pins | List pin objects
*PinsApi* | [**PinsIdDelete**](docs/PinsApi.md#pinsiddelete) | **Delete** /pins/{id} | Remove pin object
*PinsApi* | [**PinsIdGet**](docs/PinsApi.md#pinsidget) | **Get** /pins/{id} | Get pin object
*PinsApi* | [**PinsIdPost**](docs/PinsApi.md#pinsidpost) | **Post** /pins/{id} | Modify pin object
*PinsApi* | [**PinsPost**](docs/PinsApi.md#pinspost) | **Post** /pins | Add pin object
*PinsApi* | [**PinsRequestidDelete**](docs/PinsApi.md#pinsrequestiddelete) | **Delete** /pins/{requestid} | Remove pin object
*PinsApi* | [**PinsRequestidGet**](docs/PinsApi.md#pinsrequestidget) | **Get** /pins/{requestid} | Get pin object
*PinsApi* | [**PinsRequestidPost**](docs/PinsApi.md#pinsrequestidpost) | **Post** /pins/{requestid} | Replace pin object


## Documentation For Models

- [Error](docs/Error.md)
- [Failure](docs/Failure.md)
- [FailureError](docs/FailureError.md)
- [Pin](docs/Pin.md)
- [PinResults](docs/PinResults.md)
- [PinStatus](docs/PinStatus.md)
Expand Down
Loading

0 comments on commit 1be53f5

Please sign in to comment.