diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml new file mode 100644 index 0000000..7554119 --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,22 @@ +# +# Build and test on pull request +# +name: Pull Request + +on: + pull_request: + types: [opened, synchronize, reopened] + +jobs: + build: + name: Build and Test + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-go@v1 + with: + go-version: '1.13.5' + - name: Build + run: go build -v ./... + - name: Test + run: go test -v ./... diff --git a/.gitignore b/.gitignore index 5e0c79c..2e49d54 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ # OS Generated files .DS_Store - + # Temporary files -.*.swp +*.swp + +# IDE files +/.idea diff --git a/DEVELOPING.md b/DEVELOPING.md index 7dba3fa..a3c45da 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -5,17 +5,17 @@ For general information about contributing changes, see the ## How it Works -Describe the internal mechanisms necessary for developers to understand how -to get started making changes. +The provider uses the Titan `remote-sdk-go` to provide interfaces for +Titan to use. ## Building -Describe how to build the project. +Run `go build -v ./...`. ## Testing -Describe how to test the project. +Run `go test -v ./...`. ## Releasing -Describe how to generate new releases. +Push a tag of the form `v..`, and publish the draft release in GitHub. diff --git a/README.md b/README.md index 4763a9e..73a435f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ -# About this Project +# Titan S3 Web Provider -Describe the project for users. +This is a basic Titan read-only web provider designed to work with commits pushed by the S3 +remote provider. For more information on how it works, consult the titan documentation. ## Contributing diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..fbdbb90 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/titan-data/s3web-remote-go + +require ( + github.com/stretchr/testify v1.4.0 + github.com/titan-data/remote-sdk-go v0.0.3 +) + +go 1.13 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..4411a5f --- /dev/null +++ b/go.sum @@ -0,0 +1,14 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/titan-data/remote-sdk-go v0.0.2 h1:NEta4CwcUPj7PGbbxvpxIXem7t0EIbXBKegJsy1hafE= +github.com/titan-data/remote-sdk-go v0.0.2/go.mod h1:b4McaOFiLYWv2/wCQW/sE2BcCSNz/Ae6iKKEtqw703w= +github.com/titan-data/remote-sdk-go v0.0.3 h1:kGLc7JP7znTcXyl3gRML2jEST99ebdhz0Cn+nVdL6SQ= +github.com/titan-data/remote-sdk-go v0.0.3/go.mod h1:b4McaOFiLYWv2/wCQW/sE2BcCSNz/Ae6iKKEtqw703w= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/s3web/s3web.go b/s3web/s3web.go new file mode 100644 index 0000000..e566c5d --- /dev/null +++ b/s3web/s3web.go @@ -0,0 +1,54 @@ +/* + * Copyright The Titan Project Contributors. + */ +package s3web + +import ( + "errors" + "fmt" + "github.com/titan-data/remote-sdk-go/remote" + "net/url" + "reflect" + "strings" +) + +type s3webRemote struct { +} + +func (s s3webRemote) Type() string { + return "s3web" +} + +func (s s3webRemote) FromURL(url *url.URL, additionalProperties map[string]string) (map[string]interface{}, error) { + if url.Scheme != "s3web" { + return nil, errors.New("invalid remote scheme") + } + + if url.User != nil { + return nil, errors.New("remote username and password cannot be specified") + } + + if url.Hostname() == "" { + return nil, errors.New("missing remote host name") + } + + if len(additionalProperties) != 0 { + return nil, errors.New(fmt.Sprintf("invalid property '%s'", reflect.ValueOf(additionalProperties).MapKeys()[0].String())) + } + + u := fmt.Sprintf("http://%s%s", url.Host, url.Path) + return map[string]interface{}{"url": u}, nil +} + +func (s s3webRemote) ToURL(properties map[string]interface{}) (string, map[string]string, error) { + u := properties["url"].(string) + return strings.Replace(u, "http", "s3web", 1), map[string]string{}, nil +} + +func (s s3webRemote) GetParameters(remoteProperties map[string]interface{}) (map[string]interface{}, error) { + return map[string]interface{}{}, nil +} + +func init() { + remote.Register(s3webRemote{}) +} diff --git a/s3web/s3web_test.go b/s3web/s3web_test.go new file mode 100644 index 0000000..26f754b --- /dev/null +++ b/s3web/s3web_test.go @@ -0,0 +1,92 @@ +/* + * Copyright The Titan Project Contributors. + */ +package s3web + +import ( + "github.com/stretchr/testify/assert" + "github.com/titan-data/remote-sdk-go/remote" + "net/url" + "testing" +) + +func TestRegistered(t *testing.T) { + r := remote.Get("s3web") + assert.Equal(t, "s3web", r.Type()) +} + +func TestFromURL(t *testing.T) { + r := remote.Get("s3web") + u, _ := url.Parse("s3web://host/object/path") + props, _ := r.FromURL(u, map[string]string{}) + assert.Equal(t, "http://host/object/path", props["url"]) +} + +func TestNoPath(t *testing.T) { + r := remote.Get("s3web") + u, _ := url.Parse("s3web://host") + props, _ := r.FromURL(u, map[string]string{}) + assert.Equal(t, "http://host", props["url"]) +} + +func TestBadProperty(t *testing.T) { + r := remote.Get("s3web") + u, _ := url.Parse("s3web://host") + _, err := r.FromURL(u, map[string]string{"a": "b"}) + assert.NotNil(t, err) +} + +func TestBadScheme(t *testing.T) { + r := remote.Get("s3web") + u, _ := url.Parse("s3web") + _, err := r.FromURL(u, map[string]string{}) + assert.NotNil(t, err) +} + +func TestBadSchemeName(t *testing.T) { + r := remote.Get("s3web") + u, _ := url.Parse("foo://bar") + _, err := r.FromURL(u, map[string]string{}) + assert.NotNil(t, err) +} + +func TestBadUser(t *testing.T) { + r := remote.Get("s3web") + u, _ := url.Parse("s3web://user@host/path") + _, err := r.FromURL(u, map[string]string{}) + assert.NotNil(t, err) +} + +func TestBadUserPassword(t *testing.T) { + r := remote.Get("s3web") + u, _ := url.Parse("s3web://user:password@host/path") + _, err := r.FromURL(u, map[string]string{}) + assert.NotNil(t, err) +} + +func TestBadNoHost(t *testing.T) { + r := remote.Get("s3web") + u, _ := url.Parse("s3web:///path") + _, err := r.FromURL(u, map[string]string{}) + assert.NotNil(t, err) +} + +func TestPort(t *testing.T) { + r := remote.Get("s3web") + u, _ := url.Parse("s3web://host:1023/object/path") + props, _ := r.FromURL(u, map[string]string{}) + assert.Equal(t, "http://host:1023/object/path", props["url"]) +} + +func TestToURL(t *testing.T) { + r := remote.Get("s3web") + u, props, _ := r.ToURL(map[string]interface{}{"url": "http://host/path"}) + assert.Equal(t, "s3web://host/path", u) + assert.Empty(t, props) +} + +func TestParameters(t *testing.T) { + r := remote.Get("s3web") + props, _ := r.GetParameters(map[string]interface{}{"url": "http://host/path"}) + assert.Empty(t, props) +}