From 6f97a75515795206fec19a74fe3078edcb54096e Mon Sep 17 00:00:00 2001 From: Brian Stack Date: Tue, 15 May 2018 16:55:20 -0700 Subject: [PATCH] Bug 1459967: Add a go implementation --- .gometalinter.json | 26 +++++++++++ .taskcluster.yml | 31 +++++++++++++ README.md | 16 ++++++- specification.yml | 113 +++++++++++++++++++++------------------------ tcurls.go | 72 +++++++++++++++++++++++++++++ tcurls_test.go | 92 ++++++++++++++++++++++++++++++++++++ test/basic_test.js | 3 +- 7 files changed, 290 insertions(+), 63 deletions(-) create mode 100644 .gometalinter.json create mode 100644 tcurls.go create mode 100644 tcurls_test.go diff --git a/.gometalinter.json b/.gometalinter.json new file mode 100644 index 0000000..5825bac --- /dev/null +++ b/.gometalinter.json @@ -0,0 +1,26 @@ +{ + "Enable": [ + "deadcode", + "errcheck", + "gas", + "goconst", + "goimports", + "golint", + "gosimple", + "gotype", + "gotypex", + "ineffassign", + "interfacer", + "maligned", + "megacheck", + "misspell", + "nakedret", + "safesql", + "staticcheck", + "structcheck", + "unconvert", + "unparam", + "unused", + "varcheck" + ] +} diff --git a/.taskcluster.yml b/.taskcluster.yml index b08d9c1..0cc76c8 100644 --- a/.taskcluster.yml +++ b/.taskcluster.yml @@ -60,3 +60,34 @@ tasks: description: "Upload documentation for this project" owner: '{{ event.head.user.email }}' source: '{{ event.head.repo.url }}' + - provisionerId: '{{ taskcluster.docker.provisionerId }}' + workerType: '{{ taskcluster.docker.workerType }}' + extra: + github: + events: + - pull_request.opened + - pull_request.synchronize + - pull_request.reopened + - push + payload: + maxRunTime: 3600 + image: 'golang:1.10' + command: + - /bin/bash + - '-c' + - >- + mkdir -p /go/src/github.com/taskcluster/taskcluster-lib-urls && + cd /go/src/github.com/taskcluster/taskcluster-lib-urls && + git clone {{event.head.repo.url}} . && + git config advice.detachedHead false && + git checkout {{event.head.sha}} && + go get -v -d -t ./... && + go test -v -race ./... && + go get -u github.com/alecthomas/gometalinter && + gometalinter --install && + gometalinter + metadata: + name: taskcluster-lib-urls go test + description: Run library test suite - golang 1.10 + owner: '{{ event.head.user.email }}' + source: '{{ event.head.repo.url }}' diff --git a/README.md b/README.md index 7af705b..c6b19f6 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Requirements This is tested on and should run on any of Node.js `{8, 10}`. -Usage +JS Usage ----- This package exports several methods for generating URLs conditionally based on @@ -76,6 +76,20 @@ If you would like, you can set this up via [taskcluster-lib-loader](https://gith } ``` +Go Usage +-------- + +The go package exports the following functions: + +```go +func API(rootURL string, service string, version string, path string) string +func APIReference(rootURL string, service string, version string) string +func Docs(rootURL string, path string) string +func ExchangeReference(rootURL string, service string, version string) string +func Schema(rootURL string, service string, name string) string +func UI(rootURL string, path string) string +``` + Testing ------- diff --git a/specification.yml b/specification.yml index deb5bda..8f67cd3 100644 --- a/specification.yml +++ b/specification.yml @@ -11,63 +11,56 @@ # This format is explained further in `docs/urls-spec.md` ################################################################################# --- -type: api -expectedUrl: https://taskcluster.example.com/api/auth/v1/ping -oldExpectedUrl: https://auth.taskcluster.net/v1/ping -argSets: -- [auth, v1, ping] -- [auth, v1, /ping] ---- -type: api -expectedUrl: https://taskcluster.example.com/api/auth/v1/foo/ping -oldExpectedUrl: https://auth.taskcluster.net/v1/foo/ping -argSets: -- [auth, v1, foo/ping] -- [auth, v1, /foo/ping] ---- -type: docs -expectedUrl: https://taskcluster.example.com/docs/something/in/docs -oldExpectedUrl: https://docs.taskcluster.net/something/in/docs -argSets: -- [something/in/docs] -- [/something/in/docs] ---- -type: schema -expectedUrl: https://taskcluster.example.com/schemas/auth/v1/something.json -oldExpectedUrl: https://schemas.taskcluster.net/auth/v1/something.json -argSets: -- [auth, v1/something.json] -- [auth, /v1/something.json] ---- -type: schema -expectedUrl: https://taskcluster.example.com/schemas/auth/v2/something.json -oldExpectedUrl: https://schemas.taskcluster.net/auth/v2/something.json -argSets: -- [auth, v2/something.json] -- [auth, /v2/something.json] ---- -type: apiReference -expectedUrl: https://taskcluster.example.com/references/auth/v1/api.json -oldExpectedUrl: https://references.taskcluster.net/auth/v1/api.json -argSets: -- [auth, v1] ---- -type: exchangeReference -expectedUrl: https://taskcluster.example.com/references/auth/v1/exchanges.json -oldExpectedUrl: https://references.taskcluster.net/auth/v1/exchanges.json -argSets: -- [auth, v1] ---- -type: ui -expectedUrl: https://taskcluster.example.com/something -oldExpectedUrl: https://tools.taskcluster.net/something -argSets: -- [something] -- [/something] ---- -type: ui -expectedUrl: https://taskcluster.example.com/ -oldExpectedUrl: https://tools.taskcluster.net/ -argSets: -- [''] -- [/] +specs: +- type: api + expectedUrl: https://taskcluster.example.com/api/auth/v1/ping + oldExpectedUrl: https://auth.taskcluster.net/v1/ping + argSets: + - [auth, v1, ping] + - [auth, v1, /ping] +- type: api + expectedUrl: https://taskcluster.example.com/api/auth/v1/foo/ping + oldExpectedUrl: https://auth.taskcluster.net/v1/foo/ping + argSets: + - [auth, v1, foo/ping] + - [auth, v1, /foo/ping] +- type: docs + expectedUrl: https://taskcluster.example.com/docs/something/in/docs + oldExpectedUrl: https://docs.taskcluster.net/something/in/docs + argSets: + - [something/in/docs] + - [/something/in/docs] +- type: schema + expectedUrl: https://taskcluster.example.com/schemas/auth/v1/something.json + oldExpectedUrl: https://schemas.taskcluster.net/auth/v1/something.json + argSets: + - [auth, v1/something.json] + - [auth, /v1/something.json] +- type: schema + expectedUrl: https://taskcluster.example.com/schemas/auth/v2/something.json + oldExpectedUrl: https://schemas.taskcluster.net/auth/v2/something.json + argSets: + - [auth, v2/something.json] + - [auth, /v2/something.json] +- type: apiReference + expectedUrl: https://taskcluster.example.com/references/auth/v1/api.json + oldExpectedUrl: https://references.taskcluster.net/auth/v1/api.json + argSets: + - [auth, v1] +- type: exchangeReference + expectedUrl: https://taskcluster.example.com/references/auth/v1/exchanges.json + oldExpectedUrl: https://references.taskcluster.net/auth/v1/exchanges.json + argSets: + - [auth, v1] +- type: ui + expectedUrl: https://taskcluster.example.com/something + oldExpectedUrl: https://tools.taskcluster.net/something + argSets: + - [something] + - [/something] +- type: ui + expectedUrl: https://taskcluster.example.com/ + oldExpectedUrl: https://tools.taskcluster.net/ + argSets: + - [''] + - [/] diff --git a/tcurls.go b/tcurls.go new file mode 100644 index 0000000..1c4bab4 --- /dev/null +++ b/tcurls.go @@ -0,0 +1,72 @@ +package tcurls + +import ( + "fmt" + "strings" +) + +const oldRootURL = "https://taskcluster.net" + +// API generates a url for a resource in a taskcluster service +func API(rootURL string, service string, version string, path string) string { + path = strings.TrimLeft(path, "/") + switch r := strings.TrimRight(rootURL, "/"); r { + case oldRootURL: + return fmt.Sprintf("https://%s.taskcluster.net/%s/%s", service, version, path) + default: + return fmt.Sprintf("%s/api/%s/%s/%s", r, service, version, path) + } +} + +// APIReference enerates a url for a taskcluster service reference doc +func APIReference(rootURL string, service string, version string) string { + switch r := strings.TrimRight(rootURL, "/"); r { + case oldRootURL: + return fmt.Sprintf("https://references.taskcluster.net/%s/%s/api.json", service, version) + default: + return fmt.Sprintf("%s/references/%s/%s/api.json", r, service, version) + } +} + +// Docs generates a url for a taskcluster docs-site page +func Docs(rootURL string, path string) string { + path = strings.TrimLeft(path, "/") + switch r := strings.TrimRight(rootURL, "/"); r { + case oldRootURL: + return fmt.Sprintf("https://docs.taskcluster.net/%s", path) + default: + return fmt.Sprintf("%s/docs/%s", r, path) + } +} + +// ExchangeReference generates a url for a taskcluster exchange reference doc +func ExchangeReference(rootURL string, service string, version string) string { + switch r := strings.TrimRight(rootURL, "/"); r { + case oldRootURL: + return fmt.Sprintf("https://references.taskcluster.net/%s/%s/exchanges.json", service, version) + default: + return fmt.Sprintf("%s/references/%s/%s/exchanges.json", r, service, version) + } +} + +// Schema generates a url for a taskcluster schema +func Schema(rootURL string, service string, name string) string { + name = strings.TrimLeft(name, "/") + switch r := strings.TrimRight(rootURL, "/"); r { + case oldRootURL: + return fmt.Sprintf("https://schemas.taskcluster.net/%s/%s", service, name) + default: + return fmt.Sprintf("%s/schemas/%s/%s", r, service, name) + } +} + +// UI generates a url for a page in taskcluster tools site +func UI(rootURL string, path string) string { + path = strings.TrimLeft(path, "/") + switch r := strings.TrimRight(rootURL, "/"); r { + case oldRootURL: + return fmt.Sprintf("https://tools.taskcluster.net/%s", path) + default: + return fmt.Sprintf("%s/%s", r, path) + } +} diff --git a/tcurls_test.go b/tcurls_test.go new file mode 100644 index 0000000..94d98f1 --- /dev/null +++ b/tcurls_test.go @@ -0,0 +1,92 @@ +package tcurls + +import ( + "fmt" + "io/ioutil" + "testing" + + "gopkg.in/yaml.v2" +) + +const rootURL = "https://taskcluster.example.com" + +type spec struct { + FunctionType string `yaml:"type"` + ExpectedURL string `yaml:"expectedUrl"` + OldExpectedURL string `yaml:"oldExpectedUrl"` + ArgSets [][]string `yaml:"argSets"` +} + +type document struct { + Specs []spec `yaml:"specs"` +} + +func testFunc(t *testing.T, functionType string, root string, args ...string) (string, error) { + switch functionType { + case "api": + return API(root, args[0], args[1], args[2]), nil + case "apiReference": + return APIReference(root, args[0], args[1]), nil + case "docs": + return Docs(root, args[0]), nil + case "exchangeReference": + return ExchangeReference(root, args[0], args[1]), nil + case "schema": + return Schema(root, args[0], args[1]), nil + case "ui": + return UI(root, args[0]), nil + default: + return "", fmt.Errorf("Unknown function type: %s", functionType) + } +} + +func TestUrls(t *testing.T) { + data, err := ioutil.ReadFile("specification.yml") + if err != nil { + t.Error(err) + } + var specs document + err = yaml.Unmarshal([]byte(data), &specs) + if err != nil { + t.Error(err) + } + + for _, test := range specs.Specs { + // First test "new" urls + for _, argSet := range test.ArgSets { + result, err := testFunc(t, test.FunctionType, rootURL, argSet...) + if err != nil { + t.Error(err) + continue + } + if result != test.ExpectedURL { + t.Errorf("Url is not correct. Got %s wanted %s", result, test.ExpectedURL) + continue + } + result, err = testFunc(t, test.FunctionType, fmt.Sprintf("%s/", rootURL), argSet...) + if err != nil { + t.Error(err) + } + if result != test.ExpectedURL { + t.Errorf("Url is not correct. Got %s wanted %s", result, test.ExpectedURL) + continue + } + + // Now the old ones + result, err = testFunc(t, test.FunctionType, oldRootURL, argSet...) + if err != nil { + t.Error(err) + } + if result != test.OldExpectedURL { + t.Errorf("Url is not correct. Got %s wanted %s", result, test.OldExpectedURL) + } + result, err = testFunc(t, test.FunctionType, fmt.Sprintf("%s/", oldRootURL), argSet...) + if err != nil { + t.Error(err) + } + if result != test.OldExpectedURL { + t.Errorf("Url is not correct. Got %s wanted %s", result, test.OldExpectedURL) + } + } + } +} diff --git a/test/basic_test.js b/test/basic_test.js index eb780de..e52a03a 100644 --- a/test/basic_test.js +++ b/test/basic_test.js @@ -9,8 +9,7 @@ const ROOT_URL = 'https://taskcluster.example.com'; const OLD_ROOT_URL = 'https://taskcluster.net'; suite('basic test', function() { - let testCases; - yaml.loadAll(fs.readFileSync(SPEC_FILE, {encoding: 'utf8'}), testCase => { + yaml.safeLoad(fs.readFileSync(SPEC_FILE, {encoding: 'utf8'})).specs.forEach(testCase => { const {type, expectedUrl, oldExpectedUrl, argSets} = testCase; test(expectedUrl, function() {