Skip to content
This repository has been archived by the owner on Mar 14, 2022. It is now read-only.

Persist a record in the repository #109

Merged
merged 1 commit into from
Jan 31, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,22 @@ jobs:
- run:
name: Run Unit Tests
command: go test -v ./... -short
- run:
name: Install AWS CLI
command: |
sudo apt-get -y -qq update
sudo apt-get -y -qq install awscli
Copy link
Contributor

@eefahy eefahy Jan 31, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is an old version of the awscli that will prob bite you down the road. The preferred way to install it is in the deploy_to_dev job below. This can get changed later though, so not blocking this PR for that change

- run:
name: Create table
command: AWS_ACCESS_KEY_ID=999 AWS_SECRET_ACCESS_KEY=999 aws dynamodb create-table
--endpoint-url=http://localhost:4569 --table-name resources
--region localstack
--attribute-definitions "AttributeName=id,AttributeType=S"
--key-schema "AttributeName=id,KeyType=HASH"
--provisioned-throughput=ReadCapacityUnits=10,WriteCapacityUnits=10
- run:
name: Start server
command: go run cmd/tacod/main.go --port 3000
command: cd cmd/tacod && AWS_ACCESS_KEY_ID=999 AWS_SECRET_KEY=999 go run main.go --port 3000
background: true
- run:
name: wait for server
Expand Down
30 changes: 17 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ $ go get github.com/sul-dlss-labs/taco

## Running the Go Code locally without a build


```shell
$ go run main.go
$ cd cmd/tacod
$ AWS_ACCESS_KEY_ID=999999 AWS_SECRET_KEY=1231 go run main.go
```

## Building to TACO Binary
Expand Down Expand Up @@ -70,16 +70,26 @@ $ awslocal dynamodb create-table --table-name resources \
--provisioned-throughput=ReadCapacityUnits=100,WriteCapacityUnits=100
```

And add a stub record:
```
$ awslocal dynamodb put-item --table-name resources --item '{"id": {"S":"99"}, "title":{"S":"Ta-da!"}}'
Now start the API server:
```shell
% AWS_ACCESS_KEY_ID=999999 AWS_SECRET_KEY=1231 ./tacod
```

Then you can interact with it using `curl`:
```shell
% TACO_ENV=production AWS_ACCESS_KEY_ID=999999 AWS_SECRET_KEY=1231 ./tacod
curl -X POST -H "Content-Type: application/json" -d '{"title":"value1", "sourceId":"value2"}' http://localhost:8080/v1/resource
```

Now visit: http://localhost:8080/v1/resource/99
it will return a response like:
```json
{"id":"fe1f66a9-5285-4b28-8240-0482c8fff6c7"}
```

Then you can use the returned identifier to retrieve the original:

```shell
curl -H "Content-Type: application/json" http://localhost:8080/v1/resource/fe1f66a9-5285-4b28-8240-0482c8fff6c7
```

## API Code Structure

Expand All @@ -104,12 +114,6 @@ go install

Do this prior to generating code.

### To run the API code

```shell
$ go run main.go
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This already exists on line 34, so removing.

```

### Non-generated code

The API code generation does **not** touch the following, which we are writing locally:
Expand Down
2 changes: 2 additions & 0 deletions config/development.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ db:
region: "localstack"
endpoint: "localhost:4569"
disable_ssl: true
resource_repository:
table_name: "resources"
2 changes: 2 additions & 0 deletions config/production.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ db:
region: "us-east-2"
endpoint: "https://dynamodb.us-east-2.amazonaws.com"
disable_ssl: false
resource_repository:
table_name: "resources"
2 changes: 2 additions & 0 deletions config/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ db:
region: "localstack"
endpoint: "localhost:4569"
disable_ssl: true
resource_repository:
table_name: "resources"
40 changes: 35 additions & 5 deletions handlers/deposit_resource.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,55 @@
package handlers

import (
"fmt"

"github.com/go-openapi/runtime/middleware"
"github.com/google/uuid"
"github.com/sul-dlss-labs/taco"
"github.com/sul-dlss-labs/taco/generated/models"
"github.com/sul-dlss-labs/taco/generated/restapi/operations"
"github.com/sul-dlss-labs/taco/persistence"
)

// NewDepositResource -- Accepts requests to create resource and pushes them to Kinesis.
func NewDepositResource(rt *taco.Runtime) operations.DepositNewResourceHandler {
return &depositResourceEntry{}
return &depositResourceEntry{rt: rt}
}

type depositResourceEntry struct{}
type depositResourceEntry struct {
rt *taco.Runtime
}

// Handle the delete entry request
func (d *depositResourceEntry) Handle(params operations.DepositNewResourceParams) middleware.Responder {
// TODO: This should be a DRUID
resourceID, _ := uuid.NewRandom()

response := &models.DepositNewResourceOKBody{ID: resourceID.String()}
resourceID := mintID()

if err := d.persistResource(resourceID, params); err != nil {
// TODO: handle this with an error response
panic(err)
}

response := &models.DepositNewResourceOKBody{ID: resourceID}
return operations.NewDepositNewResourceOK().WithPayload(response)
}

func (d *depositResourceEntry) persistResource(resourceID string, params operations.DepositNewResourceParams) error {
resource := d.persistableResourceFromParams(resourceID, params)
fmt.Println("Saving")
return d.rt.Repository().SaveItem(resource)
}

func (d *depositResourceEntry) persistableResourceFromParams(resourceID string, params operations.DepositNewResourceParams) *persistence.Resource {
resource := &persistence.Resource{ID: resourceID}
// TODO: Expand this mapping:
resource.Title = *params.Payload.Title
resource.SourceID = *params.Payload.SourceID
return resource
}

func mintID() string {
// TODO: This should be a DRUID
resourceID, _ := uuid.NewRandom()
return resourceID.String()
}
71 changes: 71 additions & 0 deletions handlers/deposit_resource_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package handlers

import (
"errors"
"net/http"
"testing"

"github.com/appleboy/gofight"
"github.com/stretchr/testify/assert"
"github.com/sul-dlss-labs/taco/persistence"
)

func mockErrorRepo() persistence.Repository {
return &fakeErroringRepository{}
}

type fakeErroringRepository struct{}

func (f *fakeErroringRepository) GetByID(id string) (*persistence.Resource, error) {
return nil, nil
}

func (f *fakeErroringRepository) SaveItem(resource *persistence.Resource) error {
return errors.New("not found")
}

func TestCreateResourceHappyPath(t *testing.T) {
r := gofight.New()
repo := mockRepo(nil)

r.POST("/v1/resource").
SetJSON(gofight.D{
"id": "oo000oo0001",
"sourceId": "bib12345678",
"title": "My work",
}).
Run(setupFakeRuntime(repo),
func(r gofight.HTTPResponse, rq gofight.HTTPRequest) {
assert.Equal(t, http.StatusOK, r.Code)
assert.Equal(t, 1, len(repo.(*fakeRepository).CreatedResources))

})
}

func TestCreateResourceMissingSourceId(t *testing.T) {
r := gofight.New()
r.POST("/v1/resource").
SetJSON(gofight.D{
"id": "oo000oo0001",
"title": "My work",
}).
Run(setupFakeRuntime(mockRepo(nil)),
func(r gofight.HTTPResponse, rq gofight.HTTPRequest) {
assert.Equal(t, http.StatusUnprocessableEntity, r.Code)
})
}

// TODO: Handle errors
// func TestCreateResourceFailure(t *testing.T) {
// r := gofight.New()
// r.POST("/v1/resource").
// SetJSON(gofight.D{
// "id": "oo000oo0001",
// "sourceId": "bib12345678",
// "title": "My work",
// }).
// Run(setupFakeRuntime(mockErrorRepo()),
// func(r gofight.HTTPResponse, rq gofight.HTTPRequest) {
// assert.Equal(t, http.StatusUnprocessableEntity, r.Code)
// })
// }
44 changes: 11 additions & 33 deletions handlers/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,31 @@ import (
"github.com/stretchr/testify/assert"
"github.com/sul-dlss-labs/taco"
"github.com/sul-dlss-labs/taco/config"
"github.com/sul-dlss-labs/taco/generated/models"
"github.com/sul-dlss-labs/taco/persistence"
)

func mockRepo(record *models.Resource) persistence.Repository {
return &fakeRepository{record: record}
func mockRepo(record *persistence.Resource) persistence.Repository {
return &fakeRepository{record: record, CreatedResources: []persistence.Resource{}}
}

type fakeRepository struct {
record *models.Resource
record *persistence.Resource
CreatedResources []persistence.Resource
}

func (f fakeRepository) GetByID(id string) (*models.Resource, error) {
func (f *fakeRepository) GetByID(id string) (*persistence.Resource, error) {

if f.record != nil {
return f.record, nil
}
return nil, errors.New("not found")
}

func (f *fakeRepository) SaveItem(resource *persistence.Resource) error {
f.CreatedResources = append(f.CreatedResources, *resource)
return nil
}

func setupFakeRuntime(repo persistence.Repository) http.Handler {
config.Init("../config/test.yaml")
rt, _ := taco.NewRuntimeForRepository(viper.GetViper(), repo)
Expand All @@ -39,7 +44,7 @@ func setupFakeRuntime(repo persistence.Repository) http.Handler {
func TestRetrieveHappyPath(t *testing.T) {
r := gofight.New()
r.GET("/v1/resource/99").
Run(setupFakeRuntime(mockRepo(new(models.Resource))),
Run(setupFakeRuntime(mockRepo(new(persistence.Resource))),
func(r gofight.HTTPResponse, rq gofight.HTTPRequest) {
assert.Equal(t, http.StatusOK, r.Code)
})
Expand All @@ -53,30 +58,3 @@ func TestRetrieveNotFound(t *testing.T) {
assert.Equal(t, http.StatusNotFound, r.Code)
})
}

func TestCreateResourceHappyPath(t *testing.T) {
r := gofight.New()
r.POST("/v1/resource").
SetJSON(gofight.D{
"id": "oo000oo0001",
"sourceId": "bib12345678",
"title": "My work",
}).
Run(setupFakeRuntime(mockRepo(nil)),
func(r gofight.HTTPResponse, rq gofight.HTTPRequest) {
assert.Equal(t, http.StatusOK, r.Code)
})
}

func TestCreateResourceMissingSourceId(t *testing.T) {
r := gofight.New()
r.POST("/v1/resource").
SetJSON(gofight.D{
"id": "oo000oo0001",
"title": "My work",
}).
Run(setupFakeRuntime(mockRepo(nil)),
func(r gofight.HTTPResponse, rq gofight.HTTPRequest) {
assert.Equal(t, http.StatusUnprocessableEntity, r.Code)
})
}
10 changes: 9 additions & 1 deletion handlers/retrieve_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package handlers
import (
"github.com/go-openapi/runtime/middleware"
"github.com/sul-dlss-labs/taco"
"github.com/sul-dlss-labs/taco/generated/models"
"github.com/sul-dlss-labs/taco/generated/restapi/operations"
"github.com/sul-dlss-labs/taco/persistence"
)
Expand All @@ -21,9 +22,16 @@ type resourceEntry struct {
func (d *resourceEntry) Handle(params operations.RetrieveResourceParams) middleware.Responder {
resource, err := d.repository.GetByID(params.ID)
if err == nil {
return operations.NewRetrieveResourceOK().WithPayload(resource)
// TODO: expand this mapping
response := buildResponse(resource)
return operations.NewRetrieveResourceOK().WithPayload(response)
} else if err.Error() == "not found" {
return operations.NewRetrieveResourceNotFound()
}
panic(err)
}

// TODO: expand this mapping
func buildResponse(resource *persistence.Resource) *models.Resource {
return &models.Resource{ID: resource.ID, Title: &resource.Title, SourceID: &resource.SourceID}
}
Loading