summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPawel Wieczorek <p.wieczorek2@samsung.com>2018-07-30 18:09:57 +0200
committerPawel Wieczorek <p.wieczorek2@samsung.com>2018-07-30 18:09:57 +0200
commit6a626ad5e43e0a972c25ec76d92339f22acb838b (patch)
treeb46a4a9a24ceb9e3485cdb67d940cc4c47eaa33d
parent8032cffb294d3f9713f54e2ce9e761abef016d2d (diff)
parent86fc35346a6850dadbe2290369164c9dd5600f85 (diff)
downloadweles-cors.tar.gz
weles-cors.tar.bz2
weles-cors.zip
Merge branch 'api'cors
Change-Id: I73745abe146f5e9d6e3a9ba886f727da53a2b32e
-rw-r--r--.gitignore6
-rw-r--r--COPYING13
-rw-r--r--Gopkg.lock476
-rw-r--r--Gopkg.toml64
-rw-r--r--Makefile82
-rw-r--r--artifact_alias.go34
-rw-r--r--artifact_description.go154
-rw-r--r--artifact_filter.go171
-rw-r--r--artifact_filter_and_sort.go111
-rw-r--r--artifact_info.go171
-rw-r--r--artifact_pagination.go31
-rw-r--r--artifact_path.go34
-rw-r--r--artifact_sort_by.go77
-rw-r--r--artifact_sorter.go109
-rw-r--r--artifact_status.go93
-rw-r--r--artifact_status_change.go22
-rw-r--r--artifact_type.go93
-rw-r--r--artifact_uri.go46
-rw-r--r--artifactmanager.go76
-rw-r--r--artifacts/artifacts.go63
-rw-r--r--artifacts/artifacts_test.go37
-rw-r--r--artifacts/database/database.go151
-rw-r--r--artifacts/database/database_suite_test.go42
-rw-r--r--artifacts/database/database_test.go215
-rw-r--r--artifacts/database/pagination_test.go90
-rw-r--r--artifacts/downloader/downloader.go43
-rw-r--r--artifacts/downloader/downloader_test.go43
-rw-r--r--cmd/weles-server/main.go114
-rw-r--r--controller/boruterimpl.go2
-rw-r--r--controller/boruterimpl_test.go18
-rw-r--r--controller/controller.go15
-rw-r--r--controller/controller_test.go18
-rw-r--r--controller/downloaderimpl.go16
-rw-r--r--controller/downloaderimpl_test.go38
-rw-r--r--controller/dryaderimpl.go2
-rw-r--r--controller/dryaderimpl_test.go4
-rw-r--r--controller/jobscontroller.go11
-rw-r--r--controller/jobscontrollerimpl.go326
-rw-r--r--controller/jobscontrollerimpl_test.go890
-rw-r--r--controller/mock/jobscontroller.go13
-rw-r--r--controller/mock/mocks_generate.go32
-rw-r--r--controller/mock/requests.go302
-rw-r--r--controller/notifier/notification.go4
-rw-r--r--controller/notifier/notifier.go6
-rw-r--r--controller/notifier/notifierimpl.go6
-rw-r--r--controller/parserimpl.go4
-rw-r--r--controller/parserimpl_test.go24
-rw-r--r--err_response.go61
-rw-r--r--errors.go16
-rw-r--r--errors_test.go30
-rw-r--r--fixtures/artifactinfo.go61
-rw-r--r--job_filter.go208
-rw-r--r--job_filter_and_sort.go111
-rw-r--r--job_id.go34
-rw-r--r--job_id_string.go22
-rw-r--r--job_info.go156
-rw-r--r--job_pagination.go30
-rw-r--r--job_sort_by.go90
-rw-r--r--job_sorter.go108
-rw-r--r--job_status.go113
-rw-r--r--job_status_extra.go43
-rw-r--r--jobmanager.go59
-rw-r--r--list_info.go25
-rw-r--r--manager/dryad_job_runner_test.go2
-rw-r--r--manager/mock_dryad_job_runner_test.go26
-rw-r--r--manager/mocks_generate.go21
-rw-r--r--mock/artifactmanager.go25
-rw-r--r--mock/jobmanager.go13
-rw-r--r--mock/mocks_generate.go25
-rw-r--r--server/artifact_lister_handler.go170
-rw-r--r--server/artifact_lister_handler_test.go634
-rw-r--r--server/configure_weles.go96
-rw-r--r--server/doc.go44
-rw-r--r--server/embedded_spec.go1441
-rw-r--r--server/job_canceler_handler.go40
-rw-r--r--server/job_canceler_handler_test.go98
-rw-r--r--server/job_creator_handler.go38
-rw-r--r--server/job_creator_handler_test.go156
-rw-r--r--server/job_lister_handler.go152
-rw-r--r--server/job_lister_handler_test.go637
-rw-r--r--server/managers.go37
-rw-r--r--server/operations/artifacts/artifact_lister.go75
-rw-r--r--server/operations/artifacts/artifact_lister_parameters.go182
-rw-r--r--server/operations/artifacts/artifact_lister_responses.go417
-rw-r--r--server/operations/artifacts/artifact_lister_urlbuilder.go138
-rw-r--r--server/operations/jobs/job_canceler.go75
-rw-r--r--server/operations/jobs/job_canceler_parameters.go93
-rw-r--r--server/operations/jobs/job_canceler_responses.go185
-rw-r--r--server/operations/jobs/job_canceler_urlbuilder.go116
-rw-r--r--server/operations/jobs/job_creator.go75
-rw-r--r--server/operations/jobs/job_creator_parameters.go94
-rw-r--r--server/operations/jobs/job_creator_responses.go204
-rw-r--r--server/operations/jobs/job_creator_urlbuilder.go102
-rw-r--r--server/operations/jobs/job_lister.go75
-rw-r--r--server/operations/jobs/job_lister_parameters.go182
-rw-r--r--server/operations/jobs/job_lister_responses.go417
-rw-r--r--server/operations/jobs/job_lister_urlbuilder.go138
-rw-r--r--server/operations/weles_api.go344
-rw-r--r--server/server.go615
-rw-r--r--server/server_suite_test.go84
-rwxr-xr-xserver/test_sample.yml12
-rw-r--r--sort_order.go83
-rw-r--r--swagger.yml559
-rw-r--r--weles.go5
-rw-r--r--weles_suite_test.go29
105 files changed, 12778 insertions, 735 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3433cb9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+# Gopkg.lock file contains all necessary information for dep to download vendor/ directory
+/vendor/
+# No binaries should be committed.
+/bin/
+# Built documentation:
+/doc/build/
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..e4bf114
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,13 @@
+Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License
diff --git a/Gopkg.lock b/Gopkg.lock
new file mode 100644
index 0000000..f1eba11
--- /dev/null
+++ b/Gopkg.lock
@@ -0,0 +1,476 @@
+# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
+
+
+[[projects]]
+ name = "github.com/PuerkitoBio/purell"
+ packages = ["."]
+ revision = "0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4"
+ version = "v1.1.0"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/PuerkitoBio/urlesc"
+ packages = ["."]
+ revision = "de5bf2ad457846296e2031421a34e2568e304e35"
+
+[[projects]]
+ name = "github.com/asaskevich/govalidator"
+ packages = ["."]
+ revision = "ccb8e960c48f04d6935e72476ae4a51028f9e22f"
+ version = "v9"
+
+[[projects]]
+ name = "github.com/coreos/go-oidc"
+ packages = ["."]
+ revision = "1180514eaf4d9f38d0d19eef639a1d695e066e72"
+ version = "v2.0.0"
+
+[[projects]]
+ name = "github.com/davecgh/go-spew"
+ packages = ["spew"]
+ revision = "346938d642f2ec3594ed81d874461961cd0faa76"
+ version = "v1.1.0"
+
+[[projects]]
+ name = "github.com/dgrijalva/jwt-go"
+ packages = ["."]
+ revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
+ version = "v3.2.0"
+
+[[projects]]
+ name = "github.com/docker/go-units"
+ packages = ["."]
+ revision = "47565b4f722fb6ceae66b95f853feed578a4a51c"
+ version = "v0.3.3"
+
+[[projects]]
+ name = "github.com/fsnotify/fsnotify"
+ packages = ["."]
+ revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
+ version = "v1.4.7"
+
+[[projects]]
+ name = "github.com/go-gorp/gorp"
+ packages = ["."]
+ revision = "6032c66e0f5f155fd56216ed14cbbdd991034605"
+ version = "v2.1"
+
+[[projects]]
+ name = "github.com/go-openapi/analysis"
+ packages = ["."]
+ revision = "3c8fe72ed5d307113ef76c7c3035964d7e4d4b73"
+ version = "0.15.0"
+
+[[projects]]
+ name = "github.com/go-openapi/errors"
+ packages = ["."]
+ revision = "b2b2befaf267d082d779bcef52d682a47c779517"
+ version = "0.15.0"
+
+[[projects]]
+ name = "github.com/go-openapi/inflect"
+ packages = ["."]
+ revision = "b1f6470ffb9c552dc105dd869f16e36ba86ba7d0"
+ version = "0.15.0"
+
+[[projects]]
+ name = "github.com/go-openapi/jsonpointer"
+ packages = ["."]
+ revision = "3a0015ad55fa9873f41605d3e8f28cd279c32ab2"
+ version = "0.15.0"
+
+[[projects]]
+ name = "github.com/go-openapi/jsonreference"
+ packages = ["."]
+ revision = "3fb327e6747da3043567ee86abd02bb6376b6be2"
+ version = "0.15.0"
+
+[[projects]]
+ name = "github.com/go-openapi/loads"
+ packages = [
+ ".",
+ "fmts"
+ ]
+ revision = "2a2b323bab96e6b1fdee110e57d959322446e9c9"
+ version = "0.15.0"
+
+[[projects]]
+ name = "github.com/go-openapi/runtime"
+ packages = [
+ ".",
+ "flagext",
+ "logger",
+ "middleware",
+ "middleware/denco",
+ "middleware/header",
+ "middleware/untyped",
+ "security"
+ ]
+ revision = "9a3091f566c0811ef4d54b535179bc0fc484a11f"
+ version = "0.15.0"
+
+[[projects]]
+ name = "github.com/go-openapi/spec"
+ packages = ["."]
+ revision = "bce47c9386f9ecd6b86f450478a80103c3fe1402"
+ version = "0.15.0"
+
+[[projects]]
+ name = "github.com/go-openapi/strfmt"
+ packages = ["."]
+ revision = "913ee058e387ac83a67e2d9f13acecdcd5769fc6"
+ version = "0.15.0"
+
+[[projects]]
+ name = "github.com/go-openapi/swag"
+ packages = ["."]
+ revision = "2b0bd4f193d011c203529df626a65d63cb8a79e8"
+ version = "0.15.0"
+
+[[projects]]
+ name = "github.com/go-openapi/validate"
+ packages = ["."]
+ revision = "9a6e517cddf1ddd47449be5d06a6a786e51d9986"
+ version = "0.15.0"
+
+[[projects]]
+ name = "github.com/go-swagger/go-swagger"
+ packages = ["."]
+ revision = "8c101dc8c12c8e25b5803a6376b7b2b7f09a622f"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/go-swagger/scan-repo-boundary"
+ packages = ["makeplans"]
+ revision = "973b3573c01343537efb657a4aeed98262a2634a"
+
+[[projects]]
+ name = "github.com/golang/mock"
+ packages = [
+ "gomock",
+ "mockgen",
+ "mockgen/model"
+ ]
+ revision = "c34cdb4725f4c3844d095133c6e40e448b86589b"
+ version = "v1.1.1"
+
+[[projects]]
+ name = "github.com/golang/protobuf"
+ packages = ["proto"]
+ revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
+ version = "v1.1.0"
+
+[[projects]]
+ name = "github.com/gorilla/handlers"
+ packages = ["."]
+ revision = "90663712d74cb411cbef281bc1e08c19d1a76145"
+ version = "v1.3.0"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/hashicorp/hcl"
+ packages = [
+ ".",
+ "hcl/ast",
+ "hcl/parser",
+ "hcl/printer",
+ "hcl/scanner",
+ "hcl/strconv",
+ "hcl/token",
+ "json/parser",
+ "json/scanner",
+ "json/token"
+ ]
+ revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
+
+[[projects]]
+ name = "github.com/jessevdk/go-flags"
+ packages = ["."]
+ revision = "c6ca198ec95c841fdb89fc0de7496fed11ab854e"
+ version = "v1.4.0"
+
+[[projects]]
+ name = "github.com/kr/pretty"
+ packages = ["."]
+ revision = "73f6ac0b30a98e433b289500d779f50c1a6f0712"
+ version = "v0.1.0"
+
+[[projects]]
+ name = "github.com/kr/text"
+ packages = ["."]
+ revision = "e2ffdb16a802fe2bb95e2e35ff34f0e53aeef34f"
+ version = "v0.1.0"
+
+[[projects]]
+ name = "github.com/magiconair/properties"
+ packages = ["."]
+ revision = "c2353362d570a7bfa228149c62842019201cfb71"
+ version = "v1.8.0"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/mailru/easyjson"
+ packages = [
+ "buffer",
+ "jlexer",
+ "jwriter"
+ ]
+ revision = "3fdea8d05856a0c8df22ed4bc71b3219245e4485"
+
+[[projects]]
+ name = "github.com/mattn/go-sqlite3"
+ packages = ["."]
+ revision = "25ecb14adfc7543176f7d85291ec7dba82c6f7e4"
+ version = "v1.9.0"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/mitchellh/mapstructure"
+ packages = ["."]
+ revision = "bb74f1db0675b241733089d5a1faa5dd8b0ef57b"
+
+[[projects]]
+ name = "github.com/onsi/ginkgo"
+ packages = [
+ ".",
+ "config",
+ "extensions/table",
+ "internal/codelocation",
+ "internal/containernode",
+ "internal/failer",
+ "internal/leafnodes",
+ "internal/remote",
+ "internal/spec",
+ "internal/spec_iterator",
+ "internal/specrunner",
+ "internal/suite",
+ "internal/testingtproxy",
+ "internal/writer",
+ "reporters",
+ "reporters/stenographer",
+ "reporters/stenographer/support/go-colorable",
+ "reporters/stenographer/support/go-isatty",
+ "types"
+ ]
+ revision = "fa5fabab2a1bfbd924faf4c067d07ae414e2aedf"
+ version = "v1.5.0"
+
+[[projects]]
+ name = "github.com/onsi/gomega"
+ packages = [
+ ".",
+ "format",
+ "internal/assertion",
+ "internal/asyncassertion",
+ "internal/oraclematcher",
+ "internal/testingtsupport",
+ "matchers",
+ "matchers/support/goraph/bipartitegraph",
+ "matchers/support/goraph/edge",
+ "matchers/support/goraph/node",
+ "matchers/support/goraph/util",
+ "types"
+ ]
+ revision = "62bff4df71bdbc266561a0caee19f0594b17c240"
+ version = "v1.4.0"
+
+[[projects]]
+ name = "github.com/pelletier/go-toml"
+ packages = ["."]
+ revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194"
+ version = "v1.2.0"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/pquerna/cachecontrol"
+ packages = [
+ ".",
+ "cacheobject"
+ ]
+ revision = "1555304b9b35fdd2b425bccf1a5613677705e7d0"
+
+[[projects]]
+ name = "github.com/spf13/afero"
+ packages = [
+ ".",
+ "mem"
+ ]
+ revision = "787d034dfe70e44075ccc060d346146ef53270ad"
+ version = "v1.1.1"
+
+[[projects]]
+ name = "github.com/spf13/cast"
+ packages = ["."]
+ revision = "8965335b8c7107321228e3e3702cab9832751bac"
+ version = "v1.2.0"
+
+[[projects]]
+ branch = "master"
+ name = "github.com/spf13/jwalterweatherman"
+ packages = ["."]
+ revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394"
+
+[[projects]]
+ name = "github.com/spf13/pflag"
+ packages = ["."]
+ revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
+ version = "v1.0.1"
+
+[[projects]]
+ name = "github.com/spf13/viper"
+ packages = ["."]
+ revision = "b5e8006cbee93ec955a89ab31e0e3ce3204f3736"
+ version = "v1.0.2"
+
+[[projects]]
+ name = "github.com/tideland/golib"
+ packages = ["audit"]
+ revision = "b56169c6bd620eeb7cfc4b9b4027fc10d2934c84"
+ version = "v4.24.2"
+
+[[projects]]
+ name = "github.com/toqueteos/webbrowser"
+ packages = ["."]
+ revision = "3232c91b8ede8ca86e8962981d881af78875542f"
+ version = "v1.1.0"
+
+[[projects]]
+ name = "github.com/tylerb/graceful"
+ packages = ["."]
+ revision = "4654dfbb6ad53cb5e27f37d99b02e16c1872fbbb"
+ version = "v1.2.15"
+
+[[projects]]
+ branch = "master"
+ name = "golang.org/x/crypto"
+ packages = [
+ "curve25519",
+ "ed25519",
+ "ed25519/internal/edwards25519",
+ "internal/chacha20",
+ "internal/subtle",
+ "poly1305",
+ "ssh"
+ ]
+ revision = "a49355c7e3f8fe157a85be2f77e6e269a0f89602"
+
+[[projects]]
+ branch = "master"
+ name = "golang.org/x/net"
+ packages = [
+ "context",
+ "context/ctxhttp",
+ "html",
+ "html/atom",
+ "html/charset",
+ "idna",
+ "netutil"
+ ]
+ revision = "d0887baf81f4598189d4e12a37c6da86f0bba4d0"
+
+[[projects]]
+ branch = "master"
+ name = "golang.org/x/oauth2"
+ packages = [
+ ".",
+ "internal"
+ ]
+ revision = "ef147856a6ddbb60760db74283d2424e98c87bff"
+
+[[projects]]
+ branch = "master"
+ name = "golang.org/x/sys"
+ packages = ["unix"]
+ revision = "1b2967e3c290b7c545b3db0deeda16e9be4f98a2"
+
+[[projects]]
+ name = "golang.org/x/text"
+ packages = [
+ "collate",
+ "collate/build",
+ "encoding",
+ "encoding/charmap",
+ "encoding/htmlindex",
+ "encoding/internal",
+ "encoding/internal/identifier",
+ "encoding/japanese",
+ "encoding/korean",
+ "encoding/simplifiedchinese",
+ "encoding/traditionalchinese",
+ "encoding/unicode",
+ "internal/colltab",
+ "internal/gen",
+ "internal/tag",
+ "internal/triegen",
+ "internal/ucd",
+ "internal/utf8internal",
+ "language",
+ "runes",
+ "secure/bidirule",
+ "transform",
+ "unicode/bidi",
+ "unicode/cldr",
+ "unicode/norm",
+ "unicode/rangetable",
+ "width"
+ ]
+ revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
+ version = "v0.3.0"
+
+[[projects]]
+ branch = "master"
+ name = "golang.org/x/tools"
+ packages = [
+ "go/ast/astutil",
+ "go/buildutil",
+ "go/loader",
+ "imports",
+ "internal/fastwalk"
+ ]
+ revision = "18e9dfbf20567d5156dd6f1dd987092c021f7fa6"
+
+[[projects]]
+ name = "google.golang.org/appengine"
+ packages = [
+ "internal",
+ "internal/base",
+ "internal/datastore",
+ "internal/log",
+ "internal/remote_api",
+ "internal/urlfetch",
+ "urlfetch"
+ ]
+ revision = "b1f26356af11148e710935ed1ac8a7f5702c7612"
+ version = "v1.1.0"
+
+[[projects]]
+ branch = "v2"
+ name = "gopkg.in/mgo.v2"
+ packages = [
+ "bson",
+ "internal/json"
+ ]
+ revision = "9856a29383ce1c59f308dd1cf0363a79b5bef6b5"
+
+[[projects]]
+ name = "gopkg.in/square/go-jose.v2"
+ packages = [
+ ".",
+ "cipher",
+ "json"
+ ]
+ revision = "76dd09796242edb5b897103a75df2645c028c960"
+ version = "v2.1.6"
+
+[[projects]]
+ name = "gopkg.in/yaml.v2"
+ packages = ["."]
+ revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
+ version = "v2.2.1"
+
+[solve-meta]
+ analyzer-name = "dep"
+ analyzer-version = 1
+ inputs-digest = "a5e864a9f1537b8cb1a6fdceb4224e969cec77a4e1f692f62b019f7f640638b5"
+ solver-name = "gps-cdcl"
+ solver-version = 1
diff --git a/Gopkg.toml b/Gopkg.toml
new file mode 100644
index 0000000..e46920b
--- /dev/null
+++ b/Gopkg.toml
@@ -0,0 +1,64 @@
+# Packages which are not part of Weles dependencies but are required for development of it are
+# added below in the required block. Required packages are downloaded to the vendor directory but
+# are not installed. Installation of those packages should be performed using Makefile.
+required = [
+ "github.com/golang/mock/mockgen",
+ "github.com/golang/mock/mockgen/model",
+# start of go-swagger dependencies
+ "github.com/coreos/go-oidc",
+ "github.com/davecgh/go-spew/spew",
+ "github.com/dgrijalva/jwt-go",
+ "github.com/docker/go-units",
+ "github.com/go-openapi/analysis",
+ "github.com/go-openapi/errors",
+ "github.com/go-openapi/inflect",
+ "github.com/go-openapi/loads",
+ "github.com/go-openapi/loads/fmts",
+ "github.com/go-openapi/runtime",
+ "github.com/go-openapi/runtime/middleware",
+ "github.com/go-openapi/validate",
+ "github.com/gorilla/handlers",
+ "github.com/go-swagger/go-swagger",
+ "github.com/go-swagger/scan-repo-boundary/makeplans",
+ "github.com/jessevdk/go-flags",
+ "github.com/kr/pretty",
+ "github.com/spf13/pflag",
+ "github.com/spf13/viper",
+ "github.com/toqueteos/webbrowser",
+ "github.com/tylerb/graceful",
+ "golang.org/x/tools/go/loader",
+ "golang.org/x/tools/imports",
+# end of go-swagger dependencies
+ "github.com/tideland/golib/audit"
+ ]
+
+# Due to lack of go get functionality on tizen.org boruta cannot be managed via dep.
+# Move of the project to github.com is expected to resolve the issue.
+# TODO: remove boruta from ignored after migration.
+ignored = [
+ "git.tizen.org/tools/boruta",
+ "git.tizen.org/tools/boruta/http/client",
+ ]
+
+[[constraint]]
+name = "github.com/golang/mock"
+version = "v1.1.1"
+
+# Constraint go-swagger version to use one of the latest patches from master branch.
+# Without this constraint last tagged version 0.13.0 is used, which is not compatible with changes
+# in other required packages (which do not have stable version tagged)
+[[constraint]]
+name = "github.com/go-swagger/go-swagger"
+revision = "8c101dc8c12c8e25b5803a6376b7b2b7f09a622f"
+
+# WARNING!
+# Do not uncomment below lines
+# [prune]
+# unused-packages = true
+# non-go = true
+# go-tests = true
+# Prune option is set to false by default, setting it explicitly produces an error.
+# If prune would be enabled, all development tools that we plan to add to required rule would be
+# removed on dep ensure call (as they are not imported directly by any Weles package).
+# Due to usage of code generator (go-swagger) we need to be able to lock its version
+# to ensure repoducible results between developers.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..ad38d27
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,82 @@
+DEV_TOOLS_DIR = ./bin/dev-tools
+
+DEV_TOOLS = ./vendor/github.com/golang/mock/mockgen ./vendor/github.com/go-swagger/go-swagger/cmd/swagger
+MOCKGEN_BIN = $(DEV_TOOLS_DIR)/mockgen
+SWAGGER_BIN = $(DEV_TOOLS_DIR)/swagger
+
+DEV_TOOLS_BIN = $(MOCKGEN_BIN) $(SWAGGER_BIN)
+
+rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))
+
+WELES_FILES = $(filter-out *_test.go, $(call rwildcard, , *.go))
+
+SERVER_MAIN = cmd/weles-server/main.go
+SERVER_BIN = bin/weles-server
+
+.PHONY: server
+server: vendor $(WELES_FILES)
+ go build -o $(SERVER_BIN) $(SERVER_MAIN)
+
+# dep ensure is run after swagger generation to update Gopkg.lock with packages needed to build server
+.PHONY: swagger-server-generate
+swagger-server-generate: swagger.yml COPYING
+ ./$(DEV_TOOLS_DIR)/swagger generate server \
+ -A weles \
+ -f ./swagger.yml \
+ -m ../weles \
+ -s ./server \
+ -r ./COPYING \
+ --flag-strategy pflag \
+ --exclude-main \
+ --compatibility-mode=modern
+ dep ensure
+
+
+.PHONY: swagger-docs-html
+swagger-docs-html: swagger.yml
+ mkdir -p doc/build/swagger
+ docker run \
+ --rm \
+ -v $$PWD:/local \
+ --user `id -u $$USER`:`id -g $$USER` \
+ swaggerapi/swagger-codegen-cli \
+ generate \
+ -i local/swagger.yml \
+ -l html \
+ -o /local/doc/build/swagger/
+
+vendor: Gopkg.lock
+ dep ensure -v -vendor-only
+
+Gopkg.lock: Gopkg.toml
+ dep ensure -v -no-vendor
+
+.PHONY: dep-update
+dep-update: clean-vendor
+ dep ensure -update
+
+# clean-vendor has not been added to vendor dependencies as dep is able to check out appropriate
+# packages on versions set in the Gopkg.lock file. Removing vendor would force dep to re-download
+# all the packages instead of only the missing ones. Global prune is turned off (see Gopkg.toml
+# for explanation) thus vendor recipe will leave unused packages in the vendor/ directory. If that
+# bothers you, run sequentially clean-vendor and vendor recipes.
+.PHONY: clean-vendor
+clean-vendor:
+ rm -rf vendor
+
+# Due to lack of standard approach to naming and separation of both interfaces and generated mock files
+# below recipe does not have any file dependencies and is PHONY. Interface changes should be rare thus
+# it is up to the developer to regenerate mocks after interface changes.
+.PHONY: mocks
+mocks: tools
+ go generate ./mock
+ go generate ./manager
+ go generate ./controller/mock
+
+.PHONY: tools
+tools: vendor $(DEV_TOOLS_BIN)
+
+# This recipe will rebuild all tools on vendor directory change.
+# Due to short build time it is not treated as issue.
+$(DEV_TOOLS_DIR)/%: $(DEV_TOOLS)
+ go build -o $@ $(filter %$(@F),$(DEV_TOOLS))
diff --git a/artifact_alias.go b/artifact_alias.go
new file mode 100644
index 0000000..d83e8fb
--- /dev/null
+++ b/artifact_alias.go
@@ -0,0 +1,34 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package weles
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ strfmt "github.com/go-openapi/strfmt"
+)
+
+// ArtifactAlias is an alternative name of an artifact.
+// swagger:model ArtifactAlias
+type ArtifactAlias string
+
+// Validate validates this artifact alias
+func (m ArtifactAlias) Validate(formats strfmt.Registry) error {
+ return nil
+}
diff --git a/artifact_description.go b/artifact_description.go
new file mode 100644
index 0000000..383c34c
--- /dev/null
+++ b/artifact_description.go
@@ -0,0 +1,154 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package weles
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ strfmt "github.com/go-openapi/strfmt"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/swag"
+)
+
+// ArtifactDescription contains information needed to create new artifact in ArtifactDB.
+// swagger:model ArtifactDescription
+type ArtifactDescription struct {
+
+ // alias
+ Alias ArtifactAlias `json:"Alias,omitempty"`
+
+ // specifies Job for which artifact was created.
+ JobID JobID `json:"JobID,omitempty"`
+
+ // type
+ Type ArtifactType `json:"Type,omitempty"`
+
+ // URI
+ // Format: uri
+ URI ArtifactURI `json:"URI,omitempty"`
+}
+
+// Validate validates this artifact description
+func (m *ArtifactDescription) Validate(formats strfmt.Registry) error {
+ var res []error
+
+ if err := m.validateAlias(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateJobID(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateType(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateURI(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+func (m *ArtifactDescription) validateAlias(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.Alias) { // not required
+ return nil
+ }
+
+ if err := m.Alias.Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("Alias")
+ }
+ return err
+ }
+
+ return nil
+}
+
+func (m *ArtifactDescription) validateJobID(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.JobID) { // not required
+ return nil
+ }
+
+ if err := m.JobID.Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("JobID")
+ }
+ return err
+ }
+
+ return nil
+}
+
+func (m *ArtifactDescription) validateType(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.Type) { // not required
+ return nil
+ }
+
+ if err := m.Type.Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("Type")
+ }
+ return err
+ }
+
+ return nil
+}
+
+func (m *ArtifactDescription) validateURI(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.URI) { // not required
+ return nil
+ }
+
+ if err := m.URI.Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("URI")
+ }
+ return err
+ }
+
+ return nil
+}
+
+// MarshalBinary interface implementation
+func (m *ArtifactDescription) MarshalBinary() ([]byte, error) {
+ if m == nil {
+ return nil, nil
+ }
+ return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *ArtifactDescription) UnmarshalBinary(b []byte) error {
+ var res ArtifactDescription
+ if err := swag.ReadJSON(b, &res); err != nil {
+ return err
+ }
+ *m = res
+ return nil
+}
diff --git a/artifact_filter.go b/artifact_filter.go
new file mode 100644
index 0000000..f110f38
--- /dev/null
+++ b/artifact_filter.go
@@ -0,0 +1,171 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package weles
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "strconv"
+
+ strfmt "github.com/go-openapi/strfmt"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/swag"
+)
+
+// ArtifactFilter is used to filter results from ArtifactDB.
+// swagger:model ArtifactFilter
+type ArtifactFilter struct {
+
+ // alias
+ Alias []ArtifactAlias `json:"Alias"`
+
+ // job ID
+ JobID []JobID `json:"JobID"`
+
+ // status
+ Status []ArtifactStatus `json:"Status"`
+
+ // type
+ Type []ArtifactType `json:"Type"`
+}
+
+// Validate validates this artifact filter
+func (m *ArtifactFilter) Validate(formats strfmt.Registry) error {
+ var res []error
+
+ if err := m.validateAlias(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateJobID(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateStatus(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateType(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+func (m *ArtifactFilter) validateAlias(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.Alias) { // not required
+ return nil
+ }
+
+ for i := 0; i < len(m.Alias); i++ {
+
+ if err := m.Alias[i].Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("Alias" + "." + strconv.Itoa(i))
+ }
+ return err
+ }
+
+ }
+
+ return nil
+}
+
+func (m *ArtifactFilter) validateJobID(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.JobID) { // not required
+ return nil
+ }
+
+ for i := 0; i < len(m.JobID); i++ {
+
+ if err := m.JobID[i].Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("JobID" + "." + strconv.Itoa(i))
+ }
+ return err
+ }
+
+ }
+
+ return nil
+}
+
+func (m *ArtifactFilter) validateStatus(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.Status) { // not required
+ return nil
+ }
+
+ for i := 0; i < len(m.Status); i++ {
+
+ if err := m.Status[i].Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("Status" + "." + strconv.Itoa(i))
+ }
+ return err
+ }
+
+ }
+
+ return nil
+}
+
+func (m *ArtifactFilter) validateType(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.Type) { // not required
+ return nil
+ }
+
+ for i := 0; i < len(m.Type); i++ {
+
+ if err := m.Type[i].Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("Type" + "." + strconv.Itoa(i))
+ }
+ return err
+ }
+
+ }
+
+ return nil
+}
+
+// MarshalBinary interface implementation
+func (m *ArtifactFilter) MarshalBinary() ([]byte, error) {
+ if m == nil {
+ return nil, nil
+ }
+ return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *ArtifactFilter) UnmarshalBinary(b []byte) error {
+ var res ArtifactFilter
+ if err := swag.ReadJSON(b, &res); err != nil {
+ return err
+ }
+ *m = res
+ return nil
+}
diff --git a/artifact_filter_and_sort.go b/artifact_filter_and_sort.go
new file mode 100644
index 0000000..f3feda2
--- /dev/null
+++ b/artifact_filter_and_sort.go
@@ -0,0 +1,111 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package weles
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ strfmt "github.com/go-openapi/strfmt"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/swag"
+)
+
+// ArtifactFilterAndSort Data for filtering and sorting Weles Jobs lists.
+// swagger:model ArtifactFilterAndSort
+type ArtifactFilterAndSort struct {
+
+ // filter
+ Filter *ArtifactFilter `json:"Filter,omitempty"`
+
+ // sorter
+ Sorter *ArtifactSorter `json:"Sorter,omitempty"`
+}
+
+// Validate validates this artifact filter and sort
+func (m *ArtifactFilterAndSort) Validate(formats strfmt.Registry) error {
+ var res []error
+
+ if err := m.validateFilter(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateSorter(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+func (m *ArtifactFilterAndSort) validateFilter(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.Filter) { // not required
+ return nil
+ }
+
+ if m.Filter != nil {
+ if err := m.Filter.Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("Filter")
+ }
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (m *ArtifactFilterAndSort) validateSorter(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.Sorter) { // not required
+ return nil
+ }
+
+ if m.Sorter != nil {
+ if err := m.Sorter.Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("Sorter")
+ }
+ return err
+ }
+ }
+
+ return nil
+}
+
+// MarshalBinary interface implementation
+func (m *ArtifactFilterAndSort) MarshalBinary() ([]byte, error) {
+ if m == nil {
+ return nil, nil
+ }
+ return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *ArtifactFilterAndSort) UnmarshalBinary(b []byte) error {
+ var res ArtifactFilterAndSort
+ if err := swag.ReadJSON(b, &res); err != nil {
+ return err
+ }
+ *m = res
+ return nil
+}
diff --git a/artifact_info.go b/artifact_info.go
new file mode 100644
index 0000000..ae5079c
--- /dev/null
+++ b/artifact_info.go
@@ -0,0 +1,171 @@
+// Code generated by go-swagger; DO NOT EDIT.
+// NOTE: above is not entirely true. see TODO: below.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package weles
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ strfmt "github.com/go-openapi/strfmt"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/swag"
+ "github.com/go-openapi/validate"
+)
+
+// ArtifactInfo describes single artifact stored in ArtifactDB.
+// swagger:model ArtifactInfo
+type ArtifactInfo struct {
+ ArtifactDescription
+
+ // ID in the artifacts database
+ ID int64 `json:"ID,omitempty" db:",primarykey, autoincrement"`
+
+ // path
+ Path ArtifactPath `json:"Path,omitempty"`
+
+ // status
+ Status ArtifactStatus `json:"Status,omitempty"`
+
+ // is date of creating the artifact.
+ // Format: date-time
+ Timestamp strfmt.DateTime `json:"Timestamp,omitempty"`
+}
+
+// TODO
+// current go-swagger generates broken marshalling and unmarshalling:
+// https://github.com/go-swagger/go-swagger/issues/1617
+// it is commented out as implicit marshalling works fine.
+
+/*
+// UnmarshalJSON unmarshals this object from a JSON structure
+func (m *ArtifactInfo) UnmarshalJSON(raw []byte) error {
+ // AO0
+ var aO0 ArtifactDescription
+ if err := swag.ReadJSON(raw, &aO0); err != nil {
+ return err
+ }
+ m.ArtifactDescription = aO0
+
+ return nil
+}
+
+// MarshalJSON marshals this object to a JSON structure
+func (m ArtifactInfo) MarshalJSON() ([]byte, error) {
+ _parts := make([][]byte, 0, 1)
+
+ aO0, err := swag.WriteJSON(m.ArtifactDescription)
+ if err != nil {
+ return nil, err
+ }
+ _parts = append(_parts, aO0)
+
+ return swag.ConcatJSON(_parts...), nil
+}
+*/
+
+// Validate validates this artifact info
+func (m *ArtifactInfo) Validate(formats strfmt.Registry) error {
+ var res []error
+
+ // validation for a type composition with ArtifactDescription
+ if err := m.ArtifactDescription.Validate(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validatePath(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateStatus(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateTimestamp(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+func (m *ArtifactInfo) validatePath(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.Path) { // not required
+ return nil
+ }
+
+ if err := m.Path.Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("Path")
+ }
+ return err
+ }
+
+ return nil
+}
+
+func (m *ArtifactInfo) validateStatus(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.Status) { // not required
+ return nil
+ }
+
+ if err := m.Status.Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("Status")
+ }
+ return err
+ }
+
+ return nil
+}
+
+func (m *ArtifactInfo) validateTimestamp(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.Timestamp) { // not required
+ return nil
+ }
+
+ if err := validate.FormatOf("Timestamp", "body", "date-time", m.Timestamp.String(), formats); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// MarshalBinary interface implementation
+func (m *ArtifactInfo) MarshalBinary() ([]byte, error) {
+ if m == nil {
+ return nil, nil
+ }
+ return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *ArtifactInfo) UnmarshalBinary(b []byte) error {
+ var res ArtifactInfo
+ if err := swag.ReadJSON(b, &res); err != nil {
+ return err
+ }
+ *m = res
+ return nil
+}
diff --git a/artifact_pagination.go b/artifact_pagination.go
new file mode 100644
index 0000000..f7aa28a
--- /dev/null
+++ b/artifact_pagination.go
@@ -0,0 +1,31 @@
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+package weles
+
+// ArtifactPagination holds information neccessary to request for a single page of data from
+// artifactmanager.
+// When ID is set, and Forward is false - ArtifactManager should return a page of records before the
+// supplied ID.
+// When ID is set, and Forward is true - ArtifactManager should return page of records after the
+// supplied ID.
+// In both cases, returned page should not include supplied ID.
+// Limit denotes the number of records to be returned on the page.
+// When Limit is set to 0, pagination is disabled, ID and Forward fields are ignored
+// and all records are returned.
+type ArtifactPagination struct {
+ ID int64
+ Forward bool
+ Limit int32
+}
diff --git a/artifact_path.go b/artifact_path.go
new file mode 100644
index 0000000..5677d16
--- /dev/null
+++ b/artifact_path.go
@@ -0,0 +1,34 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package weles
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ strfmt "github.com/go-openapi/strfmt"
+)
+
+// ArtifactPath describes path to artifact in ArtifactDB filesystem.
+// swagger:model ArtifactPath
+type ArtifactPath string
+
+// Validate validates this artifact path
+func (m ArtifactPath) Validate(formats strfmt.Registry) error {
+ return nil
+}
diff --git a/artifact_sort_by.go b/artifact_sort_by.go
new file mode 100644
index 0000000..866532c
--- /dev/null
+++ b/artifact_sort_by.go
@@ -0,0 +1,77 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package weles
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "encoding/json"
+
+ strfmt "github.com/go-openapi/strfmt"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/validate"
+)
+
+// ArtifactSortBy denotes the key for sorting list of all artifacts.
+// * ID - sorting by artifact ID.
+//
+// swagger:model ArtifactSortBy
+type ArtifactSortBy string
+
+const (
+
+ // ArtifactSortByID captures enum value "ID"
+ ArtifactSortByID ArtifactSortBy = "ID"
+)
+
+// for schema
+var artifactSortByEnum []interface{}
+
+func init() {
+ var res []ArtifactSortBy
+ if err := json.Unmarshal([]byte(`["ID"]`), &res); err != nil {
+ panic(err)
+ }
+ for _, v := range res {
+ artifactSortByEnum = append(artifactSortByEnum, v)
+ }
+}
+
+func (m ArtifactSortBy) validateArtifactSortByEnum(path, location string, value ArtifactSortBy) error {
+ if err := validate.Enum(path, location, value, artifactSortByEnum); err != nil {
+ return err
+ }
+ return nil
+}
+
+// Validate validates this artifact sort by
+func (m ArtifactSortBy) Validate(formats strfmt.Registry) error {
+ var res []error
+
+ // value enum
+ if err := m.validateArtifactSortByEnum("", "body", m); err != nil {
+ return err
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
diff --git a/artifact_sorter.go b/artifact_sorter.go
new file mode 100644
index 0000000..80f0d28
--- /dev/null
+++ b/artifact_sorter.go
@@ -0,0 +1,109 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package weles
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ strfmt "github.com/go-openapi/strfmt"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/swag"
+)
+
+// ArtifactSorter defines the key for sorting as well as direction of sorting.
+// When ArtifactSorter is empty, artifacts are sorted by ID, Ascending.
+//
+// swagger:model ArtifactSorter
+type ArtifactSorter struct {
+
+ // sort by
+ SortBy ArtifactSortBy `json:"SortBy,omitempty"`
+
+ // sort order
+ SortOrder SortOrder `json:"SortOrder,omitempty"`
+}
+
+// Validate validates this artifact sorter
+func (m *ArtifactSorter) Validate(formats strfmt.Registry) error {
+ var res []error
+
+ if err := m.validateSortBy(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateSortOrder(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+func (m *ArtifactSorter) validateSortBy(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.SortBy) { // not required
+ return nil
+ }
+
+ if err := m.SortBy.Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("SortBy")
+ }
+ return err
+ }
+
+ return nil
+}
+
+func (m *ArtifactSorter) validateSortOrder(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.SortOrder) { // not required
+ return nil
+ }
+
+ if err := m.SortOrder.Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("SortOrder")
+ }
+ return err
+ }
+
+ return nil
+}
+
+// MarshalBinary interface implementation
+func (m *ArtifactSorter) MarshalBinary() ([]byte, error) {
+ if m == nil {
+ return nil, nil
+ }
+ return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *ArtifactSorter) UnmarshalBinary(b []byte) error {
+ var res ArtifactSorter
+ if err := swag.ReadJSON(b, &res); err != nil {
+ return err
+ }
+ *m = res
+ return nil
+}
diff --git a/artifact_status.go b/artifact_status.go
new file mode 100644
index 0000000..2c30dab
--- /dev/null
+++ b/artifact_status.go
@@ -0,0 +1,93 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package weles
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "encoding/json"
+
+ strfmt "github.com/go-openapi/strfmt"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/validate"
+)
+
+// ArtifactStatus describes artifact status and availability.
+//
+// * DOWNLOADING - artifact is currently being downloaded.
+//
+// * READY - artifact has been downloaded and is ready to use.
+//
+// * FAILED - file is not available for use (e.g. download failed).
+//
+// * PENDING - artifact download has not started yet.
+//
+// swagger:model ArtifactStatus
+type ArtifactStatus string
+
+const (
+
+ // ArtifactStatusDOWNLOADING captures enum value "DOWNLOADING"
+ ArtifactStatusDOWNLOADING ArtifactStatus = "DOWNLOADING"
+
+ // ArtifactStatusREADY captures enum value "READY"
+ ArtifactStatusREADY ArtifactStatus = "READY"
+
+ // ArtifactStatusFAILED captures enum value "FAILED"
+ ArtifactStatusFAILED ArtifactStatus = "FAILED"
+
+ // ArtifactStatusPENDING captures enum value "PENDING"
+ ArtifactStatusPENDING ArtifactStatus = "PENDING"
+)
+
+// for schema
+var artifactStatusEnum []interface{}
+
+func init() {
+ var res []ArtifactStatus
+ if err := json.Unmarshal([]byte(`["DOWNLOADING","READY","FAILED","PENDING"]`), &res); err != nil {
+ panic(err)
+ }
+ for _, v := range res {
+ artifactStatusEnum = append(artifactStatusEnum, v)
+ }
+}
+
+func (m ArtifactStatus) validateArtifactStatusEnum(path, location string, value ArtifactStatus) error {
+ if err := validate.Enum(path, location, value, artifactStatusEnum); err != nil {
+ return err
+ }
+ return nil
+}
+
+// Validate validates this artifact status
+func (m ArtifactStatus) Validate(formats strfmt.Registry) error {
+ var res []error
+
+ // value enum
+ if err := m.validateArtifactStatusEnum("", "body", m); err != nil {
+ return err
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
diff --git a/artifact_status_change.go b/artifact_status_change.go
new file mode 100644
index 0000000..525f34c
--- /dev/null
+++ b/artifact_status_change.go
@@ -0,0 +1,22 @@
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+package weles
+
+// ArtifactStatusChange contains information about new status of an artifact.
+// It is used to monitor status changes.
+type ArtifactStatusChange struct {
+ Path ArtifactPath
+ NewStatus ArtifactStatus
+}
diff --git a/artifact_type.go b/artifact_type.go
new file mode 100644
index 0000000..eada618
--- /dev/null
+++ b/artifact_type.go
@@ -0,0 +1,93 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package weles
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "encoding/json"
+
+ strfmt "github.com/go-openapi/strfmt"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/validate"
+)
+
+// ArtifactType denotes type and function of an artifact.
+//
+// * IMAGE - image file.
+//
+// * RESULT - all outputs, files built during tests, etc.
+//
+// * TEST - additional files uploaded by user for conducting test.
+//
+// * YAML - yaml file describing Weles Job.
+//
+// swagger:model ArtifactType
+type ArtifactType string
+
+const (
+
+ // ArtifactTypeIMAGE captures enum value "IMAGE"
+ ArtifactTypeIMAGE ArtifactType = "IMAGE"
+
+ // ArtifactTypeRESULT captures enum value "RESULT"
+ ArtifactTypeRESULT ArtifactType = "RESULT"
+
+ // ArtifactTypeTEST captures enum value "TEST"
+ ArtifactTypeTEST ArtifactType = "TEST"
+
+ // ArtifactTypeYAML captures enum value "YAML"
+ ArtifactTypeYAML ArtifactType = "YAML"
+)
+
+// for schema
+var artifactTypeEnum []interface{}
+
+func init() {
+ var res []ArtifactType
+ if err := json.Unmarshal([]byte(`["IMAGE","RESULT","TEST","YAML"]`), &res); err != nil {
+ panic(err)
+ }
+ for _, v := range res {
+ artifactTypeEnum = append(artifactTypeEnum, v)
+ }
+}
+
+func (m ArtifactType) validateArtifactTypeEnum(path, location string, value ArtifactType) error {
+ if err := validate.Enum(path, location, value, artifactTypeEnum); err != nil {
+ return err
+ }
+ return nil
+}
+
+// Validate validates this artifact type
+func (m ArtifactType) Validate(formats strfmt.Registry) error {
+ var res []error
+
+ // value enum
+ if err := m.validateArtifactTypeEnum("", "body", m); err != nil {
+ return err
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
diff --git a/artifact_uri.go b/artifact_uri.go
new file mode 100644
index 0000000..3e42aa8
--- /dev/null
+++ b/artifact_uri.go
@@ -0,0 +1,46 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package weles
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ strfmt "github.com/go-openapi/strfmt"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/validate"
+)
+
+// ArtifactURI is used to identify artifact's source.
+// swagger:model ArtifactURI
+type ArtifactURI strfmt.URI
+
+// Validate validates this artifact URI
+func (m ArtifactURI) Validate(formats strfmt.Registry) error {
+ var res []error
+
+ if err := validate.FormatOf("", "body", "uri", strfmt.URI(m).String(), formats); err != nil {
+ return err
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
diff --git a/artifactmanager.go b/artifactmanager.go
index cc9058c..284acf0 100644
--- a/artifactmanager.go
+++ b/artifactmanager.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,86 +14,16 @@
* limitations under the License
*/
-// File artifactmanager.go defines ArtifactManager interface and structures related to it.
+// File artifactmanager.go defines ArtifactManager interface.
package weles
-import "time"
-
-// ArtifactType denotes type and function of an artifact.
-type ArtifactType string
-
-const (
- // AM_IMAGEFILE - image file.
- AM_IMAGEFILE ArtifactType = "IMAGE"
- // AM_RESULTFILE - all outputs, files built during tests, etc.
- AM_RESULTFILE ArtifactType = "RESULT"
- // AM_TESTFILE - additional files uploaded by user for conducting test.
- AM_TESTFILE ArtifactType = "TESTFILE"
- // AM_YAMLFILE - yaml file describing Weles Job.
- AM_YAMLFILE ArtifactType = "YAMLFILE"
-)
-
-// ArtifactPath describes path to artifact in ArtifactDB filesystem.
-type ArtifactPath string
-
-// ArtifactStatus describes artifact status and availability.
-type ArtifactStatus string
-
-const (
- // AM_DOWNLOADING - artifact is currently being downloaded.
- AM_DOWNLOADING ArtifactStatus = "DOWNLOADING"
- // AM_READY - artifact has been downloaded and is ready to use.
- AM_READY ArtifactStatus = "READY"
- // AM_FAILED - file is not available for use (e.g. download failed).
- AM_FAILED ArtifactStatus = "FAILED"
- // AM_PENDING - artifact download has not started yet.
- AM_PENDING ArtifactStatus = "PENDING"
-)
-
-// ArtifactURI is used to identify artifact's source.
-type ArtifactURI string
-
-// ArtifactAlias is used to identify artifact's alias.
-type ArtifactAlias string
-
-// ArtifactDescription contains information needed to create new artifact in ArtifactDB.
-type ArtifactDescription struct {
- JobID JobID
- Type ArtifactType
- Alias ArtifactAlias
- URI ArtifactURI
-}
-
-// ArtifactInfo describes single artifact stored in ArtifactDB.
-type ArtifactInfo struct {
- ArtifactDescription
- Path ArtifactPath
- Status ArtifactStatus
- Timestamp time.Time
-}
-
-// ArtifactFilter is used to filter results from ArtifactDB.
-type ArtifactFilter struct {
- JobID []JobID
- Type []ArtifactType
- Status []ArtifactStatus
- Alias []ArtifactAlias
-}
-
-// ArtifactStatusChange contains information about new status of an artifact.
-// It is used to monitor status changes.
-type ArtifactStatusChange struct {
- Path ArtifactPath
- NewStatus ArtifactStatus
-}
-
// ArtifactManager provides access to content in ArtifactDB required for Job execution.
// It provides data from ArtifactDB for lookup and retrieval.
// It is responsible for downloading job artifacts to ArtifactDB.
type ArtifactManager interface {
// List filters ArtifactDB and returns list of all matching artifacts.
- ListArtifact(filter ArtifactFilter) ([]ArtifactInfo, error)
+ ListArtifact(filter ArtifactFilter, sorter ArtifactSorter, paginator ArtifactPagination) ([]ArtifactInfo, ListInfo, error)
// Push inserts artifact to ArtifactDB and returns its path.
PushArtifact(artifact ArtifactDescription, ch chan ArtifactStatusChange) (ArtifactPath, error)
diff --git a/artifacts/artifacts.go b/artifacts/artifacts.go
index ad1fc07..0263b21 100644
--- a/artifacts/artifacts.go
+++ b/artifacts/artifacts.go
@@ -24,18 +24,20 @@ import (
"strconv"
"time"
- . "git.tizen.org/tools/weles"
- . "git.tizen.org/tools/weles/artifacts/database"
- . "git.tizen.org/tools/weles/artifacts/downloader"
+ "github.com/go-openapi/strfmt"
+
+ "git.tizen.org/tools/weles"
+ "git.tizen.org/tools/weles/artifacts/database"
+ "git.tizen.org/tools/weles/artifacts/downloader"
)
// ArtifactDownloader downloads requested file if there is need to.
type ArtifactDownloader interface {
// Download starts downloading requested artifact.
- Download(URI ArtifactURI, path ArtifactPath, ch chan ArtifactStatusChange) error
+ Download(URI weles.ArtifactURI, path weles.ArtifactPath, ch chan weles.ArtifactStatusChange) error
// CheckInCache checks if file already exists in ArtifactDB.
- CheckInCache(URI ArtifactURI) (ArtifactInfo, error)
+ CheckInCache(URI weles.ArtifactURI) (weles.ArtifactInfo, error)
// Close waits for all jobs to finish, and gracefully closes ArtifactDownloader.
Close()
@@ -45,34 +47,23 @@ type ArtifactDownloader interface {
// or information about artifacts stored there.
// Storage implements ArtifactManager interface.
type Storage struct {
- ArtifactManager
- db ArtifactDB
+ weles.ArtifactManager
+ db database.ArtifactDB
dir string
downloader ArtifactDownloader
- notifier chan ArtifactStatusChange
+ notifier chan weles.ArtifactStatusChange
}
-const (
- // defaultDb is default ArtifactDB name.
- defaultDb = "weles.db"
- // defaultDir is default directory for ArtifactManager storage.
- defaultDir = "/tmp/weles/"
- // notifierCap is default notifier channel capacity.
- notifierCap = 100
- // workersCount is default number of workers.
- workersCount = 16
-)
-
-func newArtifactManager(db, dir string) (ArtifactManager, error) {
+func newArtifactManager(db, dir string, notifierCap, workersCount, queueCap int) (weles.ArtifactManager, error) {
err := os.MkdirAll(dir, os.ModePerm)
if err != nil {
return nil, err
}
- notifier := make(chan ArtifactStatusChange, notifierCap)
+ notifier := make(chan weles.ArtifactStatusChange, notifierCap)
am := Storage{
dir: dir,
- downloader: NewDownloader(notifier, workersCount),
+ downloader: downloader.NewDownloader(notifier, workersCount, queueCap),
notifier: notifier,
}
err = am.db.Open(db)
@@ -87,23 +78,17 @@ func newArtifactManager(db, dir string) (ArtifactManager, error) {
// NewArtifactManager returns initialized Storage implementing ArtifactManager interface.
// If db or dir is empy, default value will be used.
-func NewArtifactManager(db, dir string) (ArtifactManager, error) {
- if db == "" {
- db = defaultDb
- }
- if dir == "" {
- dir = defaultDir
- }
- return newArtifactManager(filepath.Join(dir, db), dir)
+func NewArtifactManager(db, dir string, notifierCap, workersCount, queueCap int) (weles.ArtifactManager, error) {
+ return newArtifactManager(filepath.Join(dir, db), dir, notifierCap, workersCount, queueCap)
}
// ListArtifact is part of implementation of ArtifactManager interface.
-func (s *Storage) ListArtifact(filter ArtifactFilter) ([]ArtifactInfo, error) {
- return s.db.Filter(filter)
+func (s *Storage) ListArtifact(filter weles.ArtifactFilter, sorter weles.ArtifactSorter, paginator weles.ArtifactPagination) ([]weles.ArtifactInfo, weles.ListInfo, error) {
+ return s.db.Filter(filter, sorter, paginator)
}
// PushArtifact is part of implementation of ArtifactManager interface.
-func (s *Storage) PushArtifact(artifact ArtifactDescription, ch chan ArtifactStatusChange) (ArtifactPath, error) {
+func (s *Storage) PushArtifact(artifact weles.ArtifactDescription, ch chan weles.ArtifactStatusChange) (weles.ArtifactPath, error) {
path, err := s.CreateArtifact(artifact)
if err != nil {
return "", err
@@ -111,20 +96,20 @@ func (s *Storage) PushArtifact(artifact ArtifactDescription, ch chan ArtifactSta
err = s.downloader.Download(artifact.URI, path, ch)
if err != nil {
- s.db.SetStatus(ArtifactStatusChange{path, AM_FAILED})
+ s.db.SetStatus(weles.ArtifactStatusChange{path, weles.ArtifactStatusFAILED})
return "", err
}
return path, nil
}
// CreateArtifact is part of implementation of ArtifactManager interface.
-func (s *Storage) CreateArtifact(artifact ArtifactDescription) (ArtifactPath, error) {
+func (s *Storage) CreateArtifact(artifact weles.ArtifactDescription) (weles.ArtifactPath, error) {
path, err := s.getNewPath(artifact)
if err != nil {
return "", err
}
- err = s.db.InsertArtifactInfo(&ArtifactInfo{artifact, path, "", time.Now().UTC()})
+ err = s.db.InsertArtifactInfo(&weles.ArtifactInfo{ArtifactDescription: artifact, Path: path, Status: "", Timestamp: strfmt.DateTime(time.Now().UTC())})
if err != nil {
return "", err
}
@@ -132,7 +117,7 @@ func (s *Storage) CreateArtifact(artifact ArtifactDescription) (ArtifactPath, er
}
// GetArtifactInfo is part of implementation of ArtifactManager interface.
-func (s *Storage) GetArtifactInfo(path ArtifactPath) (ArtifactInfo, error) {
+func (s *Storage) GetArtifactInfo(path weles.ArtifactPath) (weles.ArtifactInfo, error) {
return s.db.SelectPath(path)
}
@@ -144,7 +129,7 @@ func (s *Storage) Close() error {
}
// getNewPath prepares new path for artifact.
-func (s *Storage) getNewPath(ad ArtifactDescription) (ArtifactPath, error) {
+func (s *Storage) getNewPath(ad weles.ArtifactDescription) (weles.ArtifactPath, error) {
var (
jobDir = filepath.Join(s.dir, strconv.FormatUint(uint64(ad.JobID), 10))
typeDir = filepath.Join(jobDir, string(ad.Type))
@@ -163,7 +148,7 @@ func (s *Storage) getNewPath(ad ArtifactDescription) (ArtifactPath, error) {
return "", err
}
defer f.Close()
- return ArtifactPath(f.Name()), err
+ return weles.ArtifactPath(f.Name()), err
}
// listenToChanges updates artifact's status in db every time Storage is notified
diff --git a/artifacts/artifacts_test.go b/artifacts/artifacts_test.go
index 0336835..e4a69bf 100644
--- a/artifacts/artifacts_test.go
+++ b/artifacts/artifacts_test.go
@@ -64,23 +64,23 @@ With gently smiling jaws!
var (
description = weles.ArtifactDescription{
- job,
- weles.AM_IMAGEFILE,
"alias",
+ job,
+ weles.ArtifactTypeIMAGE,
"uri",
}
dSameJobNType = weles.ArtifactDescription{
- job,
- weles.AM_IMAGEFILE,
"other alias",
+ job,
+ weles.ArtifactTypeIMAGE,
"other uri",
}
dSameJobOtherType = weles.ArtifactDescription{
- job,
- weles.AM_YAMLFILE,
"another alias",
+ job,
+ weles.ArtifactTypeYAML,
"another uri",
}
)
@@ -90,7 +90,8 @@ With gently smiling jaws!
Expect(err).ToNot(HaveOccurred())
dbPath = filepath.Join(testDir, "test.db")
- silverKangaroo, err = newArtifactManager(dbPath, testDir)
+ silverKangaroo, err = newArtifactManager(dbPath, testDir, 100, 16, 100)
+ //TODO add tests against different notifier cap, queue cap and workers count.
Expect(err).ToNot(HaveOccurred())
})
@@ -195,7 +196,8 @@ With gently smiling jaws!
)
DescribeTable("NewArtifactManager()", func(db, dir string) {
- copperPanda, err := NewArtifactManager(db, dir)
+ copperPanda, err := NewArtifactManager(db, dir, 100, 16, 100)
+ //TODO: add tests against different notifier cap and workers count.
Expect(err).ToNot(HaveOccurred())
if db == "" {
@@ -215,7 +217,6 @@ With gently smiling jaws!
Expect(err).ToNot(HaveOccurred())
},
Entry("create database in default directory", defaultDb, defaultDir),
- Entry("create database in default directory, when arguments are empty", "", ""),
Entry("create database in custom directory", customDb, customDir),
)
})
@@ -226,16 +227,16 @@ With gently smiling jaws!
ch chan weles.ArtifactStatusChange
ad weles.ArtifactDescription = weles.ArtifactDescription{
- job,
- weles.AM_IMAGEFILE,
"somealias",
+ job,
+ weles.ArtifactTypeIMAGE,
validURL,
}
adInvalid weles.ArtifactDescription = weles.ArtifactDescription{
- job,
- weles.AM_IMAGEFILE,
"somealias",
+ job,
+ weles.ArtifactTypeIMAGE,
invalidURL,
}
)
@@ -255,11 +256,11 @@ With gently smiling jaws!
Expect(err).ToNot(HaveOccurred())
- Eventually(ch).Should(Receive(Equal(weles.ArtifactStatusChange{path, weles.AM_PENDING})))
- Eventually(ch).Should(Receive(Equal(weles.ArtifactStatusChange{path, weles.AM_DOWNLOADING})))
+ Eventually(ch).Should(Receive(Equal(weles.ArtifactStatusChange{path, weles.ArtifactStatusPENDING})))
+ Eventually(ch).Should(Receive(Equal(weles.ArtifactStatusChange{path, weles.ArtifactStatusDOWNLOADING})))
Eventually(ch).Should(Receive(Equal(weles.ArtifactStatusChange{path, finalStatus})))
- if finalStatus != weles.AM_FAILED {
+ if finalStatus != weles.ArtifactStatusFAILED {
By("Check if file exists and has proper content")
content, err := ioutil.ReadFile(string(path))
@@ -278,8 +279,8 @@ With gently smiling jaws!
By("Check if artifact is in ArtifactDB")
Expect(checkPathInDb(path)).To(BeTrue())
},
- Entry("push artifact to db and download file", ad, weles.AM_READY),
- Entry("do not push an invalid artifact", adInvalid, weles.AM_FAILED),
+ Entry("push artifact to db and download file", ad, weles.ArtifactStatusREADY),
+ Entry("do not push an invalid artifact", adInvalid, weles.ArtifactStatusFAILED),
)
})
})
diff --git a/artifacts/database/database.go b/artifacts/database/database.go
index 7049c9c..1de09f1 100644
--- a/artifacts/database/database.go
+++ b/artifacts/database/database.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,9 +19,10 @@ package database
import (
"database/sql"
+ "errors"
"strings"
- . "git.tizen.org/tools/weles"
+ "git.tizen.org/tools/weles"
"github.com/go-gorp/gorp"
// sqlite3 is imported for side-effects and will be used
@@ -29,11 +30,6 @@ import (
_ "github.com/mattn/go-sqlite3"
)
-type artifactInfoRecord struct {
- ID int64 `db:",primarykey, autoincrement"`
- ArtifactInfo
-}
-
// ArtifactDB is responsible for database connection and queries.
type ArtifactDB struct {
handler *sql.DB
@@ -55,7 +51,7 @@ func (aDB *ArtifactDB) Open(dbPath string) error {
// initDB initializes tables.
func (aDB *ArtifactDB) initDB() error {
// Add tables.
- aDB.dbmap.AddTableWithName(artifactInfoRecord{}, "artifacts").SetKeys(true, "ID")
+ aDB.dbmap.AddTableWithName(weles.ArtifactInfo{}, "artifacts").SetKeys(true, "ID")
return aDB.dbmap.CreateTablesIfNotExists()
}
@@ -66,31 +62,39 @@ func (aDB *ArtifactDB) Close() error {
}
// InsertArtifactInfo inserts information about artifact to database.
-func (aDB *ArtifactDB) InsertArtifactInfo(ai *ArtifactInfo) error {
- ar := artifactInfoRecord{
- ArtifactInfo: *ai,
- }
- return aDB.dbmap.Insert(&ar)
+func (aDB *ArtifactDB) InsertArtifactInfo(ai *weles.ArtifactInfo) error {
+ return aDB.dbmap.Insert(ai)
}
// SelectPath selects artifact from database based on its path.
-func (aDB *ArtifactDB) SelectPath(path ArtifactPath) (ArtifactInfo, error) {
- ar := artifactInfoRecord{}
- err := aDB.dbmap.SelectOne(&ar, "select * from artifacts where Path=?", path)
+func (aDB *ArtifactDB) SelectPath(path weles.ArtifactPath) (weles.ArtifactInfo, error) {
+ // ar := artifactInfoRecord{}
+ ai := weles.ArtifactInfo{}
+ err := aDB.dbmap.SelectOne(&ai, "select * from artifacts where Path=?", path)
if err != nil {
- return ArtifactInfo{}, err
+ return weles.ArtifactInfo{}, err
}
- return ar.ArtifactInfo, nil
+ return ai, nil
}
// prepareQuery prepares query based on given filter.
// TODO code duplication
-func prepareQuery(filter ArtifactFilter) (string, []interface{}) {
+func prepareQuery(
+ filter weles.ArtifactFilter,
+ sorter weles.ArtifactSorter,
+ paginator weles.ArtifactPagination,
+ getTotal, getRemaining bool, offset int64) (string, []interface{}) {
var (
conditions []string
- query = "select * from artifacts "
+ query string
args []interface{}
)
+ if getTotal == false && getRemaining == false {
+ query = "select * from artifacts "
+ } else {
+ query = "select count(*) from artifacts "
+ }
+
if len(filter.JobID) > 0 {
q := make([]string, len(filter.JobID))
for i, job := range filter.JobID {
@@ -123,65 +127,113 @@ func prepareQuery(filter ArtifactFilter) (string, []interface{}) {
}
conditions = append(conditions, " Alias in ("+strings.Join(q, ",")+")")
}
+ if getTotal == false && paginator.ID != 0 {
+ if (paginator.Forward == true && sorter.SortOrder == weles.SortOrderDescending) || (paginator.Forward == false && (sorter.SortOrder == weles.SortOrderAscending || sorter.SortOrder == "")) {
+ conditions = append(conditions, " ID < ? ")
+ args = append(args, paginator.ID)
+ } else {
+ conditions = append(conditions, " ID > ? ")
+ args = append(args, paginator.ID)
+ }
+ }
+
if len(conditions) > 0 {
query += " where " + strings.Join(conditions, " AND ")
}
+ //TODO: make timestamp also db key, add to where clause and order by as described in:
+ // https://www.sqlite.org/rowvalue.html#scrolling_window_queries
+ if sorter.SortOrder == weles.SortOrderDescending {
+ query += " ORDER BY ID DESC "
+ } else if sorter.SortOrder == weles.SortOrderAscending || sorter.SortOrder == "" {
+ query += " ORDER BY ID ASC "
+ }
+ if paginator.Limit != 0 {
+ if offset == 0 {
+ query += " LIMIT ? "
+ args = append(args, paginator.Limit)
+ } else {
+ query += " LIMIT ? OFFSET ?"
+ args = append(args, paginator.Limit, offset)
+ }
+ }
return query, args
}
// Filter fetches elements matching ArtifactFilter from database.
-func (aDB *ArtifactDB) Filter(filter ArtifactFilter) ([]ArtifactInfo, error) {
- results := []artifactInfoRecord{}
-
- query, args := prepareQuery(filter)
-
+func (aDB *ArtifactDB) Filter(filter weles.ArtifactFilter, sorter weles.ArtifactSorter, paginator weles.ArtifactPagination) ([]weles.ArtifactInfo, weles.ListInfo, error) {
+ results := []weles.ArtifactInfo{}
+ var tr, rr int64
// TODO gorp doesn't support passing list of arguments to where in(...) clause yet.
// Thats why it's done with the use prepareQuery.
- _, err := aDB.dbmap.Select(&results, query, args...)
+ trans, err := aDB.dbmap.Begin()
if err != nil {
- return nil, err
+ return nil, weles.ListInfo{}, errors.New("Failed to open transaction while filtering " + err.Error())
}
- artifacts := make([]ArtifactInfo, len(results))
- for i, res := range results {
- artifacts[i] = res.ArtifactInfo
+ queryForTotal, argsForTotal := prepareQuery(filter, sorter, paginator, true, false, 0)
+ queryForRemaining, argsForRemaining := prepareQuery(filter, sorter, paginator, false, true, 0)
+ var offset int64
+
+ rr, err = aDB.dbmap.SelectInt(queryForRemaining, argsForRemaining...)
+ if err != nil {
+ return nil, weles.ListInfo{}, errors.New("Failed to get remaining records " + err.Error())
+ }
+
+ tr, err = aDB.dbmap.SelectInt(queryForTotal, argsForTotal...)
+ if err != nil {
+ return nil, weles.ListInfo{}, errors.New("Failed to get total records " + err.Error())
+ }
+ // TODO: refactor this file. below is to ignore pagination object when pagination is turned off.
+ if paginator.Limit == 0 {
+ paginator.Forward = true
+ paginator.ID = 0
+ }
+
+ if paginator.Forward == false {
+ offset = rr - int64(paginator.Limit)
+ }
+
+ queryForData, argsForData := prepareQuery(filter, sorter, paginator, false, false, offset)
+ _, err = aDB.dbmap.Select(&results, queryForData, argsForData...)
+ if err != nil {
+ return nil, weles.ListInfo{}, err
}
- return artifacts, nil
+ if err := trans.Commit(); err != nil {
+ return nil, weles.ListInfo{}, errors.New("Failed to commit transaction while filtering " + err.Error())
+ }
+ return results, weles.ListInfo{TotalRecords: uint64(tr), RemainingRecords: uint64(rr - int64(len(results)))}, nil
}
// Select fetches artifacts from ArtifactDB.
-func (aDB *ArtifactDB) Select(arg interface{}) (artifacts []ArtifactInfo, err error) {
+func (aDB *ArtifactDB) Select(arg interface{}) (artifacts []weles.ArtifactInfo, err error) {
var (
- results []artifactInfoRecord
+ results []weles.ArtifactInfo
query string
)
// TODO prepare efficient way of executing generic select.
switch arg.(type) {
- case JobID:
+ case weles.JobID:
query = "select * from artifacts where JobID = ?"
- case ArtifactType:
+ case weles.ArtifactType:
query = "select * from artifacts where Type = ?"
- case ArtifactAlias:
+ case weles.ArtifactAlias:
query = "select * from artifacts where Alias = ?"
- case ArtifactStatus:
+ case weles.ArtifactStatus:
query = "select * from artifacts where Status = ?"
default:
return nil, ErrUnsupportedQueryType
}
+ query += " ORDER BY id"
_, err = aDB.dbmap.Select(&results, query, arg)
if err != nil {
return nil, err
}
- artifacts = make([]ArtifactInfo, len(results))
- for i, res := range results {
- artifacts[i] = res.ArtifactInfo
- }
- return artifacts, nil
+ return results, nil
}
// getID fetches ID of an artifact with provided path.
-func (aDB *ArtifactDB) getID(path ArtifactPath) (int64, error) {
+func (aDB *ArtifactDB) getID(path weles.ArtifactPath) (int64, error) {
res, err := aDB.dbmap.SelectInt("select ID from artifacts where Path=?", path)
if err != nil {
return 0, err
@@ -190,22 +242,19 @@ func (aDB *ArtifactDB) getID(path ArtifactPath) (int64, error) {
}
// SetStatus changes artifact's status in ArtifactDB.
-func (aDB *ArtifactDB) SetStatus(change ArtifactStatusChange) error {
+func (aDB *ArtifactDB) SetStatus(change weles.ArtifactStatusChange) error {
ai, err := aDB.SelectPath(change.Path)
if err != nil {
return err
}
- ar := artifactInfoRecord{
- ArtifactInfo: ai,
- }
- id, err := aDB.getID(ar.Path)
+ id, err := aDB.getID(ai.Path)
if err != nil {
return err
}
- ar.ID = id
+ ai.ID = id
- ar.Status = change.NewStatus
- _, err = aDB.dbmap.Update(&ar)
+ ai.Status = change.NewStatus
+ _, err = aDB.dbmap.Update(&ai)
return err
}
diff --git a/artifacts/database/database_suite_test.go b/artifacts/database/database_suite_test.go
index 55dbe8c..1bd072f 100644
--- a/artifacts/database/database_suite_test.go
+++ b/artifacts/database/database_suite_test.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,52 @@
* limitations under the License
*/
-package database_test
+package database
import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "testing"
+
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
- "testing"
+ "git.tizen.org/tools/weles/fixtures"
)
func TestDatabase(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Database Suite")
}
+
+var (
+ silverHoneybadger ArtifactDB
+ tmpDirPath string
+)
+
+var _ = BeforeSuite(func() {
+
+ var err error
+ tmpDirPath, err = ioutil.TempDir("", "weles-")
+ Expect(err).ToNot(HaveOccurred())
+ err = silverHoneybadger.Open(filepath.Join(tmpDirPath, "test_pagination.db"))
+ Expect(err).ToNot(HaveOccurred())
+ artifacts := fixtures.CreateArtifactInfoSlice(100)
+ trans, err := silverHoneybadger.dbmap.Begin()
+ Expect(err).ToNot(HaveOccurred())
+ for _, artifact := range artifacts {
+ err = silverHoneybadger.InsertArtifactInfo(&artifact)
+ Expect(err).ToNot(HaveOccurred())
+ }
+ trans.Commit()
+
+})
+
+var _ = AfterSuite(func() {
+
+ err := silverHoneybadger.Close()
+ Expect(err).ToNot(HaveOccurred())
+ err = os.RemoveAll(tmpDirPath)
+ Expect(err).ToNot(HaveOccurred())
+})
diff --git a/artifacts/database/database_test.go b/artifacts/database/database_test.go
index 5f4dff7..df06d63 100644
--- a/artifacts/database/database_test.go
+++ b/artifacts/database/database_test.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,8 @@ import (
"path/filepath"
"time"
+ "github.com/go-openapi/strfmt"
+
"git.tizen.org/tools/weles"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/extensions/table"
@@ -42,74 +44,140 @@ var _ = Describe("ArtifactDB", func() {
tmpDir string
artifact = weles.ArtifactInfo{
- weles.ArtifactDescription{
- job,
- weles.AM_IMAGEFILE,
- "some alias",
- "http://example.com",
+ ArtifactDescription: weles.ArtifactDescription{
+ Alias: "some alias",
+ JobID: job,
+ Type: weles.ArtifactTypeIMAGE,
+ URI: "http://example.com",
},
- "path1",
- weles.AM_PENDING,
- time.Now().UTC(),
+ Path: "path1",
+ Status: weles.ArtifactStatusPENDING,
+ Timestamp: strfmt.DateTime(time.Now().Round(time.Millisecond).UTC()),
}
aImageReady = weles.ArtifactInfo{
- weles.ArtifactDescription{
- job + 1,
- weles.AM_IMAGEFILE,
- "other alias",
- "http://example.com/1",
+ ArtifactDescription: weles.ArtifactDescription{
+ Alias: "other alias",
+ JobID: job + 1,
+ Type: weles.ArtifactTypeIMAGE,
+ URI: "http://example.com/1",
},
- "path2",
- weles.AM_READY,
- time.Now().UTC(),
+ Path: "path2",
+ Status: weles.ArtifactStatusREADY,
+ Timestamp: strfmt.DateTime(time.Now().Round(time.Millisecond).UTC()),
}
aYamlFailed = weles.ArtifactInfo{
- weles.ArtifactDescription{
- job + 1,
- weles.AM_YAMLFILE,
- "other alias",
- "http://example.com/2",
+ ArtifactDescription: weles.ArtifactDescription{
+ Alias: "other alias",
+ JobID: job + 1,
+ Type: weles.ArtifactTypeYAML,
+ URI: "http://example.com/2",
},
- "path3",
- weles.AM_FAILED,
- time.Now().UTC(),
+ Path: "path3",
+ Status: weles.ArtifactStatusFAILED,
+ Timestamp: strfmt.DateTime(time.Now().Round(time.Millisecond).UTC()),
}
aTestFailed = weles.ArtifactInfo{
- weles.ArtifactDescription{
- job + 2,
- weles.AM_TESTFILE,
- "alias",
- "http://example.com/2",
+ ArtifactDescription: weles.ArtifactDescription{
+ Alias: "alias",
+ JobID: job + 2,
+ Type: weles.ArtifactTypeTEST,
+ URI: "http://example.com/3",
},
- "path4",
- weles.AM_FAILED,
- time.Unix(3000, 60).UTC(),
+ Path: "path4",
+ Status: weles.ArtifactStatusFAILED,
+ Timestamp: strfmt.DateTime(time.Unix(3000, 60).Round(time.Millisecond).UTC()),
}
testArtifacts = []weles.ArtifactInfo{artifact, aImageReady, aYamlFailed, aTestFailed}
- oneJobFilter = weles.ArtifactFilter{[]weles.JobID{artifact.JobID}, nil, nil, nil}
- twoJobsFilter = weles.ArtifactFilter{[]weles.JobID{artifact.JobID, aImageReady.JobID}, nil, nil, nil}
- noJobFilter = weles.ArtifactFilter{[]weles.JobID{invalidJob}, nil, nil, nil}
-
- oneTypeFilter = weles.ArtifactFilter{nil, []weles.ArtifactType{aYamlFailed.Type}, nil, nil}
- twoTypesFilter = weles.ArtifactFilter{nil, []weles.ArtifactType{aYamlFailed.Type, aTestFailed.Type}, nil, nil}
- noTypeFilter = weles.ArtifactFilter{nil, []weles.ArtifactType{invalidType}, nil, nil}
-
- oneStatusFilter = weles.ArtifactFilter{nil, nil, []weles.ArtifactStatus{artifact.Status}, nil}
- twoStatusFilter = weles.ArtifactFilter{nil, nil, []weles.ArtifactStatus{artifact.Status, aYamlFailed.Status}, nil}
- noStatusFilter = weles.ArtifactFilter{nil, nil, []weles.ArtifactStatus{invalidStatus}, nil}
-
- oneAliasFilter = weles.ArtifactFilter{nil, nil, nil, []weles.ArtifactAlias{artifact.Alias}}
- twoAliasFilter = weles.ArtifactFilter{nil, nil, nil, []weles.ArtifactAlias{artifact.Alias, aImageReady.Alias}}
- noAliasFilter = weles.ArtifactFilter{nil, nil, nil, []weles.ArtifactAlias{invalidAlias}}
-
- fullFilter = weles.ArtifactFilter{twoJobsFilter.JobID, twoTypesFilter.Type, twoStatusFilter.Status, twoAliasFilter.Alias}
- noMatchFilter = weles.ArtifactFilter{oneJobFilter.JobID, oneTypeFilter.Type, nil, nil}
- emptyFilter = weles.ArtifactFilter{}
+ oneJobFilter = weles.ArtifactFilter{
+ JobID: []weles.JobID{artifact.JobID},
+ Alias: nil,
+ Status: nil,
+ Type: nil}
+ twoJobsFilter = weles.ArtifactFilter{
+ JobID: []weles.JobID{artifact.JobID, aImageReady.JobID},
+ Alias: nil,
+ Status: nil,
+ Type: nil}
+ noJobFilter = weles.ArtifactFilter{
+ JobID: []weles.JobID{invalidJob},
+ Alias: nil,
+ Status: nil,
+ Type: nil}
+
+ oneTypeFilter = weles.ArtifactFilter{
+ JobID: nil,
+ Type: []weles.ArtifactType{aYamlFailed.Type},
+ Status: nil,
+ Alias: nil}
+
+ twoTypesFilter = weles.ArtifactFilter{
+ JobID: nil,
+ Type: []weles.ArtifactType{aYamlFailed.Type, aTestFailed.Type},
+ Status: nil,
+ Alias: nil}
+
+ noTypeFilter = weles.ArtifactFilter{
+ JobID: nil,
+ Type: []weles.ArtifactType{invalidType},
+ Status: nil,
+ Alias: nil}
+
+ oneStatusFilter = weles.ArtifactFilter{
+ JobID: nil,
+ Type: nil,
+ Status: []weles.ArtifactStatus{artifact.Status},
+ Alias: nil}
+
+ twoStatusFilter = weles.ArtifactFilter{
+ JobID: nil,
+ Type: nil,
+ Status: []weles.ArtifactStatus{artifact.Status, aYamlFailed.Status},
+ Alias: nil}
+
+ noStatusFilter = weles.ArtifactFilter{
+ JobID: nil,
+ Type: nil,
+ Status: []weles.ArtifactStatus{invalidStatus},
+ Alias: nil}
+
+ oneAliasFilter = weles.ArtifactFilter{
+ JobID: nil,
+ Type: nil,
+ Status: nil,
+ Alias: []weles.ArtifactAlias{artifact.Alias}}
+
+ twoAliasFilter = weles.ArtifactFilter{
+ JobID: nil,
+ Type: nil,
+ Status: nil,
+ Alias: []weles.ArtifactAlias{artifact.Alias, aImageReady.Alias}}
+
+ noAliasFilter = weles.ArtifactFilter{
+ JobID: nil,
+ Type: nil,
+ Status: nil,
+ Alias: []weles.ArtifactAlias{invalidAlias}}
+
+ fullFilter = weles.ArtifactFilter{
+ JobID: []weles.JobID{artifact.JobID, aImageReady.JobID, aYamlFailed.JobID},
+ Type: twoTypesFilter.Type,
+ Status: twoStatusFilter.Status,
+ Alias: twoAliasFilter.Alias}
+
+ noMatchFilter = weles.ArtifactFilter{
+ JobID: oneJobFilter.JobID,
+ Type: oneTypeFilter.Type,
+ Status: nil,
+ Alias: nil}
+
+ emptyFilter = weles.ArtifactFilter{}
+ //default values of sorter passed by server
+ defaultSorter = weles.ArtifactSorter{SortOrder: weles.SortOrderDescending, SortBy: weles.ArtifactSortByID}
)
jobsInDB := func(job weles.JobID) int64 {
@@ -180,6 +248,7 @@ var _ = Describe("ArtifactDB", func() {
} else {
Expect(err).ToNot(HaveOccurred())
}
+ expectedArtifact.ID = result.ID
Expect(result).To(Equal(expectedArtifact))
},
Entry("retrieve artifact based on path", artifact.Path, nil, artifact),
@@ -190,8 +259,11 @@ var _ = Describe("ArtifactDB", func() {
Describe("Select", func() {
BeforeEach(func() {
+ trans, err := goldenUnicorn.dbmap.Begin()
+ Expect(err).ToNot(HaveOccurred())
+ defer trans.Commit()
for _, a := range testArtifacts {
- err := goldenUnicorn.InsertArtifactInfo(&a)
+ err := trans.Insert(&a)
Expect(err).ToNot(HaveOccurred())
}
})
@@ -205,36 +277,55 @@ var _ = Describe("ArtifactDB", func() {
Expect(result).To(BeNil())
} else {
Expect(err).ToNot(HaveOccurred())
- Expect(result).To(Equal(expectedResult))
+ for i := range result {
+ Expect(result[i].ArtifactDescription).To(Equal(expectedResult[i].ArtifactDescription))
+ Expect(result[i].Path).To(Equal(expectedResult[i].Path))
+ Expect(result[i].Status).To(Equal(expectedResult[i].Status))
+ Expect(result[i].Timestamp).To(Equal(expectedResult[i].Timestamp))
+ }
}
},
// types supported by select.
Entry("select JobID", artifact.JobID, nil, artifact),
- Entry("select Type", weles.AM_YAMLFILE, nil, aYamlFailed),
- Entry("select Status", weles.AM_READY, nil, aImageReady),
+ Entry("select Type", weles.ArtifactTypeYAML, nil, aYamlFailed),
+ Entry("select Status", weles.ArtifactStatusREADY, nil, aImageReady),
Entry("select Alias", artifact.Alias, nil, artifact),
// type bool is not supported by select.
Entry("select unsupported value", true, ErrUnsupportedQueryType),
// test query itsef.
Entry("select multiple entries for JobID", aImageReady.JobID, nil, aImageReady, aYamlFailed),
Entry("select no entries for invalid JobID", invalidJob, nil),
- Entry("select multiple entries for Type", weles.AM_IMAGEFILE, nil, artifact, aImageReady),
+ Entry("select multiple entries for Type", weles.ArtifactTypeIMAGE, nil, artifact, aImageReady),
Entry("select multiple entries for Alias", aImageReady.Alias, nil, aImageReady, aYamlFailed),
- Entry("select multiple entries for Status", weles.AM_FAILED, nil, aYamlFailed, aTestFailed),
+ Entry("select multiple entries for Status", weles.ArtifactStatusFAILED, nil, aYamlFailed, aTestFailed),
)
})
Describe("List", func() {
BeforeEach(func() {
+ trans, err := goldenUnicorn.dbmap.Begin()
+ Expect(err).ToNot(HaveOccurred())
+ defer trans.Commit()
for _, a := range testArtifacts {
- err := goldenUnicorn.InsertArtifactInfo(&a)
+ err := trans.Insert(&a)
Expect(err).ToNot(HaveOccurred())
}
})
DescribeTable("list artifacts matching filter",
func(filter weles.ArtifactFilter, expected ...weles.ArtifactInfo) {
- results, err := goldenUnicorn.Filter(filter)
+ results, _, err := goldenUnicorn.Filter(filter, defaultSorter, weles.ArtifactPagination{})
Expect(err).ToNot(HaveOccurred())
+ //TODO: match all fields except ID.
+ for i := range results {
+ for j := range expected {
+ if results[i].JobID == expected[j].JobID {
+ if results[i].URI == expected[j].URI {
+ expected[j].ID = results[i].ID
+ }
+ }
+ }
+
+ }
Expect(results).To(ConsistOf(expected))
},
Entry("filter one JobID", oneJobFilter, artifact),
@@ -278,8 +369,8 @@ var _ = Describe("ArtifactDB", func() {
Expect(a).To(Equal(weles.ArtifactInfo{}))
}
},
- Entry("change status of artifact not present in ArtifactDB", weles.ArtifactStatusChange{invalidPath, weles.AM_DOWNLOADING}, sql.ErrNoRows),
- Entry("change status of artifact present in ArtifactDB", weles.ArtifactStatusChange{artifact.Path, weles.AM_DOWNLOADING}, nil),
+ Entry("change status of artifact not present in ArtifactDB", weles.ArtifactStatusChange{Path: invalidPath, NewStatus: weles.ArtifactStatusDOWNLOADING}, sql.ErrNoRows),
+ Entry("change status of artifact present in ArtifactDB", weles.ArtifactStatusChange{Path: artifact.Path, NewStatus: weles.ArtifactStatusDOWNLOADING}, nil),
)
})
})
diff --git a/artifacts/database/pagination_test.go b/artifacts/database/pagination_test.go
new file mode 100644
index 0000000..a2530fd
--- /dev/null
+++ b/artifacts/database/pagination_test.go
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package database
+
+import (
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/ginkgo/extensions/table"
+ . "github.com/onsi/gomega"
+
+ "git.tizen.org/tools/weles"
+)
+
+//TODO: add some consts and limit 'magic' to multipliers
+
+var _ = Describe("ArtifactDB pagination", func() {
+
+ emptyF := weles.ArtifactFilter{}
+ //below struct is not empty but filled with default values passed from server.
+ descS := weles.ArtifactSorter{SortOrder: weles.SortOrderDescending, SortBy: weles.ArtifactSortByID}
+ ascS := weles.ArtifactSorter{SortOrder: weles.SortOrderAscending, SortBy: weles.ArtifactSortByID}
+ Context("Database is filled with 100 records", func() {
+ DescribeTable("paginating through artifacts",
+ func(
+ filter weles.ArtifactFilter,
+ paginator weles.ArtifactPagination,
+ sorter weles.ArtifactSorter,
+ expectedResponseLength, expectedTotalRecords, expectedRemainingRecords int) {
+ result, list, err := silverHoneybadger.Filter(filter, sorter, paginator)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(len(result)).To(BeEquivalentTo(expectedResponseLength))
+ Expect(list.TotalRecords).To(BeEquivalentTo(expectedTotalRecords))
+ Expect(list.RemainingRecords).To(BeEquivalentTo(expectedRemainingRecords))
+ },
+ Entry("p1/1, no limit, forward, sort desc", emptyF,
+ weles.ArtifactPagination{Forward: true},
+ descS, 100, 100, 0),
+ Entry("p1/4, limit 30, forward, sort desc", emptyF,
+ weles.ArtifactPagination{Limit: 30, Forward: true},
+ descS, 30, 100, 70),
+ Entry("p2/4, limit 30, forward, sort desc", emptyF,
+ weles.ArtifactPagination{ID: 71, Limit: 30, Forward: true},
+ descS, 30, 100, 40),
+ Entry("p3/4, limit 30, forward, sort desc", emptyF,
+ weles.ArtifactPagination{ID: 41, Limit: 30, Forward: true},
+ descS, 30, 100, 10),
+ Entry("p4/4, limit 30, forward, sort desc", emptyF,
+ weles.ArtifactPagination{ID: 11, Limit: 30, Forward: true},
+ descS, 10, 100, 0),
+ Entry("p3/4, limit 30, backward, sort desc", emptyF,
+ weles.ArtifactPagination{ID: 10, Limit: 30, Forward: false},
+ descS, 30, 100, 60),
+ Entry("p2/4, limit 30, backward, sort desc", emptyF,
+ weles.ArtifactPagination{ID: 40, Limit: 30, Forward: false},
+ descS, 30, 100, 30),
+ Entry("p1/4, limit 30, backward, sort desc", emptyF,
+ weles.ArtifactPagination{ID: 70, Limit: 30, Forward: false},
+ descS, 30, 100, 0),
+ Entry("p1/1, no limit, forward, sort asc", emptyF,
+ weles.ArtifactPagination{Forward: true},
+ ascS, 100, 100, 0),
+ Entry("1/4, no limit, forward, sort asc", emptyF,
+ weles.ArtifactPagination{Limit: 30, Forward: true},
+ ascS, 30, 100, 70),
+ Entry("p2/4, no limit, forward, sort asc", emptyF,
+ weles.ArtifactPagination{ID: 30, Limit: 30, Forward: true},
+ ascS, 30, 100, 40),
+ Entry("p3/4, no limit, forward, sort asc", emptyF,
+ weles.ArtifactPagination{ID: 60, Limit: 30, Forward: true},
+ ascS, 30, 100, 10),
+ Entry("p4/4, no limit, forward, sort asc", emptyF,
+ weles.ArtifactPagination{ID: 90, Limit: 30, Forward: true},
+ ascS, 10, 100, 0),
+ )
+ })
+
+})
diff --git a/artifacts/downloader/downloader.go b/artifacts/downloader/downloader.go
index 2edf024..59ea167 100644
--- a/artifacts/downloader/downloader.go
+++ b/artifacts/downloader/downloader.go
@@ -24,28 +24,25 @@ import (
"os"
"sync"
- . "git.tizen.org/tools/weles"
+ "git.tizen.org/tools/weles"
)
// Downloader implements ArtifactDownloader interface.
type Downloader struct {
- notification chan ArtifactStatusChange // can be used to monitor ArtifactStatusChanges.
+ notification chan weles.ArtifactStatusChange // can be used to monitor ArtifactStatusChanges.
queue chan downloadJob
wg sync.WaitGroup
}
// downloadJob provides necessary info for download to be done.
type downloadJob struct {
- path ArtifactPath
- uri ArtifactURI
- ch chan ArtifactStatusChange
+ path weles.ArtifactPath
+ uri weles.ArtifactURI
+ ch chan weles.ArtifactStatusChange
}
-// queueCap is the default length of download queue.
-const queueCap = 100
-
// newDownloader returns initilized Downloader.
-func newDownloader(notification chan ArtifactStatusChange, workers int, queueSize int) *Downloader {
+func newDownloader(notification chan weles.ArtifactStatusChange, workers int, queueSize int) *Downloader {
d := &Downloader{
notification: notification,
@@ -61,7 +58,7 @@ func newDownloader(notification chan ArtifactStatusChange, workers int, queueSiz
}
// NewDownloader returns Downloader initialized with default queue length
-func NewDownloader(notification chan ArtifactStatusChange, workerCount int) *Downloader {
+func NewDownloader(notification chan weles.ArtifactStatusChange, workerCount, queueCap int) *Downloader {
return newDownloader(notification, workerCount, queueCap)
}
@@ -73,7 +70,7 @@ func (d *Downloader) Close() {
}
// getData downloads file from provided location and saves it in a prepared path.
-func (d *Downloader) getData(URI ArtifactURI, path ArtifactPath) error {
+func (d *Downloader) getData(URI weles.ArtifactURI, path weles.ArtifactPath) error {
resp, err := http.Get(string(URI))
if err != nil {
return err
@@ -97,33 +94,33 @@ func (d *Downloader) getData(URI ArtifactURI, path ArtifactPath) error {
// download downloads artifact from provided URI and saves it to specified path.
// It sends notification about status changes to two channels - Downloader's notification
// channel, and other one, that can be specified passed as an argument.
-func (d *Downloader) download(URI ArtifactURI, path ArtifactPath, ch chan ArtifactStatusChange) {
+func (d *Downloader) download(URI weles.ArtifactURI, path weles.ArtifactPath, ch chan weles.ArtifactStatusChange) {
if path == "" {
return
}
- change := ArtifactStatusChange{
+ change := weles.ArtifactStatusChange{
Path: path,
- NewStatus: AM_DOWNLOADING,
+ NewStatus: weles.ArtifactStatusDOWNLOADING,
}
- channels := []chan ArtifactStatusChange{ch, d.notification}
+ channels := []chan weles.ArtifactStatusChange{ch, d.notification}
notify(change, channels)
err := d.getData(URI, path)
if err != nil {
os.Remove(string(path))
- change.NewStatus = AM_FAILED
+ change.NewStatus = weles.ArtifactStatusFAILED
} else {
- change.NewStatus = AM_READY
+ change.NewStatus = weles.ArtifactStatusREADY
}
notify(change, channels)
}
// Download is part of implementation of ArtifactDownloader interface.
// It puts new downloadJob on the queue.
-func (d *Downloader) Download(URI ArtifactURI, path ArtifactPath, ch chan ArtifactStatusChange) error {
- channels := []chan ArtifactStatusChange{ch, d.notification}
- notify(ArtifactStatusChange{path, AM_PENDING}, channels)
+func (d *Downloader) Download(URI weles.ArtifactURI, path weles.ArtifactPath, ch chan weles.ArtifactStatusChange) error {
+ channels := []chan weles.ArtifactStatusChange{ch, d.notification}
+ notify(weles.ArtifactStatusChange{path, weles.ArtifactStatusPENDING}, channels)
job := downloadJob{
path: path,
@@ -148,12 +145,12 @@ func (d *Downloader) work() {
// CheckInCache is part of implementation of ArtifactDownloader interface.
// TODO implement.
-func (d *Downloader) CheckInCache(URI ArtifactURI) (ArtifactInfo, error) {
- return ArtifactInfo{}, ErrNotImplemented
+func (d *Downloader) CheckInCache(URI weles.ArtifactURI) (weles.ArtifactInfo, error) {
+ return weles.ArtifactInfo{}, weles.ErrNotImplemented
}
// notify sends ArtifactStatusChange to all specified channels.
-func notify(change ArtifactStatusChange, channels []chan ArtifactStatusChange) {
+func notify(change weles.ArtifactStatusChange, channels []chan weles.ArtifactStatusChange) {
for _, ch := range channels {
ch <- change
}
diff --git a/artifacts/downloader/downloader_test.go b/artifacts/downloader/downloader_test.go
index c6537b3..0b74bff 100644
--- a/artifacts/downloader/downloader_test.go
+++ b/artifacts/downloader/downloader_test.go
@@ -55,6 +55,7 @@ I call it stupid of the pig.
notifyCap int = 100 // notitication channel capacity.
notification chan weles.ArtifactStatusChange
workersCount = 8
+ queueCap = 100
)
checkChannels := func(ch1, ch2 chan weles.ArtifactStatusChange, change weles.ArtifactStatusChange) {
@@ -67,7 +68,7 @@ I call it stupid of the pig.
var err error
// prepare Downloader.
notification = make(chan weles.ArtifactStatusChange, notifyCap)
- platinumKoala = NewDownloader(notification, workersCount)
+ platinumKoala = NewDownloader(notification, workersCount, queueCap)
// prepare temporary directories.
tmpDir, err = ioutil.TempDir("", "weles-")
@@ -125,10 +126,10 @@ I call it stupid of the pig.
}
},
- Entry("download valid file to valid path", validURL, true, weles.AM_READY),
- Entry("fail when url is invalid", invalidURL, true, weles.AM_FAILED),
- Entry("fail when path is invalid", validURL, false, weles.AM_FAILED),
- Entry("fail when url and path are invalid", invalidURL, false, weles.AM_FAILED),
+ Entry("download valid file to valid path", validURL, true, weles.ArtifactStatusREADY),
+ Entry("fail when url is invalid", invalidURL, true, weles.ArtifactStatusFAILED),
+ Entry("fail when path is invalid", validURL, false, weles.ArtifactStatusFAILED),
+ Entry("fail when url and path are invalid", invalidURL, false, weles.ArtifactStatusFAILED),
)
DescribeTable("download(): Notify channels and save data to file",
@@ -142,11 +143,11 @@ I call it stupid of the pig.
}
filename := weles.ArtifactPath(filepath.Join(dir, "test"))
- status := weles.ArtifactStatusChange{filename, weles.AM_DOWNLOADING}
+ status := weles.ArtifactStatusChange{filename, weles.ArtifactStatusDOWNLOADING}
platinumKoala.download(weles.ArtifactURI(ts.URL), weles.ArtifactPath(filename), ch)
- status.NewStatus = weles.AM_DOWNLOADING
+ status.NewStatus = weles.ArtifactStatusDOWNLOADING
checkChannels(ch, platinumKoala.notification, status)
status.NewStatus = finalResult
@@ -161,10 +162,10 @@ I call it stupid of the pig.
}
},
- Entry("download valid file to valid path", validURL, true, weles.AM_READY),
- Entry("fail when url is invalid", invalidURL, true, weles.AM_FAILED),
- Entry("fail when path is invalid", validURL, false, weles.AM_FAILED),
- Entry("fail when url and path are invalid", invalidURL, false, weles.AM_FAILED),
+ Entry("download valid file to valid path", validURL, true, weles.ArtifactStatusREADY),
+ Entry("fail when url is invalid", invalidURL, true, weles.ArtifactStatusFAILED),
+ Entry("fail when path is invalid", validURL, false, weles.ArtifactStatusFAILED),
+ Entry("fail when url and path are invalid", invalidURL, false, weles.ArtifactStatusFAILED),
)
DescribeTable("Download(): Notify ch channel about any changes",
@@ -181,19 +182,19 @@ I call it stupid of the pig.
err := platinumKoala.Download(weles.ArtifactURI(ts.URL), path, ch)
Expect(err).ToNot(HaveOccurred())
- status := weles.ArtifactStatusChange{path, weles.AM_PENDING}
+ status := weles.ArtifactStatusChange{path, weles.ArtifactStatusPENDING}
Eventually(ch).Should(Receive(Equal(status)))
- status.NewStatus = weles.AM_DOWNLOADING
+ status.NewStatus = weles.ArtifactStatusDOWNLOADING
Eventually(ch).Should(Receive(Equal(status)))
status.NewStatus = finalResult
Eventually(ch).Should(Receive(Equal(status)))
},
- Entry("download valid file to valid path", validURL, true, weles.AM_READY),
- Entry("fail when url is invalid", invalidURL, true, weles.AM_FAILED),
- Entry("fail when path is invalid", validURL, false, weles.AM_FAILED),
- Entry("fail when url and path are invalid", invalidURL, false, weles.AM_FAILED),
+ Entry("download valid file to valid path", validURL, true, weles.ArtifactStatusREADY),
+ Entry("fail when url is invalid", invalidURL, true, weles.ArtifactStatusFAILED),
+ Entry("fail when path is invalid", validURL, false, weles.ArtifactStatusFAILED),
+ Entry("fail when url and path are invalid", invalidURL, false, weles.ArtifactStatusFAILED),
)
DescribeTable("Download(): Download files to specified path.",
@@ -206,16 +207,16 @@ I call it stupid of the pig.
err := platinumKoala.Download(weles.ArtifactURI(ts.URL), path, ch)
Expect(err).ToNot(HaveOccurred())
- Eventually(ch).Should(Receive(Equal(weles.ArtifactStatusChange{path, weles.AM_PENDING})))
- Eventually(ch).Should(Receive(Equal(weles.ArtifactStatusChange{path, weles.AM_DOWNLOADING})))
+ Eventually(ch).Should(Receive(Equal(weles.ArtifactStatusChange{path, weles.ArtifactStatusPENDING})))
+ Eventually(ch).Should(Receive(Equal(weles.ArtifactStatusChange{path, weles.ArtifactStatusDOWNLOADING})))
if poem != "" {
- Eventually(ch).Should(Receive(Equal(weles.ArtifactStatusChange{path, weles.AM_READY})))
+ Eventually(ch).Should(Receive(Equal(weles.ArtifactStatusChange{path, weles.ArtifactStatusREADY})))
content, err := ioutil.ReadFile(string(path))
Expect(err).ToNot(HaveOccurred())
Expect(string(content)).To(BeIdenticalTo(poem))
} else {
- Eventually(ch).Should(Receive(Equal(weles.ArtifactStatusChange{path, weles.AM_FAILED})))
+ Eventually(ch).Should(Receive(Equal(weles.ArtifactStatusChange{path, weles.ArtifactStatusFAILED})))
content, err := ioutil.ReadFile(string(path))
Expect(err).To(HaveOccurred())
Expect(content).To(BeNil())
diff --git a/cmd/weles-server/main.go b/cmd/weles-server/main.go
new file mode 100644
index 0000000..d3b66db
--- /dev/null
+++ b/cmd/weles-server/main.go
@@ -0,0 +1,114 @@
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package main
+
+import (
+ "fmt"
+ "log"
+ "os"
+ "time"
+
+ loads "github.com/go-openapi/loads"
+ flag "github.com/spf13/pflag"
+
+ "git.tizen.org/tools/boruta/http/client"
+ "git.tizen.org/tools/weles/artifacts"
+ "git.tizen.org/tools/weles/controller"
+ "git.tizen.org/tools/weles/manager"
+ "git.tizen.org/tools/weles/parser"
+ "git.tizen.org/tools/weles/server"
+ "git.tizen.org/tools/weles/server/operations"
+)
+
+var (
+ borutaAddress string
+ borutaRefreshPeriod time.Duration
+ artifactDBName string
+ artifactDBLocation string
+ artifactDownloadQueueCap int
+ activeWorkersCap int
+ notifierChannelCap int
+)
+
+// This file was generated by the swagger tool.
+// Make sure to regenerate server only with the Makefile which has --exclude-main option set.
+
+func exitOnErr(ctx string, err error) {
+ if err != nil {
+ log.Fatal(ctx, err)
+ }
+}
+
+func main() {
+
+ swaggerSpec, err := loads.Embedded(server.SwaggerJSON, server.FlatSwaggerJSON)
+ exitOnErr("failed to load embedded swagger spec", err)
+
+ var srv *server.Server // make sure init is called
+ var apiDefaults server.APIDefaults
+
+ flag.Int32Var(&apiDefaults.PageLimit, "page-limit", 0, "Default limit of page size returned by Weles API. If set to 0 pagination will be turned off")
+ flag.StringVar(&borutaAddress, "boruta-address", "http://127.0.0.1:8487", "Boruta address. Must contain protocol.")
+ flag.DurationVar(&borutaRefreshPeriod, "boruta-refresh-period", 2*time.Second, "Boruta refresh period")
+ flag.StringVar(&artifactDBName, "db-file", "weles.db", "name of *.db file. Should be located in --db-location")
+ flag.StringVar(&artifactDBLocation, "db-location", "/tmp/weles/", "location of *.db file and place where Weles will store artifacts.")
+ //TODO: when cyberdryads or testlab instance will be present, performance tests should be done
+ // to set default values of below:
+ flag.IntVar(&artifactDownloadQueueCap, "artifact-download-queue-cap", 100, "Capacity of artifact download queue")
+ flag.IntVar(&activeWorkersCap, "active-workers-cap", 16, "Maximum number of active workers.")
+ flag.IntVar(&notifierChannelCap, "notifier-channel-cap", 100, "Notifier channel capacity.")
+
+ //TODO: input validation
+
+ flag.Usage = func() {
+ fmt.Fprint(os.Stderr, "Usage:\n")
+ fmt.Fprint(os.Stderr, " weles-server [OPTIONS]\n\n")
+
+ title := "Weles"
+ fmt.Fprint(os.Stderr, title+"\n\n")
+ desc := "This is a Weles server. You can find out more about Weles at [http://tbd.tbd](http://tbd.tbd)."
+ if desc != "" {
+ fmt.Fprintf(os.Stderr, desc+"\n\n")
+ }
+ fmt.Fprintln(os.Stderr, flag.CommandLine.FlagUsages())
+ }
+ // parse the CLI flags
+ flag.Parse()
+
+ var yap parser.Parser
+ am, err := artifacts.NewArtifactManager(
+ artifactDBName,
+ artifactDBLocation,
+ notifierChannelCap,
+ activeWorkersCap,
+ artifactDownloadQueueCap)
+ exitOnErr("failed to initialize ArtifactManager ", err)
+ bor := client.NewBorutaClient(borutaAddress)
+ djm := manager.NewDryadJobManager()
+ jm := controller.NewJobManager(am, &yap, bor, borutaRefreshPeriod, djm)
+
+ api := operations.NewWelesAPI(swaggerSpec)
+ // get server with flag values filled out
+ srv = server.NewServer(api)
+
+ defer srv.Shutdown()
+
+ apiDefaults.Managers = server.NewManagers(jm, am)
+
+ srv.WelesConfigureAPI(&apiDefaults)
+ err = srv.Serve()
+ exitOnErr("failed to serve the API", err)
+}
diff --git a/controller/boruterimpl.go b/controller/boruterimpl.go
index 25c2866..b45b871 100644
--- a/controller/boruterimpl.go
+++ b/controller/boruterimpl.go
@@ -265,7 +265,7 @@ func (h *BoruterImpl) getDeadline(config weles.Config) time.Time {
// Request registers new request in Boruta and adds it to monitored requests.
func (h *BoruterImpl) Request(j weles.JobID) {
- err := h.jobs.SetStatusAndInfo(j, weles.JOB_WAITING, "")
+ err := h.jobs.SetStatusAndInfo(j, weles.JobStatusWAITING, "")
if err != nil {
h.SendFail(j, fmt.Sprintf("Internal Weles error while changing Job status : %s", err.Error()))
return
diff --git a/controller/boruterimpl_test.go b/controller/boruterimpl_test.go
index 6512505..0c646e6 100644
--- a/controller/boruterimpl_test.go
+++ b/controller/boruterimpl_test.go
@@ -120,7 +120,7 @@ var _ = Describe("BoruterImpl", func() {
It("should ignore ListRequests errors", func() {
counter := 5
mutex := &sync.Mutex{}
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_WAITING, "")
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusWAITING, "")
jc.EXPECT().GetConfig(j).Return(config, nil)
req.EXPECT().NewRequest(caps, priority, owner, gomock.Any(), gomock.Any()).Return(rid, nil)
req.EXPECT().ListRequests(nil).AnyTimes().Return([]boruta.ReqInfo{}, err).Do(func(boruta.ListFilter) {
@@ -142,7 +142,7 @@ var _ = Describe("BoruterImpl", func() {
Describe("Request", func() {
It("should register job successfully", func() {
var va, dl time.Time
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_WAITING, "")
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusWAITING, "")
jc.EXPECT().GetConfig(j).Return(config, nil)
req.EXPECT().NewRequest(caps, priority, owner, gomock.Any(), gomock.Any()).Return(rid, nil).Do(
func(c boruta.Capabilities, p boruta.Priority, ui boruta.UserInfo, validAfter time.Time, deadline time.Time) {
@@ -167,7 +167,7 @@ var _ = Describe("BoruterImpl", func() {
config.Timeouts.JobTimeout = weles.ValidPeriod(0)
defaultDelay := 24 * time.Hour
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_WAITING, "")
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusWAITING, "")
jc.EXPECT().GetConfig(j).Return(config, nil)
req.EXPECT().NewRequest(caps, priority, owner, gomock.Any(), gomock.Any()).Return(rid, nil).Do(
func(c boruta.Capabilities, p boruta.Priority, ui boruta.UserInfo, validAfter time.Time, deadline time.Time) {
@@ -188,7 +188,7 @@ var _ = Describe("BoruterImpl", func() {
expectRegistered(1)
})
It("should fail if NewRequest fails", func() {
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_WAITING, "")
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusWAITING, "")
jc.EXPECT().GetConfig(j).Return(config, nil)
req.EXPECT().NewRequest(caps, priority, owner, gomock.Any(), gomock.Any()).Return(boruta.ReqID(0), err)
req.EXPECT().ListRequests(nil).AnyTimes()
@@ -199,7 +199,7 @@ var _ = Describe("BoruterImpl", func() {
eventuallyEmpty(1)
})
It("should fail if GetConfig fails", func() {
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_WAITING, "")
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusWAITING, "")
jc.EXPECT().GetConfig(j).Return(weles.Config{}, err)
req.EXPECT().ListRequests(nil).AnyTimes()
@@ -209,7 +209,7 @@ var _ = Describe("BoruterImpl", func() {
eventuallyEmpty(1)
})
It("should fail if SetStatusAndInfo fails", func() {
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_WAITING, "").Return(err)
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusWAITING, "").Return(err)
req.EXPECT().ListRequests(nil).AnyTimes()
h.Request(j)
@@ -219,7 +219,7 @@ var _ = Describe("BoruterImpl", func() {
})
It("should call NewRequest with empty caps if no device type provided", func() {
config.DeviceType = ""
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_WAITING, "")
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusWAITING, "")
jc.EXPECT().GetConfig(j).Return(config, nil)
req.EXPECT().NewRequest(boruta.Capabilities{}, priority, owner, gomock.Any(), gomock.Any()).Return(boruta.ReqID(0), err)
req.EXPECT().ListRequests(nil).AnyTimes()
@@ -238,7 +238,7 @@ var _ = Describe("BoruterImpl", func() {
}
for k, v := range m {
config.Priority = k
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_WAITING, "")
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusWAITING, "")
jc.EXPECT().GetConfig(j).Return(config, nil)
req.EXPECT().NewRequest(caps, v, owner, gomock.Any(), gomock.Any()).Return(boruta.ReqID(0), err)
req.EXPECT().ListRequests(nil).AnyTimes()
@@ -264,7 +264,7 @@ var _ = Describe("BoruterImpl", func() {
ai := boruta.AccessInfo{Addr: &net.IPNet{IP: net.IPv4(1, 2, 3, 4), Mask: net.IPv4Mask(5, 6, 7, 8)}}
BeforeEach(func() {
var va, dl time.Time
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_WAITING, "")
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusWAITING, "")
jc.EXPECT().GetConfig(j).Return(config, nil)
req.EXPECT().NewRequest(caps, priority, owner, gomock.Any(), gomock.Any()).Return(rid, nil).Do(
func(c boruta.Capabilities, p boruta.Priority, ui boruta.UserInfo, validAfter time.Time, deadline time.Time) {
diff --git a/controller/controller.go b/controller/controller.go
index 59d39ba..d9446e7 100644
--- a/controller/controller.go
+++ b/controller/controller.go
@@ -109,7 +109,7 @@ func (c *Controller) CreateJob(yaml []byte) (weles.JobID, error) {
// CancelJob cancels Job identified by argument. Job execution is stopped.
// It is a part of JobManager implementation.
func (c *Controller) CancelJob(j weles.JobID) error {
- err := c.jobs.SetStatusAndInfo(j, weles.JOB_CANCELED, "")
+ err := c.jobs.SetStatusAndInfo(j, weles.JobStatusCANCELED, "")
if err != nil {
return err
}
@@ -118,12 +118,11 @@ func (c *Controller) CancelJob(j weles.JobID) error {
return nil
}
-// ListJobs returns information on Jobs. If argument is a nil/empty slice
-// information about all Jobs is returned. Otherwise result is filtered
-// and contains information about requested Jobs only.
+// ListJobs returns information on Jobs.
// It is a part of JobManager implementation.
-func (c *Controller) ListJobs(filter []weles.JobID) ([]weles.JobInfo, error) {
- return c.jobs.List(filter)
+func (c *Controller) ListJobs(filter weles.JobFilter, sorter weles.JobSorter,
+ paginator weles.JobPagination) ([]weles.JobInfo, weles.ListInfo, error) {
+ return c.jobs.List(filter, sorter, paginator)
}
// loop implements main loop of the Controller reacting to different events
@@ -165,13 +164,13 @@ func (c *Controller) loop() {
// fail sets Job in FAILED state and if needed stops Job's execution on Dryad
// and releases Dryad to Boruta.
func (c *Controller) fail(j weles.JobID, msg string) {
- c.jobs.SetStatusAndInfo(j, weles.JOB_FAILED, msg)
+ c.jobs.SetStatusAndInfo(j, weles.JobStatusFAILED, msg)
c.dryader.CancelJob(j)
c.boruter.Release(j)
}
// succeed sets Job in COMPLETED state.
func (c *Controller) succeed(j weles.JobID) {
- c.jobs.SetStatusAndInfo(j, weles.JOB_COMPLETED, "")
+ c.jobs.SetStatusAndInfo(j, weles.JobStatusCOMPLETED, "")
c.boruter.Release(j)
}
diff --git a/controller/controller_test.go b/controller/controller_test.go
index b792a95..fa389d6 100644
--- a/controller/controller_test.go
+++ b/controller/controller_test.go
@@ -149,7 +149,7 @@ var _ = Describe("Controller", func() {
Describe("CancelJob", func() {
It("should cancel Job, stop execution on Dryad and release Dryad to Boruta", func() {
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_CANCELED, "")
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusCANCELED, "")
dry.EXPECT().CancelJob(j)
bor.EXPECT().Release(j)
@@ -158,7 +158,7 @@ var _ = Describe("Controller", func() {
Expect(retErr).To(BeNil())
})
It("should return error if Job fails to be cancelled", func() {
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_CANCELED, "").Return(testErr)
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusCANCELED, "").Return(testErr)
retErr := h.CancelJob(j)
@@ -167,18 +167,22 @@ var _ = Describe("Controller", func() {
})
Describe("ListJobs", func() {
It("should call JobsController method", func() {
- filter := []weles.JobID{2, 3, 5}
+ filter := weles.JobFilter{}
+ sorter := weles.JobSorter{}
+ paginator := weles.JobPagination{}
list := []weles.JobInfo{
weles.JobInfo{
JobID: weles.JobID(3),
Name: "test name",
},
}
- jc.EXPECT().List(filter).Return(list, testErr)
+ info := weles.ListInfo{}
+ jc.EXPECT().List(filter, sorter, paginator).Return(list, info, testErr)
- ret, retErr := h.ListJobs(filter)
+ ret, retInfo, retErr := h.ListJobs(filter, sorter, paginator)
Expect(retErr).To(Equal(testErr))
+ Expect(retInfo).To(Equal(info))
Expect(ret).To(Equal(list))
})
})
@@ -203,13 +207,13 @@ var _ = Describe("Controller", func() {
}, &borChan),
Entry("should complete Job after Dryad Job is done",
func() {
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_COMPLETED, "")
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusCOMPLETED, "")
bor.EXPECT().Release(j).Do(setDone)
}, &dryChan),
)
DescribeTable("Action fail",
func(cnn *chan notifier.Notification) {
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_FAILED, testMsg)
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusFAILED, testMsg)
dry.EXPECT().CancelJob(j)
bor.EXPECT().Release(j)
*cnn <- notiFail
diff --git a/controller/downloaderimpl.go b/controller/downloaderimpl.go
index 6f3a14f..c5e2a8d 100644
--- a/controller/downloaderimpl.go
+++ b/controller/downloaderimpl.go
@@ -99,10 +99,10 @@ func (h *DownloaderImpl) pathStatusChange(path string, status weles.ArtifactStat
return
}
switch status {
- case weles.AM_READY:
+ case weles.ArtifactStatusREADY:
i.ready++
info = fmt.Sprintf(formatReady, i.ready, i.paths)
- case weles.AM_FAILED:
+ case weles.ArtifactStatusFAILED:
i.failed++
info = "Failed to download artifact"
default:
@@ -136,7 +136,7 @@ func (h *DownloaderImpl) loop() {
continue
}
- err := h.jobs.SetStatusAndInfo(j, weles.JOB_DOWNLOADING, info)
+ err := h.jobs.SetStatusAndInfo(j, weles.JobStatusDOWNLOADING, info)
if err != nil {
h.removePath(string(change.Path))
h.fail(j, fmt.Sprintf(formatJobStatus, err.Error()))
@@ -211,7 +211,7 @@ func (h *DownloaderImpl) push(j weles.JobID, t weles.ArtifactType, alias string,
func (h *DownloaderImpl) pullCreate(j weles.JobID, alias string) (string, error) {
p, err := h.artifacts.CreateArtifact(weles.ArtifactDescription{
JobID: j,
- Type: weles.AM_TESTFILE,
+ Type: weles.ArtifactTypeTEST,
Alias: weles.ArtifactAlias(alias),
})
return string(p), err
@@ -272,7 +272,7 @@ func (h *DownloaderImpl) sendIfReady(j weles.JobID) {
func (h *DownloaderImpl) DispatchDownloads(j weles.JobID) {
h.initializeJobInfo(j)
- err := h.jobs.SetStatusAndInfo(j, weles.JOB_DOWNLOADING, "")
+ err := h.jobs.SetStatusAndInfo(j, weles.JobStatusDOWNLOADING, "")
if err != nil {
h.fail(j, fmt.Sprintf(formatJobStatus, err.Error()))
return
@@ -286,7 +286,7 @@ func (h *DownloaderImpl) DispatchDownloads(j weles.JobID) {
for i, image := range config.Action.Deploy.Images {
if image.URI != "" {
- path, err := h.push(j, weles.AM_IMAGEFILE, fmt.Sprintf("Image_%d", i), image.URI)
+ path, err := h.push(j, weles.ArtifactTypeIMAGE, fmt.Sprintf("Image_%d", i), image.URI)
if err != nil {
h.fail(j, fmt.Sprintf(formatURI, image.URI, err.Error()))
return
@@ -294,7 +294,7 @@ func (h *DownloaderImpl) DispatchDownloads(j weles.JobID) {
config.Action.Deploy.Images[i].Path = path
}
if image.ChecksumURI != "" {
- path, err := h.push(j, weles.AM_IMAGEFILE, fmt.Sprintf("ImageMD5_%d", i), image.ChecksumURI)
+ path, err := h.push(j, weles.ArtifactTypeIMAGE, fmt.Sprintf("ImageMD5_%d", i), image.ChecksumURI)
if err != nil {
h.fail(j, fmt.Sprintf(formatURI, image.ChecksumURI, err.Error()))
return
@@ -307,7 +307,7 @@ func (h *DownloaderImpl) DispatchDownloads(j weles.JobID) {
switch ta.(type) {
case weles.Push:
action := ta.(weles.Push)
- path, err := h.push(j, weles.AM_TESTFILE, action.Alias, action.URI)
+ path, err := h.push(j, weles.ArtifactTypeTEST, action.Alias, action.URI)
if err != nil {
h.fail(j, fmt.Sprintf(formatURI, action.URI, err.Error()))
return
diff --git a/controller/downloaderimpl_test.go b/controller/downloaderimpl_test.go
index 28e9098..1d401bf 100644
--- a/controller/downloaderimpl_test.go
+++ b/controller/downloaderimpl_test.go
@@ -172,7 +172,7 @@ var _ = Describe("DownloaderImpl", func() {
eventuallyNoti(offset+1, false, msg)
expectPath(offset+1, 0, pathsNo)
eventuallyInfoEmpty(offset + 1)
- sendChange(0, pathsNo, weles.AM_READY)
+ sendChange(0, pathsNo, weles.ArtifactStatusREADY)
eventuallyPathEmpty(offset + 1)
}
defaultSetStatusAndInfo := func(successfulEntries int, fail bool) *gomock.Call {
@@ -180,14 +180,14 @@ var _ = Describe("DownloaderImpl", func() {
var prev, call *gomock.Call
for i = 0; i < successfulEntries; i++ {
- call = jc.EXPECT().SetStatusAndInfo(j, weles.JOB_DOWNLOADING, infos[i])
+ call = jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusDOWNLOADING, infos[i])
if prev != nil {
call.After(prev)
}
prev = call
}
if fail {
- call = jc.EXPECT().SetStatusAndInfo(j, weles.JOB_DOWNLOADING, infos[i]).Return(err)
+ call = jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusDOWNLOADING, infos[i]).Return(err)
if prev != nil {
call.After(prev)
}
@@ -198,7 +198,7 @@ var _ = Describe("DownloaderImpl", func() {
jc.EXPECT().GetConfig(j).Return(config, nil)
}
defaultPush := func(successfulEntries int, fail bool) *gomock.Call {
- types := []weles.ArtifactType{weles.AM_IMAGEFILE, weles.AM_IMAGEFILE, weles.AM_IMAGEFILE, weles.AM_IMAGEFILE, weles.AM_TESTFILE, weles.AM_TESTFILE, weles.AM_TESTFILE}
+ types := []weles.ArtifactType{weles.ArtifactTypeIMAGE, weles.ArtifactTypeIMAGE, weles.ArtifactTypeIMAGE, weles.ArtifactTypeIMAGE, weles.ArtifactTypeTEST, weles.ArtifactTypeTEST, weles.ArtifactTypeTEST}
aliases := []weles.ArtifactAlias{"Image_0", "ImageMD5_0", "Image_1", "ImageMD5_2", "alias_0", "alias_1", "alias_3"}
uris := []weles.ArtifactURI{"image_0", "md5_0", "image_1", "md5_2", "uri_0", "uri_1", "uri_3"}
var i int
@@ -222,7 +222,7 @@ var _ = Describe("DownloaderImpl", func() {
return call
}
defaultCreate := func(successfulEntries int, fail bool) *gomock.Call {
- types := []weles.ArtifactType{weles.AM_TESTFILE, weles.AM_TESTFILE}
+ types := []weles.ArtifactType{weles.ArtifactTypeTEST, weles.ArtifactTypeTEST}
aliases := []weles.ArtifactAlias{"alias_2", "alias_4"}
returnPaths := []weles.ArtifactPath{weles.ArtifactPath(paths[7]), weles.ArtifactPath(paths[8])}
var i int
@@ -260,7 +260,7 @@ var _ = Describe("DownloaderImpl", func() {
expectPath(1, 0, 7)
expectInfo(1, true, 7)
- sendChange(0, 7, weles.AM_READY)
+ sendChange(0, 7, weles.ArtifactStatusREADY)
eventuallyNoti(1, true, "")
eventuallyEmpty(1)
@@ -365,7 +365,7 @@ var _ = Describe("DownloaderImpl", func() {
})
It("should handle downloading failure", func() {
c := defaultSetStatusAndInfo(4, false)
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_DOWNLOADING, "Failed to download artifact").After(c)
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusDOWNLOADING, "Failed to download artifact").After(c)
defaultGetConfig()
defaultPush(7, false)
defaultCreate(2, false)
@@ -376,14 +376,14 @@ var _ = Describe("DownloaderImpl", func() {
expectPath(1, 0, 7)
expectInfo(1, true, 7)
- sendChange(0, 3, weles.AM_READY)
- sendChange(3, 4, weles.AM_FAILED)
+ sendChange(0, 3, weles.ArtifactStatusREADY)
+ sendChange(3, 4, weles.ArtifactStatusFAILED)
eventuallyNoti(1, false, formatDownload)
expectPath(1, 4, 7)
eventuallyInfoEmpty(1)
- sendChange(4, 7, weles.AM_DOWNLOADING)
+ sendChange(4, 7, weles.ArtifactStatusDOWNLOADING)
eventuallyPathEmpty(1)
})
It("should block reply until configuration is saved and all artifacts are downloaded", func() {
@@ -409,7 +409,7 @@ var _ = Describe("DownloaderImpl", func() {
expectPath(1, 0, 7)
expectInfo(1, false, 7)
- sendChange(0, 7, weles.AM_READY)
+ sendChange(0, 7, weles.ArtifactStatusREADY)
holdDownload.Done()
eventuallyNoti(1, true, "")
@@ -427,7 +427,7 @@ var _ = Describe("DownloaderImpl", func() {
expectPath(1, 0, 7)
expectInfo(1, true, 7)
- sendChange(0, 7, weles.AM_READY)
+ sendChange(0, 7, weles.ArtifactStatusREADY)
eventuallyNoti(1, false, "Internal Weles error while changing Job status : test error")
eventuallyEmpty(1)
@@ -454,7 +454,7 @@ var _ = Describe("DownloaderImpl", func() {
expectPath(1, 0, 7)
expectInfo(1, false, 7)
- sendChange(0, 7, weles.AM_READY)
+ sendChange(0, 7, weles.ArtifactStatusREADY)
eventuallyNoti(1, false, "Internal Weles error while changing Job status : test error")
@@ -464,7 +464,7 @@ var _ = Describe("DownloaderImpl", func() {
})
It("should leave no data left if failure response is sent while pushing", func() {
c := defaultSetStatusAndInfo(1, false)
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_DOWNLOADING, "1 / 1 artifacts ready").Return(err).After(c)
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusDOWNLOADING, "1 / 1 artifacts ready").Return(err).After(c)
defaultGetConfig()
holdDownload := sync.WaitGroup{}
holdDownload.Add(1)
@@ -482,12 +482,12 @@ var _ = Describe("DownloaderImpl", func() {
expectPath(1, 0, 1)
expectInfo(1, false, 1)
- sendChange(0, 1, weles.AM_READY)
+ sendChange(0, 1, weles.ArtifactStatusREADY)
eventuallyNoti(1, false, "Internal Weles error while changing Job status : test error")
holdDownload.Done()
- sendChange(1, 2, weles.AM_READY)
+ sendChange(1, 2, weles.ArtifactStatusREADY)
eventuallyEmpty(1)
})
@@ -503,13 +503,13 @@ var _ = Describe("DownloaderImpl", func() {
expectPath(1, 0, 7)
expectInfo(1, true, 7)
- sendChange(0, 7, weles.AM_DOWNLOADING)
- sendChange(0, 7, weles.AM_PENDING)
+ sendChange(0, 7, weles.ArtifactStatusDOWNLOADING)
+ sendChange(0, 7, weles.ArtifactStatusPENDING)
expectPath(1, 0, 7)
expectInfo(1, true, 7)
- sendChange(0, 7, weles.AM_READY)
+ sendChange(0, 7, weles.ArtifactStatusREADY)
eventuallyNoti(1, true, "")
eventuallyEmpty(1)
diff --git a/controller/dryaderimpl.go b/controller/dryaderimpl.go
index ac5b1d3..b4e4eae 100644
--- a/controller/dryaderimpl.go
+++ b/controller/dryaderimpl.go
@@ -90,7 +90,7 @@ func (h *DryaderImpl) remove(j weles.JobID) {
// setStatus sets Jobs status to RUNNING and updates info.
func (h *DryaderImpl) setStatus(j weles.JobID, msg string) {
- err := h.jobs.SetStatusAndInfo(j, weles.JOB_RUNNING, msg)
+ err := h.jobs.SetStatusAndInfo(j, weles.JobStatusRUNNING, msg)
if err != nil {
h.remove(j)
h.SendFail(j, fmt.Sprintf("Internal Weles error while changing Job status : %s", err.Error()))
diff --git a/controller/dryaderimpl_test.go b/controller/dryaderimpl_test.go
index 4e11997..deac3cf 100644
--- a/controller/dryaderimpl_test.go
+++ b/controller/dryaderimpl_test.go
@@ -162,7 +162,7 @@ var _ = Describe("DryaderImpl", func() {
It("should update status of the Job", func() {
for i, s := range updateStates {
change := weles.DryadJobInfo{Job: j, Status: s}
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_RUNNING, updateMsgs[i])
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusRUNNING, updateMsgs[i])
h.(*DryaderImpl).listener <- weles.DryadJobStatusChange(change)
@@ -179,7 +179,7 @@ var _ = Describe("DryaderImpl", func() {
DescribeTable("should fail if updating status of the Job fails",
func(s weles.DryadJobStatus, msg string) {
change := weles.DryadJobInfo{Job: j, Status: s}
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_RUNNING, msg).Return(err)
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusRUNNING, msg).Return(err)
h.(*DryaderImpl).listener <- weles.DryadJobStatusChange(change)
diff --git a/controller/jobscontroller.go b/controller/jobscontroller.go
index 1c309cb..ec8d991 100644
--- a/controller/jobscontroller.go
+++ b/controller/jobscontroller.go
@@ -50,8 +50,11 @@ type JobsController interface {
SetDryad(weles.JobID, weles.Dryad) error
// GetDryad returns Dryad acquired for the Job.
GetDryad(weles.JobID) (weles.Dryad, error)
- // List returns information about Jobs. If argument is a nil/empty slice
- // information about all Jobs is returned. Otherwise result is filtered
- // and contains information about requested Jobs only.
- List([]weles.JobID) ([]weles.JobInfo, error)
+ // List returns information on Jobs. It takes 3 arguments:
+ // - JobFilter containing filters
+ // - JobSorter containing sorting key and sorting direction
+ // - JobPagination containing element after/before which a page should be returned. It also
+ // contains information about direction of pagination and the size of the returned page which
+ // must always be set.
+ List(filter weles.JobFilter, sorter weles.JobSorter, paginator weles.JobPagination) ([]weles.JobInfo, weles.ListInfo, error)
}
diff --git a/controller/jobscontrollerimpl.go b/controller/jobscontrollerimpl.go
index b7cd51c..ca7a623 100644
--- a/controller/jobscontrollerimpl.go
+++ b/controller/jobscontrollerimpl.go
@@ -20,9 +20,15 @@
package controller
import (
+ "fmt"
+ "regexp"
+ "sort"
+ "strings"
"sync"
"time"
+ "github.com/go-openapi/strfmt"
+
"git.tizen.org/tools/weles"
)
@@ -80,13 +86,13 @@ func (js *JobsControllerImpl) NewJob(yaml []byte) (weles.JobID, error) {
j := js.nextID()
- now := time.Now()
+ now := strfmt.DateTime(time.Now())
js.jobs[j] = &Job{
JobInfo: weles.JobInfo{
JobID: j,
Created: now,
Updated: now,
- Status: weles.JOB_NEW,
+ Status: weles.JobStatusNEW,
},
yaml: yaml,
}
@@ -120,7 +126,7 @@ func (js *JobsControllerImpl) SetConfig(j weles.JobID, conf weles.Config) error
}
job.config = conf
- job.Updated = time.Now()
+ job.Updated = strfmt.DateTime(time.Now())
return nil
}
@@ -131,29 +137,29 @@ func isStatusChangeValid(oldStatus, newStatus weles.JobStatus) bool {
return true
}
switch oldStatus {
- case weles.JOB_NEW:
+ case weles.JobStatusNEW:
switch newStatus {
- case weles.JOB_PARSING, weles.JOB_CANCELED, weles.JOB_FAILED:
+ case weles.JobStatusPARSING, weles.JobStatusCANCELED, weles.JobStatusFAILED:
return true
}
- case weles.JOB_PARSING:
+ case weles.JobStatusPARSING:
switch newStatus {
- case weles.JOB_DOWNLOADING, weles.JOB_CANCELED, weles.JOB_FAILED:
+ case weles.JobStatusDOWNLOADING, weles.JobStatusCANCELED, weles.JobStatusFAILED:
return true
}
- case weles.JOB_DOWNLOADING:
+ case weles.JobStatusDOWNLOADING:
switch newStatus {
- case weles.JOB_WAITING, weles.JOB_CANCELED, weles.JOB_FAILED:
+ case weles.JobStatusWAITING, weles.JobStatusCANCELED, weles.JobStatusFAILED:
return true
}
- case weles.JOB_WAITING:
+ case weles.JobStatusWAITING:
switch newStatus {
- case weles.JOB_RUNNING, weles.JOB_CANCELED, weles.JOB_FAILED:
+ case weles.JobStatusRUNNING, weles.JobStatusCANCELED, weles.JobStatusFAILED:
return true
}
- case weles.JOB_RUNNING:
+ case weles.JobStatusRUNNING:
switch newStatus {
- case weles.JOB_COMPLETED, weles.JOB_CANCELED, weles.JOB_FAILED:
+ case weles.JobStatusCOMPLETED, weles.JobStatusCANCELED, weles.JobStatusFAILED:
return true
}
}
@@ -162,15 +168,15 @@ func isStatusChangeValid(oldStatus, newStatus weles.JobStatus) bool {
// SetStatusAndInfo changes status of the Job and updates info. Only valid
// changes are allowed.
-// There are 3 terminal statuses: JOB_FAILED, JOB_CANCELED, JOB_COMPLETED;
-// and 5 non-terminal statuses: JOB_NEW, JOB_PARSING, JOB_DOWNLOADING,
-// JOB_WAITING, JOB_RUNNING.
+// There are 3 terminal statuses: JobStatusFAILED, JobStatusCANCELED, JobStatusCOMPLETED;
+// and 5 non-terminal statuses: JobStatusNEW, JobStatusPARSING, JobStatusDOWNLOADING,
+// JobStatusWAITING, JobStatusRUNNING.
// Only below changes of statuses are allowed:
-// * JOB_NEW --> {JOB_PARSING, JOB_CANCELED, JOB_FAILED}
-// * JOB_PARSING --> {JOB_DOWNLOADING, JOB_CANCELED, JOB_FAILED}
-// * JOB_DOWNLOADING --> {JOB_WAITING, JOB_CANCELED, JOB_FAILED}
-// * JOB_WAITING --> {JOB_RUNNING, JOB_CANCELED, JOB_FAILED}
-// * JOB_RUNNING --> {JOB_COMPLETED, JOB_CANCELED, JOB_FAILED}
+// * JobStatusNEW --> {JobStatusPARSING, JobStatusCANCELED, JobStatusFAILED}
+// * JobStatusPARSING --> {JobStatusDOWNLOADING, JobStatusCANCELED, JobStatusFAILED}
+// * JobStatusDOWNLOADING --> {JobStatusWAITING, JobStatusCANCELED, JobStatusFAILED}
+// * JobStatusWAITING --> {JobStatusRUNNING, JobStatusCANCELED, JobStatusFAILED}
+// * JobStatusRUNNING --> {JobStatusCOMPLETED, JobStatusCANCELED, JobStatusFAILED}
func (js *JobsControllerImpl) SetStatusAndInfo(j weles.JobID, newStatus weles.JobStatus, msg string) error {
js.mutex.Lock()
defer js.mutex.Unlock()
@@ -186,7 +192,7 @@ func (js *JobsControllerImpl) SetStatusAndInfo(j weles.JobID, newStatus weles.Jo
job.Status = newStatus
job.Info = msg
- job.Updated = time.Now()
+ job.Updated = strfmt.DateTime(time.Now())
return nil
}
@@ -230,26 +236,276 @@ func (js *JobsControllerImpl) GetDryad(j weles.JobID) (weles.Dryad, error) {
return job.dryad, nil
}
-// List returns information about Jobs. If argument is a nil/empty slice
-// information about all Jobs is returned. Otherwise result is filtered
-// and contains information about requested Jobs only.
-func (js *JobsControllerImpl) List(filter []weles.JobID) ([]weles.JobInfo, error) {
+// List returns information on Jobs. It takes 3 arguments:
+// - JobFilter containing filters
+// - JobSorter containing sorting key and sorting direction
+// - JobPagination containing element after/before which a page should be returned. It also
+// contains information about direction of listing and the size of the returned page which
+// must always be set.
+func (js *JobsControllerImpl) List(filter weles.JobFilter, sorter weles.JobSorter,
+ paginator weles.JobPagination) ([]weles.JobInfo, weles.ListInfo, error) {
+
js.mutex.RLock()
defer js.mutex.RUnlock()
ret := make([]weles.JobInfo, 0, len(js.jobs))
- if len(filter) == 0 {
- // Get all Jobs.
- for _, job := range js.jobs {
+
+ f, err := prepareFilter(&filter)
+ if err != nil {
+ return nil, weles.ListInfo{}, err
+ }
+
+ // Filter jobs.
+ for _, job := range js.jobs {
+ if job.passesFilter(f) || (paginator.Limit != 0 && job.JobID == paginator.JobID) {
ret = append(ret, job.JobInfo)
}
+ }
+ // Sort jobs.
+ ps := &jobSorter{
+ jobs: ret,
+ by: byJobIDAsc,
+ }
+ switch sorter.SortBy {
+ case weles.JobSortByCreatedDate:
+ ps.setByFunction(sorter.SortOrder, byCreatedAsc, byCreatedDesc)
+ case weles.JobSortByUpdatedDate:
+ ps.setByFunction(sorter.SortOrder, byUpdatedAsc, byUpdatedDesc)
+ case weles.JobSortByJobStatus:
+ ps.setByFunction(sorter.SortOrder, byStatusAsc, byStatusDesc)
+ }
+ sort.Sort(ps)
+
+ // Pagination.
+ var index, elems, total, left int
+ if paginator.Limit == 0 {
+ // Pagination is disabled. Return all records.
+ total = len(ret)
+ elems = total
+ left = 0
+ } else if paginator.JobID == weles.JobID(0) {
+ total = len(ret)
+ elems = min(int(paginator.Limit), total)
+ left = total - elems
} else {
- // Get filtered Jobs.
- for _, j := range filter {
- job, ok := js.jobs[j]
- if ok {
- ret = append(ret, job.JobInfo)
+ index = -1
+ for i, job := range ret {
+ if job.JobID == paginator.JobID {
+ index = i
+ break
}
}
+ if index == -1 {
+ return nil, weles.ListInfo{}, weles.ErrInvalidArgument(fmt.Sprintf("JobID: %d not found", paginator.JobID))
+ }
+ if paginator.Forward {
+ index++
+ total = len(ret)
+ elems = min(int(paginator.Limit), total-index)
+ left = total - index - elems
+ } else {
+ total = len(ret)
+ elems = min(int(paginator.Limit), index)
+ left = index - elems
+ index -= elems
+ }
+ if !js.jobs[paginator.JobID].passesFilter(f) {
+ total--
+ }
+ }
+ info := weles.ListInfo{TotalRecords: uint64(total), RemainingRecords: uint64(left)}
+ return ret[index : index+elems], info, nil
+}
+
+func min(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+type filter struct {
+ CreatedAfter time.Time
+ CreatedBefore time.Time
+ UpdatedAfter time.Time
+ UpdatedBefore time.Time
+ Info *regexp.Regexp
+ JobID map[weles.JobID]interface{}
+ Name *regexp.Regexp
+ Status map[weles.JobStatus]interface{}
+}
+
+func prepareFilterRegexp(arr []string) (*regexp.Regexp, error) {
+ if len(arr) == 0 {
+ return nil, nil
+ }
+
+ var size int
+ for _, s := range arr {
+ size += 3 + len(s)
+ }
+
+ var str strings.Builder
+ str.Grow(size)
+ for _, s := range arr {
+ str.WriteString("|(" + s + ")")
+ }
+
+ return regexp.Compile(str.String()[1:])
+}
+
+func prepareFilter(in *weles.JobFilter) (out *filter, err error) {
+ var regErr error
+
+ out = new(filter)
+
+ out.CreatedAfter = time.Time(in.CreatedAfter)
+ out.CreatedBefore = time.Time(in.CreatedBefore)
+ out.Info, regErr = prepareFilterRegexp(in.Info)
+ if regErr != nil {
+ return nil, weles.ErrInvalidArgument("cannot compile regex from Info: " + regErr.Error())
+ }
+ if len(in.JobID) > 0 {
+ out.JobID = make(map[weles.JobID]interface{})
+ for _, x := range in.JobID {
+ out.JobID[x] = nil
+ }
+ }
+ out.Name, regErr = prepareFilterRegexp(in.Name)
+ if regErr != nil {
+ return nil, weles.ErrInvalidArgument("cannot compile regex from Name: " + regErr.Error())
+ }
+ if len(in.Status) > 0 {
+ out.Status = make(map[weles.JobStatus]interface{})
+ for _, x := range in.Status {
+ out.Status[x] = nil
+ }
+ }
+ out.UpdatedAfter = time.Time(in.UpdatedAfter)
+ out.UpdatedBefore = time.Time(in.UpdatedBefore)
+
+ return out, nil
+}
+
+func (job *Job) passesFilter(f *filter) bool {
+ if !f.CreatedAfter.IsZero() {
+ if !time.Time(job.JobInfo.Created).After(f.CreatedAfter) {
+ return false
+ }
+ }
+ if !f.CreatedBefore.IsZero() {
+ if !time.Time(job.JobInfo.Created).Before(f.CreatedBefore) {
+ return false
+ }
+ }
+ if !f.UpdatedAfter.IsZero() {
+ if !time.Time(job.JobInfo.Updated).After(f.UpdatedAfter) {
+ return false
+ }
+ }
+ if !f.UpdatedBefore.IsZero() {
+ if !time.Time(job.JobInfo.Updated).Before(f.UpdatedBefore) {
+ return false
+ }
+ }
+ if f.Info != nil {
+ if !f.Info.MatchString(job.JobInfo.Info) {
+ return false
+ }
+ }
+ if f.JobID != nil {
+ _, present := f.JobID[job.JobInfo.JobID]
+ if !present {
+ return false
+ }
+ }
+ if f.Name != nil {
+ if !f.Name.MatchString(job.JobInfo.Name) {
+ return false
+ }
+ }
+ if f.Status != nil {
+ _, present := f.Status[job.JobInfo.Status]
+ if !present {
+ return false
+ }
+ }
+ return true
+}
+
+func byCreatedAsc(i1, i2 *weles.JobInfo) bool {
+ if time.Time(i1.Created).Equal(time.Time(i2.Created)) {
+ return byJobIDAsc(i1, i2)
+ }
+ return time.Time(i1.Created).Before(time.Time(i2.Created))
+}
+
+func byCreatedDesc(i1, i2 *weles.JobInfo) bool {
+ if time.Time(i1.Created).Equal(time.Time(i2.Created)) {
+ return byJobIDAsc(i1, i2)
+ }
+ return time.Time(i1.Created).After(time.Time(i2.Created))
+}
+
+func byUpdatedAsc(i1, i2 *weles.JobInfo) bool {
+ if time.Time(i1.Updated).Equal(time.Time(i2.Updated)) {
+ return byJobIDAsc(i1, i2)
+ }
+ return time.Time(i1.Updated).Before(time.Time(i2.Updated))
+}
+
+func byUpdatedDesc(i1, i2 *weles.JobInfo) bool {
+ if time.Time(i1.Updated).Equal(time.Time(i2.Updated)) {
+ return byJobIDAsc(i1, i2)
+ }
+ return time.Time(i1.Updated).After(time.Time(i2.Updated))
+}
+
+func byStatusAsc(i1, i2 *weles.JobInfo) bool {
+ if i1.Status.ToInt() == i2.Status.ToInt() {
+ return byJobIDAsc(i1, i2)
+ }
+ return i1.Status.ToInt() < i2.Status.ToInt()
+}
+
+func byStatusDesc(i1, i2 *weles.JobInfo) bool {
+ if i1.Status.ToInt() == i2.Status.ToInt() {
+ return byJobIDAsc(i1, i2)
+ }
+ return i1.Status.ToInt() > i2.Status.ToInt()
+}
+
+func byJobIDAsc(i1, i2 *weles.JobInfo) bool {
+ return i1.JobID < i2.JobID
+}
+
+type jobSorter struct {
+ jobs []weles.JobInfo
+ by func(i1, i2 *weles.JobInfo) bool
+}
+
+// Len is part of sort.Interface.
+func (s *jobSorter) Len() int {
+ return len(s.jobs)
+}
+
+// Swap is part of sort.Interface.
+func (s *jobSorter) Swap(i, j int) {
+ s.jobs[i], s.jobs[j] = s.jobs[j], s.jobs[i]
+}
+
+// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
+func (s *jobSorter) Less(i, j int) bool {
+ return s.by(&s.jobs[i], &s.jobs[j])
+}
+
+// by is the type of a "less" function that defines the ordering of its JobInfo arguments.
+type by func(p1, p2 *weles.JobInfo) bool
+
+func (s *jobSorter) setByFunction(order weles.SortOrder, asc, desc by) {
+ switch order {
+ case weles.SortOrderAscending:
+ s.by = asc
+ case weles.SortOrderDescending:
+ s.by = desc
}
- return ret, nil
}
diff --git a/controller/jobscontrollerimpl_test.go b/controller/jobscontrollerimpl_test.go
index 8598f4a..9f3f841 100644
--- a/controller/jobscontrollerimpl_test.go
+++ b/controller/jobscontrollerimpl_test.go
@@ -21,9 +21,13 @@ import (
"net"
"time"
- "git.tizen.org/tools/weles"
. "github.com/onsi/ginkgo"
+ . "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega"
+
+ strfmt "github.com/go-openapi/strfmt"
+
+ "git.tizen.org/tools/weles"
)
var _ = Describe("JobsControllerImpl", func() {
@@ -43,249 +47,727 @@ var _ = Describe("JobsControllerImpl", func() {
})
Describe("With JobsController initialized", func() {
var jc JobsController
- var initID, j weles.JobID
+ var initID, invalidID weles.JobID
+
ipAddr := &net.IPNet{IP: net.IPv4(1, 2, 3, 4), Mask: net.IPv4Mask(5, 6, 7, 8)}
yaml := []byte("test yaml")
- var invalidID weles.JobID
BeforeEach(func() {
jc = NewJobsController()
initID = jc.(*JobsControllerImpl).lastID
-
- var err error
- j, err = jc.NewJob(yaml)
- Expect(err).NotTo(HaveOccurred())
- Expect(j).To(Equal(initID + 1))
-
invalidID = initID - 1
})
- Describe("NewJob", func() {
- It("should create new Job structure", func() {
- before := time.Now()
- j, err := jc.NewJob(yaml)
- after := time.Now()
+ Describe("With Job created", func() {
+ var j weles.JobID
+ BeforeEach(func() {
+ var err error
+ j, err = jc.NewJob(yaml)
Expect(err).NotTo(HaveOccurred())
- Expect(j).To(Equal(initID + 2))
+ Expect(j).To(Equal(initID + 1))
+ })
+ Describe("NewJob", func() {
+ It("should create new Job structure", func() {
+ before := time.Now()
+ j, err := jc.NewJob(yaml)
+ after := time.Now()
- Expect(jc.(*JobsControllerImpl).lastID).To(Equal(j))
- Expect(len(jc.(*JobsControllerImpl).jobs)).To(Equal(2))
+ Expect(err).NotTo(HaveOccurred())
+ Expect(j).To(Equal(initID + 2))
- job, ok := jc.(*JobsControllerImpl).jobs[j]
- Expect(ok).To(BeTrue())
- Expect(job.JobID).To(Equal(j))
- Expect(job.Created).To(Equal(job.Updated))
- Expect(job.Created).To(BeTemporally(">=", before))
- Expect(job.Created).To(BeTemporally("<=", after))
- Expect(job.Status).To(Equal(weles.JOB_NEW))
- Expect(job.yaml).To(Equal(yaml))
- })
- })
- Describe("GetYaml", func() {
- It("should return proper yaml for existing job", func() {
- retyaml, err := jc.GetYaml(j)
- Expect(err).NotTo(HaveOccurred())
- Expect(retyaml).To(Equal(yaml))
- })
- It("should return error for not existing job", func() {
- yaml, err := jc.GetYaml(invalidID)
- Expect(err).To(Equal(weles.ErrJobNotFound))
- Expect(yaml).To(BeZero())
+ Expect(jc.(*JobsControllerImpl).lastID).To(Equal(j))
+ Expect(len(jc.(*JobsControllerImpl).jobs)).To(Equal(2))
+
+ job, ok := jc.(*JobsControllerImpl).jobs[j]
+ Expect(ok).To(BeTrue())
+ Expect(job.JobID).To(Equal(j))
+ Expect(job.Created).To(Equal(job.Updated))
+ Expect(time.Time(job.Created)).To(BeTemporally(">=", before))
+ Expect(time.Time(job.Created)).To(BeTemporally("<=", after))
+ Expect(job.Status).To(Equal(weles.JobStatusNEW))
+ Expect(job.yaml).To(Equal(yaml))
+ })
})
- })
- Describe("SetStatus", func() {
- allStatus := []weles.JobStatus{
- weles.JOB_NEW,
- weles.JOB_PARSING,
- weles.JOB_DOWNLOADING,
- weles.JOB_WAITING,
- weles.JOB_RUNNING,
- weles.JOB_FAILED,
- weles.JOB_CANCELED,
- weles.JOB_COMPLETED,
- }
- validChanges := map[weles.JobStatus](map[weles.JobStatus]bool){
- weles.JOB_NEW: map[weles.JobStatus]bool{
- weles.JOB_NEW: true,
- weles.JOB_PARSING: true,
- weles.JOB_FAILED: true,
- weles.JOB_CANCELED: true,
- },
- weles.JOB_PARSING: map[weles.JobStatus]bool{
- weles.JOB_PARSING: true,
- weles.JOB_DOWNLOADING: true,
- weles.JOB_FAILED: true,
- weles.JOB_CANCELED: true,
- },
- weles.JOB_DOWNLOADING: map[weles.JobStatus]bool{
- weles.JOB_DOWNLOADING: true,
- weles.JOB_WAITING: true,
- weles.JOB_FAILED: true,
- weles.JOB_CANCELED: true,
- },
- weles.JOB_WAITING: map[weles.JobStatus]bool{
- weles.JOB_WAITING: true,
- weles.JOB_RUNNING: true,
- weles.JOB_FAILED: true,
- weles.JOB_CANCELED: true,
- },
- weles.JOB_RUNNING: map[weles.JobStatus]bool{
- weles.JOB_RUNNING: true,
- weles.JOB_FAILED: true,
- weles.JOB_CANCELED: true,
- weles.JOB_COMPLETED: true,
- },
- weles.JOB_FAILED: map[weles.JobStatus]bool{
- weles.JOB_FAILED: true,
- },
- weles.JOB_CANCELED: map[weles.JobStatus]bool{
- weles.JOB_CANCELED: true,
- },
- weles.JOB_COMPLETED: map[weles.JobStatus]bool{
- weles.JOB_COMPLETED: true,
- },
- }
- It("should return error for not existing job", func() {
- for _, status := range allStatus {
- err := jc.SetStatusAndInfo(invalidID, status, "test info")
+ Describe("GetYaml", func() {
+ It("should return proper yaml for existing job", func() {
+ retyaml, err := jc.GetYaml(j)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(retyaml).To(Equal(yaml))
+ })
+ It("should return error for not existing job", func() {
+ yaml, err := jc.GetYaml(invalidID)
Expect(err).To(Equal(weles.ErrJobNotFound))
- }
+ Expect(yaml).To(BeZero())
+ })
})
- It("should work to change status only for valid transitions", func() {
- job := jc.(*JobsControllerImpl).jobs[j]
- for _, oldStatus := range allStatus {
- for _, newStatus := range allStatus {
- job.Status = oldStatus
- if _, ok := validChanges[oldStatus][newStatus]; !ok {
- info := fmt.Sprintf("failing to change from '%s' to '%s'", oldStatus, newStatus)
- By(info, func() {
- oldJob := *job
- err := jc.SetStatusAndInfo(j, newStatus, info)
- Expect(err).To(Equal(weles.ErrJobStatusChangeNotAllowed))
- Expect(job).To(Equal(&oldJob))
- })
- } else {
- info := fmt.Sprintf("changing from '%s' to '%s'", oldStatus, newStatus)
- oldUpdated := job.Updated
- By(info, func() {
- err := jc.SetStatusAndInfo(j, newStatus, info)
- Expect(err).NotTo(HaveOccurred())
- Expect(job.Status).To(Equal(newStatus))
- Expect(job.Info).To(Equal(info))
- Expect(job.Updated).To(BeTemporally(">=", oldUpdated))
- })
+ Describe("SetStatus", func() {
+ allStatus := []weles.JobStatus{
+ weles.JobStatusNEW,
+ weles.JobStatusPARSING,
+ weles.JobStatusDOWNLOADING,
+ weles.JobStatusWAITING,
+ weles.JobStatusRUNNING,
+ weles.JobStatusFAILED,
+ weles.JobStatusCANCELED,
+ weles.JobStatusCOMPLETED,
+ }
+ validChanges := map[weles.JobStatus](map[weles.JobStatus]bool){
+ weles.JobStatusNEW: map[weles.JobStatus]bool{
+ weles.JobStatusNEW: true,
+ weles.JobStatusPARSING: true,
+ weles.JobStatusFAILED: true,
+ weles.JobStatusCANCELED: true,
+ },
+ weles.JobStatusPARSING: map[weles.JobStatus]bool{
+ weles.JobStatusPARSING: true,
+ weles.JobStatusDOWNLOADING: true,
+ weles.JobStatusFAILED: true,
+ weles.JobStatusCANCELED: true,
+ },
+ weles.JobStatusDOWNLOADING: map[weles.JobStatus]bool{
+ weles.JobStatusDOWNLOADING: true,
+ weles.JobStatusWAITING: true,
+ weles.JobStatusFAILED: true,
+ weles.JobStatusCANCELED: true,
+ },
+ weles.JobStatusWAITING: map[weles.JobStatus]bool{
+ weles.JobStatusWAITING: true,
+ weles.JobStatusRUNNING: true,
+ weles.JobStatusFAILED: true,
+ weles.JobStatusCANCELED: true,
+ },
+ weles.JobStatusRUNNING: map[weles.JobStatus]bool{
+ weles.JobStatusRUNNING: true,
+ weles.JobStatusFAILED: true,
+ weles.JobStatusCANCELED: true,
+ weles.JobStatusCOMPLETED: true,
+ },
+ weles.JobStatusFAILED: map[weles.JobStatus]bool{
+ weles.JobStatusFAILED: true,
+ },
+ weles.JobStatusCANCELED: map[weles.JobStatus]bool{
+ weles.JobStatusCANCELED: true,
+ },
+ weles.JobStatusCOMPLETED: map[weles.JobStatus]bool{
+ weles.JobStatusCOMPLETED: true,
+ },
+ }
+ It("should return error for not existing job", func() {
+ for _, status := range allStatus {
+ err := jc.SetStatusAndInfo(invalidID, status, "test info")
+ Expect(err).To(Equal(weles.ErrJobNotFound))
+ }
+ })
+ It("should work to change status only for valid transitions", func() {
+ job := jc.(*JobsControllerImpl).jobs[j]
+ for _, oldStatus := range allStatus {
+ for _, newStatus := range allStatus {
+ job.Status = oldStatus
+ if _, ok := validChanges[oldStatus][newStatus]; !ok {
+ info := fmt.Sprintf("failing to change from '%s' to '%s'", oldStatus, newStatus)
+ By(info, func() {
+ oldJob := *job
+ err := jc.SetStatusAndInfo(j, newStatus, info)
+ Expect(err).To(Equal(weles.ErrJobStatusChangeNotAllowed))
+ Expect(job).To(Equal(&oldJob))
+ })
+ } else {
+ info := fmt.Sprintf("changing from '%s' to '%s'", oldStatus, newStatus)
+ oldUpdated := job.Updated
+ By(info, func() {
+ err := jc.SetStatusAndInfo(j, newStatus, info)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(job.Status).To(Equal(newStatus))
+ Expect(job.Info).To(Equal(info))
+ Expect(time.Time(job.Updated)).To(BeTemporally(">=", time.Time(oldUpdated)))
+ })
+ }
}
}
- }
+ })
})
- })
- Describe("SetConfig", func() {
- It("should set config for existing job", func() {
- config := weles.Config{JobName: "Test Job"}
- before := time.Now()
- err := jc.SetConfig(j, config)
- after := time.Now()
- Expect(err).NotTo(HaveOccurred())
+ Describe("SetConfig", func() {
+ It("should set config for existing job", func() {
+ config := weles.Config{JobName: "Test Job"}
+ before := time.Now()
+ err := jc.SetConfig(j, config)
+ after := time.Now()
+ Expect(err).NotTo(HaveOccurred())
- Expect(jc.(*JobsControllerImpl).jobs[j].config).To(Equal(config))
- Expect(jc.(*JobsControllerImpl).jobs[j].Updated).To(BeTemporally(">=", before))
- Expect(jc.(*JobsControllerImpl).jobs[j].Updated).To(BeTemporally("<=", after))
- })
- It("should return error for not existing job", func() {
- config := weles.Config{JobName: "Test Job"}
- err := jc.SetConfig(invalidID, config)
- Expect(err).To(Equal(weles.ErrJobNotFound))
+ Expect(jc.(*JobsControllerImpl).jobs[j].config).To(Equal(config))
+ Expect(time.Time(jc.(*JobsControllerImpl).jobs[j].Updated)).To(BeTemporally(">=", before))
+ Expect(time.Time(jc.(*JobsControllerImpl).jobs[j].Updated)).To(BeTemporally("<=", after))
+ })
+ It("should return error for not existing job", func() {
+ config := weles.Config{JobName: "Test Job"}
+ err := jc.SetConfig(invalidID, config)
+ Expect(err).To(Equal(weles.ErrJobNotFound))
+ })
})
- })
- Describe("GetConfig", func() {
- It("should return proper config for existing job", func() {
- expectedConfig := weles.Config{JobName: "Test config"}
- err := jc.SetConfig(j, expectedConfig)
- Expect(err).NotTo(HaveOccurred())
+ Describe("GetConfig", func() {
+ It("should return proper config for existing job", func() {
+ expectedConfig := weles.Config{JobName: "Test config"}
+ err := jc.SetConfig(j, expectedConfig)
+ Expect(err).NotTo(HaveOccurred())
- config, err := jc.GetConfig(j)
- Expect(err).NotTo(HaveOccurred())
- Expect(config).To(Equal(expectedConfig))
- })
- It("should return error for not existing job", func() {
- config, err := jc.GetConfig(invalidID)
- Expect(err).To(Equal(weles.ErrJobNotFound))
- Expect(config).To(BeZero())
+ config, err := jc.GetConfig(j)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(config).To(Equal(expectedConfig))
+ })
+ It("should return error for not existing job", func() {
+ config, err := jc.GetConfig(invalidID)
+ Expect(err).To(Equal(weles.ErrJobNotFound))
+ Expect(config).To(BeZero())
+ })
})
- })
- Describe("SetDryad", func() {
- It("should set Dryad for existing job", func() {
- dryad := weles.Dryad{Addr: ipAddr}
- err := jc.SetDryad(j, dryad)
- Expect(err).NotTo(HaveOccurred())
+ Describe("SetDryad", func() {
+ It("should set Dryad for existing job", func() {
+ dryad := weles.Dryad{Addr: ipAddr}
+ err := jc.SetDryad(j, dryad)
+ Expect(err).NotTo(HaveOccurred())
- Expect(jc.(*JobsControllerImpl).jobs[j].dryad).To(Equal(dryad))
- })
- It("should return error for not existing job", func() {
- dryad := weles.Dryad{Addr: ipAddr}
- err := jc.SetDryad(invalidID, dryad)
- Expect(err).To(Equal(weles.ErrJobNotFound))
+ Expect(jc.(*JobsControllerImpl).jobs[j].dryad).To(Equal(dryad))
+ })
+ It("should return error for not existing job", func() {
+ dryad := weles.Dryad{Addr: ipAddr}
+ err := jc.SetDryad(invalidID, dryad)
+ Expect(err).To(Equal(weles.ErrJobNotFound))
+ })
})
- })
- Describe("GetDryad", func() {
- It("should return proper Dryad structure for existing job", func() {
- expectedDryad := weles.Dryad{Addr: ipAddr}
- err := jc.SetDryad(j, expectedDryad)
- Expect(err).NotTo(HaveOccurred())
+ Describe("GetDryad", func() {
+ It("should return proper Dryad structure for existing job", func() {
+ expectedDryad := weles.Dryad{Addr: ipAddr}
+ err := jc.SetDryad(j, expectedDryad)
+ Expect(err).NotTo(HaveOccurred())
- dryad, err := jc.GetDryad(j)
- Expect(err).NotTo(HaveOccurred())
- Expect(dryad).To(Equal(expectedDryad))
- })
- It("should return error for not existing job", func() {
- dryad, err := jc.GetDryad(invalidID)
- Expect(err).To(Equal(weles.ErrJobNotFound))
- Expect(dryad).To(BeZero())
+ dryad, err := jc.GetDryad(j)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(dryad).To(Equal(expectedDryad))
+ })
+ It("should return error for not existing job", func() {
+ dryad, err := jc.GetDryad(invalidID)
+ Expect(err).To(Equal(weles.ErrJobNotFound))
+ Expect(dryad).To(BeZero())
+ })
})
})
-
Describe("List", func() {
- var jobids []weles.JobID
- const elems int = 5
- expectIDs := func(result []weles.JobInfo, expected []weles.JobID) {
+ var elems int
+ magicDate := time.Now()
+ expectIDsFull := func(result []weles.JobInfo, info weles.ListInfo, expected []weles.JobID, total int, remaining int) {
Expect(len(result)).To(Equal(len(expected)))
for _, j := range expected {
Expect(result).To(ContainElement(WithTransform(func(info weles.JobInfo) weles.JobID {
return info.JobID
}, Equal(j))))
}
+ Expect(info.TotalRecords).To(Equal(uint64(total)))
+ Expect(info.RemainingRecords).To(Equal(uint64(remaining)))
}
- BeforeEach(func() {
- jobids = []weles.JobID{j}
- for i := 1; i <= elems; i++ {
- j, err := jc.NewJob(yaml)
+ expectIDs := func(result []weles.JobInfo, info weles.ListInfo, expected []weles.JobID) {
+ expectIDsFull(result, info, expected, len(expected), 0)
+ }
+ defaultPagination := weles.JobPagination{Limit: 100}
+ Describe("Filter", func() {
+ jobids := []weles.JobID{}
+ BeforeEach(func() {
+ elems = 5
+ jobids = []weles.JobID{}
+ for i := 1; i <= elems; i++ {
+ j, err := jc.NewJob(yaml)
+ Expect(err).NotTo(HaveOccurred())
+ jobids = append(jobids, j)
+ }
+ })
+ It("should return all Jobs", func() {
+ list, info, err := jc.List(weles.JobFilter{}, weles.JobSorter{}, defaultPagination)
Expect(err).NotTo(HaveOccurred())
- jobids = append(jobids, j)
- }
- })
- It("should return all Jobs if filter is nil", func() {
- list, err := jc.List(nil)
- Expect(err).NotTo(HaveOccurred())
- expectIDs(list, jobids)
- })
- It("should return all Jobs if filter is empty", func() {
- list, err := jc.List([]weles.JobID{})
- Expect(err).NotTo(HaveOccurred())
- expectIDs(list, jobids)
+ expectIDs(list, info, jobids)
+ })
+ Describe("Created", func() {
+ BeforeEach(func() {
+ jc.(*JobsControllerImpl).mutex.Lock()
+ defer jc.(*JobsControllerImpl).mutex.Unlock()
+ for i := 0; i < elems; i++ {
+ jc.(*JobsControllerImpl).jobs[jobids[i]].JobInfo.Created = strfmt.DateTime(magicDate.AddDate(i-(elems)/2, 0, 0))
+ }
+ })
+ It("should return only jobs created after magicDate", func() {
+ f := weles.JobFilter{CreatedAfter: strfmt.DateTime(magicDate), CreatedBefore: strfmt.DateTime{}}
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).NotTo(HaveOccurred())
+ expectIDs(list, info, jobids[elems/2+1:])
+ })
+ It("should return only jobs created before magicDate", func() {
+ f := weles.JobFilter{CreatedAfter: strfmt.DateTime{}, CreatedBefore: strfmt.DateTime(magicDate)}
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).NotTo(HaveOccurred())
+ expectIDs(list, info, jobids[:elems/2])
+ })
+ It("should return no jobs if created before and created after dates conflict", func() {
+ f := weles.JobFilter{CreatedAfter: strfmt.DateTime(magicDate), CreatedBefore: strfmt.DateTime(magicDate)}
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).NotTo(HaveOccurred())
+ expectIDs(list, info, []weles.JobID{})
+ })
+ })
+ Describe("Updated", func() {
+ BeforeEach(func() {
+ jc.(*JobsControllerImpl).mutex.Lock()
+ defer jc.(*JobsControllerImpl).mutex.Unlock()
+ for i := 0; i < elems; i++ {
+ jc.(*JobsControllerImpl).jobs[jobids[i]].JobInfo.Updated = strfmt.DateTime(magicDate.AddDate(i-(elems)/2, 0, 0))
+ }
+ })
+ It("should return only jobs updated after magicDate", func() {
+ f := weles.JobFilter{UpdatedAfter: strfmt.DateTime(magicDate), UpdatedBefore: strfmt.DateTime{}}
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).NotTo(HaveOccurred())
+ expectIDs(list, info, jobids[elems/2+1:])
+ })
+ It("should return only jobs updated before magicDate", func() {
+ f := weles.JobFilter{UpdatedAfter: strfmt.DateTime{}, UpdatedBefore: strfmt.DateTime(magicDate)}
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).NotTo(HaveOccurred())
+ expectIDs(list, info, jobids[:elems/2])
+ })
+ It("should return no jobs if updated before and updated after dates conflict", func() {
+ f := weles.JobFilter{UpdatedAfter: strfmt.DateTime(magicDate), UpdatedBefore: strfmt.DateTime(magicDate)}
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).NotTo(HaveOccurred())
+ expectIDs(list, info, []weles.JobID{})
+ })
+ })
+ Describe("Info", func() {
+ BeforeEach(func() {
+ jc.(*JobsControllerImpl).mutex.Lock()
+ defer jc.(*JobsControllerImpl).mutex.Unlock()
+ jc.(*JobsControllerImpl).jobs[jobids[0]].JobInfo.Info = "Lumberjack"
+ jc.(*JobsControllerImpl).jobs[jobids[1]].JobInfo.Info = "I cut down trees, I wear high heels"
+ jc.(*JobsControllerImpl).jobs[jobids[2]].JobInfo.Info = "Suspenders and a bra"
+ jc.(*JobsControllerImpl).jobs[jobids[3]].JobInfo.Info = "I wish I'd been a girlie"
+ jc.(*JobsControllerImpl).jobs[jobids[4]].JobInfo.Info = "Just like my dear papa."
+ })
+ It("should return only jobs containing given substing in Info", func() {
+ f := weles.JobFilter{Info: []string{"ear"}} // ear matches "wear" (line 1) and "dear" (line 4).
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).NotTo(HaveOccurred())
+ expectIDs(list, info, []weles.JobID{jobids[1], jobids[4]})
+ })
+ It("should return only jobs containing any substing in Info", func() {
+ f := weles.JobFilter{Info: []string{"ear", "I"}} // ear matches "wear" (line 1) and "dear" (line 4), "I" matches lines 1 and 3.
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).NotTo(HaveOccurred())
+ expectIDs(list, info, []weles.JobID{jobids[1], jobids[3], jobids[4]})
+ })
+ It("should return only jobs matching pattern", func() {
+ f := weles.JobFilter{Info: []string{"a .*e"}} // matches "a girlie" (line 3).
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).NotTo(HaveOccurred())
+ expectIDs(list, info, []weles.JobID{jobids[3]})
+ })
+ It("should return only jobs matching any pattern", func() {
+ f := weles.JobFilter{Info: []string{"a .*e", "k$"}} // "a .*e" matches "a girlie" (line 3), "k$" matches "Lumberjack" (line 0).
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).NotTo(HaveOccurred())
+ expectIDs(list, info, []weles.JobID{jobids[0], jobids[3]})
+ })
+ It("should return error if Info regexp is invalid", func() {
+ f := weles.JobFilter{Info: []string{"[$$$*"}}
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).To(Equal(weles.ErrInvalidArgument("cannot compile regex from Info: error parsing regexp: missing closing ]: `[$$$*)`")))
+ Expect(list).To(BeNil())
+ Expect(info).To(BeZero())
+ })
+ })
+ Describe("JobID", func() {
+ It("should return only jobs matching JobIDs", func() {
+ f := weles.JobFilter{JobID: []weles.JobID{jobids[0], jobids[2], jobids[4]}}
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).NotTo(HaveOccurred())
+ expectIDs(list, info, []weles.JobID{jobids[0], jobids[2], jobids[4]})
+ })
+ It("should ignore not existing JobIDs", func() {
+ f := weles.JobFilter{JobID: []weles.JobID{jobids[1], invalidID}}
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).NotTo(HaveOccurred())
+ expectIDs(list, info, []weles.JobID{jobids[1]})
+ })
+ It("should return all jobs if JobIDs slice is empty", func() {
+ f := weles.JobFilter{JobID: []weles.JobID{}}
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).NotTo(HaveOccurred())
+ expectIDs(list, info, jobids)
+ })
+ })
+ Describe("Name", func() {
+ BeforeEach(func() {
+ jc.(*JobsControllerImpl).mutex.Lock()
+ defer jc.(*JobsControllerImpl).mutex.Unlock()
+ jc.(*JobsControllerImpl).jobs[jobids[0]].JobInfo.Name = "Lumberjack"
+ jc.(*JobsControllerImpl).jobs[jobids[1]].JobInfo.Name = "I cut down trees, I wear high heels"
+ jc.(*JobsControllerImpl).jobs[jobids[2]].JobInfo.Name = "Suspenders and a bra"
+ jc.(*JobsControllerImpl).jobs[jobids[3]].JobInfo.Name = "I wish I'd been a girlie"
+ jc.(*JobsControllerImpl).jobs[jobids[4]].JobInfo.Name = "Just like my dear papa."
+ })
+ It("should return only jobs containing given substing in Name", func() {
+ f := weles.JobFilter{Name: []string{"ear"}} // ear matches "wear" (line 1) and "dear" (line 4).
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).NotTo(HaveOccurred())
+ expectIDs(list, info, []weles.JobID{jobids[1], jobids[4]})
+ })
+ It("should return only jobs containing any substing in Name", func() {
+ f := weles.JobFilter{Name: []string{"ear", "I"}} // ear matches "wear" (line 1) and "dear" (line 4), "I" matches lines 1 and 3.
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).NotTo(HaveOccurred())
+ expectIDs(list, info, []weles.JobID{jobids[1], jobids[3], jobids[4]})
+ })
+ It("should return only jobs matching pattern", func() {
+ f := weles.JobFilter{Name: []string{"a .*e"}} // matches "a girlie" (line 3).
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).NotTo(HaveOccurred())
+ expectIDs(list, info, []weles.JobID{jobids[3]})
+ })
+ It("should return only jobs matching any pattern", func() {
+ f := weles.JobFilter{Name: []string{"a .*e", "k$"}} // "a .*e" matches "a girlie" (line 3), "k$" matches "Lumberjack" (line 0).
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).NotTo(HaveOccurred())
+ expectIDs(list, info, []weles.JobID{jobids[0], jobids[3]})
+ })
+ It("should return error if Name regexp is invalid", func() {
+ f := weles.JobFilter{Name: []string{"[$$$*"}}
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).To(Equal(weles.ErrInvalidArgument("cannot compile regex from Name: error parsing regexp: missing closing ]: `[$$$*)`")))
+ Expect(list).To(BeNil())
+ Expect(info).To(BeZero())
+ })
+ })
+ Describe("Status", func() {
+ BeforeEach(func() {
+ jc.(*JobsControllerImpl).mutex.Lock()
+ defer jc.(*JobsControllerImpl).mutex.Unlock()
+ jc.(*JobsControllerImpl).jobs[jobids[0]].JobInfo.Status = weles.JobStatusNEW
+ jc.(*JobsControllerImpl).jobs[jobids[1]].JobInfo.Status = weles.JobStatusPARSING
+ jc.(*JobsControllerImpl).jobs[jobids[2]].JobInfo.Status = weles.JobStatusDOWNLOADING
+ jc.(*JobsControllerImpl).jobs[jobids[3]].JobInfo.Status = weles.JobStatusWAITING
+ jc.(*JobsControllerImpl).jobs[jobids[4]].JobInfo.Status = weles.JobStatusWAITING
+ })
+ It("should return all jobs if Status slice is empty", func() {
+ f := weles.JobFilter{Status: []weles.JobStatus{}}
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).NotTo(HaveOccurred())
+ expectIDs(list, info, jobids)
+ })
+ It("should return only jobs matching Status", func() {
+ f := weles.JobFilter{Status: []weles.JobStatus{weles.JobStatusWAITING}}
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).NotTo(HaveOccurred())
+ expectIDs(list, info, []weles.JobID{jobids[3], jobids[4]})
+ })
+ It("should return only jobs matching any Status", func() {
+ f := weles.JobFilter{Status: []weles.JobStatus{weles.JobStatusPARSING, weles.JobStatusDOWNLOADING}}
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).NotTo(HaveOccurred())
+ expectIDs(list, info, []weles.JobID{jobids[1], jobids[2]})
+ })
+ It("should ignore not set Status", func() {
+ f := weles.JobFilter{Status: []weles.JobStatus{weles.JobStatusNEW, weles.JobStatus("ThereIsNoSuchStatus")}}
+ list, info, err := jc.List(f, weles.JobSorter{}, defaultPagination)
+ Expect(err).NotTo(HaveOccurred())
+ expectIDs(list, info, []weles.JobID{jobids[0]})
+ })
+ })
})
- It("should return only filtered Jobs", func() {
- filter := []weles.JobID{jobids[1], jobids[2]}
- list, err := jc.List(filter)
- Expect(err).NotTo(HaveOccurred())
- expectIDs(list, filter)
+ Describe("Sorter", func() {
+ jobids := []weles.JobID{}
+ checkOrder := func(result []weles.JobInfo, info weles.ListInfo, expected []weles.JobID, order []int) {
+ Expect(len(result)).To(Equal(len(expected)))
+ for i := range result {
+ ExpectWithOffset(1, result[i].JobID).To(Equal(expected[order[i]]))
+ }
+ ExpectWithOffset(1, info.TotalRecords).To(Equal(uint64(len(expected))))
+ ExpectWithOffset(1, info.RemainingRecords).To(BeZero())
+ }
+ BeforeEach(func() {
+ jobids = []weles.JobID{}
+ elems = 10
+ for i := 1; i <= elems; i++ {
+ j, err := jc.NewJob(yaml)
+ Expect(err).NotTo(HaveOccurred())
+ jobids = append(jobids, j)
+ }
+ // Manipulate data so the sort effect will be visible.
+ jc.(*JobsControllerImpl).mutex.Lock()
+ defer jc.(*JobsControllerImpl).mutex.Unlock()
+ jc.(*JobsControllerImpl).jobs[jobids[0]].JobInfo = weles.JobInfo{
+ JobID: jobids[0],
+ Created: strfmt.DateTime(magicDate.AddDate(5, 0, 0)),
+ Updated: strfmt.DateTime(magicDate.AddDate(3, 0, 0)),
+ Status: weles.JobStatusNEW,
+ }
+ jc.(*JobsControllerImpl).jobs[jobids[1]].JobInfo = weles.JobInfo{
+ JobID: jobids[1],
+ Created: strfmt.DateTime(magicDate.AddDate(4, 0, 0)),
+ Updated: strfmt.DateTime(magicDate.AddDate(1, 0, 0)),
+ Status: weles.JobStatusWAITING,
+ }
+ jc.(*JobsControllerImpl).jobs[jobids[2]].JobInfo = weles.JobInfo{
+ JobID: jobids[2],
+ Created: strfmt.DateTime(magicDate.AddDate(2, 0, 0)),
+ Updated: strfmt.DateTime(magicDate.AddDate(2, 0, 0)),
+ Status: weles.JobStatusCANCELED,
+ }
+ jc.(*JobsControllerImpl).jobs[jobids[3]].JobInfo = weles.JobInfo{
+ JobID: jobids[3],
+ Created: strfmt.DateTime(magicDate.AddDate(3, 0, 0)),
+ Updated: strfmt.DateTime(magicDate.AddDate(4, 0, 0)),
+ Status: weles.JobStatusPARSING,
+ }
+ jc.(*JobsControllerImpl).jobs[jobids[4]].JobInfo = weles.JobInfo{
+ JobID: jobids[4],
+ Created: strfmt.DateTime(magicDate.AddDate(1, 0, 0)),
+ Updated: strfmt.DateTime(magicDate.AddDate(5, 0, 0)),
+ Status: weles.JobStatusDOWNLOADING,
+ }
+
+ jc.(*JobsControllerImpl).jobs[jobids[5]].JobInfo = weles.JobInfo{
+ JobID: jobids[5],
+ Created: strfmt.DateTime(magicDate.AddDate(6, 0, 0)),
+ Updated: strfmt.DateTime(magicDate.AddDate(6, 0, 0)),
+ Status: weles.JobStatusRUNNING,
+ }
+ jc.(*JobsControllerImpl).jobs[jobids[6]].JobInfo = weles.JobInfo{
+ JobID: jobids[6],
+ Created: strfmt.DateTime(magicDate.AddDate(6, 1, 0)),
+ Updated: strfmt.DateTime(magicDate.AddDate(6, 1, 0)),
+ Status: weles.JobStatusFAILED,
+ }
+ jc.(*JobsControllerImpl).jobs[jobids[7]].JobInfo = weles.JobInfo{
+ JobID: jobids[7],
+ Created: strfmt.DateTime(magicDate.AddDate(6, 2, 0)),
+ Updated: strfmt.DateTime(magicDate.AddDate(6, 2, 0)),
+ Status: weles.JobStatusCOMPLETED,
+ }
+ jc.(*JobsControllerImpl).jobs[jobids[8]].JobInfo = weles.JobInfo{
+ JobID: jobids[8],
+ Created: strfmt.DateTime(magicDate.AddDate(6, 3, 0)),
+ Updated: strfmt.DateTime(magicDate.AddDate(6, 3, 0)),
+ Status: weles.JobStatus("InvalidJobStatus"),
+ }
+ jc.(*JobsControllerImpl).jobs[jobids[9]].JobInfo = weles.JobInfo{
+ JobID: jobids[9],
+ Created: strfmt.DateTime(magicDate.AddDate(1, 0, 0)),
+ Updated: strfmt.DateTime(magicDate.AddDate(5, 0, 0)),
+ Status: weles.JobStatusDOWNLOADING,
+ }
+ })
+ DescribeTable("sorter",
+ func(s weles.JobSorter, order []int) {
+ list, info, err := jc.List(weles.JobFilter{}, s, defaultPagination)
+ Expect(err).NotTo(HaveOccurred())
+ checkOrder(list, info, jobids, order)
+ },
+ Entry("should sort by JobID if Sorter is empty",
+ weles.JobSorter{},
+ []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}),
+ Entry("should sort by JobID if SortBy is invalid",
+ weles.JobSorter{SortBy: weles.JobSortBy("InvalidSortBy")},
+ []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}),
+ Entry("should sort by JobID if SortBy is CreatedDate and SortOrder is invalid ",
+ weles.JobSorter{SortBy: weles.JobSortByCreatedDate, SortOrder: weles.SortOrder("InvalidSortOrder")},
+ []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}),
+ Entry("should sort by JobID if SortBy is UpdatedDate and SortOrder is invalid ",
+ weles.JobSorter{SortBy: weles.JobSortByUpdatedDate, SortOrder: weles.SortOrder("InvalidSortOrder")},
+ []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}),
+ Entry("should sort by JobID if SortBy is Status and SortOrder is invalid ",
+ weles.JobSorter{SortBy: weles.JobSortByJobStatus, SortOrder: weles.SortOrder("InvalidSortOrder")},
+ []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}),
+ Entry("should sort by CreatedAsc",
+ weles.JobSorter{SortBy: weles.JobSortByCreatedDate, SortOrder: weles.SortOrderAscending},
+ []int{4, 9, 2, 3, 1, 0, 5, 6, 7, 8}),
+ Entry("should sort by CreatedDesc",
+ weles.JobSorter{SortBy: weles.JobSortByCreatedDate, SortOrder: weles.SortOrderDescending},
+ []int{8, 7, 6, 5, 0, 1, 3, 2, 4, 9}),
+ Entry("should sort by UpdatedAsc",
+ weles.JobSorter{SortBy: weles.JobSortByUpdatedDate, SortOrder: weles.SortOrderAscending},
+ []int{1, 2, 0, 3, 4, 9, 5, 6, 7, 8}),
+ Entry("should sort by UpdatesDesc",
+ weles.JobSorter{SortBy: weles.JobSortByUpdatedDate, SortOrder: weles.SortOrderDescending},
+ []int{8, 7, 6, 5, 4, 9, 3, 0, 2, 1}),
+ Entry("should sort by StatusAsc",
+ weles.JobSorter{SortBy: weles.JobSortByJobStatus, SortOrder: weles.SortOrderAscending},
+ []int{8, 0, 3, 4, 9, 1, 5, 7, 6, 2}),
+ Entry("should sort by StatusDesc",
+ weles.JobSorter{SortBy: weles.JobSortByJobStatus, SortOrder: weles.SortOrderDescending},
+ []int{2, 6, 7, 5, 1, 4, 9, 3, 0, 8}),
+ )
})
- It("should ignore not existing Jobs listed in filter", func() {
- filter := []weles.JobID{jobids[1], jobids[2]}
- list, err := jc.List(append(filter, weles.JobID(0x0BCA)))
- Expect(err).NotTo(HaveOccurred())
- expectIDs(list, filter)
+ Describe("Paginator", func() {
+ jobids := []weles.JobID{}
+ evenjobids := []weles.JobID{}
+ elems = 10
+ for i := 0; i < elems; i++ {
+ j := weles.JobID(i + 100)
+ jobids = append(jobids, j)
+ if i%2 == 0 {
+ evenjobids = append(evenjobids, j)
+ }
+ }
+ evenFilter := weles.JobFilter{JobID: evenjobids}
+ singleFilter := weles.JobFilter{JobID: []weles.JobID{jobids[3]}}
+ emptyFilter := weles.JobFilter{JobID: []weles.JobID{invalidID}}
+
+ BeforeEach(func() {
+ jc.(*JobsControllerImpl).mutex.Lock()
+ defer jc.(*JobsControllerImpl).mutex.Unlock()
+ for i := 0; i < elems; i++ {
+ j := jobids[i]
+ jc.(*JobsControllerImpl).jobs[j] = &Job{
+ JobInfo: weles.JobInfo{
+ JobID: j,
+ },
+ }
+ }
+ })
+ DescribeTable("paginator",
+ func(f weles.JobFilter, p weles.JobPagination, expected []weles.JobID, total int, remaining int) {
+ list, info, err := jc.List(f, weles.JobSorter{}, p)
+ Expect(err).NotTo(HaveOccurred())
+ expectIDsFull(list, info, expected, total, remaining)
+ },
+ Entry("should return all records if limit is 0 (pagination disabled)",
+ weles.JobFilter{},
+ weles.JobPagination{Limit: 0},
+ jobids, 10, 0),
+ Entry("should return slice of records if page is too small",
+ weles.JobFilter{},
+ weles.JobPagination{Limit: 3},
+ jobids[:3], 10, 7),
+ Entry("should return all records if page fits exactly",
+ weles.JobFilter{},
+ weles.JobPagination{Limit: 10},
+ jobids, 10, 0),
+ Entry("should return all records if page is big",
+ weles.JobFilter{},
+ weles.JobPagination{Limit: 100},
+ jobids, 10, 0),
+
+ Entry("when iterating forward should return all records if limit is 0 (pagination disabled)",
+ weles.JobFilter{},
+ weles.JobPagination{Limit: 0, JobID: jobids[3], Forward: true},
+ jobids, 10, 0),
+ Entry("when iterating forward should return slice of records if page is too small",
+ weles.JobFilter{},
+ weles.JobPagination{Limit: 3, JobID: jobids[3], Forward: true},
+ jobids[4:7], 10, 3),
+ Entry("when iterating forward should return all records if page fits exactly",
+ weles.JobFilter{},
+ weles.JobPagination{Limit: 6, JobID: jobids[3], Forward: true},
+ jobids[4:], 10, 0),
+ Entry("when iterating forward should return all records if page is big",
+ weles.JobFilter{},
+ weles.JobPagination{Limit: 100, JobID: jobids[3], Forward: true},
+ jobids[4:], 10, 0),
+
+ Entry("when iterating backwards should return all records if limit is 0 (pagination disabled)",
+ weles.JobFilter{},
+ weles.JobPagination{Limit: 0, JobID: jobids[3], Forward: false},
+ jobids, 10, 0),
+ Entry("when iterating backwards should return slice of records if page is too small",
+ weles.JobFilter{},
+ weles.JobPagination{Limit: 1, JobID: jobids[3], Forward: false},
+ jobids[2:3], 10, 2),
+ Entry("when iterating backwards should return all records if page fits exactly",
+ weles.JobFilter{},
+ weles.JobPagination{Limit: 3, JobID: jobids[3], Forward: false},
+ jobids[:3], 10, 0),
+ Entry("when iterating backwards should return all records if page is big",
+ weles.JobFilter{},
+ weles.JobPagination{Limit: 100, JobID: jobids[3], Forward: false},
+ jobids[:3], 10, 0),
+
+ Entry("should return all matching records if limit is 0 (pagination disabled) with filter",
+ evenFilter,
+ weles.JobPagination{Limit: 0},
+ evenjobids, 5, 0),
+ Entry("should return slice of records if page is too small with filter",
+ evenFilter,
+ weles.JobPagination{Limit: 3},
+ evenjobids[:3], 5, 2),
+ Entry("should return all records if page fits exactly with filter",
+ evenFilter,
+ weles.JobPagination{Limit: 5},
+ evenjobids, 5, 0),
+ Entry("should return all records if page is big with filter",
+ evenFilter,
+ weles.JobPagination{Limit: 100},
+ evenjobids, 5, 0),
+
+ Entry("when iterating forward should return all matching records if limit is 0 (pagination disabled) with filter",
+ evenFilter,
+ weles.JobPagination{Limit: 0, JobID: jobids[3], Forward: true},
+ evenjobids, 5, 0),
+ Entry("when iterating forward should return slice of records if page is too small with filter",
+ evenFilter,
+ weles.JobPagination{Limit: 2, JobID: jobids[3], Forward: true},
+ []weles.JobID{jobids[4], jobids[6]}, 5, 1),
+ Entry("when iterating forward should return all records if page fits exactly with filter",
+ evenFilter,
+ weles.JobPagination{Limit: 3, JobID: jobids[3], Forward: true},
+ []weles.JobID{jobids[4], jobids[6], jobids[8]}, 5, 0),
+ Entry("when iterating forward should return all records if page is big with filter",
+ evenFilter,
+ weles.JobPagination{Limit: 100, JobID: jobids[3], Forward: true},
+ []weles.JobID{jobids[4], jobids[6], jobids[8]}, 5, 0),
+
+ Entry("when iterating backwards should return all matching records if limit is 0 (pagination disabled) with filter",
+ evenFilter,
+ weles.JobPagination{Limit: 0, JobID: jobids[3], Forward: false},
+ evenjobids, 5, 0),
+ Entry("when iterating backwards should return slice of records if page is too small with filter",
+ evenFilter,
+ weles.JobPagination{Limit: 1, JobID: jobids[3], Forward: false},
+ []weles.JobID{jobids[2]}, 5, 1),
+ Entry("when iterating backwards should return all records if page fits exactly with filter",
+ evenFilter,
+ weles.JobPagination{Limit: 2, JobID: jobids[3], Forward: false},
+ []weles.JobID{jobids[0], jobids[2]}, 5, 0),
+ Entry("when iterating backwards should return all records if page is big with filter",
+ evenFilter,
+ weles.JobPagination{Limit: 100, JobID: jobids[3], Forward: false},
+ []weles.JobID{jobids[0], jobids[2]}, 5, 0),
+
+ Entry("when iterating forward should return no records with single filter",
+ singleFilter,
+ weles.JobPagination{Limit: 100, JobID: jobids[3], Forward: true},
+ []weles.JobID{}, 1, 0),
+ Entry("when iterating forward should return no records with empty filter",
+ emptyFilter,
+ weles.JobPagination{Limit: 100, JobID: jobids[3], Forward: true},
+ []weles.JobID{}, 0, 0),
+ Entry("when iterating backwards should return no records with single filter",
+ singleFilter,
+ weles.JobPagination{Limit: 100, JobID: jobids[3], Forward: false},
+ []weles.JobID{}, 1, 0),
+ Entry("when iterating backwards should return no records with empty filter",
+ emptyFilter,
+ weles.JobPagination{Limit: 100, JobID: jobids[3], Forward: false},
+ []weles.JobID{}, 0, 0),
+ )
+ It("when iterating forward should return error when JobID does not exist", func() {
+ list, info, err := jc.List(weles.JobFilter{}, weles.JobSorter{}, weles.JobPagination{Limit: 100, JobID: invalidID, Forward: true})
+ Expect(err).To(Equal(weles.ErrInvalidArgument(fmt.Sprintf("JobID: %d not found", invalidID))))
+ Expect(list).To(BeNil())
+ Expect(info).To(BeZero())
+ })
+ It("when iterating backwards should return error when JobID does not exist", func() {
+ list, info, err := jc.List(weles.JobFilter{}, weles.JobSorter{}, weles.JobPagination{Limit: 100, JobID: invalidID, Forward: false})
+ Expect(err).To(Equal(weles.ErrInvalidArgument(fmt.Sprintf("JobID: %d not found", invalidID))))
+ Expect(list).To(BeNil())
+ Expect(info).To(BeZero())
+ })
})
})
})
diff --git a/controller/mock/jobscontroller.go b/controller/mock/jobscontroller.go
index 300d51e..f690d4b 100644
--- a/controller/mock/jobscontroller.go
+++ b/controller/mock/jobscontroller.go
@@ -73,16 +73,17 @@ func (mr *MockJobsControllerMockRecorder) GetYaml(arg0 interface{}) *gomock.Call
}
// List mocks base method
-func (m *MockJobsController) List(arg0 []weles.JobID) ([]weles.JobInfo, error) {
- ret := m.ctrl.Call(m, "List", arg0)
+func (m *MockJobsController) List(arg0 weles.JobFilter, arg1 weles.JobSorter, arg2 weles.JobPagination) ([]weles.JobInfo, weles.ListInfo, error) {
+ ret := m.ctrl.Call(m, "List", arg0, arg1, arg2)
ret0, _ := ret[0].([]weles.JobInfo)
- ret1, _ := ret[1].(error)
- return ret0, ret1
+ ret1, _ := ret[1].(weles.ListInfo)
+ ret2, _ := ret[2].(error)
+ return ret0, ret1, ret2
}
// List indicates an expected call of List
-func (mr *MockJobsControllerMockRecorder) List(arg0 interface{}) *gomock.Call {
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockJobsController)(nil).List), arg0)
+func (mr *MockJobsControllerMockRecorder) List(arg0, arg1, arg2 interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockJobsController)(nil).List), arg0, arg1, arg2)
}
// NewJob mocks base method
diff --git a/controller/mock/mocks_generate.go b/controller/mock/mocks_generate.go
new file mode 100644
index 0000000..044d63b
--- /dev/null
+++ b/controller/mock/mocks_generate.go
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package mock
+
+//go:generate ../../bin/dev-tools/mockgen -package mock -destination=./jobscontroller.go git.tizen.org/tools/weles/controller JobsController
+
+// TODO: fix relative path to absolute
+// You need to have Boruta checked out next to Weles for below go generate to work.
+// Reflect mode will not work in below case.
+//go:generate ../../bin/dev-tools/mockgen -package mock -destination=./requests.go -source=../../../boruta/boruta.go
+
+//go:generate ../../bin/dev-tools/mockgen -package mock -destination=./boruter.go git.tizen.org/tools/weles/controller Boruter
+
+//go:generate ../../bin/dev-tools/mockgen -package mock -destination=./downloader.go git.tizen.org/tools/weles/controller Downloader
+
+//go:generate ../../bin/dev-tools/mockgen -package mock -destination=./dryader.go git.tizen.org/tools/weles/controller Dryader
+
+//go:generate ../../bin/dev-tools/mockgen -package mock -destination=./parser.go git.tizen.org/tools/weles/controller Parser
diff --git a/controller/mock/requests.go b/controller/mock/requests.go
index aeded1b..2e6c208 100644
--- a/controller/mock/requests.go
+++ b/controller/mock/requests.go
@@ -1,16 +1,52 @@
// Code generated by MockGen. DO NOT EDIT.
-// Source: git.tizen.org/tools/boruta (interfaces: Requests)
+// Source: ../../../boruta/boruta.go
// Package mock is a generated GoMock package.
package mock
import (
+ rsa "crypto/rsa"
boruta "git.tizen.org/tools/boruta"
gomock "github.com/golang/mock/gomock"
reflect "reflect"
time "time"
)
+// MockListFilter is a mock of ListFilter interface
+type MockListFilter struct {
+ ctrl *gomock.Controller
+ recorder *MockListFilterMockRecorder
+}
+
+// MockListFilterMockRecorder is the mock recorder for MockListFilter
+type MockListFilterMockRecorder struct {
+ mock *MockListFilter
+}
+
+// NewMockListFilter creates a new mock instance
+func NewMockListFilter(ctrl *gomock.Controller) *MockListFilter {
+ mock := &MockListFilter{ctrl: ctrl}
+ mock.recorder = &MockListFilterMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use
+func (m *MockListFilter) EXPECT() *MockListFilterMockRecorder {
+ return m.recorder
+}
+
+// Match mocks base method
+func (m *MockListFilter) Match(req *boruta.ReqInfo) bool {
+ ret := m.ctrl.Call(m, "Match", req)
+ ret0, _ := ret[0].(bool)
+ return ret0
+}
+
+// Match indicates an expected call of Match
+func (mr *MockListFilterMockRecorder) Match(req interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Match", reflect.TypeOf((*MockListFilter)(nil).Match), req)
+}
+
// MockRequests is a mock of Requests interface
type MockRequests struct {
ctrl *gomock.Controller
@@ -34,90 +70,282 @@ func (m *MockRequests) EXPECT() *MockRequestsMockRecorder {
return m.recorder
}
-// AcquireWorker mocks base method
-func (m *MockRequests) AcquireWorker(arg0 boruta.ReqID) (boruta.AccessInfo, error) {
- ret := m.ctrl.Call(m, "AcquireWorker", arg0)
- ret0, _ := ret[0].(boruta.AccessInfo)
+// NewRequest mocks base method
+func (m *MockRequests) NewRequest(caps boruta.Capabilities, priority boruta.Priority, owner boruta.UserInfo, validAfter, deadline time.Time) (boruta.ReqID, error) {
+ ret := m.ctrl.Call(m, "NewRequest", caps, priority, owner, validAfter, deadline)
+ ret0, _ := ret[0].(boruta.ReqID)
ret1, _ := ret[1].(error)
return ret0, ret1
}
-// AcquireWorker indicates an expected call of AcquireWorker
-func (mr *MockRequestsMockRecorder) AcquireWorker(arg0 interface{}) *gomock.Call {
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcquireWorker", reflect.TypeOf((*MockRequests)(nil).AcquireWorker), arg0)
+// NewRequest indicates an expected call of NewRequest
+func (mr *MockRequestsMockRecorder) NewRequest(caps, priority, owner, validAfter, deadline interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewRequest", reflect.TypeOf((*MockRequests)(nil).NewRequest), caps, priority, owner, validAfter, deadline)
}
// CloseRequest mocks base method
-func (m *MockRequests) CloseRequest(arg0 boruta.ReqID) error {
- ret := m.ctrl.Call(m, "CloseRequest", arg0)
+func (m *MockRequests) CloseRequest(reqID boruta.ReqID) error {
+ ret := m.ctrl.Call(m, "CloseRequest", reqID)
ret0, _ := ret[0].(error)
return ret0
}
// CloseRequest indicates an expected call of CloseRequest
-func (mr *MockRequestsMockRecorder) CloseRequest(arg0 interface{}) *gomock.Call {
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseRequest", reflect.TypeOf((*MockRequests)(nil).CloseRequest), arg0)
+func (mr *MockRequestsMockRecorder) CloseRequest(reqID interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseRequest", reflect.TypeOf((*MockRequests)(nil).CloseRequest), reqID)
+}
+
+// UpdateRequest mocks base method
+func (m *MockRequests) UpdateRequest(reqInfo *boruta.ReqInfo) error {
+ ret := m.ctrl.Call(m, "UpdateRequest", reqInfo)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// UpdateRequest indicates an expected call of UpdateRequest
+func (mr *MockRequestsMockRecorder) UpdateRequest(reqInfo interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateRequest", reflect.TypeOf((*MockRequests)(nil).UpdateRequest), reqInfo)
}
// GetRequestInfo mocks base method
-func (m *MockRequests) GetRequestInfo(arg0 boruta.ReqID) (boruta.ReqInfo, error) {
- ret := m.ctrl.Call(m, "GetRequestInfo", arg0)
+func (m *MockRequests) GetRequestInfo(reqID boruta.ReqID) (boruta.ReqInfo, error) {
+ ret := m.ctrl.Call(m, "GetRequestInfo", reqID)
ret0, _ := ret[0].(boruta.ReqInfo)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetRequestInfo indicates an expected call of GetRequestInfo
-func (mr *MockRequestsMockRecorder) GetRequestInfo(arg0 interface{}) *gomock.Call {
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRequestInfo", reflect.TypeOf((*MockRequests)(nil).GetRequestInfo), arg0)
+func (mr *MockRequestsMockRecorder) GetRequestInfo(reqID interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRequestInfo", reflect.TypeOf((*MockRequests)(nil).GetRequestInfo), reqID)
}
// ListRequests mocks base method
-func (m *MockRequests) ListRequests(arg0 boruta.ListFilter) ([]boruta.ReqInfo, error) {
- ret := m.ctrl.Call(m, "ListRequests", arg0)
+func (m *MockRequests) ListRequests(filter boruta.ListFilter) ([]boruta.ReqInfo, error) {
+ ret := m.ctrl.Call(m, "ListRequests", filter)
ret0, _ := ret[0].([]boruta.ReqInfo)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ListRequests indicates an expected call of ListRequests
-func (mr *MockRequestsMockRecorder) ListRequests(arg0 interface{}) *gomock.Call {
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListRequests", reflect.TypeOf((*MockRequests)(nil).ListRequests), arg0)
+func (mr *MockRequestsMockRecorder) ListRequests(filter interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListRequests", reflect.TypeOf((*MockRequests)(nil).ListRequests), filter)
}
-// NewRequest mocks base method
-func (m *MockRequests) NewRequest(arg0 boruta.Capabilities, arg1 boruta.Priority, arg2 boruta.UserInfo, arg3, arg4 time.Time) (boruta.ReqID, error) {
- ret := m.ctrl.Call(m, "NewRequest", arg0, arg1, arg2, arg3, arg4)
- ret0, _ := ret[0].(boruta.ReqID)
+// AcquireWorker mocks base method
+func (m *MockRequests) AcquireWorker(reqID boruta.ReqID) (boruta.AccessInfo, error) {
+ ret := m.ctrl.Call(m, "AcquireWorker", reqID)
+ ret0, _ := ret[0].(boruta.AccessInfo)
ret1, _ := ret[1].(error)
return ret0, ret1
}
-// NewRequest indicates an expected call of NewRequest
-func (mr *MockRequestsMockRecorder) NewRequest(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewRequest", reflect.TypeOf((*MockRequests)(nil).NewRequest), arg0, arg1, arg2, arg3, arg4)
+// AcquireWorker indicates an expected call of AcquireWorker
+func (mr *MockRequestsMockRecorder) AcquireWorker(reqID interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcquireWorker", reflect.TypeOf((*MockRequests)(nil).AcquireWorker), reqID)
}
// ProlongAccess mocks base method
-func (m *MockRequests) ProlongAccess(arg0 boruta.ReqID) error {
- ret := m.ctrl.Call(m, "ProlongAccess", arg0)
+func (m *MockRequests) ProlongAccess(reqID boruta.ReqID) error {
+ ret := m.ctrl.Call(m, "ProlongAccess", reqID)
ret0, _ := ret[0].(error)
return ret0
}
// ProlongAccess indicates an expected call of ProlongAccess
-func (mr *MockRequestsMockRecorder) ProlongAccess(arg0 interface{}) *gomock.Call {
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProlongAccess", reflect.TypeOf((*MockRequests)(nil).ProlongAccess), arg0)
+func (mr *MockRequestsMockRecorder) ProlongAccess(reqID interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProlongAccess", reflect.TypeOf((*MockRequests)(nil).ProlongAccess), reqID)
}
-// UpdateRequest mocks base method
-func (m *MockRequests) UpdateRequest(arg0 *boruta.ReqInfo) error {
- ret := m.ctrl.Call(m, "UpdateRequest", arg0)
+// MockSuperviser is a mock of Superviser interface
+type MockSuperviser struct {
+ ctrl *gomock.Controller
+ recorder *MockSuperviserMockRecorder
+}
+
+// MockSuperviserMockRecorder is the mock recorder for MockSuperviser
+type MockSuperviserMockRecorder struct {
+ mock *MockSuperviser
+}
+
+// NewMockSuperviser creates a new mock instance
+func NewMockSuperviser(ctrl *gomock.Controller) *MockSuperviser {
+ mock := &MockSuperviser{ctrl: ctrl}
+ mock.recorder = &MockSuperviserMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use
+func (m *MockSuperviser) EXPECT() *MockSuperviserMockRecorder {
+ return m.recorder
+}
+
+// Register mocks base method
+func (m *MockSuperviser) Register(caps boruta.Capabilities) error {
+ ret := m.ctrl.Call(m, "Register", caps)
ret0, _ := ret[0].(error)
return ret0
}
-// UpdateRequest indicates an expected call of UpdateRequest
-func (mr *MockRequestsMockRecorder) UpdateRequest(arg0 interface{}) *gomock.Call {
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateRequest", reflect.TypeOf((*MockRequests)(nil).UpdateRequest), arg0)
+// Register indicates an expected call of Register
+func (mr *MockSuperviserMockRecorder) Register(caps interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Register", reflect.TypeOf((*MockSuperviser)(nil).Register), caps)
+}
+
+// SetFail mocks base method
+func (m *MockSuperviser) SetFail(uuid boruta.WorkerUUID, reason string) error {
+ ret := m.ctrl.Call(m, "SetFail", uuid, reason)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// SetFail indicates an expected call of SetFail
+func (mr *MockSuperviserMockRecorder) SetFail(uuid, reason interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetFail", reflect.TypeOf((*MockSuperviser)(nil).SetFail), uuid, reason)
+}
+
+// MockWorkers is a mock of Workers interface
+type MockWorkers struct {
+ ctrl *gomock.Controller
+ recorder *MockWorkersMockRecorder
+}
+
+// MockWorkersMockRecorder is the mock recorder for MockWorkers
+type MockWorkersMockRecorder struct {
+ mock *MockWorkers
+}
+
+// NewMockWorkers creates a new mock instance
+func NewMockWorkers(ctrl *gomock.Controller) *MockWorkers {
+ mock := &MockWorkers{ctrl: ctrl}
+ mock.recorder = &MockWorkersMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use
+func (m *MockWorkers) EXPECT() *MockWorkersMockRecorder {
+ return m.recorder
+}
+
+// ListWorkers mocks base method
+func (m *MockWorkers) ListWorkers(groups boruta.Groups, caps boruta.Capabilities) ([]boruta.WorkerInfo, error) {
+ ret := m.ctrl.Call(m, "ListWorkers", groups, caps)
+ ret0, _ := ret[0].([]boruta.WorkerInfo)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// ListWorkers indicates an expected call of ListWorkers
+func (mr *MockWorkersMockRecorder) ListWorkers(groups, caps interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListWorkers", reflect.TypeOf((*MockWorkers)(nil).ListWorkers), groups, caps)
+}
+
+// GetWorkerInfo mocks base method
+func (m *MockWorkers) GetWorkerInfo(uuid boruta.WorkerUUID) (boruta.WorkerInfo, error) {
+ ret := m.ctrl.Call(m, "GetWorkerInfo", uuid)
+ ret0, _ := ret[0].(boruta.WorkerInfo)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// GetWorkerInfo indicates an expected call of GetWorkerInfo
+func (mr *MockWorkersMockRecorder) GetWorkerInfo(uuid interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkerInfo", reflect.TypeOf((*MockWorkers)(nil).GetWorkerInfo), uuid)
+}
+
+// SetState mocks base method
+func (m *MockWorkers) SetState(uuid boruta.WorkerUUID, state boruta.WorkerState) error {
+ ret := m.ctrl.Call(m, "SetState", uuid, state)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// SetState indicates an expected call of SetState
+func (mr *MockWorkersMockRecorder) SetState(uuid, state interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetState", reflect.TypeOf((*MockWorkers)(nil).SetState), uuid, state)
+}
+
+// SetGroups mocks base method
+func (m *MockWorkers) SetGroups(uuid boruta.WorkerUUID, groups boruta.Groups) error {
+ ret := m.ctrl.Call(m, "SetGroups", uuid, groups)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// SetGroups indicates an expected call of SetGroups
+func (mr *MockWorkersMockRecorder) SetGroups(uuid, groups interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetGroups", reflect.TypeOf((*MockWorkers)(nil).SetGroups), uuid, groups)
+}
+
+// Deregister mocks base method
+func (m *MockWorkers) Deregister(uuid boruta.WorkerUUID) error {
+ ret := m.ctrl.Call(m, "Deregister", uuid)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// Deregister indicates an expected call of Deregister
+func (mr *MockWorkersMockRecorder) Deregister(uuid interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Deregister", reflect.TypeOf((*MockWorkers)(nil).Deregister), uuid)
+}
+
+// MockDryad is a mock of Dryad interface
+type MockDryad struct {
+ ctrl *gomock.Controller
+ recorder *MockDryadMockRecorder
+}
+
+// MockDryadMockRecorder is the mock recorder for MockDryad
+type MockDryadMockRecorder struct {
+ mock *MockDryad
+}
+
+// NewMockDryad creates a new mock instance
+func NewMockDryad(ctrl *gomock.Controller) *MockDryad {
+ mock := &MockDryad{ctrl: ctrl}
+ mock.recorder = &MockDryadMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use
+func (m *MockDryad) EXPECT() *MockDryadMockRecorder {
+ return m.recorder
+}
+
+// PutInMaintenance mocks base method
+func (m *MockDryad) PutInMaintenance(msg string) error {
+ ret := m.ctrl.Call(m, "PutInMaintenance", msg)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// PutInMaintenance indicates an expected call of PutInMaintenance
+func (mr *MockDryadMockRecorder) PutInMaintenance(msg interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutInMaintenance", reflect.TypeOf((*MockDryad)(nil).PutInMaintenance), msg)
+}
+
+// Prepare mocks base method
+func (m *MockDryad) Prepare() (*rsa.PrivateKey, error) {
+ ret := m.ctrl.Call(m, "Prepare")
+ ret0, _ := ret[0].(*rsa.PrivateKey)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// Prepare indicates an expected call of Prepare
+func (mr *MockDryadMockRecorder) Prepare() *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Prepare", reflect.TypeOf((*MockDryad)(nil).Prepare))
+}
+
+// Healthcheck mocks base method
+func (m *MockDryad) Healthcheck() error {
+ ret := m.ctrl.Call(m, "Healthcheck")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// Healthcheck indicates an expected call of Healthcheck
+func (mr *MockDryadMockRecorder) Healthcheck() *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Healthcheck", reflect.TypeOf((*MockDryad)(nil).Healthcheck))
}
diff --git a/controller/notifier/notification.go b/controller/notifier/notification.go
index 3b650ed..255d881 100644
--- a/controller/notifier/notification.go
+++ b/controller/notifier/notification.go
@@ -20,7 +20,7 @@
package notifier
import (
- . "git.tizen.org/tools/weles"
+ "git.tizen.org/tools/weles"
)
// BuffSize is the default channel buffer size for channels passing
@@ -31,7 +31,7 @@ const BuffSize = 32
// It is passed to Controller's listening channels.
type Notification struct {
// JobID identifies Job.
- JobID
+ weles.JobID
// Ok reports if Job processing stage has ended with success.
OK bool
// Msg contains additional information for the final user.
diff --git a/controller/notifier/notifier.go b/controller/notifier/notifier.go
index c36a47e..a299680 100644
--- a/controller/notifier/notifier.go
+++ b/controller/notifier/notifier.go
@@ -20,7 +20,7 @@
package notifier
import (
- . "git.tizen.org/tools/weles"
+ "git.tizen.org/tools/weles"
)
// Notifier defines interface providing channel for notifying Controller.
@@ -30,8 +30,8 @@ type Notifier interface {
Listen() <-chan Notification
// SendFail notifies Controller about failure.
- SendFail(j JobID, msg string)
+ SendFail(j weles.JobID, msg string)
// SendOK notifies Controller about success.
- SendOK(j JobID)
+ SendOK(j weles.JobID)
}
diff --git a/controller/notifier/notifierimpl.go b/controller/notifier/notifierimpl.go
index 247fd47..60bb7e6 100644
--- a/controller/notifier/notifierimpl.go
+++ b/controller/notifier/notifierimpl.go
@@ -22,7 +22,7 @@
package notifier
import (
- . "git.tizen.org/tools/weles"
+ "git.tizen.org/tools/weles"
)
// Impl implements Notifier interface
@@ -45,7 +45,7 @@ func (h *Impl) Listen() <-chan Notification {
}
// SendFail notifies Controller about failure.
-func (h *Impl) SendFail(j JobID, msg string) {
+func (h *Impl) SendFail(j weles.JobID, msg string) {
h.channel <- Notification{
JobID: j,
OK: false,
@@ -54,7 +54,7 @@ func (h *Impl) SendFail(j JobID, msg string) {
}
// SendOK notifies Controller about success.
-func (h *Impl) SendOK(j JobID) {
+func (h *Impl) SendOK(j weles.JobID) {
h.channel <- Notification{
JobID: j,
OK: true,
diff --git a/controller/parserimpl.go b/controller/parserimpl.go
index 613623a..17f0643 100644
--- a/controller/parserimpl.go
+++ b/controller/parserimpl.go
@@ -52,7 +52,7 @@ func NewParser(j JobsController, a weles.ArtifactManager, p weles.Parser) Parser
// Parse prepares new Job to be processed by saving yaml file in ArtifactDB,
// parsing yaml and preparing Job's configuration.
func (h *ParserImpl) Parse(j weles.JobID) {
- err := h.jobs.SetStatusAndInfo(j, weles.JOB_PARSING, "")
+ err := h.jobs.SetStatusAndInfo(j, weles.JobStatusPARSING, "")
if err != nil {
h.SendFail(j, fmt.Sprintf("Internal Weles error while changing Job status : %s", err.Error()))
return
@@ -66,7 +66,7 @@ func (h *ParserImpl) Parse(j weles.JobID) {
path, err := h.artifacts.CreateArtifact(weles.ArtifactDescription{
JobID: j,
- Type: weles.AM_YAMLFILE,
+ Type: weles.ArtifactTypeYAML,
})
if err != nil {
h.SendFail(j, fmt.Sprintf("Internal Weles error while creating file path in ArtifactDB : %s", err.Error()))
diff --git a/controller/parserimpl_test.go b/controller/parserimpl_test.go
index 0f48ff3..9ff7556 100644
--- a/controller/parserimpl_test.go
+++ b/controller/parserimpl_test.go
@@ -66,9 +66,9 @@ var _ = Describe("ParserImpl", func() {
Describe("Parse", func() {
It("should handle job successfully", func() {
gomock.InOrder(
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_PARSING, ""),
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusPARSING, ""),
jc.EXPECT().GetYaml(j).Return(yaml, nil),
- am.EXPECT().CreateArtifact(weles.ArtifactDescription{JobID: j, Type: weles.AM_YAMLFILE}).Return(goodpath, nil),
+ am.EXPECT().CreateArtifact(weles.ArtifactDescription{JobID: j, Type: weles.ArtifactTypeYAML}).Return(goodpath, nil),
yp.EXPECT().ParseYaml(yaml).Return(&config, nil),
jc.EXPECT().SetConfig(j, config),
)
@@ -82,9 +82,9 @@ var _ = Describe("ParserImpl", func() {
})
It("should fail when unable to set config", func() {
gomock.InOrder(
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_PARSING, ""),
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusPARSING, ""),
jc.EXPECT().GetYaml(j).Return(yaml, nil),
- am.EXPECT().CreateArtifact(weles.ArtifactDescription{JobID: j, Type: weles.AM_YAMLFILE}).Return(goodpath, nil),
+ am.EXPECT().CreateArtifact(weles.ArtifactDescription{JobID: j, Type: weles.ArtifactTypeYAML}).Return(goodpath, nil),
yp.EXPECT().ParseYaml(yaml).Return(&config, nil),
jc.EXPECT().SetConfig(j, config).Return(err),
)
@@ -100,9 +100,9 @@ var _ = Describe("ParserImpl", func() {
})
It("should fail when unable to parse yaml", func() {
gomock.InOrder(
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_PARSING, ""),
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusPARSING, ""),
jc.EXPECT().GetYaml(j).Return(yaml, nil),
- am.EXPECT().CreateArtifact(weles.ArtifactDescription{JobID: j, Type: weles.AM_YAMLFILE}).Return(goodpath, nil),
+ am.EXPECT().CreateArtifact(weles.ArtifactDescription{JobID: j, Type: weles.ArtifactTypeYAML}).Return(goodpath, nil),
yp.EXPECT().ParseYaml(yaml).Return(&weles.Config{}, err),
)
@@ -117,9 +117,9 @@ var _ = Describe("ParserImpl", func() {
})
It("should fail when unable to write yaml file", func() {
gomock.InOrder(
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_PARSING, ""),
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusPARSING, ""),
jc.EXPECT().GetYaml(j).Return(yaml, nil),
- am.EXPECT().CreateArtifact(weles.ArtifactDescription{JobID: j, Type: weles.AM_YAMLFILE}).Return(badpath, nil),
+ am.EXPECT().CreateArtifact(weles.ArtifactDescription{JobID: j, Type: weles.ArtifactTypeYAML}).Return(badpath, nil),
)
h.Parse(j)
@@ -133,9 +133,9 @@ var _ = Describe("ParserImpl", func() {
})
It("should fail when unable to create path in ArtifactDB", func() {
gomock.InOrder(
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_PARSING, ""),
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusPARSING, ""),
jc.EXPECT().GetYaml(j).Return(yaml, nil),
- am.EXPECT().CreateArtifact(weles.ArtifactDescription{JobID: j, Type: weles.AM_YAMLFILE}).Return(weles.ArtifactPath(""), err),
+ am.EXPECT().CreateArtifact(weles.ArtifactDescription{JobID: j, Type: weles.ArtifactTypeYAML}).Return(weles.ArtifactPath(""), err),
)
h.Parse(j)
@@ -149,7 +149,7 @@ var _ = Describe("ParserImpl", func() {
})
It("should fail when unable to get yaml", func() {
gomock.InOrder(
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_PARSING, ""),
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusPARSING, ""),
jc.EXPECT().GetYaml(j).Return([]byte{}, err),
)
@@ -163,7 +163,7 @@ var _ = Describe("ParserImpl", func() {
Eventually(r).Should(Receive(Equal(expectedNotification)))
})
It("should fail when unable to change job status", func() {
- jc.EXPECT().SetStatusAndInfo(j, weles.JOB_PARSING, "").Return(err)
+ jc.EXPECT().SetStatusAndInfo(j, weles.JobStatusPARSING, "").Return(err)
h.Parse(j)
diff --git a/err_response.go b/err_response.go
new file mode 100644
index 0000000..1220654
--- /dev/null
+++ b/err_response.go
@@ -0,0 +1,61 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package weles
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ strfmt "github.com/go-openapi/strfmt"
+
+ "github.com/go-openapi/swag"
+)
+
+// ErrResponse is a standard error response containing information about the error. It consists of error type and message.
+// swagger:model ErrResponse
+type ErrResponse struct {
+
+ // message
+ Message string `json:"message,omitempty"`
+
+ // type
+ Type string `json:"type,omitempty"`
+}
+
+// Validate validates this err response
+func (m *ErrResponse) Validate(formats strfmt.Registry) error {
+ return nil
+}
+
+// MarshalBinary interface implementation
+func (m *ErrResponse) MarshalBinary() ([]byte, error) {
+ if m == nil {
+ return nil, nil
+ }
+ return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *ErrResponse) UnmarshalBinary(b []byte) error {
+ var res ErrResponse
+ if err := swag.ReadJSON(b, &res); err != nil {
+ return err
+ }
+ *m = res
+ return nil
+}
diff --git a/errors.go b/errors.go
index 327e9af..1d40aa4 100644
--- a/errors.go
+++ b/errors.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ package weles
import (
"errors"
+ "fmt"
)
var (
@@ -30,4 +31,17 @@ var (
// ErrJobStatusChangeNotAllowed is returned when Job status change is not
// possible. It suggests internal Weles logic error.
ErrJobStatusChangeNotAllowed = errors.New("job status change not allowed")
+ // ErrBeforeAfterNotAllowed is returned when client places request for a list
+ // with both before and after parameters.
+ ErrBeforeAfterNotAllowed = errors.New("setting both before and after qeury parameters is not allowed")
+ // ErrArtifactNotFound is returned by API when no artifact is returned by ArtifactManager
+ ErrArtifactNotFound = errors.New("artifact not found")
)
+
+// ErrInvalidArgument is returned when argument passed to public API cannot
+// be parsed.
+type ErrInvalidArgument string
+
+func (err ErrInvalidArgument) Error() string {
+ return fmt.Sprintf("invalid argument: %s", string(err))
+}
diff --git a/errors_test.go b/errors_test.go
new file mode 100644
index 0000000..f3f848f
--- /dev/null
+++ b/errors_test.go
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package weles
+
+import (
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Errors", func() {
+ It("ErrInvalidArgument", func() {
+ details := "because tracks were bad"
+ err := ErrInvalidArgument(details)
+ Expect(err.Error()).To(Equal("invalid argument: " + details))
+ })
+})
diff --git a/fixtures/artifactinfo.go b/fixtures/artifactinfo.go
new file mode 100644
index 0000000..0baa794
--- /dev/null
+++ b/fixtures/artifactinfo.go
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package fixtures
+
+import (
+ "math/rand"
+ "time"
+
+ "github.com/go-openapi/strfmt"
+ "github.com/tideland/golib/audit"
+
+ "git.tizen.org/tools/weles"
+)
+
+// CreateArtifactInfoSlice returns slice of ArtifactInfos of sliceLength length.
+// It is filled with random data used for testing.
+func CreateArtifactInfoSlice(sliceLength int) []weles.ArtifactInfo {
+ // checking for errors omitted due to fixed input.
+ dateTimeIter, _ := time.Parse("Mon Jan 2 15:04:05 -0700 MST 2006", "Tue Jan 2 15:04:05 +0100 CET 1900")
+ durationIncrement, _ := time.ParseDuration("+25h")
+ artifactInfo := make([]weles.ArtifactInfo, sliceLength)
+ gen := audit.NewGenerator(rand.New(rand.NewSource(time.Now().UTC().UnixNano())))
+ for i := range artifactInfo {
+ tmp := weles.ArtifactInfo{}
+ timestamp := gen.Time(time.Local, dateTimeIter, durationIncrement)
+ tmp.Timestamp = strfmt.DateTime(timestamp)
+ tmp.ID = int64(i + 1)
+ tmp.ArtifactDescription.Alias = weles.ArtifactAlias(gen.Word())
+ tmp.ArtifactDescription.JobID = weles.JobID(i + 1)
+ tmp.ArtifactDescription.Type = weles.ArtifactType(gen.OneStringOf(
+ string(weles.ArtifactTypeIMAGE),
+ string(weles.ArtifactTypeRESULT),
+ string(weles.ArtifactTypeTEST),
+ string(weles.ArtifactTypeYAML)))
+ tmp.ArtifactDescription.URI = weles.ArtifactURI(gen.URL())
+ tmp.Path = weles.ArtifactPath(gen.URL())
+ tmp.Status = weles.ArtifactStatus(gen.OneStringOf(
+ string(weles.ArtifactStatusDOWNLOADING),
+ string(weles.ArtifactStatusPENDING),
+ string(weles.ArtifactStatusREADY),
+ string(weles.ArtifactStatusFAILED)))
+
+ dateTimeIter = dateTimeIter.Add(durationIncrement)
+ artifactInfo[i] = tmp
+ }
+ return artifactInfo
+}
diff --git a/job_filter.go b/job_filter.go
new file mode 100644
index 0000000..8f63c47
--- /dev/null
+++ b/job_filter.go
@@ -0,0 +1,208 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package weles
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "strconv"
+
+ strfmt "github.com/go-openapi/strfmt"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/swag"
+ "github.com/go-openapi/validate"
+)
+
+// JobFilter is used to filter Weles Jobs.
+// swagger:model JobFilter
+type JobFilter struct {
+
+ // created after
+ // Format: date-time
+ CreatedAfter strfmt.DateTime `json:"CreatedAfter,omitempty"`
+
+ // created before
+ // Format: date-time
+ CreatedBefore strfmt.DateTime `json:"CreatedBefore,omitempty"`
+
+ // info
+ Info []string `json:"Info"`
+
+ // job ID
+ JobID []JobID `json:"JobID"`
+
+ // name
+ Name []string `json:"Name"`
+
+ // status
+ Status []JobStatus `json:"Status"`
+
+ // updated after
+ // Format: date-time
+ UpdatedAfter strfmt.DateTime `json:"UpdatedAfter,omitempty"`
+
+ // updated before
+ // Format: date-time
+ UpdatedBefore strfmt.DateTime `json:"UpdatedBefore,omitempty"`
+}
+
+// Validate validates this job filter
+func (m *JobFilter) Validate(formats strfmt.Registry) error {
+ var res []error
+
+ if err := m.validateCreatedAfter(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateCreatedBefore(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateJobID(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateStatus(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateUpdatedAfter(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateUpdatedBefore(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+func (m *JobFilter) validateCreatedAfter(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.CreatedAfter) { // not required
+ return nil
+ }
+
+ if err := validate.FormatOf("CreatedAfter", "body", "date-time", m.CreatedAfter.String(), formats); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (m *JobFilter) validateCreatedBefore(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.CreatedBefore) { // not required
+ return nil
+ }
+
+ if err := validate.FormatOf("CreatedBefore", "body", "date-time", m.CreatedBefore.String(), formats); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (m *JobFilter) validateJobID(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.JobID) { // not required
+ return nil
+ }
+
+ for i := 0; i < len(m.JobID); i++ {
+
+ if err := m.JobID[i].Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("JobID" + "." + strconv.Itoa(i))
+ }
+ return err
+ }
+
+ }
+
+ return nil
+}
+
+func (m *JobFilter) validateStatus(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.Status) { // not required
+ return nil
+ }
+
+ for i := 0; i < len(m.Status); i++ {
+
+ if err := m.Status[i].Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("Status" + "." + strconv.Itoa(i))
+ }
+ return err
+ }
+
+ }
+
+ return nil
+}
+
+func (m *JobFilter) validateUpdatedAfter(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.UpdatedAfter) { // not required
+ return nil
+ }
+
+ if err := validate.FormatOf("UpdatedAfter", "body", "date-time", m.UpdatedAfter.String(), formats); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (m *JobFilter) validateUpdatedBefore(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.UpdatedBefore) { // not required
+ return nil
+ }
+
+ if err := validate.FormatOf("UpdatedBefore", "body", "date-time", m.UpdatedBefore.String(), formats); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// MarshalBinary interface implementation
+func (m *JobFilter) MarshalBinary() ([]byte, error) {
+ if m == nil {
+ return nil, nil
+ }
+ return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *JobFilter) UnmarshalBinary(b []byte) error {
+ var res JobFilter
+ if err := swag.ReadJSON(b, &res); err != nil {
+ return err
+ }
+ *m = res
+ return nil
+}
diff --git a/job_filter_and_sort.go b/job_filter_and_sort.go
new file mode 100644
index 0000000..511ac8b
--- /dev/null
+++ b/job_filter_and_sort.go
@@ -0,0 +1,111 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package weles
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ strfmt "github.com/go-openapi/strfmt"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/swag"
+)
+
+// JobFilterAndSort Data for filtering and sorting Weles Jobs lists.
+// swagger:model JobFilterAndSort
+type JobFilterAndSort struct {
+
+ // filter
+ Filter *JobFilter `json:"Filter,omitempty"`
+
+ // sorter
+ Sorter *JobSorter `json:"Sorter,omitempty"`
+}
+
+// Validate validates this job filter and sort
+func (m *JobFilterAndSort) Validate(formats strfmt.Registry) error {
+ var res []error
+
+ if err := m.validateFilter(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateSorter(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+func (m *JobFilterAndSort) validateFilter(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.Filter) { // not required
+ return nil
+ }
+
+ if m.Filter != nil {
+ if err := m.Filter.Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("Filter")
+ }
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (m *JobFilterAndSort) validateSorter(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.Sorter) { // not required
+ return nil
+ }
+
+ if m.Sorter != nil {
+ if err := m.Sorter.Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("Sorter")
+ }
+ return err
+ }
+ }
+
+ return nil
+}
+
+// MarshalBinary interface implementation
+func (m *JobFilterAndSort) MarshalBinary() ([]byte, error) {
+ if m == nil {
+ return nil, nil
+ }
+ return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *JobFilterAndSort) UnmarshalBinary(b []byte) error {
+ var res JobFilterAndSort
+ if err := swag.ReadJSON(b, &res); err != nil {
+ return err
+ }
+ *m = res
+ return nil
+}
diff --git a/job_id.go b/job_id.go
new file mode 100644
index 0000000..2338cf4
--- /dev/null
+++ b/job_id.go
@@ -0,0 +1,34 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package weles
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ strfmt "github.com/go-openapi/strfmt"
+)
+
+// JobID is a unique identifier for Weles Job.
+// swagger:model JobID
+type JobID uint64
+
+// Validate validates this job ID
+func (m JobID) Validate(formats strfmt.Registry) error {
+ return nil
+}
diff --git a/job_id_string.go b/job_id_string.go
new file mode 100644
index 0000000..dfd0e4a
--- /dev/null
+++ b/job_id_string.go
@@ -0,0 +1,22 @@
+// Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+package weles
+
+import "fmt"
+
+// String is a method that satisfies fmt.Stringer interface. It returns JobID as string.
+func (jid JobID) String() string {
+ return fmt.Sprintf("%d", jid)
+}
diff --git a/job_info.go b/job_info.go
new file mode 100644
index 0000000..86f3d93
--- /dev/null
+++ b/job_info.go
@@ -0,0 +1,156 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package weles
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ strfmt "github.com/go-openapi/strfmt"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/swag"
+ "github.com/go-openapi/validate"
+)
+
+// JobInfo contains information about a Job available for public API.
+// swagger:model JobInfo
+type JobInfo struct {
+
+ // is the Job creation time in UTC.
+ // Format: date-time
+ Created strfmt.DateTime `json:"created,omitempty"`
+
+ // provides additional information about current state, e.g. cause of failure
+ Info string `json:"info,omitempty"`
+
+ // is a unique Job identifier
+ JobID JobID `json:"jobID,omitempty"`
+
+ // is the Job name acquired from yaml file during Job creation.
+ Name string `json:"name,omitempty"`
+
+ // specifies current state of the Job.
+ Status JobStatus `json:"status,omitempty"`
+
+ // is the time of latest Jobs' status modification.
+ // Format: date-time
+ Updated strfmt.DateTime `json:"updated,omitempty"`
+}
+
+// Validate validates this job info
+func (m *JobInfo) Validate(formats strfmt.Registry) error {
+ var res []error
+
+ if err := m.validateCreated(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateJobID(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateStatus(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateUpdated(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+func (m *JobInfo) validateCreated(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.Created) { // not required
+ return nil
+ }
+
+ if err := validate.FormatOf("created", "body", "date-time", m.Created.String(), formats); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (m *JobInfo) validateJobID(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.JobID) { // not required
+ return nil
+ }
+
+ if err := m.JobID.Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("jobID")
+ }
+ return err
+ }
+
+ return nil
+}
+
+func (m *JobInfo) validateStatus(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.Status) { // not required
+ return nil
+ }
+
+ if err := m.Status.Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("status")
+ }
+ return err
+ }
+
+ return nil
+}
+
+func (m *JobInfo) validateUpdated(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.Updated) { // not required
+ return nil
+ }
+
+ if err := validate.FormatOf("updated", "body", "date-time", m.Updated.String(), formats); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// MarshalBinary interface implementation
+func (m *JobInfo) MarshalBinary() ([]byte, error) {
+ if m == nil {
+ return nil, nil
+ }
+ return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *JobInfo) UnmarshalBinary(b []byte) error {
+ var res JobInfo
+ if err := swag.ReadJSON(b, &res); err != nil {
+ return err
+ }
+ *m = res
+ return nil
+}
diff --git a/job_pagination.go b/job_pagination.go
new file mode 100644
index 0000000..03088e0
--- /dev/null
+++ b/job_pagination.go
@@ -0,0 +1,30 @@
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+package weles
+
+// JobPagination holds information neccessary to request for a single page of data.
+// When JobID is set, and Forward is false - Controller should return a page of records before the
+// supplied JobID.
+// When JobID is set, and Forward is true - Controller should return page of record after the
+// supplied JobID.
+// In both cases, returned page should not include supplied JobID.
+// Limit denotes the number of records to be returned on the page.
+// When Limit is set to 0, pagination is disabled, JobID and Forward fields are ignored
+// and all records are returned.
+type JobPagination struct {
+ JobID JobID
+ Forward bool
+ Limit int32
+}
diff --git a/job_sort_by.go b/job_sort_by.go
new file mode 100644
index 0000000..dc3a366
--- /dev/null
+++ b/job_sort_by.go
@@ -0,0 +1,90 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package weles
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "encoding/json"
+
+ strfmt "github.com/go-openapi/strfmt"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/validate"
+)
+
+// JobSortBy denotes key for sorting Jobs list. Jobs are sorted by ID (Ascending) by default.
+// You can sort jobs additionaly by
+//
+// * CreatedDate - sorting by date of creation of the weles job.
+//
+// * UpdatedDate - sorting by date of update of the weles job.
+//
+// * JobStatus - sorting by the Job Status. Descending order will sort in the order JobStatuses are listed in the docs (from NEW at the start to CANCELED at the end). Ascending will reverse this order.
+// When sorting is applied, and there are many jobs with the same date/status, they will be sorted by JobID (Ascending)
+//
+// swagger:model JobSortBy
+type JobSortBy string
+
+const (
+
+ // JobSortByCreatedDate captures enum value "CreatedDate"
+ JobSortByCreatedDate JobSortBy = "CreatedDate"
+
+ // JobSortByUpdatedDate captures enum value "UpdatedDate"
+ JobSortByUpdatedDate JobSortBy = "UpdatedDate"
+
+ // JobSortByJobStatus captures enum value "JobStatus"
+ JobSortByJobStatus JobSortBy = "JobStatus"
+)
+
+// for schema
+var jobSortByEnum []interface{}
+
+func init() {
+ var res []JobSortBy
+ if err := json.Unmarshal([]byte(`["CreatedDate","UpdatedDate","JobStatus"]`), &res); err != nil {
+ panic(err)
+ }
+ for _, v := range res {
+ jobSortByEnum = append(jobSortByEnum, v)
+ }
+}
+
+func (m JobSortBy) validateJobSortByEnum(path, location string, value JobSortBy) error {
+ if err := validate.Enum(path, location, value, jobSortByEnum); err != nil {
+ return err
+ }
+ return nil
+}
+
+// Validate validates this job sort by
+func (m JobSortBy) Validate(formats strfmt.Registry) error {
+ var res []error
+
+ // value enum
+ if err := m.validateJobSortByEnum("", "body", m); err != nil {
+ return err
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
diff --git a/job_sorter.go b/job_sorter.go
new file mode 100644
index 0000000..efcdcdb
--- /dev/null
+++ b/job_sorter.go
@@ -0,0 +1,108 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package weles
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ strfmt "github.com/go-openapi/strfmt"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/swag"
+)
+
+// JobSorter defines the key for sorting as well as direction of sorting.
+//
+// swagger:model JobSorter
+type JobSorter struct {
+
+ // sort by
+ SortBy JobSortBy `json:"SortBy,omitempty"`
+
+ // sort order
+ SortOrder SortOrder `json:"SortOrder,omitempty"`
+}
+
+// Validate validates this job sorter
+func (m *JobSorter) Validate(formats strfmt.Registry) error {
+ var res []error
+
+ if err := m.validateSortBy(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateSortOrder(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+func (m *JobSorter) validateSortBy(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.SortBy) { // not required
+ return nil
+ }
+
+ if err := m.SortBy.Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("SortBy")
+ }
+ return err
+ }
+
+ return nil
+}
+
+func (m *JobSorter) validateSortOrder(formats strfmt.Registry) error {
+
+ if swag.IsZero(m.SortOrder) { // not required
+ return nil
+ }
+
+ if err := m.SortOrder.Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("SortOrder")
+ }
+ return err
+ }
+
+ return nil
+}
+
+// MarshalBinary interface implementation
+func (m *JobSorter) MarshalBinary() ([]byte, error) {
+ if m == nil {
+ return nil, nil
+ }
+ return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *JobSorter) UnmarshalBinary(b []byte) error {
+ var res JobSorter
+ if err := swag.ReadJSON(b, &res); err != nil {
+ return err
+ }
+ *m = res
+ return nil
+}
diff --git a/job_status.go b/job_status.go
new file mode 100644
index 0000000..45e0888
--- /dev/null
+++ b/job_status.go
@@ -0,0 +1,113 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package weles
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "encoding/json"
+
+ strfmt "github.com/go-openapi/strfmt"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/validate"
+)
+
+// JobStatus specifies state of the Job.
+//
+// * NEW - The new Job has been created.
+//
+// * PARSING - Provided yaml file is being parsed and interpreted.
+//
+// * DOWNLOADING - Images and/or files required for the test are being downloaded.
+//
+// * WAITING - Job is waiting for Boruta worker.
+//
+// * RUNNING - Job is being executed.
+//
+// * COMPLETED - Job is completed. This is terminal state.
+//
+// * FAILED - Job execution has failed. This is terminal state.
+//
+// * CANCELED -Job has been canceled with API call. This is terminal state.
+//
+// swagger:model JobStatus
+type JobStatus string
+
+const (
+
+ // JobStatusNEW captures enum value "NEW"
+ JobStatusNEW JobStatus = "NEW"
+
+ // JobStatusPARSING captures enum value "PARSING"
+ JobStatusPARSING JobStatus = "PARSING"
+
+ // JobStatusDOWNLOADING captures enum value "DOWNLOADING"
+ JobStatusDOWNLOADING JobStatus = "DOWNLOADING"
+
+ // JobStatusWAITING captures enum value "WAITING"
+ JobStatusWAITING JobStatus = "WAITING"
+
+ // JobStatusRUNNING captures enum value "RUNNING"
+ JobStatusRUNNING JobStatus = "RUNNING"
+
+ // JobStatusCOMPLETED captures enum value "COMPLETED"
+ JobStatusCOMPLETED JobStatus = "COMPLETED"
+
+ // JobStatusFAILED captures enum value "FAILED"
+ JobStatusFAILED JobStatus = "FAILED"
+
+ // JobStatusCANCELED captures enum value "CANCELED"
+ JobStatusCANCELED JobStatus = "CANCELED"
+)
+
+// for schema
+var jobStatusEnum []interface{}
+
+func init() {
+ var res []JobStatus
+ if err := json.Unmarshal([]byte(`["NEW","PARSING","DOWNLOADING","WAITING","RUNNING","COMPLETED","FAILED","CANCELED"]`), &res); err != nil {
+ panic(err)
+ }
+ for _, v := range res {
+ jobStatusEnum = append(jobStatusEnum, v)
+ }
+}
+
+func (m JobStatus) validateJobStatusEnum(path, location string, value JobStatus) error {
+ if err := validate.Enum(path, location, value, jobStatusEnum); err != nil {
+ return err
+ }
+ return nil
+}
+
+// Validate validates this job status
+func (m JobStatus) Validate(formats strfmt.Registry) error {
+ var res []error
+
+ // value enum
+ if err := m.validateJobStatusEnum("", "body", m); err != nil {
+ return err
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
diff --git a/job_status_extra.go b/job_status_extra.go
new file mode 100644
index 0000000..ed9968f
--- /dev/null
+++ b/job_status_extra.go
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+// File job_status_extra.go enhances generated JobStatus type.
+
+package weles
+
+// ToInt converts JobStatus to int.
+func (status JobStatus) ToInt() int {
+ switch status {
+ case JobStatusNEW:
+ return 1
+ case JobStatusPARSING:
+ return 2
+ case JobStatusDOWNLOADING:
+ return 3
+ case JobStatusWAITING:
+ return 4
+ case JobStatusRUNNING:
+ return 5
+ case JobStatusCOMPLETED:
+ return 6
+ case JobStatusFAILED:
+ return 7
+ case JobStatusCANCELED:
+ return 8
+ default:
+ return -1
+ }
+}
diff --git a/jobmanager.go b/jobmanager.go
index c5ce847..060493f 100644
--- a/jobmanager.go
+++ b/jobmanager.go
@@ -14,56 +14,10 @@
* limitations under the License
*/
-// File jobmanager.go provides JobManager interface with Job related
-// structures.
+// File jobmanager.go provides JobManager interface.
package weles
-import "time"
-
-// JobStatus specifies state of the Job.
-type JobStatus string
-
-const (
- // JOB_NEW - The new Job has been created.
- JOB_NEW JobStatus = "NEW"
- // JOB_PARSING - Provided yaml file is being parsed and interpreted.
- JOB_PARSING JobStatus = "PARSING"
- // JOB_DOWNLOADING - Images and/or files required for the test are being
- // downloaded.
- JOB_DOWNLOADING JobStatus = "DOWNLOADING"
- // JOB_WAITING - Job is waiting for Boruta worker.
- JOB_WAITING JobStatus = "WAITING"
- // JOB_RUNNING - Job is being executed.
- JOB_RUNNING JobStatus = "RUNNING"
- // JOB_COMPLETED - Job is completed.
- // This is a terminal state.
- JOB_COMPLETED JobStatus = "COMPLETED"
- // JOB_FAILED - Job execution has failed.
- // This is a terminal state.
- JOB_FAILED JobStatus = "FAILED"
- // JOB_CANCELED - Job has been canceled with API call.
- // This is a terminal state.
- JOB_CANCELED JobStatus = "CANCELED"
-)
-
-// JobInfo contains Job information available for public API.
-type JobInfo struct {
- // JobID is a unique Job identifier.
- JobID JobID
- // Name is the Job name acquired from yaml file during Job creation.
- Name string
- // Created is the Job creation time in UTC.
- Created time.Time
- // Updated is the time of latest Jobs' status modification.
- Updated time.Time
- // Status specifies current state of the Job.
- Status JobStatus
- // Info provides additional information about current state,
- // e.g. cause of failure.
- Info string
-}
-
// JobManager interface defines API for actions that can be called on Weles' Jobs
// by external modules. These methods are intended to be used by HTTP server.
type JobManager interface {
@@ -72,8 +26,11 @@ type JobManager interface {
CreateJob(yaml []byte) (JobID, error)
// CancelJob stops execution of Job identified by JobID.
CancelJob(JobID) error
- // ListJobs returns information on Jobs. If argument is a nil/empty slice
- // information about all Jobs is returned. Otherwise result is filtered
- // and contains information about requested Jobs only.
- ListJobs([]JobID) ([]JobInfo, error)
+ // ListJobs returns information on Jobs. It takes 3 arguments:
+ // - JobFilter containing filters
+ // - JobSorter containing sorting key and sorting direction
+ // - JobPagination containing element after/before which a page should be returned. It also
+ // contains information about direction of listing and the size of the returned page which
+ // must always be set.
+ ListJobs(JobFilter, JobSorter, JobPagination) ([]JobInfo, ListInfo, error)
}
diff --git a/list_info.go b/list_info.go
new file mode 100644
index 0000000..c9800d7
--- /dev/null
+++ b/list_info.go
@@ -0,0 +1,25 @@
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+package weles
+
+// ListInfo is a struct holding information to be returned by JobManager and ArtifactManager with
+// lists of data.
+// TotalRecords is returned to the user by API.
+// RemainingRecords is used by API internally to return correct HTTP status codes (206 partial
+// content and 200 ok).
+type ListInfo struct {
+ TotalRecords uint64
+ RemainingRecords uint64
+}
diff --git a/manager/dryad_job_runner_test.go b/manager/dryad_job_runner_test.go
index ceaaf26..9a82455 100644
--- a/manager/dryad_job_runner_test.go
+++ b/manager/dryad_job_runner_test.go
@@ -16,7 +16,7 @@
package manager
-//go:generate mockgen -package=manager -destination=mock_dryad_test.go git.tizen.org/tools/weles/manager/dryad SessionProvider,DeviceCommunicationProvider
+//go:generate ../bin/dev-tools/mockgen -package=manager -destination=mock_dryad_test.go git.tizen.org/tools/weles/manager/dryad SessionProvider,DeviceCommunicationProvider
import (
"context"
diff --git a/manager/mock_dryad_job_runner_test.go b/manager/mock_dryad_job_runner_test.go
index b433a84..f8d6c74 100644
--- a/manager/mock_dryad_job_runner_test.go
+++ b/manager/mock_dryad_job_runner_test.go
@@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT.
-// Source: manager/manager.go
+// Source: git.tizen.org/tools/weles/manager (interfaces: DryadJobRunner)
// Package manager is a generated GoMock package.
package manager
@@ -32,18 +32,6 @@ func (m *MockDryadJobRunner) EXPECT() *MockDryadJobRunnerMockRecorder {
return m.recorder
}
-// Deploy mocks base method
-func (m *MockDryadJobRunner) Deploy() error {
- ret := m.ctrl.Call(m, "Deploy")
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Deploy indicates an expected call of Deploy
-func (mr *MockDryadJobRunnerMockRecorder) Deploy() *gomock.Call {
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Deploy", reflect.TypeOf((*MockDryadJobRunner)(nil).Deploy))
-}
-
// Boot mocks base method
func (m *MockDryadJobRunner) Boot() error {
ret := m.ctrl.Call(m, "Boot")
@@ -56,6 +44,18 @@ func (mr *MockDryadJobRunnerMockRecorder) Boot() *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Boot", reflect.TypeOf((*MockDryadJobRunner)(nil).Boot))
}
+// Deploy mocks base method
+func (m *MockDryadJobRunner) Deploy() error {
+ ret := m.ctrl.Call(m, "Deploy")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// Deploy indicates an expected call of Deploy
+func (mr *MockDryadJobRunnerMockRecorder) Deploy() *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Deploy", reflect.TypeOf((*MockDryadJobRunner)(nil).Deploy))
+}
+
// Test mocks base method
func (m *MockDryadJobRunner) Test() error {
ret := m.ctrl.Call(m, "Test")
diff --git a/manager/mocks_generate.go b/manager/mocks_generate.go
new file mode 100644
index 0000000..8781111
--- /dev/null
+++ b/manager/mocks_generate.go
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package manager
+
+//go:generate ../bin/dev-tools/mockgen -package manager -destination=../manager/mock_dryad_test.go git.tizen.org/tools/weles/manager/dryad SessionProvider,DeviceCommunicationProvider
+
+//go:generate ../bin/dev-tools/mockgen -package manager -destination=../manager/mock_dryad_job_runner_test.go git.tizen.org/tools/weles/manager DryadJobRunner
diff --git a/mock/artifactmanager.go b/mock/artifactmanager.go
index eaa2ae7..848b5fb 100644
--- a/mock/artifactmanager.go
+++ b/mock/artifactmanager.go
@@ -33,6 +33,18 @@ func (m *MockArtifactManager) EXPECT() *MockArtifactManagerMockRecorder {
return m.recorder
}
+// Close mocks base method
+func (m *MockArtifactManager) Close() error {
+ ret := m.ctrl.Call(m, "Close")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// Close indicates an expected call of Close
+func (mr *MockArtifactManagerMockRecorder) Close() *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockArtifactManager)(nil).Close))
+}
+
// CreateArtifact mocks base method
func (m *MockArtifactManager) CreateArtifact(arg0 weles.ArtifactDescription) (weles.ArtifactPath, error) {
ret := m.ctrl.Call(m, "CreateArtifact", arg0)
@@ -60,16 +72,17 @@ func (mr *MockArtifactManagerMockRecorder) GetArtifactInfo(arg0 interface{}) *go
}
// ListArtifact mocks base method
-func (m *MockArtifactManager) ListArtifact(arg0 weles.ArtifactFilter) ([]weles.ArtifactInfo, error) {
- ret := m.ctrl.Call(m, "ListArtifact", arg0)
+func (m *MockArtifactManager) ListArtifact(arg0 weles.ArtifactFilter, arg1 weles.ArtifactSorter, arg2 weles.ArtifactPagination) ([]weles.ArtifactInfo, weles.ListInfo, error) {
+ ret := m.ctrl.Call(m, "ListArtifact", arg0, arg1, arg2)
ret0, _ := ret[0].([]weles.ArtifactInfo)
- ret1, _ := ret[1].(error)
- return ret0, ret1
+ ret1, _ := ret[1].(weles.ListInfo)
+ ret2, _ := ret[2].(error)
+ return ret0, ret1, ret2
}
// ListArtifact indicates an expected call of ListArtifact
-func (mr *MockArtifactManagerMockRecorder) ListArtifact(arg0 interface{}) *gomock.Call {
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListArtifact", reflect.TypeOf((*MockArtifactManager)(nil).ListArtifact), arg0)
+func (mr *MockArtifactManagerMockRecorder) ListArtifact(arg0, arg1, arg2 interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListArtifact", reflect.TypeOf((*MockArtifactManager)(nil).ListArtifact), arg0, arg1, arg2)
}
// PushArtifact mocks base method
diff --git a/mock/jobmanager.go b/mock/jobmanager.go
index 31ab6bc..3b821ad 100644
--- a/mock/jobmanager.go
+++ b/mock/jobmanager.go
@@ -59,14 +59,15 @@ func (mr *MockJobManagerMockRecorder) CreateJob(arg0 interface{}) *gomock.Call {
}
// ListJobs mocks base method
-func (m *MockJobManager) ListJobs(arg0 []weles.JobID) ([]weles.JobInfo, error) {
- ret := m.ctrl.Call(m, "ListJobs", arg0)
+func (m *MockJobManager) ListJobs(arg0 weles.JobFilter, arg1 weles.JobSorter, arg2 weles.JobPagination) ([]weles.JobInfo, weles.ListInfo, error) {
+ ret := m.ctrl.Call(m, "ListJobs", arg0, arg1, arg2)
ret0, _ := ret[0].([]weles.JobInfo)
- ret1, _ := ret[1].(error)
- return ret0, ret1
+ ret1, _ := ret[1].(weles.ListInfo)
+ ret2, _ := ret[2].(error)
+ return ret0, ret1, ret2
}
// ListJobs indicates an expected call of ListJobs
-func (mr *MockJobManagerMockRecorder) ListJobs(arg0 interface{}) *gomock.Call {
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListJobs", reflect.TypeOf((*MockJobManager)(nil).ListJobs), arg0)
+func (mr *MockJobManagerMockRecorder) ListJobs(arg0, arg1, arg2 interface{}) *gomock.Call {
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListJobs", reflect.TypeOf((*MockJobManager)(nil).ListJobs), arg0, arg1, arg2)
}
diff --git a/mock/mocks_generate.go b/mock/mocks_generate.go
new file mode 100644
index 0000000..76d531d
--- /dev/null
+++ b/mock/mocks_generate.go
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package mock
+
+//go:generate ../bin/dev-tools/mockgen -package mock -destination=./artifactmanager.go git.tizen.org/tools/weles ArtifactManager
+
+//go:generate ../bin/dev-tools/mockgen -package mock -destination=./dryadjobmanager.go git.tizen.org/tools/weles DryadJobManager
+
+//go:generate ../bin/dev-tools/mockgen -package mock -destination=./jobmanager.go git.tizen.org/tools/weles JobManager
+
+//go:generate ../bin/dev-tools/mockgen -package mock -destination=./parser.go git.tizen.org/tools/weles Parser
diff --git a/server/artifact_lister_handler.go b/server/artifact_lister_handler.go
new file mode 100644
index 0000000..33432ac
--- /dev/null
+++ b/server/artifact_lister_handler.go
@@ -0,0 +1,170 @@
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+package server
+
+import (
+ "github.com/go-openapi/runtime/middleware"
+
+ "git.tizen.org/tools/weles"
+ "git.tizen.org/tools/weles/server/operations/artifacts"
+)
+
+// ArtifactLister is a handler which passess requests for listing artifacts to artifactmanager.
+func (a *APIDefaults) ArtifactLister(params artifacts.ArtifactListerParams) middleware.Responder {
+ if (params.After != nil) && (params.Before != nil) {
+ return artifacts.NewArtifactListerBadRequest().WithPayload(&weles.ErrResponse{
+ Message: weles.ErrBeforeAfterNotAllowed.Error()})
+ }
+
+ artifactInfoReceived := []weles.ArtifactInfo{}
+ listInfo := weles.ListInfo{}
+ var err error
+ paginator := weles.ArtifactPagination{}
+ if a.PageLimit != 0 {
+ paginator = setArtifactPaginator(params, a.PageLimit)
+ }
+
+ if params.ArtifactFilterAndSort != nil {
+ artifactInfoReceived, listInfo, err = a.Managers.AM.ListArtifact(
+ *params.ArtifactFilterAndSort.Filter, *params.ArtifactFilterAndSort.Sorter, paginator)
+ } else {
+ artifactInfoReceived, listInfo, err = a.Managers.AM.ListArtifact(
+ weles.ArtifactFilter{}, weles.ArtifactSorter{}, paginator)
+ }
+
+ // TODO: remove this when artifactmanager will return this.
+ if len(artifactInfoReceived) == 0 {
+ return artifacts.NewArtifactListerNotFound().WithPayload(
+ &weles.ErrResponse{Message: weles.ErrArtifactNotFound.Error()})
+ }
+
+ switch err {
+ default:
+ return artifacts.NewArtifactListerInternalServerError().WithPayload(
+ &weles.ErrResponse{Message: err.Error()})
+ case weles.ErrArtifactNotFound:
+ return artifacts.NewArtifactListerNotFound().WithPayload(
+ &weles.ErrResponse{Message: weles.ErrArtifactNotFound.Error()})
+ case nil:
+ }
+
+ artifactInfoReturned := artifactInfoReceivedToReturn(artifactInfoReceived)
+
+ if (listInfo.RemainingRecords == 0) || (a.PageLimit == 0) { //last page...
+ return responderArtifact200(listInfo, paginator, artifactInfoReturned, a.PageLimit)
+ } //not last page...
+ return responderArtifact206(listInfo, paginator, artifactInfoReturned, a.PageLimit)
+
+}
+
+func responderArtifact206(
+ listInfo weles.ListInfo,
+ paginator weles.ArtifactPagination,
+ artifactInfoReturned []*weles.ArtifactInfo,
+ defaultPageLimit int32) (responder *artifacts.ArtifactListerPartialContent) {
+ var artifactListerURL artifacts.ArtifactListerURL
+
+ responder = artifacts.NewArtifactListerPartialContent()
+ responder.SetTotalRecords(listInfo.TotalRecords)
+ responder.SetRemainingRecords(listInfo.RemainingRecords)
+
+ tmp := artifactInfoReturned[len(artifactInfoReturned)-1].ID
+ artifactListerURL.After = &tmp
+
+ if defaultPageLimit != paginator.Limit {
+ tmp := int32(paginator.Limit)
+ artifactListerURL.Limit = &tmp
+ }
+ responder.SetNext(artifactListerURL.String())
+
+ if paginator.ID != 0 { //... and not the first
+ //paginator.ID is from query parameter not artifactmanager
+ var artifactListerURL artifacts.ArtifactListerURL
+ tmp = artifactInfoReturned[0].ID
+ artifactListerURL.Before = &tmp
+ if defaultPageLimit != paginator.Limit {
+ tmp := int32(paginator.Limit)
+ artifactListerURL.Limit = &tmp
+ }
+ responder.SetPrevious(artifactListerURL.String())
+ }
+ responder.SetPayload(artifactInfoReturned)
+ return
+}
+
+func responderArtifact200(
+ listInfo weles.ListInfo,
+ paginator weles.ArtifactPagination,
+ artifactInfoReturned []*weles.ArtifactInfo,
+ defaultPageLimit int32) (responder *artifacts.ArtifactListerOK) {
+
+ var artifactListerURL artifacts.ArtifactListerURL
+ responder = artifacts.NewArtifactListerOK()
+ responder.SetTotalRecords(listInfo.TotalRecords)
+ if paginator.ID != 0 { //not the first page
+ // keep in mind that ArtifactPath in paginator is taken from query parameter,
+ // not ArtifactManager
+ if paginator.Forward == true {
+ tmp := artifactInfoReturned[0].ID
+ artifactListerURL.Before = &tmp
+ if defaultPageLimit != paginator.Limit {
+ tmp := int32(paginator.Limit)
+ artifactListerURL.Limit = &tmp
+ }
+ responder.SetPrevious(artifactListerURL.String())
+ }
+ if paginator.Forward == false {
+ tmp := artifactInfoReturned[len(artifactInfoReturned)-1].ID
+ artifactListerURL.After = &tmp
+ if defaultPageLimit != paginator.Limit {
+ tmp2 := int32(paginator.Limit)
+ artifactListerURL.Limit = &tmp2
+ }
+ responder.SetNext(artifactListerURL.String())
+ }
+ }
+ responder.SetPayload(artifactInfoReturned)
+ return
+}
+
+func setArtifactPaginator(
+ params artifacts.ArtifactListerParams,
+ defaultPageLimit int32) (paginator weles.ArtifactPagination) {
+ paginator.Forward = true
+ if params.After != nil {
+ paginator.ID = *params.After
+ } else if params.Before != nil {
+ paginator.ID = *params.Before
+ paginator.Forward = false
+ }
+ if params.Limit == nil {
+ paginator.Limit = defaultPageLimit
+ } else {
+ paginator.Limit = *params.Limit
+ }
+ return paginator
+}
+
+// artifactInfoReceivedToReturn does the same thing as jobInfoReceivedToReturn.
+// TODO:make ArtifactInfos and JobInfos types implement interface with a function that will return
+// slice of pointers. Will probably need to use reflect which I'm not familiar with thus not done now.
+func artifactInfoReceivedToReturn(artifactInfoReceived []weles.ArtifactInfo) []*weles.ArtifactInfo {
+ artifactInfoReturned := make([]*weles.ArtifactInfo, len(artifactInfoReceived))
+ for i := range artifactInfoReceived {
+ artifactInfoReturned[i] = &artifactInfoReceived[i]
+ }
+ return artifactInfoReturned
+
+}
diff --git a/server/artifact_lister_handler_test.go b/server/artifact_lister_handler_test.go
new file mode 100644
index 0000000..e70fe7d
--- /dev/null
+++ b/server/artifact_lister_handler_test.go
@@ -0,0 +1,634 @@
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+package server_test
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "strconv"
+ "strings"
+
+ "github.com/golang/mock/gomock"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/ginkgo/extensions/table"
+ . "github.com/onsi/gomega"
+
+ "git.tizen.org/tools/weles"
+ "git.tizen.org/tools/weles/fixtures"
+ "git.tizen.org/tools/weles/mock"
+ "git.tizen.org/tools/weles/server"
+)
+
+var _ = Describe("ArtifactListerHandler", func() {
+ var (
+ mockCtrl *gomock.Controller
+ apiDefaults *server.APIDefaults
+ mockArtifactManager *mock.MockArtifactManager
+ testserver *httptest.Server
+ )
+
+ BeforeEach(func() {
+ mockCtrl, _, mockArtifactManager, apiDefaults, testserver = testServerSetup()
+ })
+
+ AfterEach(func() {
+ mockCtrl.Finish()
+ testserver.Close()
+ })
+ Describe("Listing artifacts", func() {
+ createRequest := func(
+ reqBody io.Reader,
+ path string,
+ query string,
+ contentH string,
+ acceptH string) (req *http.Request) {
+ if path == "" {
+ path = "/api/v1/artifacts/list"
+ }
+ req, err := http.NewRequest(http.MethodPost, testserver.URL+path+query, reqBody)
+ Expect(err).ToNot(HaveOccurred())
+ req.Header.Set("Content-Type", contentH)
+ req.Header.Set("Accept", acceptH)
+ return req
+ }
+
+ filterSorterReqBody := func(
+ filter weles.ArtifactFilter,
+ sorter weles.ArtifactSorter,
+ contentH string) (rb *bytes.Reader) {
+
+ artifactFilterSort := weles.ArtifactFilterAndSort{
+ Filter: &filter,
+ Sorter: &sorter}
+
+ artifactFilterSortMarshalled, err := json.Marshal(artifactFilterSort)
+ Expect(err).ToNot(HaveOccurred())
+
+ return bytes.NewReader(artifactFilterSortMarshalled)
+
+ }
+
+ checkReceivedArtifactInfo := func(respBody []byte, artifactInfo []weles.ArtifactInfo, acceptH string) {
+ artifactInfoMarshalled, err := json.Marshal(artifactInfo)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(string(respBody)).To(MatchJSON(string(artifactInfoMarshalled)))
+
+ }
+
+ checkReceivedArtifactErr := func(respBody []byte, e error, acceptH string) {
+ errMarshalled, err := json.Marshal(weles.ErrResponse{
+ Message: e.Error(),
+ Type: ""})
+ Expect(err).ToNot(HaveOccurred())
+ Expect(string(respBody)).To(MatchJSON(string(errMarshalled)))
+ }
+
+ //few structs to test against
+ emptyFilterA := weles.ArtifactFilter{}
+
+ filledFilterA1 := weles.ArtifactFilter{
+ Alias: []weles.ArtifactAlias{"sdaaa", "aalliass"},
+ JobID: []weles.JobID{1, 43, 3},
+ Status: []weles.ArtifactStatus{
+ weles.ArtifactStatusDOWNLOADING,
+ weles.ArtifactStatusREADY},
+ Type: []weles.ArtifactType{
+ weles.ArtifactTypeRESULT,
+ weles.ArtifactTypeYAML}}
+
+ filledFilterA2 := weles.ArtifactFilter{
+ Alias: []weles.ArtifactAlias{"aalliass"},
+ JobID: []weles.JobID{1, 43, 3, 9, 2, 10404},
+ Status: []weles.ArtifactStatus{
+ weles.ArtifactStatusFAILED},
+ Type: []weles.ArtifactType{
+ weles.ArtifactTypeRESULT,
+ weles.ArtifactTypeYAML}}
+
+ filledSorterA1 := weles.ArtifactSorter{
+ SortBy: weles.ArtifactSortByID,
+ SortOrder: weles.SortOrderAscending}
+
+ filledSorterA2 := weles.ArtifactSorter{
+ SortBy: weles.ArtifactSortByID}
+
+ emptySorterA := weles.ArtifactSorter{}
+
+ emptyPaginatorA := weles.ArtifactPagination{}
+ emptyPaginatorFw := weles.ArtifactPagination{Forward: true}
+ artifactInfo420 := fixtures.CreateArtifactInfoSlice(420)
+
+ type queryPaginator struct {
+ query string
+ paginator weles.ArtifactPagination
+ }
+
+ queryPaginatorOK := []queryPaginator{
+ {
+ query: "",
+ paginator: emptyPaginatorFw},
+ {
+ query: "?before=10",
+ paginator: weles.ArtifactPagination{
+ ID: int64(10),
+ Forward: false}},
+ {
+ query: "?before=30&limit=50",
+ paginator: weles.ArtifactPagination{
+ ID: int64(30),
+ Forward: false,
+ Limit: 50}},
+ {
+ query: "?after=40",
+ paginator: weles.ArtifactPagination{
+ ID: int64(40),
+ Forward: true}},
+ {
+ query: "?after=70",
+ paginator: weles.ArtifactPagination{
+ ID: int64(70),
+ Forward: true,
+ Limit: 200}},
+ {
+ query: "?limit=50",
+ paginator: weles.ArtifactPagination{
+ ID: int64(0),
+ Forward: true,
+ Limit: 50}}}
+
+ Context("a: Server receives correct request and has pagination turned off", func() {
+ // ginkgo does not like It clauses with the same name. To avoid conflicts with
+ // tests of listing jobs, each it clause not referring to artifacts explicitly
+ // is prefixed with a:
+ for _, curr := range queryPaginatorOK { //expected behaviour- handler should ignore
+ // different queries when pagination is turned off globally.
+ DescribeTable("should respond with all avaliable artifacts, ignoring query params",
+ func(
+ filter weles.ArtifactFilter,
+ sorter weles.ArtifactSorter,
+ query string) {
+ apiDefaults.PageLimit = 0
+
+ listInfo := weles.ListInfo{
+ TotalRecords: uint64(len(artifactInfo420)),
+ RemainingRecords: 0}
+
+ mockArtifactManager.EXPECT().ListArtifact(
+ filter, sorter, emptyPaginatorA).Return(
+ artifactInfo420, listInfo, nil)
+
+ reqBody := filterSorterReqBody(filter, sorter, JSON)
+
+ client := testserver.Client()
+ req := createRequest(reqBody, "", query, JSON, JSON)
+ resp, err := client.Do(req)
+ Expect(err).ToNot(HaveOccurred())
+
+ defer resp.Body.Close()
+ respBody, err := ioutil.ReadAll(resp.Body)
+ Expect(err).ToNot(HaveOccurred())
+
+ checkReceivedArtifactInfo(respBody, artifactInfo420, JSON)
+
+ Expect(resp.StatusCode).To(Equal(200))
+ Expect(resp.Header.Get("Next")).To(Equal(""))
+ Expect(resp.Header.Get("Previous")).To(Equal(""))
+ Expect(resp.Header.Get("RemainingRecords")).To(Equal(""))
+ Expect(resp.Header.Get("TotalRecords")).To(Equal(strconv.Itoa(len(artifactInfo420))))
+
+ },
+
+ Entry("a: given empty filter and sorter",
+ emptyFilterA, emptySorterA, curr.query),
+
+ Entry("a: given filled filter and sorter",
+ filledFilterA2, filledSorterA2, curr.query),
+
+ Entry("a: given filled filter and empty sorter",
+ filledFilterA1, emptySorterA, curr.query),
+
+ Entry("a: given empty filter and filled sorter",
+ emptyFilterA, filledSorterA1, curr.query),
+ )
+ }
+ })
+
+ Context("a: Server receives correct request and has pagination turned on", func() {
+ artifactInfoAll := fixtures.CreateArtifactInfoSlice(100)
+ globalLimit := []int32{70, 100, 111}
+ for _, currgl := range globalLimit {
+ for _, curr := range queryPaginatorOK {
+ // expected behaviour- handler passes query parameters to AM.
+ // this should impact data returned by JM. It is not reflected in the
+ // below mock of JM as it is out of scope of server unit tests.
+ DescribeTable("a: should respond with all artifacts",
+ func(artifactInfo []weles.ArtifactInfo,
+ filter weles.ArtifactFilter,
+ sorter weles.ArtifactSorter,
+ paginator weles.ArtifactPagination,
+ query string, gl int32) {
+
+ apiDefaults.PageLimit = gl
+
+ if !strings.Contains(query, "limit") {
+ paginator.Limit = apiDefaults.PageLimit
+ }
+
+ listInfo := weles.ListInfo{
+ TotalRecords: uint64(len(artifactInfo)),
+ RemainingRecords: 0}
+
+ mockArtifactManager.EXPECT().ListArtifact(
+ filter, sorter, paginator).Return(
+ artifactInfo, listInfo, nil)
+
+ reqBody := filterSorterReqBody(filter, sorter, JSON)
+ req := createRequest(reqBody, "", query, JSON, JSON)
+
+ client := testserver.Client()
+ resp, err := client.Do(req)
+ Expect(err).ToNot(HaveOccurred())
+
+ defer resp.Body.Close()
+ respBody, err := ioutil.ReadAll(resp.Body)
+ Expect(err).ToNot(HaveOccurred())
+
+ checkReceivedArtifactInfo(respBody, artifactInfo, JSON)
+
+ Expect(resp.StatusCode).To(Equal(200))
+ // Next and Previous headers are ignored here as they are tested in other context.
+ Expect(resp.Header.Get("RemainingRecords")).To(Equal(""))
+ Expect(resp.Header.Get("TotalRecords")).To(
+ Equal(strconv.Itoa(len(artifactInfo))))
+ },
+ Entry("given empty request when AM has less jobs than page size",
+ artifactInfoAll[:40], emptyFilterA, emptySorterA, curr.paginator, curr.query, currgl),
+
+ Entry("given filled filter, when AM returns less jobs than Default Page size",
+ artifactInfoAll[10:67], filledFilterA2, emptySorterA, curr.paginator, curr.query, currgl),
+
+ Entry("given filled filter, when AM returns same amount of filtered jobs as Default Page Size",
+ artifactInfoAll, filledFilterA1, filledSorterA2, curr.paginator, curr.query, currgl),
+ )
+ }
+ }
+ })
+
+ Context("a: Pagination on", func() {
+ artifactInfoAll := fixtures.CreateArtifactInfoSlice(400)
+ DescribeTable("a: paginating forward",
+ func(artifactInfo []weles.ArtifactInfo,
+ startingPageNo int,
+ filter weles.ArtifactFilter,
+ sorter weles.ArtifactSorter) {
+
+ apiDefaults.PageLimit = 100
+
+ //prepare data for first call
+ artifactInfoStartingPage := artifactInfo[(startingPageNo-1)*
+ int(apiDefaults.PageLimit) : startingPageNo*
+ int(apiDefaults.PageLimit)] //first page of data
+
+ startingPageQuery := ""
+
+ paginator := weles.ArtifactPagination{Limit: apiDefaults.PageLimit, Forward: true}
+ if startingPageNo != 1 {
+ paginator.Forward = true
+ paginator.ID = artifactInfoStartingPage[0].ID
+ startingPageQuery = "?after=" +
+ strconv.FormatInt(artifactInfo[(startingPageNo-1)*int(apiDefaults.PageLimit)].ID, 10)
+ }
+
+ listInfo := weles.ListInfo{TotalRecords: uint64(len(artifactInfo)), RemainingRecords: uint64(len(artifactInfo) - startingPageNo*len(artifactInfoStartingPage))}
+
+ first := mockArtifactManager.EXPECT().ListArtifact(filter, sorter, paginator).Return(artifactInfoStartingPage, listInfo, nil)
+
+ reqBody := filterSorterReqBody(filter, sorter, JSON)
+ req := createRequest(reqBody, "", startingPageQuery, JSON, JSON)
+ req.Close = true
+ client := testserver.Client()
+ resp, err := client.Do(req)
+ Expect(err).ToNot(HaveOccurred())
+
+ respBody, err := ioutil.ReadAll(resp.Body)
+ Expect(err).ToNot(HaveOccurred())
+
+ checkReceivedArtifactInfo(respBody, artifactInfoStartingPage, JSON)
+ Expect(resp.StatusCode).To(Equal(206))
+
+ Expect(resp.Header.Get("Next")).To(Equal("/api/v1/artifacts/list" + "?after=" +
+ strconv.FormatInt(artifactInfoStartingPage[apiDefaults.PageLimit-1].ID, 10)))
+ prevCheck := ""
+ if startingPageNo != 1 {
+ prevCheck = "/api/v1/artifacts/list" + "?before=" + strconv.FormatInt(artifactInfoStartingPage[0].ID, 10)
+ }
+ Expect(resp.Header.Get("Previous")).To(Equal(prevCheck))
+ Expect(resp.Header.Get("RemainingRecords")).To(Equal(strconv.Itoa(len(artifactInfo) - (startingPageNo * len(artifactInfoStartingPage)))))
+ Expect(resp.Header.Get("TotalRecords")).To(Equal(strconv.Itoa(len(artifactInfo))))
+
+ nextPage := resp.Header.Get("Next")
+ resp.Body.Close()
+ testserver.CloseClientConnections()
+
+ //prepare data for second call based on previous
+ var artifactInfo2 []weles.ArtifactInfo
+ var secondReturnCode int
+ if (len(artifactInfo) - startingPageNo*int(apiDefaults.PageLimit)) <= int(apiDefaults.PageLimit) {
+
+ artifactInfo2 = artifactInfo[startingPageNo*int(apiDefaults.PageLimit):] //next page is not full
+ secondReturnCode = 200
+ } else {
+ artifactInfo2 = artifactInfo[startingPageNo*int(apiDefaults.PageLimit) : (startingPageNo+1)*int(apiDefaults.PageLimit)] //last page is full
+ secondReturnCode = 206
+ }
+
+ paginator2 := weles.ArtifactPagination{
+ Limit: apiDefaults.PageLimit,
+ Forward: true,
+ ID: artifactInfoStartingPage[int(apiDefaults.PageLimit)-
+ 1].ID}
+ listInfo2 := weles.ListInfo{TotalRecords: listInfo.TotalRecords}
+
+ if tmp := len(artifactInfo) - (startingPageNo+1)*int(apiDefaults.PageLimit); tmp < 0 {
+ listInfo2.RemainingRecords = 0
+ } else {
+ listInfo2.RemainingRecords = uint64(tmp)
+ }
+ //filter and sorter should stay the same.
+ mockArtifactManager.EXPECT().ListArtifact(filter, sorter, paginator2).Return(artifactInfo2, listInfo2, nil).After(first)
+
+ client2 := testserver.Client()
+ req2 := createRequest(reqBody, nextPage, "", JSON, JSON)
+ req2.Close = true
+ resp2, err := client2.Do(req2)
+ Expect(err).ToNot(HaveOccurred())
+
+ defer resp2.Body.Close()
+ respBody2, err := ioutil.ReadAll(resp2.Body)
+ Expect(err).ToNot(HaveOccurred())
+
+ checkReceivedArtifactInfo(respBody2, artifactInfo2, JSON)
+
+ Expect(resp2.StatusCode).To(Equal(secondReturnCode))
+
+ if secondReturnCode == 200 {
+ Expect(resp2.Header.Get("Next")).To(Equal(""))
+ prevCheck = "/api/v1/artifacts/list" + "?before=" +
+ strconv.FormatInt(artifactInfo2[0].ID, 10)
+ Expect(resp2.Header.Get("Previous")).To(Equal(prevCheck))
+ } else {
+ prevCheck = "/api/v1/artifacts/list" + "?before=" +
+ strconv.FormatInt(artifactInfo2[0].ID, 10)
+ nextCheck := "/api/v1/artifacts/list" + "?after=" +
+ strconv.FormatInt(artifactInfo2[int(apiDefaults.PageLimit)-1].ID, 10)
+ Expect(resp2.Header.Get("Next")).To(Equal(nextCheck))
+ Expect(resp2.Header.Get("Previous")).To(Equal(prevCheck))
+ }
+ if tmp := strconv.Itoa(len(artifactInfo) -
+ startingPageNo*len(artifactInfoStartingPage) -
+ len(artifactInfo2)); tmp != "0" {
+ Expect(resp2.Header.Get("RemainingRecords")).To(Equal(tmp))
+ } else {
+
+ Expect(resp2.Header.Get("RemainingRecords")).To(Equal(""))
+ }
+ Expect(resp2.Header.Get("TotalRecords")).To(
+ Equal(strconv.Itoa(len(artifactInfo))))
+
+ },
+ Entry("a: 1->2/2", // from 1 to 2 out of 2 (pages)
+ artifactInfoAll[:170], 1, emptyFilterA, emptySorterA),
+ Entry("a: 1->2/3",
+ artifactInfoAll[:270], 1, emptyFilterA, emptySorterA),
+ Entry("a: 2->3/3",
+ artifactInfoAll[:300], 2, emptyFilterA, emptySorterA),
+ Entry("a: 2->3/4",
+ artifactInfoAll[:350], 2, emptyFilterA, emptySorterA),
+ )
+
+ DescribeTable("a: paginating backward",
+ func(artifactInfo []weles.ArtifactInfo,
+ startingPageNo int, pages int,
+ filter weles.ArtifactFilter,
+ sorter weles.ArtifactSorter) {
+
+ apiDefaults.PageLimit = 100
+ paginator := weles.ArtifactPagination{Limit: apiDefaults.PageLimit}
+ //prepare data for first call
+ artifactInfoStartingPage := []weles.ArtifactInfo{}
+ var startingPageQuery string
+ listInfo := weles.ListInfo{}
+ if startingPageNo == pages {
+ artifactInfoStartingPage = artifactInfo[(startingPageNo-1)*
+ int(apiDefaults.PageLimit) : len(artifactInfo)-1]
+ paginator.Forward = true
+ paginator.ID = artifactInfoStartingPage[len(artifactInfoStartingPage)-1].ID
+ startingPageQuery = "?after=" + strconv.FormatInt(paginator.ID, 10)
+ listInfo = weles.ListInfo{
+ TotalRecords: uint64(len(artifactInfo)),
+ RemainingRecords: 0}
+
+ } else {
+ artifactInfoStartingPage = artifactInfo[(startingPageNo)*int(apiDefaults.PageLimit) : (startingPageNo+1)*
+ int(apiDefaults.PageLimit)] //first page of data
+ paginator.Forward = false
+ paginator.ID = artifactInfo[(startingPageNo*int(apiDefaults.PageLimit))-1].ID
+ startingPageQuery = "?before=" + strconv.FormatInt(paginator.ID, 10)
+ listInfo = weles.ListInfo{
+ TotalRecords: uint64(len(artifactInfo)),
+ RemainingRecords: uint64(len(artifactInfo) - (int(apiDefaults.PageLimit) * startingPageNo))}
+
+ }
+
+ first := mockArtifactManager.EXPECT().ListArtifact(filter, sorter, paginator).Return(artifactInfoStartingPage, listInfo, nil)
+
+ reqBody := filterSorterReqBody(filter, sorter, JSON)
+ req := createRequest(reqBody, "", startingPageQuery, JSON, JSON)
+ req.Close = true
+ client := testserver.Client()
+ resp, err := client.Do(req)
+ Expect(err).ToNot(HaveOccurred())
+
+ respBody, err := ioutil.ReadAll(resp.Body)
+ Expect(err).ToNot(HaveOccurred())
+
+ checkReceivedArtifactInfo(respBody, artifactInfoStartingPage, JSON)
+
+ if startingPageNo == pages {
+ Expect(resp.StatusCode).To(Equal(200))
+ Expect(resp.Header.Get("Previous")).To(Equal("/api/v1/artifacts/list?before=" + strconv.FormatInt(artifactInfoStartingPage[0].ID, 10)))
+ Expect(resp.Header.Get("Next")).To(Equal(""))
+ Expect(resp.Header.Get("TotalRecords")).To(Equal(strconv.Itoa(len(artifactInfo))))
+ Expect(resp.Header.Get("RemainingRecords")).To(Equal(""))
+
+ } else {
+ Expect(resp.StatusCode).To(Equal(206))
+ Expect(resp.Header.Get("Previous")).To(Equal("/api/v1/artifacts/list?before=" + strconv.FormatInt(artifactInfoStartingPage[0].ID, 10)))
+ Expect(resp.Header.Get("Next")).To(Equal("/api/v1/artifacts/list?after=" + strconv.FormatInt(artifactInfoStartingPage[len(artifactInfoStartingPage)-1].ID, 10)))
+ Expect(resp.Header.Get("TotalRecords")).To(Equal(strconv.Itoa(len(artifactInfo))))
+ Expect(resp.Header.Get("RemainingRecords")).To(Equal(strconv.FormatUint(listInfo.RemainingRecords, 10)))
+ }
+
+ prevPage := resp.Header.Get("Previous")
+
+ resp.Body.Close()
+ testserver.CloseClientConnections()
+
+ //prepare data for second call based on previous
+
+ artifactInfo2 := []weles.ArtifactInfo{}
+ paginator2 := weles.ArtifactPagination{Limit: apiDefaults.PageLimit, Forward: false, ID: artifactInfoStartingPage[0].ID}
+ listInfo2 := weles.ListInfo{TotalRecords: listInfo.TotalRecords}
+ if startingPageNo == pages {
+ artifactInfo2 = artifactInfo[(startingPageNo-2)*int(apiDefaults.PageLimit) : (startingPageNo-1)*int(apiDefaults.PageLimit)]
+ if startingPageNo-1 == 1 {
+ listInfo2.RemainingRecords = 0
+ } else {
+ listInfo2.RemainingRecords = uint64((pages - (startingPageNo - 1)) * int(apiDefaults.PageLimit))
+ }
+ } else {
+ artifactInfo2 = artifactInfo[(startingPageNo-1)*int(apiDefaults.PageLimit) : startingPageNo*int(apiDefaults.PageLimit)]
+ listInfo2.RemainingRecords = uint64(apiDefaults.PageLimit)
+ }
+
+ mockArtifactManager.EXPECT().ListArtifact(filter, sorter, paginator2).Return(artifactInfo2, listInfo2, nil).After(first)
+ client2 := testserver.Client()
+ req2 := createRequest(reqBody, prevPage, "", JSON, JSON)
+ req2.Close = true
+ resp2, err := client2.Do(req2)
+ Expect(err).ToNot(HaveOccurred())
+
+ defer resp2.Body.Close()
+ respBody2, err := ioutil.ReadAll(resp2.Body)
+ Expect(err).ToNot(HaveOccurred())
+
+ checkReceivedArtifactInfo(respBody2, artifactInfo2, JSON)
+ if startingPageNo == pages {
+
+ if startingPageNo-1 == 1 {
+ Expect(resp2.StatusCode).To(Equal(200))
+ Expect(resp2.Header.Get("RemainingRecords")).To(Equal(""))
+ Expect(resp2.Header.Get("Previous")).To(Equal(""))
+
+ } else {
+ Expect(resp2.StatusCode).To(Equal(206))
+ Expect(resp2.Header.Get("RemainingRecords")).To(Equal("100"))
+ Expect(resp2.Header.Get("Previous")).To(Equal("/api/v1/artifacts/list?before=" + strconv.FormatInt(artifactInfo2[0].ID, 10)))
+ }
+ } else {
+ Expect(resp2.StatusCode).To(Equal(206))
+ Expect(resp2.Header.Get("RemainingRecords")).To(Equal("100"))
+ }
+
+ Expect(resp2.Header.Get("Next")).To(Equal("/api/v1/artifacts/list?after=" + strconv.FormatInt(artifactInfo2[len(artifactInfo2)-1].ID, 10)))
+ Expect(resp2.Header.Get("TotalRecords")).To(Equal(strconv.Itoa(len(artifactInfo))))
+ },
+ Entry("a: 2->1/2",
+ artifactInfoAll[:170], 2, 2, emptyFilterA, emptySorterA),
+ Entry("a: 2->1/3",
+ artifactInfoAll[:270], 2, 3, emptyFilterA, emptySorterA),
+ Entry("a: 3->2/4",
+ artifactInfoAll[:350], 3, 4, emptyFilterA, emptySorterA),
+ Entry("a: 3->2/3",
+ artifactInfoAll[:300], 3, 3, emptyFilterA, emptySorterA),
+ )
+ })
+
+ Context("a: There is an error", func() {
+ DescribeTable("Server should respond with error from ArtifactManager",
+ func(pageLimit int, aviArtifacts int, filter weles.ArtifactFilter, sorter weles.ArtifactSorter, statusCode int, amerr error) {
+ apiDefaults.PageLimit = int32(pageLimit)
+ artifactInfo := fixtures.CreateArtifactInfoSlice(aviArtifacts)
+ paginator := weles.ArtifactPagination{Limit: apiDefaults.PageLimit}
+
+ if pageLimit == 0 {
+ paginator.Forward = false
+ } else {
+ paginator.Forward = true
+ }
+ listInfo := weles.ListInfo{TotalRecords: uint64(aviArtifacts), RemainingRecords: 0}
+
+ mockArtifactManager.EXPECT().ListArtifact(filter, sorter, paginator).Return(artifactInfo, listInfo, amerr)
+ reqBody := filterSorterReqBody(filter, sorter, JSON)
+ client := testserver.Client()
+ req := createRequest(reqBody, "", "", JSON, JSON)
+ resp, err := client.Do(req)
+ Expect(err).ToNot(HaveOccurred())
+
+ defer resp.Body.Close()
+ respBody, err := ioutil.ReadAll(resp.Body)
+ Expect(err).ToNot(HaveOccurred())
+
+ checkReceivedArtifactErr(respBody, amerr, JSON)
+
+ Expect(resp.StatusCode).To(Equal(statusCode))
+ Expect(resp.Header.Get("Next")).To(Equal(""))
+ Expect(resp.Header.Get("Previous")).To(Equal(""))
+ Expect(resp.Header.Get("TotalRecords")).To(Equal(""))
+ Expect(resp.Header.Get("RemainingRecords")).To(Equal(""))
+
+ },
+ Entry("404 status, Artifact not found error, when server has 0 artifacts avaliable,pagination off",
+ 0, 0, emptyFilterA, emptySorterA, 404, weles.ErrArtifactNotFound),
+
+ Entry("404 status, Artifact not found error when server has 0 jobs avaliable, pagination on",
+ 100, 0, emptyFilterA, emptySorterA, 404, weles.ErrArtifactNotFound),
+ Entry("404 status, Artifact not found error, when server has 100 artifacts but none fulfilling filter, pagination off",
+ 0, 100, filledFilterA1, emptySorterA, 404, weles.ErrArtifactNotFound),
+ Entry("404status, Artifact not found error when server has 100 artifacts but none fulfilling filter, pagination on",
+ 100, 100, filledFilterA1, emptySorterA, 404, weles.ErrArtifactNotFound),
+ Entry("500 status, ArtifactManager unexpected error when server has 100 artifacts, pagination off",
+ 0, 100, emptyFilterA, emptySorterA, 500, errors.New("This is some errors string")),
+ Entry("500 status, ArtifactManager unexpected error when server has 100 artifacts, pagination on",
+ 100, 100, emptyFilterA, emptySorterA, 500, errors.New("This is some errors string")),
+ )
+ })
+
+ DescribeTable("a: rror returned by server due to both before and after query params set am",
+ func(defaultPageLimit int32, query string, filter weles.ArtifactFilter, sorter weles.ArtifactSorter) {
+ apiDefaults.PageLimit = defaultPageLimit
+
+ reqBody := filterSorterReqBody(filter, sorter, JSON)
+ req := createRequest(reqBody, "", query, JSON, JSON)
+
+ client := testserver.Client()
+ resp, err := client.Do(req)
+ Expect(err).ToNot(HaveOccurred())
+ defer resp.Body.Close()
+
+ respBody, err := ioutil.ReadAll(resp.Body)
+ Expect(err).ToNot(HaveOccurred())
+ checkReceivedArtifactErr(respBody, weles.ErrBeforeAfterNotAllowed, JSON)
+
+ Expect(resp.StatusCode).To(Equal(400))
+ Expect(resp.Header.Get("Next")).To(Equal(""))
+ Expect(resp.Header.Get("Previous")).To(Equal(""))
+ Expect(resp.Header.Get("TotalRecords")).To(Equal(""))
+ Expect(resp.Header.Get("RemainingRecords")).To(Equal(""))
+
+ },
+ Entry("a: json, pagination off",
+ int32(0), "?before=10&after=20", emptyFilterA, emptySorterA),
+ Entry("a: json, pagination on",
+ int32(100), "?before=10&after=20", emptyFilterA, emptySorterA),
+ )
+
+ })
+})
diff --git a/server/configure_weles.go b/server/configure_weles.go
new file mode 100644
index 0000000..e4c11ab
--- /dev/null
+++ b/server/configure_weles.go
@@ -0,0 +1,96 @@
+// This file is safe to edit. Once it exists it will not be overwritten
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package server
+
+import (
+ "crypto/tls"
+ "net/http"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/runtime"
+
+ "git.tizen.org/tools/weles/server/operations"
+ "git.tizen.org/tools/weles/server/operations/artifacts"
+ "git.tizen.org/tools/weles/server/operations/jobs"
+)
+
+func configureFlags(api *operations.WelesAPI) {
+ // api.CommandLineOptionsGroups = []swag.CommandLineOptionsGroup{ ... }
+}
+
+// WelesConfigureAPI configures the API and handlers.
+func (s *Server) WelesConfigureAPI(a *APIDefaults) {
+ if s.api != nil {
+ s.handler = welesConfigureAPI(s.api, a)
+ }
+}
+
+func welesConfigureAPI(api *operations.WelesAPI, a *APIDefaults) http.Handler {
+ // configure the api here
+ api.ServeError = errors.ServeError
+
+ api.JSONConsumer = runtime.JSONConsumer()
+ api.MultipartformConsumer = runtime.DiscardConsumer
+
+ api.JSONProducer = runtime.JSONProducer()
+
+ api.SetDefaultProduces("application/json")
+ api.SetDefaultConsumes("application/json")
+
+ api.JobsJobCreatorHandler = jobs.JobCreatorHandlerFunc(a.Managers.JobCreator)
+ api.JobsJobCancelerHandler = jobs.JobCancelerHandlerFunc(a.Managers.JobCanceller)
+ api.JobsJobListerHandler = jobs.JobListerHandlerFunc(a.JobLister)
+
+ api.ArtifactsArtifactListerHandler = artifacts.ArtifactListerHandlerFunc(a.ArtifactLister)
+
+ api.ServerShutdown = func() {}
+
+ return setupGlobalMiddleware(api.Serve(setupMiddlewares))
+}
+
+// The TLS configuration before HTTPS server starts.
+func configureTLS(tlsConfig *tls.Config) {
+ // Make all necessary changes to the TLS configuration here.
+}
+
+// As soon as server is initialized but not run yet, this function will be called.
+// If you need to modify a config, store server instance to stop it individually later, this is the place.
+// This function can be called multiple times, depending on the number of serving schemes.
+// scheme value will be set accordingly: "http", "https" or "unix"
+func configureServer(s *http.Server, scheme, addr string) {
+}
+
+// The middleware configuration is for the handler executors. These do not apply to the swagger.json document.
+// The middleware executes after routing but before authentication, binding and validation
+func setupMiddlewares(handler http.Handler) http.Handler {
+ return handler
+}
+
+// The middleware configuration happens before anything, this middleware also applies to serving the swagger.json document.
+// So this is a good place to plug in a panic handling middleware, logging and metrics
+func setupGlobalMiddleware(handler http.Handler) http.Handler {
+ return handler
+}
+
+func configureAPI(api *operations.WelesAPI) http.Handler {
+ // WARNING
+ // as go-swagger generated code (server.go) includes calls to this function its definition
+ // must be present. This function should not be called anywhere.
+
+ return setupGlobalMiddleware(api.Serve(setupMiddlewares))
+}
diff --git a/server/doc.go b/server/doc.go
new file mode 100644
index 0000000..41796c5
--- /dev/null
+++ b/server/doc.go
@@ -0,0 +1,44 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+/*
+Package server Weles
+This is a Weles server. You can find out more about Weles at [http://tbd.tbd](http://tbd.tbd).
+
+Terms Of Service:
+
+http://tbd.tbd/terms/
+
+
+ Schemes:
+ http
+ Host: localhost:8088
+ BasePath: /api/v1
+ Version: 0.0.0
+ License: Apache 2.0 http://www.apache.org/licenses/LICENSE-2.0.html
+ Contact: <tbd@tbd.com>
+
+ Consumes:
+ - application/json
+ - multipart/form-data
+
+ Produces:
+ - application/json
+
+swagger:meta
+*/
+package server
diff --git a/server/embedded_spec.go b/server/embedded_spec.go
new file mode 100644
index 0000000..8197aae
--- /dev/null
+++ b/server/embedded_spec.go
@@ -0,0 +1,1441 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package server
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "encoding/json"
+)
+
+var (
+ // SwaggerJSON embedded version of the swagger document used at generation time
+ SwaggerJSON json.RawMessage
+ // FlatSwaggerJSON embedded flattened version of the swagger document used at generation time
+ FlatSwaggerJSON json.RawMessage
+)
+
+func init() {
+ SwaggerJSON = json.RawMessage([]byte(`{
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "schemes": [
+ "http"
+ ],
+ "swagger": "2.0",
+ "info": {
+ "description": "This is a Weles server. You can find out more about Weles at [http://tbd.tbd](http://tbd.tbd).",
+ "title": "Weles",
+ "termsOfService": "http://tbd.tbd/terms/",
+ "contact": {
+ "email": "tbd@tbd.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ },
+ "version": "0.0.0"
+ },
+ "host": "localhost:8088",
+ "basePath": "/api/v1",
+ "paths": {
+ "/artifacts/list": {
+ "post": {
+ "description": "ArtifactLister returns information on filtered Weles artifacts.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "artifacts"
+ ],
+ "summary": "List artifacts with filter and sort features",
+ "operationId": "ArtifactLister",
+ "parameters": [
+ {
+ "description": "Artifact Filter and Sort object.",
+ "name": "artifactFilterAndSort",
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/ArtifactFilterAndSort"
+ }
+ },
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "ID of the last element from previous page.",
+ "name": "after",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "ID of first element from next page.",
+ "name": "before",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "format": "int32",
+ "description": "Custom page limit. Denotes number of ArtifactInfo structures that will be returned.",
+ "name": "limit",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ArtifactInfo"
+ }
+ },
+ "headers": {
+ "Next": {
+ "type": "string",
+ "format": "URI",
+ "description": "URI to request next page of data. Please note that the same body must be used as in initial request.\n"
+ },
+ "Previous": {
+ "type": "string",
+ "format": "URI",
+ "description": "URI to request next page of data. Please note that the same body must be used as in initial request.\n"
+ },
+ "TotalRecords": {
+ "type": "integer",
+ "format": "uint64",
+ "description": "count of records currently fulfilling the requested ArtifactFilter. Please note that this value may change when requesting for the same data at a different moment in time.\n"
+ }
+ }
+ },
+ "206": {
+ "description": "Partial Content",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ArtifactInfo"
+ }
+ },
+ "headers": {
+ "Next": {
+ "type": "string",
+ "format": "URI",
+ "description": "URI to request next page of data. Please note that the same body must be used as in initial request.\n"
+ },
+ "Previous": {
+ "type": "string",
+ "format": "URI",
+ "description": "URI to request next page of data. Please note that the same body must be used as in initial request.\n"
+ },
+ "RemainingRecords": {
+ "type": "integer",
+ "format": "uint64",
+ "description": "number of records after current page. Please note that this value may change when requesting for the same data at a different moment in time.\n"
+ },
+ "TotalRecords": {
+ "type": "integer",
+ "format": "uint64",
+ "description": "count of records currently fulfilling the requested ArtifactFilter. Please note that this value may change when requesting for the same data at a different moment in time.\n"
+ }
+ }
+ },
+ "400": {
+ "$ref": "#/responses/BadRequest"
+ },
+ "404": {
+ "$ref": "#/responses/NotFound"
+ },
+ "500": {
+ "$ref": "#/responses/InternalServer"
+ }
+ }
+ }
+ },
+ "/jobs": {
+ "post": {
+ "description": "adds new Job in Weles using recipe passed in YAML format.",
+ "consumes": [
+ "multipart/form-data"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "jobs"
+ ],
+ "summary": "Add new job",
+ "operationId": "JobCreator",
+ "parameters": [
+ {
+ "type": "file",
+ "description": "is Job description yaml file.",
+ "name": "yamlfile",
+ "in": "formData",
+ "required": true
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "Created",
+ "schema": {
+ "$ref": "#/definitions/JobID"
+ }
+ },
+ "415": {
+ "$ref": "#/responses/UnsupportedMediaType"
+ },
+ "422": {
+ "$ref": "#/responses/UnprocessableEntity"
+ },
+ "500": {
+ "$ref": "#/responses/InternalServer"
+ }
+ }
+ }
+ },
+ "/jobs/list": {
+ "post": {
+ "description": "JobLister returns information on filtered Weles Jobs.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "jobs"
+ ],
+ "summary": "List jobs with filter and sort features",
+ "operationId": "JobLister",
+ "parameters": [
+ {
+ "description": "Job Filter and Sort object.",
+ "name": "jobFilterAndSort",
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/JobFilterAndSort"
+ }
+ },
+ {
+ "type": "integer",
+ "format": "uint64",
+ "description": "JobID of the last element from previous page.",
+ "name": "after",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "format": "uint64",
+ "description": "JobID of first element from next page.",
+ "name": "before",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "format": "int32",
+ "description": "Custom page limit. Denotes number of JobInfo structures that will be returned.",
+ "name": "limit",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/JobInfo"
+ }
+ },
+ "headers": {
+ "Next": {
+ "type": "string",
+ "format": "URI",
+ "description": "URI to request next page of data. Please note that the same body must be used as in initial request.\n"
+ },
+ "Previous": {
+ "type": "string",
+ "format": "URI",
+ "description": "URI to request previous page of data. Please note that the same body must be used as in initial request.\n"
+ },
+ "TotalRecords": {
+ "type": "integer",
+ "format": "uint64",
+ "description": "count of records currently fulfilling the requested JobFilter. Please note that this value may change when requesting for the same data at a different moment in time.\n"
+ }
+ }
+ },
+ "206": {
+ "description": "Partial Content",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/JobInfo"
+ }
+ },
+ "headers": {
+ "Next": {
+ "type": "string",
+ "format": "URI",
+ "description": "URI to request next page of data. Please note that the same body must be used as in initial request.\n"
+ },
+ "Previous": {
+ "type": "string",
+ "format": "URI",
+ "description": "URI to request previous page of data. Please note that the same body must be used as in initial request.\n"
+ },
+ "RemainingRecords": {
+ "type": "integer",
+ "format": "uint64",
+ "description": "number of records after current page. Please note that this value may change when requesting for the same data at a different moment in time.\n"
+ },
+ "TotalRecords": {
+ "type": "integer",
+ "format": "uint64",
+ "description": "count of records currently fulfilling requested JobFilter. Please note that this value may change when requesting for the same data at a different moment in time.\n"
+ }
+ }
+ },
+ "400": {
+ "$ref": "#/responses/BadRequest"
+ },
+ "404": {
+ "$ref": "#/responses/NotFound"
+ },
+ "500": {
+ "$ref": "#/responses/InternalServer"
+ }
+ }
+ }
+ },
+ "/jobs/{JobID}/cancel": {
+ "post": {
+ "description": "JobCanceler stops execution of Job identified by JobID.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "jobs"
+ ],
+ "summary": "Cancel existing job",
+ "operationId": "JobCanceler",
+ "parameters": [
+ {
+ "type": "integer",
+ "format": "uint64",
+ "name": "JobID",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No Content"
+ },
+ "403": {
+ "$ref": "#/responses/Forbidden"
+ },
+ "404": {
+ "$ref": "#/responses/NotFound"
+ },
+ "500": {
+ "$ref": "#/responses/InternalServer"
+ }
+ }
+ }
+ }
+ },
+ "definitions": {
+ "ArtifactAlias": {
+ "description": "is an alternative name of an artifact.",
+ "type": "string"
+ },
+ "ArtifactDescription": {
+ "description": "contains information needed to create new artifact in ArtifactDB.",
+ "type": "object",
+ "properties": {
+ "Alias": {
+ "$ref": "#/definitions/ArtifactAlias"
+ },
+ "JobID": {
+ "description": "specifies Job for which artifact was created.",
+ "$ref": "#/definitions/JobID"
+ },
+ "Type": {
+ "$ref": "#/definitions/ArtifactType"
+ },
+ "URI": {
+ "$ref": "#/definitions/ArtifactURI"
+ }
+ }
+ },
+ "ArtifactFilter": {
+ "description": "is used to filter results from ArtifactDB.",
+ "type": "object",
+ "properties": {
+ "Alias": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ArtifactAlias"
+ }
+ },
+ "JobID": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/JobID"
+ }
+ },
+ "Status": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ArtifactStatus"
+ }
+ },
+ "Type": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ArtifactType"
+ }
+ }
+ }
+ },
+ "ArtifactFilterAndSort": {
+ "description": "Data for filtering and sorting Weles Jobs lists.",
+ "type": "object",
+ "properties": {
+ "Filter": {
+ "$ref": "#/definitions/ArtifactFilter"
+ },
+ "Sorter": {
+ "$ref": "#/definitions/ArtifactSorter"
+ }
+ }
+ },
+ "ArtifactInfo": {
+ "description": "describes single artifact stored in ArtifactDB.",
+ "type": "object",
+ "allOf": [
+ {
+ "$ref": "#/definitions/ArtifactDescription"
+ }
+ ],
+ "properties": {
+ "ID": {
+ "description": "unique identification of the artifact.",
+ "type": "integer",
+ "format": "int64",
+ "x-go-custom-tag": "db:\",primarykey, autoincrement\""
+ },
+ "Path": {
+ "$ref": "#/definitions/ArtifactPath"
+ },
+ "Status": {
+ "$ref": "#/definitions/ArtifactStatus"
+ },
+ "Timestamp": {
+ "description": "is date of creating the artifact.",
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ },
+ "ArtifactPath": {
+ "description": "describes path to artifact in ArtifactDB filesystem.",
+ "type": "string"
+ },
+ "ArtifactSortBy": {
+ "description": "denotes the key for sorting list of all artifacts.\n* ID - sorting by artifact ID.\n",
+ "type": "string",
+ "enum": [
+ "ID"
+ ]
+ },
+ "ArtifactSorter": {
+ "description": "defines the key for sorting as well as direction of sorting.\nWhen ArtifactSorter is empty, artifacts are sorted by ID, Ascending.\n",
+ "type": "object",
+ "properties": {
+ "SortBy": {
+ "$ref": "#/definitions/ArtifactSortBy"
+ },
+ "SortOrder": {
+ "$ref": "#/definitions/SortOrder"
+ }
+ }
+ },
+ "ArtifactStatus": {
+ "description": "describes artifact status and availability.\n\n* DOWNLOADING - artifact is currently being downloaded.\n\n* READY - artifact has been downloaded and is ready to use.\n\n* FAILED - file is not available for use (e.g. download failed).\n\n* PENDING - artifact download has not started yet.\n",
+ "type": "string",
+ "enum": [
+ "DOWNLOADING",
+ "READY",
+ "FAILED",
+ "PENDING"
+ ]
+ },
+ "ArtifactType": {
+ "description": "denotes type and function of an artifact.\n\n* IMAGE - image file.\n\n* RESULT - all outputs, files built during tests, etc.\n\n* TEST - additional files uploaded by user for conducting test.\n\n* YAML - yaml file describing Weles Job.\n",
+ "type": "string",
+ "enum": [
+ "IMAGE",
+ "RESULT",
+ "TEST",
+ "YAML"
+ ]
+ },
+ "ArtifactURI": {
+ "description": "is used to identify artifact's source.",
+ "type": "string",
+ "format": "uri"
+ },
+ "ErrResponse": {
+ "description": "is a standard error response containing information about the error. It consists of error type and message.",
+ "type": "object",
+ "properties": {
+ "message": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ }
+ }
+ },
+ "JobFilter": {
+ "description": "is used to filter Weles Jobs.",
+ "type": "object",
+ "properties": {
+ "CreatedAfter": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "CreatedBefore": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "Info": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "JobID": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/JobID"
+ }
+ },
+ "Name": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Status": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/JobStatus"
+ }
+ },
+ "UpdatedAfter": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "UpdatedBefore": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ },
+ "JobFilterAndSort": {
+ "description": "Data for filtering and sorting Weles Jobs lists.",
+ "type": "object",
+ "properties": {
+ "Filter": {
+ "$ref": "#/definitions/JobFilter"
+ },
+ "Sorter": {
+ "$ref": "#/definitions/JobSorter"
+ }
+ }
+ },
+ "JobID": {
+ "description": "is a unique identifier for Weles Job.",
+ "type": "integer",
+ "format": "uint64"
+ },
+ "JobInfo": {
+ "description": "contains information about a Job available for public API.",
+ "type": "object",
+ "properties": {
+ "created": {
+ "description": "is the Job creation time in UTC.",
+ "type": "string",
+ "format": "date-time"
+ },
+ "info": {
+ "description": "provides additional information about current state, e.g. cause of failure",
+ "type": "string"
+ },
+ "jobID": {
+ "description": "is a unique Job identifier",
+ "$ref": "#/definitions/JobID"
+ },
+ "name": {
+ "description": "is the Job name acquired from yaml file during Job creation.",
+ "type": "string"
+ },
+ "status": {
+ "description": "specifies current state of the Job.",
+ "$ref": "#/definitions/JobStatus"
+ },
+ "updated": {
+ "description": "is the time of latest Jobs' status modification.",
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ },
+ "JobSortBy": {
+ "description": "denotes key for sorting Jobs list. Jobs are sorted by ID (Ascending) by default.\nYou can sort jobs additionaly by\n\n* CreatedDate - sorting by date of creation of the weles job.\n\n* UpdatedDate - sorting by date of update of the weles job.\n\n* JobStatus - sorting by the Job Status. Descending order will sort in the order JobStatuses are listed in the docs (from NEW at the start to CANCELED at the end). Ascending will reverse this order.\nWhen sorting is applied, and there are many jobs with the same date/status, they will be sorted by JobID (Ascending)\n",
+ "type": "string",
+ "enum": [
+ "CreatedDate",
+ "UpdatedDate",
+ "JobStatus"
+ ]
+ },
+ "JobSorter": {
+ "description": "defines the key for sorting as well as direction of sorting.\n",
+ "type": "object",
+ "properties": {
+ "SortBy": {
+ "$ref": "#/definitions/JobSortBy"
+ },
+ "SortOrder": {
+ "$ref": "#/definitions/SortOrder"
+ }
+ }
+ },
+ "JobStatus": {
+ "description": "specifies state of the Job.\n\n* NEW - The new Job has been created.\n\n* PARSING - Provided yaml file is being parsed and interpreted.\n\n* DOWNLOADING - Images and/or files required for the test are being downloaded.\n\n* WAITING - Job is waiting for Boruta worker.\n\n* RUNNING - Job is being executed.\n\n* COMPLETED - Job is completed. This is terminal state.\n\n* FAILED - Job execution has failed. This is terminal state.\n\n* CANCELED -Job has been canceled with API call. This is terminal state.\n",
+ "type": "string",
+ "enum": [
+ "NEW",
+ "PARSING",
+ "DOWNLOADING",
+ "WAITING",
+ "RUNNING",
+ "COMPLETED",
+ "FAILED",
+ "CANCELED"
+ ]
+ },
+ "SortOrder": {
+ "description": "denotes direction of sorting of weles jobs or artifacts.\n\n* Ascending - from oldest to newest.\n\n* Descending - from newest to oldest.\n",
+ "type": "string",
+ "enum": [
+ "Ascending",
+ "Descending"
+ ]
+ }
+ },
+ "responses": {
+ "BadRequest": {
+ "description": "Bad Request",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ },
+ "Forbidden": {
+ "description": "Forbidden",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ },
+ "InternalServer": {
+ "description": "Internal Server error",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ },
+ "NotFound": {
+ "description": "Not Found",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ },
+ "UnprocessableEntity": {
+ "description": "Unprocessable entity",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ },
+ "UnsupportedMediaType": {
+ "description": "Unsupported media type",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ }
+ },
+ "tags": [
+ {
+ "description": "Info and management of Weles jobs.",
+ "name": "jobs"
+ },
+ {
+ "description": "Info about all artifacts used by Weles jobs.",
+ "name": "artifacts"
+ }
+ ],
+ "externalDocs": {
+ "description": "TBD",
+ "url": "http://TBD.tbd"
+ }
+}`))
+ FlatSwaggerJSON = json.RawMessage([]byte(`{
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "schemes": [
+ "http"
+ ],
+ "swagger": "2.0",
+ "info": {
+ "description": "This is a Weles server. You can find out more about Weles at [http://tbd.tbd](http://tbd.tbd).",
+ "title": "Weles",
+ "termsOfService": "http://tbd.tbd/terms/",
+ "contact": {
+ "email": "tbd@tbd.com"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+ },
+ "version": "0.0.0"
+ },
+ "host": "localhost:8088",
+ "basePath": "/api/v1",
+ "paths": {
+ "/artifacts/list": {
+ "post": {
+ "description": "ArtifactLister returns information on filtered Weles artifacts.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "artifacts"
+ ],
+ "summary": "List artifacts with filter and sort features",
+ "operationId": "ArtifactLister",
+ "parameters": [
+ {
+ "description": "Artifact Filter and Sort object.",
+ "name": "artifactFilterAndSort",
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/ArtifactFilterAndSort"
+ }
+ },
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "ID of the last element from previous page.",
+ "name": "after",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "format": "int64",
+ "description": "ID of first element from next page.",
+ "name": "before",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "format": "int32",
+ "description": "Custom page limit. Denotes number of ArtifactInfo structures that will be returned.",
+ "name": "limit",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ArtifactInfo"
+ }
+ },
+ "headers": {
+ "Next": {
+ "type": "string",
+ "format": "URI",
+ "description": "URI to request next page of data. Please note that the same body must be used as in initial request.\n"
+ },
+ "Previous": {
+ "type": "string",
+ "format": "URI",
+ "description": "URI to request next page of data. Please note that the same body must be used as in initial request.\n"
+ },
+ "TotalRecords": {
+ "type": "integer",
+ "format": "uint64",
+ "description": "count of records currently fulfilling the requested ArtifactFilter. Please note that this value may change when requesting for the same data at a different moment in time.\n"
+ }
+ }
+ },
+ "206": {
+ "description": "Partial Content",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ArtifactInfo"
+ }
+ },
+ "headers": {
+ "Next": {
+ "type": "string",
+ "format": "URI",
+ "description": "URI to request next page of data. Please note that the same body must be used as in initial request.\n"
+ },
+ "Previous": {
+ "type": "string",
+ "format": "URI",
+ "description": "URI to request next page of data. Please note that the same body must be used as in initial request.\n"
+ },
+ "RemainingRecords": {
+ "type": "integer",
+ "format": "uint64",
+ "description": "number of records after current page. Please note that this value may change when requesting for the same data at a different moment in time.\n"
+ },
+ "TotalRecords": {
+ "type": "integer",
+ "format": "uint64",
+ "description": "count of records currently fulfilling the requested ArtifactFilter. Please note that this value may change when requesting for the same data at a different moment in time.\n"
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ },
+ "404": {
+ "description": "Not Found",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ },
+ "500": {
+ "description": "Internal Server error",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ }
+ }
+ }
+ },
+ "/jobs": {
+ "post": {
+ "description": "adds new Job in Weles using recipe passed in YAML format.",
+ "consumes": [
+ "multipart/form-data"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "jobs"
+ ],
+ "summary": "Add new job",
+ "operationId": "JobCreator",
+ "parameters": [
+ {
+ "type": "file",
+ "description": "is Job description yaml file.",
+ "name": "yamlfile",
+ "in": "formData",
+ "required": true
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "Created",
+ "schema": {
+ "$ref": "#/definitions/JobID"
+ }
+ },
+ "415": {
+ "description": "Unsupported media type",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ },
+ "422": {
+ "description": "Unprocessable entity",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ },
+ "500": {
+ "description": "Internal Server error",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ }
+ }
+ }
+ },
+ "/jobs/list": {
+ "post": {
+ "description": "JobLister returns information on filtered Weles Jobs.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "jobs"
+ ],
+ "summary": "List jobs with filter and sort features",
+ "operationId": "JobLister",
+ "parameters": [
+ {
+ "description": "Job Filter and Sort object.",
+ "name": "jobFilterAndSort",
+ "in": "body",
+ "schema": {
+ "$ref": "#/definitions/JobFilterAndSort"
+ }
+ },
+ {
+ "type": "integer",
+ "format": "uint64",
+ "description": "JobID of the last element from previous page.",
+ "name": "after",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "format": "uint64",
+ "description": "JobID of first element from next page.",
+ "name": "before",
+ "in": "query"
+ },
+ {
+ "type": "integer",
+ "format": "int32",
+ "description": "Custom page limit. Denotes number of JobInfo structures that will be returned.",
+ "name": "limit",
+ "in": "query"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/JobInfo"
+ }
+ },
+ "headers": {
+ "Next": {
+ "type": "string",
+ "format": "URI",
+ "description": "URI to request next page of data. Please note that the same body must be used as in initial request.\n"
+ },
+ "Previous": {
+ "type": "string",
+ "format": "URI",
+ "description": "URI to request previous page of data. Please note that the same body must be used as in initial request.\n"
+ },
+ "TotalRecords": {
+ "type": "integer",
+ "format": "uint64",
+ "description": "count of records currently fulfilling the requested JobFilter. Please note that this value may change when requesting for the same data at a different moment in time.\n"
+ }
+ }
+ },
+ "206": {
+ "description": "Partial Content",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/JobInfo"
+ }
+ },
+ "headers": {
+ "Next": {
+ "type": "string",
+ "format": "URI",
+ "description": "URI to request next page of data. Please note that the same body must be used as in initial request.\n"
+ },
+ "Previous": {
+ "type": "string",
+ "format": "URI",
+ "description": "URI to request previous page of data. Please note that the same body must be used as in initial request.\n"
+ },
+ "RemainingRecords": {
+ "type": "integer",
+ "format": "uint64",
+ "description": "number of records after current page. Please note that this value may change when requesting for the same data at a different moment in time.\n"
+ },
+ "TotalRecords": {
+ "type": "integer",
+ "format": "uint64",
+ "description": "count of records currently fulfilling requested JobFilter. Please note that this value may change when requesting for the same data at a different moment in time.\n"
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ },
+ "404": {
+ "description": "Not Found",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ },
+ "500": {
+ "description": "Internal Server error",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ }
+ }
+ }
+ },
+ "/jobs/{JobID}/cancel": {
+ "post": {
+ "description": "JobCanceler stops execution of Job identified by JobID.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "jobs"
+ ],
+ "summary": "Cancel existing job",
+ "operationId": "JobCanceler",
+ "parameters": [
+ {
+ "type": "integer",
+ "format": "uint64",
+ "name": "JobID",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No Content"
+ },
+ "403": {
+ "description": "Forbidden",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ },
+ "404": {
+ "description": "Not Found",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ },
+ "500": {
+ "description": "Internal Server error",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ }
+ }
+ }
+ }
+ },
+ "definitions": {
+ "ArtifactAlias": {
+ "description": "is an alternative name of an artifact.",
+ "type": "string"
+ },
+ "ArtifactDescription": {
+ "description": "contains information needed to create new artifact in ArtifactDB.",
+ "type": "object",
+ "properties": {
+ "Alias": {
+ "$ref": "#/definitions/ArtifactAlias"
+ },
+ "JobID": {
+ "description": "specifies Job for which artifact was created.",
+ "$ref": "#/definitions/JobID"
+ },
+ "Type": {
+ "$ref": "#/definitions/ArtifactType"
+ },
+ "URI": {
+ "$ref": "#/definitions/ArtifactURI"
+ }
+ }
+ },
+ "ArtifactFilter": {
+ "description": "is used to filter results from ArtifactDB.",
+ "type": "object",
+ "properties": {
+ "Alias": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ArtifactAlias"
+ }
+ },
+ "JobID": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/JobID"
+ }
+ },
+ "Status": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ArtifactStatus"
+ }
+ },
+ "Type": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/ArtifactType"
+ }
+ }
+ }
+ },
+ "ArtifactFilterAndSort": {
+ "description": "Data for filtering and sorting Weles Jobs lists.",
+ "type": "object",
+ "properties": {
+ "Filter": {
+ "$ref": "#/definitions/ArtifactFilter"
+ },
+ "Sorter": {
+ "$ref": "#/definitions/ArtifactSorter"
+ }
+ }
+ },
+ "ArtifactInfo": {
+ "description": "describes single artifact stored in ArtifactDB.",
+ "type": "object",
+ "allOf": [
+ {
+ "$ref": "#/definitions/ArtifactDescription"
+ }
+ ],
+ "properties": {
+ "ID": {
+ "description": "unique identification of the artifact.",
+ "type": "integer",
+ "format": "int64",
+ "x-go-custom-tag": "db:\",primarykey, autoincrement\""
+ },
+ "Path": {
+ "$ref": "#/definitions/ArtifactPath"
+ },
+ "Status": {
+ "$ref": "#/definitions/ArtifactStatus"
+ },
+ "Timestamp": {
+ "description": "is date of creating the artifact.",
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ },
+ "ArtifactPath": {
+ "description": "describes path to artifact in ArtifactDB filesystem.",
+ "type": "string"
+ },
+ "ArtifactSortBy": {
+ "description": "denotes the key for sorting list of all artifacts.\n* ID - sorting by artifact ID.\n",
+ "type": "string",
+ "enum": [
+ "ID"
+ ]
+ },
+ "ArtifactSorter": {
+ "description": "defines the key for sorting as well as direction of sorting.\nWhen ArtifactSorter is empty, artifacts are sorted by ID, Ascending.\n",
+ "type": "object",
+ "properties": {
+ "SortBy": {
+ "$ref": "#/definitions/ArtifactSortBy"
+ },
+ "SortOrder": {
+ "$ref": "#/definitions/SortOrder"
+ }
+ }
+ },
+ "ArtifactStatus": {
+ "description": "describes artifact status and availability.\n\n* DOWNLOADING - artifact is currently being downloaded.\n\n* READY - artifact has been downloaded and is ready to use.\n\n* FAILED - file is not available for use (e.g. download failed).\n\n* PENDING - artifact download has not started yet.\n",
+ "type": "string",
+ "enum": [
+ "DOWNLOADING",
+ "READY",
+ "FAILED",
+ "PENDING"
+ ]
+ },
+ "ArtifactType": {
+ "description": "denotes type and function of an artifact.\n\n* IMAGE - image file.\n\n* RESULT - all outputs, files built during tests, etc.\n\n* TEST - additional files uploaded by user for conducting test.\n\n* YAML - yaml file describing Weles Job.\n",
+ "type": "string",
+ "enum": [
+ "IMAGE",
+ "RESULT",
+ "TEST",
+ "YAML"
+ ]
+ },
+ "ArtifactURI": {
+ "description": "is used to identify artifact's source.",
+ "type": "string",
+ "format": "uri"
+ },
+ "ErrResponse": {
+ "description": "is a standard error response containing information about the error. It consists of error type and message.",
+ "type": "object",
+ "properties": {
+ "message": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ }
+ }
+ },
+ "JobFilter": {
+ "description": "is used to filter Weles Jobs.",
+ "type": "object",
+ "properties": {
+ "CreatedAfter": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "CreatedBefore": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "Info": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "JobID": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/JobID"
+ }
+ },
+ "Name": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Status": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/JobStatus"
+ }
+ },
+ "UpdatedAfter": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "UpdatedBefore": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ },
+ "JobFilterAndSort": {
+ "description": "Data for filtering and sorting Weles Jobs lists.",
+ "type": "object",
+ "properties": {
+ "Filter": {
+ "$ref": "#/definitions/JobFilter"
+ },
+ "Sorter": {
+ "$ref": "#/definitions/JobSorter"
+ }
+ }
+ },
+ "JobID": {
+ "description": "is a unique identifier for Weles Job.",
+ "type": "integer",
+ "format": "uint64"
+ },
+ "JobInfo": {
+ "description": "contains information about a Job available for public API.",
+ "type": "object",
+ "properties": {
+ "created": {
+ "description": "is the Job creation time in UTC.",
+ "type": "string",
+ "format": "date-time"
+ },
+ "info": {
+ "description": "provides additional information about current state, e.g. cause of failure",
+ "type": "string"
+ },
+ "jobID": {
+ "description": "is a unique Job identifier",
+ "$ref": "#/definitions/JobID"
+ },
+ "name": {
+ "description": "is the Job name acquired from yaml file during Job creation.",
+ "type": "string"
+ },
+ "status": {
+ "description": "specifies current state of the Job.",
+ "$ref": "#/definitions/JobStatus"
+ },
+ "updated": {
+ "description": "is the time of latest Jobs' status modification.",
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ },
+ "JobSortBy": {
+ "description": "denotes key for sorting Jobs list. Jobs are sorted by ID (Ascending) by default.\nYou can sort jobs additionaly by\n\n* CreatedDate - sorting by date of creation of the weles job.\n\n* UpdatedDate - sorting by date of update of the weles job.\n\n* JobStatus - sorting by the Job Status. Descending order will sort in the order JobStatuses are listed in the docs (from NEW at the start to CANCELED at the end). Ascending will reverse this order.\nWhen sorting is applied, and there are many jobs with the same date/status, they will be sorted by JobID (Ascending)\n",
+ "type": "string",
+ "enum": [
+ "CreatedDate",
+ "UpdatedDate",
+ "JobStatus"
+ ]
+ },
+ "JobSorter": {
+ "description": "defines the key for sorting as well as direction of sorting.\n",
+ "type": "object",
+ "properties": {
+ "SortBy": {
+ "$ref": "#/definitions/JobSortBy"
+ },
+ "SortOrder": {
+ "$ref": "#/definitions/SortOrder"
+ }
+ }
+ },
+ "JobStatus": {
+ "description": "specifies state of the Job.\n\n* NEW - The new Job has been created.\n\n* PARSING - Provided yaml file is being parsed and interpreted.\n\n* DOWNLOADING - Images and/or files required for the test are being downloaded.\n\n* WAITING - Job is waiting for Boruta worker.\n\n* RUNNING - Job is being executed.\n\n* COMPLETED - Job is completed. This is terminal state.\n\n* FAILED - Job execution has failed. This is terminal state.\n\n* CANCELED -Job has been canceled with API call. This is terminal state.\n",
+ "type": "string",
+ "enum": [
+ "NEW",
+ "PARSING",
+ "DOWNLOADING",
+ "WAITING",
+ "RUNNING",
+ "COMPLETED",
+ "FAILED",
+ "CANCELED"
+ ]
+ },
+ "SortOrder": {
+ "description": "denotes direction of sorting of weles jobs or artifacts.\n\n* Ascending - from oldest to newest.\n\n* Descending - from newest to oldest.\n",
+ "type": "string",
+ "enum": [
+ "Ascending",
+ "Descending"
+ ]
+ }
+ },
+ "responses": {
+ "BadRequest": {
+ "description": "Bad Request",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ },
+ "Forbidden": {
+ "description": "Forbidden",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ },
+ "InternalServer": {
+ "description": "Internal Server error",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ },
+ "NotFound": {
+ "description": "Not Found",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ },
+ "UnprocessableEntity": {
+ "description": "Unprocessable entity",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ },
+ "UnsupportedMediaType": {
+ "description": "Unsupported media type",
+ "schema": {
+ "$ref": "#/definitions/ErrResponse"
+ }
+ }
+ },
+ "tags": [
+ {
+ "description": "Info and management of Weles jobs.",
+ "name": "jobs"
+ },
+ {
+ "description": "Info about all artifacts used by Weles jobs.",
+ "name": "artifacts"
+ }
+ ],
+ "externalDocs": {
+ "description": "TBD",
+ "url": "http://TBD.tbd"
+ }
+}`))
+}
diff --git a/server/job_canceler_handler.go b/server/job_canceler_handler.go
new file mode 100644
index 0000000..9881629
--- /dev/null
+++ b/server/job_canceler_handler.go
@@ -0,0 +1,40 @@
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+package server
+
+import (
+ "git.tizen.org/tools/weles"
+ "git.tizen.org/tools/weles/server/operations/jobs"
+ middleware "github.com/go-openapi/runtime/middleware"
+)
+
+// JobCanceller is a handler which passess JobID to JobManager to cancel a job.
+func (m *Managers) JobCanceller(params jobs.JobCancelerParams) middleware.Responder {
+ err := m.JM.CancelJob(weles.JobID(params.JobID))
+ switch err {
+ case nil:
+ return jobs.NewJobCancelerNoContent()
+
+ case weles.ErrJobNotFound:
+ return jobs.NewJobCancelerNotFound().WithPayload(&weles.ErrResponse{Message: err.Error(), Type: ""})
+
+ case weles.ErrJobStatusChangeNotAllowed:
+ return jobs.NewJobCancelerForbidden().WithPayload(&weles.ErrResponse{Message: err.Error(), Type: ""})
+
+ default:
+ return jobs.NewJobCancelerInternalServerError().WithPayload(&weles.ErrResponse{Message: err.Error(), Type: ""})
+ }
+
+}
diff --git a/server/job_canceler_handler_test.go b/server/job_canceler_handler_test.go
new file mode 100644
index 0000000..51d141a
--- /dev/null
+++ b/server/job_canceler_handler_test.go
@@ -0,0 +1,98 @@
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+package server_test
+
+import (
+ "encoding/json"
+ "errors"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+
+ "github.com/golang/mock/gomock"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/ginkgo/extensions/table"
+ . "github.com/onsi/gomega"
+
+ "git.tizen.org/tools/weles"
+ "git.tizen.org/tools/weles/mock"
+)
+
+var _ = Describe("JobCancelerHandler", func() {
+
+ var (
+ mockCtrl *gomock.Controller
+ mockJobManager *mock.MockJobManager
+ testserver *httptest.Server
+ )
+
+ BeforeEach(func() {
+ mockCtrl, mockJobManager, _, _, testserver = testServerSetup()
+ })
+
+ AfterEach(func() {
+ mockCtrl.Finish()
+ testserver.Close()
+ })
+
+ Describe("cancelling a job", func() {
+ getClientResp := func(accept string) (resp *http.Response) {
+ client := testserver.Client()
+ req, err := http.NewRequest(http.MethodPost, testserver.URL+"/api/v1/jobs/1234/cancel", nil)
+ Expect(err).ToNot(HaveOccurred())
+ if accept != OMIT {
+ req.Header.Set("Accept", accept)
+ }
+ resp, err = client.Do(req)
+ Expect(err).ToNot(HaveOccurred())
+ return resp
+ }
+ Context("correct request", func() {
+ It("should respond with 204 Status Code", func() {
+ mockJobManager.EXPECT().CancelJob(weles.JobID(1234))
+ resp := getClientResp("")
+ defer resp.Body.Close()
+ Expect(resp.StatusCode).To(Equal(204))
+ })
+ })
+ Context("server should respond", func() {
+ DescribeTable("with appropriate error",
+ func(accept string, erro error, statuscode int) {
+
+ mockJobManager.EXPECT().CancelJob(weles.JobID(1234)).Return(erro)
+ resp := getClientResp(accept)
+ defer resp.Body.Close()
+
+ respBody, err := ioutil.ReadAll(resp.Body)
+ Expect(err).ToNot(HaveOccurred())
+ errorEncoded, err := json.Marshal(weles.ErrResponse{
+ Message: erro.Error(),
+ Type: ""})
+ Expect(err).ToNot(HaveOccurred())
+ Expect(string(respBody)).To(MatchJSON(string(errorEncoded)))
+
+ Expect(resp.StatusCode).To(Equal(statuscode))
+ },
+ Entry("job does not exist - 404", JSON, weles.ErrJobNotFound, 404),
+ Entry("job does not exist - 404", OMIT, weles.ErrJobNotFound, 404),
+ Entry("job already has final status - 403", JSON, weles.ErrJobStatusChangeNotAllowed, 403),
+ Entry("job already has final status - 403", OMIT, weles.ErrJobStatusChangeNotAllowed, 403),
+ Entry("unexpected error - 500", JSON, errors.New("Some other error"), 500),
+ Entry("unexpected error - 500", OMIT, errors.New("Some other error"), 500),
+ )
+ })
+
+ })
+})
diff --git a/server/job_creator_handler.go b/server/job_creator_handler.go
new file mode 100644
index 0000000..9d6e886
--- /dev/null
+++ b/server/job_creator_handler.go
@@ -0,0 +1,38 @@
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+package server
+
+import (
+ "git.tizen.org/tools/weles"
+ "git.tizen.org/tools/weles/server/operations/jobs"
+ middleware "github.com/go-openapi/runtime/middleware"
+
+ "io/ioutil"
+)
+
+// JobCreator is a handler which passes yaml file with job description to jobmanager.
+func (m *Managers) JobCreator(params jobs.JobCreatorParams) middleware.Responder {
+ byteContainer, err := ioutil.ReadAll(params.Yamlfile)
+ if err != nil {
+ return jobs.NewJobCreatorUnprocessableEntity().WithPayload(&weles.ErrResponse{Message: err.Error(), Type: ""})
+ }
+
+ jobID, err := m.JM.CreateJob(byteContainer)
+ if err != nil {
+ return jobs.NewJobCreatorInternalServerError().WithPayload(&weles.ErrResponse{Message: err.Error(), Type: ""})
+ }
+
+ return jobs.NewJobCreatorCreated().WithPayload(jobID)
+}
diff --git a/server/job_creator_handler_test.go b/server/job_creator_handler_test.go
new file mode 100644
index 0000000..8f71431
--- /dev/null
+++ b/server/job_creator_handler_test.go
@@ -0,0 +1,156 @@
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+package server_test
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "io/ioutil"
+ "mime/multipart"
+ "net/http"
+ "net/http/httptest"
+ "os"
+
+ "github.com/golang/mock/gomock"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/ginkgo/extensions/table"
+ . "github.com/onsi/gomega"
+
+ "git.tizen.org/tools/weles"
+ "git.tizen.org/tools/weles/mock"
+ "git.tizen.org/tools/weles/server"
+ "git.tizen.org/tools/weles/server/operations/jobs"
+)
+
+var _ = Describe("JobCreatorHandler", func() {
+
+ var (
+ mockCtrl *gomock.Controller
+ mockJobManager *mock.MockJobManager
+ apiDefaults *server.APIDefaults
+ testserver *httptest.Server
+ )
+
+ BeforeEach(func() {
+ mockCtrl, mockJobManager, _, apiDefaults, testserver = testServerSetup()
+ })
+
+ AfterEach(func() {
+ testserver.Close()
+ mockCtrl.Finish()
+ })
+
+ Describe("Creating a job", func() {
+ requestBody := func(fileName string, fieldName string, acceptH string) (req *http.Request) {
+ bodyBuf := &bytes.Buffer{}
+ bodyWriter := multipart.NewWriter(bodyBuf)
+ //create new form-data header with provided key-value pair
+ fileWriter, err := bodyWriter.CreateFormFile(fieldName, fileName)
+ Expect(err).ToNot(HaveOccurred())
+
+ file, err := os.OpenFile(fileName, os.O_RDONLY, 0755)
+ Expect(err).ToNot(HaveOccurred())
+ defer file.Close()
+ _, err = io.Copy(fileWriter, file)
+ Expect(err).ToNot(HaveOccurred())
+ bodyWriter.Close()
+
+ req, err = http.NewRequest(http.MethodPost, testserver.URL+"/api/v1/jobs/", bodyBuf)
+ Expect(err).ToNot(HaveOccurred())
+
+ req.Header.Set("Content-Type", bodyWriter.FormDataContentType())
+ if acceptH != OMIT {
+ req.Header.Set("Accept", acceptH)
+ }
+ return req
+ }
+ mockInput := func(fileName string) (orgBody []byte) {
+ file, err := os.OpenFile(fileName, os.O_RDONLY, 0755)
+ Expect(err).ToNot(HaveOccurred())
+ defer file.Close()
+ orgBody, err = ioutil.ReadAll(file)
+ Expect(err).ToNot(HaveOccurred())
+ return orgBody
+ }
+ Context("server receives correct POST request with accept header XML/JSON", func() {
+ DescribeTable("should respond with 201 and JobID in body (XML/JSON)",
+ func(accept string, expect string) {
+
+ req := requestBody("test_sample.yml", "yamlfile", accept)
+ orgBody := mockInput("test_sample.yml")
+ client := testserver.Client()
+ mockJobManager.EXPECT().CreateJob(orgBody).Return(weles.JobID(1234), nil)
+
+ resp, err := client.Do(req)
+ Expect(err).ToNot(HaveOccurred())
+
+ defer resp.Body.Close()
+ resp_body, err := ioutil.ReadAll(resp.Body)
+ Expect(err).ToNot(HaveOccurred())
+
+ Expect(resp_body).To(MatchJSON(expect))
+ Expect(resp.StatusCode).To(Equal(201))
+
+ },
+ Entry("json", JSON, "1234\n"),
+ Entry("default json", OMIT, "1234\n"),
+ )
+ })
+ Context("server receives correct POST request but CreateJob returns error", func() {
+ DescribeTable("should respond with 500 and error message in body (XML/JSON)",
+ func(accept string, expect string) {
+
+ req := requestBody("test_sample.yml", "yamlfile", accept)
+ orgBody := mockInput("test_sample.yml")
+ client := testserver.Client()
+ mockJobManager.EXPECT().CreateJob(orgBody).Return(weles.JobID(0), errors.New("Unparsable"))
+
+ resp, err := client.Do(req)
+ Expect(err).ToNot(HaveOccurred())
+
+ defer resp.Body.Close()
+ resp_body, err := ioutil.ReadAll(resp.Body)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(string(resp_body)).To(MatchJSON(expect))
+ Expect(resp.StatusCode).To(Equal(500))
+ },
+ Entry("json", JSON, "{\"message\":\"Unparsable\"}\n"),
+ Entry("default json", OMIT, "{\"message\":\"Unparsable\"}\n"),
+ )
+ })
+
+ Context("handler receives nil instead of file", func() {
+ It("should return unprocessable entity object", func() {
+ req, err := http.NewRequest(http.MethodPost, testserver.URL+"/api/v1/jobs/", errReader(0))
+ Expect(err).ToNot(HaveOccurred())
+ params := jobs.JobCreatorParams{Yamlfile: errReader(0), HTTPRequest: req}
+
+ ret := apiDefaults.Managers.JobCreator(params)
+ Expect(ret.(*jobs.JobCreatorUnprocessableEntity).Payload).To(Equal(&weles.ErrResponse{Message: "reader error"}))
+
+ })
+ })
+ })
+})
+
+type errReader int
+
+func (errReader) Read(p []byte) (n int, err error) {
+ return 0, errors.New("reader error")
+}
+func (errReader) Close() (err error) {
+ return errors.New("close error")
+}
diff --git a/server/job_lister_handler.go b/server/job_lister_handler.go
new file mode 100644
index 0000000..152dd5b
--- /dev/null
+++ b/server/job_lister_handler.go
@@ -0,0 +1,152 @@
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+package server
+
+import (
+ "github.com/go-openapi/runtime/middleware"
+
+ "git.tizen.org/tools/weles"
+ "git.tizen.org/tools/weles/server/operations/jobs"
+)
+
+// JobLister is a handler which passess requests for listing jobs to jobmanager.
+func (a *APIDefaults) JobLister(params jobs.JobListerParams) middleware.Responder {
+ if (params.After != nil) && (params.Before != nil) {
+ return jobs.NewJobListerBadRequest().WithPayload(&weles.ErrResponse{Message: weles.ErrBeforeAfterNotAllowed.Error()})
+ }
+
+ var jobInfoReceived []weles.JobInfo
+ var listInfo weles.ListInfo
+ var err error
+ paginator := weles.JobPagination{}
+
+ if a.PageLimit != 0 {
+ paginator = setJobPaginator(params, a.PageLimit)
+ }
+
+ if params.JobFilterAndSort != nil {
+ jobInfoReceived, listInfo, err = a.Managers.JM.ListJobs(*params.JobFilterAndSort.Filter, *params.JobFilterAndSort.Sorter, paginator)
+ } else {
+ jobInfoReceived, listInfo, err = a.Managers.JM.ListJobs(weles.JobFilter{}, weles.JobSorter{}, paginator)
+ }
+ if err != nil {
+ // due to weles.ErrInvalidArgument implementing error interface rather than being error
+ // (which is intentional as we want to pass underlying error) switch err.(type) checks only
+ // weles.ErrInvalidArgument. Rest of error handling should be in default clause of the type
+ // switch.
+ switch err.(type) {
+ default:
+ if err == weles.ErrJobNotFound {
+ return jobs.NewJobListerNotFound().WithPayload(&weles.ErrResponse{Message: weles.ErrJobNotFound.Error()})
+ }
+ return jobs.NewJobListerInternalServerError().WithPayload(&weles.ErrResponse{Message: err.Error()})
+ case weles.ErrInvalidArgument:
+ return jobs.NewJobListerBadRequest().WithPayload(&weles.ErrResponse{Message: err.Error()})
+ }
+ }
+ jobInfoReturned := jobInfoReceivedToReturned(jobInfoReceived)
+
+ if (listInfo.RemainingRecords == 0) || (a.PageLimit == 0) {
+ return responder200(listInfo, paginator, jobInfoReturned, a.PageLimit)
+ }
+ return responder206(listInfo, paginator, jobInfoReturned, a.PageLimit)
+
+}
+
+func responder206(listInfo weles.ListInfo, paginator weles.JobPagination, jobInfoReturned []*weles.JobInfo, defaultPageLimit int32) (responder *jobs.JobListerPartialContent) {
+ var jobListerURL jobs.JobListerURL
+
+ responder = jobs.NewJobListerPartialContent()
+ responder.SetTotalRecords(listInfo.TotalRecords)
+ responder.SetRemainingRecords(listInfo.RemainingRecords)
+
+ tmp := uint64(jobInfoReturned[len(jobInfoReturned)-1].JobID)
+ jobListerURL.After = &tmp
+
+ if defaultPageLimit != paginator.Limit {
+ tmp := int32(paginator.Limit)
+ jobListerURL.Limit = &tmp
+ }
+ responder.SetNext(jobListerURL.String())
+ if paginator.JobID != 0 { // not the first page
+ var jobListerURL jobs.JobListerURL
+ tmp = uint64(jobInfoReturned[0].JobID)
+ jobListerURL.Before = &tmp
+ if defaultPageLimit != paginator.Limit {
+ tmp := int32(paginator.Limit)
+ jobListerURL.Limit = &tmp
+ }
+ responder.SetPrevious(jobListerURL.String())
+ }
+ responder.SetPayload(jobInfoReturned)
+ return
+}
+
+func responder200(listInfo weles.ListInfo, paginator weles.JobPagination, jobInfoReturned []*weles.JobInfo, defaultPageLimit int32) (responder *jobs.JobListerOK) {
+ var jobListerURL jobs.JobListerURL
+ responder = jobs.NewJobListerOK()
+ responder.SetTotalRecords(listInfo.TotalRecords)
+ if paginator.JobID != 0 { //not the first page
+ // keep in mind that JobID in paginator is taken from query parameter, not jobmanager
+ if paginator.Forward == true {
+ tmp := uint64(jobInfoReturned[0].JobID)
+ jobListerURL.Before = &tmp
+ if defaultPageLimit != paginator.Limit {
+ tmp := int32(paginator.Limit)
+ jobListerURL.Limit = &tmp
+ }
+ responder.SetPrevious(jobListerURL.String())
+ }
+ if paginator.Forward == false {
+ tmp := uint64(jobInfoReturned[len(jobInfoReturned)-1].JobID)
+ jobListerURL.After = &tmp
+ if defaultPageLimit != paginator.Limit {
+ tmp := int32(paginator.Limit)
+ jobListerURL.Limit = &tmp
+ }
+ responder.SetNext(jobListerURL.String())
+ }
+ }
+ responder.SetPayload(jobInfoReturned)
+ return
+}
+
+func setJobPaginator(params jobs.JobListerParams, defaultPageLimit int32) (paginator weles.JobPagination) {
+ paginator.Forward = true
+ if params.After != nil {
+ paginator.JobID = weles.JobID(*params.After)
+ } else if params.Before != nil {
+ paginator.JobID = weles.JobID(*params.Before)
+ paginator.Forward = false
+ }
+
+ if params.Limit == nil {
+ paginator.Limit = defaultPageLimit
+ } else {
+ paginator.Limit = *params.Limit
+ }
+ return
+}
+
+//jobInfoReceivedToReturned is a function which changes the slice of values to slice of pointers.
+//It is required due to fact that swagger generates responses as slices of pointers rather than
+//slices of values that the interface provides.
+func jobInfoReceivedToReturned(jobInfoReceived []weles.JobInfo) []*weles.JobInfo {
+ jobInfoReturned := make([]*weles.JobInfo, len(jobInfoReceived))
+ for i := range jobInfoReceived {
+ jobInfoReturned[i] = &jobInfoReceived[i]
+ }
+ return jobInfoReturned
+}
diff --git a/server/job_lister_handler_test.go b/server/job_lister_handler_test.go
new file mode 100644
index 0000000..bfc75e0
--- /dev/null
+++ b/server/job_lister_handler_test.go
@@ -0,0 +1,637 @@
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+package server_test
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "strconv"
+ "strings"
+ "time"
+
+ "git.tizen.org/tools/weles"
+ "git.tizen.org/tools/weles/mock"
+ "git.tizen.org/tools/weles/server"
+
+ "github.com/go-openapi/strfmt"
+ "github.com/golang/mock/gomock"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/ginkgo/extensions/table"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("JobListerHandler", func() {
+
+ var (
+ mockCtrl *gomock.Controller
+ apiDefaults *server.APIDefaults
+ mockJobManager *mock.MockJobManager
+ testserver *httptest.Server
+ )
+
+ BeforeEach(func() {
+ mockCtrl, mockJobManager, _, apiDefaults, testserver = testServerSetup()
+ })
+
+ AfterEach(func() {
+ mockCtrl.Finish()
+ testserver.Close()
+ })
+
+ Describe("Listing jobs", func() {
+ createRequest := func(
+ reqBody io.Reader,
+ path string,
+ query string,
+ contentH string,
+ acceptH string) (req *http.Request) {
+ if path == "" {
+ path = "/api/v1/jobs/list"
+ }
+ req, err := http.NewRequest(http.MethodPost, testserver.URL+path+query, reqBody)
+ Expect(err).ToNot(HaveOccurred())
+ req.Header.Set("Content-Type", contentH)
+ req.Header.Set("Accept", acceptH)
+ return req
+ }
+
+ filterSorterReqBody := func(
+ filter weles.JobFilter,
+ sorter weles.JobSorter,
+ contentH string) (
+ rb *bytes.Reader) {
+
+ jobFilterSort := weles.JobFilterAndSort{
+ Filter: &filter,
+ Sorter: &sorter}
+
+ jobFilterSortMarshalled, err := json.Marshal(jobFilterSort)
+ Expect(err).ToNot(HaveOccurred())
+
+ return bytes.NewReader(jobFilterSortMarshalled)
+
+ }
+
+ checkReceivedJobInfo := func(respBody []byte, jobInfo []weles.JobInfo, acceptH string) {
+ jobInfoMarshalled, err := json.Marshal(jobInfo)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(string(respBody)).To(MatchJSON(string(jobInfoMarshalled)))
+
+ }
+
+ checkReceivedErr := func(respBody []byte, jmerr error, acceptH string) {
+ errMarshalled, err := json.Marshal(weles.ErrResponse{
+ Message: jmerr.Error(),
+ Type: ""})
+ Expect(err).ToNot(HaveOccurred())
+ Expect(string(respBody)).To(MatchJSON(string(errMarshalled)))
+ }
+
+ //few structs to test against
+ someTime := strfmt.DateTime(time.Date(2017, time.May, 3, 11, 34, 55, 0, time.UTC))
+ // time.Date nsec argument must be 0 as it is 0ed out when transported via api.
+
+ filledFilter1 := weles.JobFilter{
+ CreatedAfter: someTime,
+ Status: []weles.JobStatus{
+ weles.JobStatusNEW,
+ weles.JobStatusPARSING,
+ weles.JobStatusDOWNLOADING}}
+
+ filledFilter2 := weles.JobFilter{
+ JobID: []weles.JobID{10, 100, 131},
+ Info: []string{
+ "something",
+ "something else",
+ "some really different thing"}}
+
+ filledFilter3 := weles.JobFilter{
+ UpdatedBefore: someTime,
+ Name: []string{
+ "daass",
+ "sdasa",
+ "asdasf32qw;;dq"}}
+
+ emptyFilter := weles.JobFilter{}
+
+ filledSorter1 := weles.JobSorter{
+ SortBy: weles.JobSortByCreatedDate,
+ SortOrder: weles.SortOrderAscending}
+
+ filledSorter2 := weles.JobSorter{
+ SortBy: weles.JobSortByJobStatus,
+ SortOrder: weles.SortOrderDescending}
+
+ emptySorter := weles.JobSorter{}
+
+ emptyPaginator := weles.JobPagination{}
+ emptyPaginator2 := weles.JobPagination{Forward: true}
+ after100 := "?after=100"
+ pAfter100 := weles.JobPagination{
+ JobID: weles.JobID(100),
+ Forward: true}
+ after100Limit50 := "?after=100&limit=50"
+ pAfter100Limit50 := weles.JobPagination{
+ JobID: weles.JobID(100),
+ Forward: true,
+ Limit: int32(50)}
+
+ before100 := "?before=100"
+ pBefore100 := weles.JobPagination{
+ JobID: weles.JobID(100),
+ Forward: false}
+
+ before100Limit50 := "?before=100&limit=50"
+ pBefore100Limit50 := weles.JobPagination{
+ JobID: weles.JobID(100),
+ Forward: false,
+ Limit: int32(50)}
+
+ limit50 := "?limit=50"
+ pLimit50 := weles.JobPagination{
+ Forward: true,
+ Limit: int32(50)}
+
+ type queryPaginator struct {
+ query string
+ paginator weles.JobPagination
+ }
+
+ queryPaginatorOK := []queryPaginator{
+ {
+ query: "",
+ paginator: emptyPaginator2},
+ {
+ query: before100,
+ paginator: pBefore100},
+ {
+ query: before100Limit50,
+ paginator: pBefore100Limit50},
+ {
+ query: after100,
+ paginator: pAfter100},
+ {
+ query: after100Limit50,
+ paginator: pAfter100Limit50},
+ {
+ query: limit50,
+ paginator: pLimit50}}
+
+ queriesOK := []string{"", before100, before100Limit50, after100, after100Limit50, limit50}
+
+ jobInfoSlice420 := createJobInfoSlice(420)
+
+ Context("j: Server receives correct request and has pagination turned off", func() {
+ for _, currQuery := range queriesOK { // expected behaviour - handler should ignore
+ // different queries when pagination is turned off globally.
+ DescribeTable("should respond with all avaliable jobs, ignoring query params",
+ func(filter weles.JobFilter, sorter weles.JobSorter, query string) {
+ apiDefaults.PageLimit = 0
+
+ listInfo := weles.ListInfo{
+ TotalRecords: uint64(len(jobInfoSlice420)),
+ RemainingRecords: 0}
+
+ mockJobManager.EXPECT().ListJobs(
+ filter, sorter, emptyPaginator).Return(
+ jobInfoSlice420, listInfo, nil)
+
+ reqBody := filterSorterReqBody(filter, sorter, JSON)
+
+ client := testserver.Client()
+ req := createRequest(reqBody, "", query, JSON, JSON)
+ resp, err := client.Do(req)
+ Expect(err).ToNot(HaveOccurred())
+
+ defer resp.Body.Close()
+ respBody, err := ioutil.ReadAll(resp.Body)
+ Expect(err).ToNot(HaveOccurred())
+
+ checkReceivedJobInfo(respBody, jobInfoSlice420, JSON)
+
+ Expect(resp.StatusCode).To(Equal(200))
+ Expect(resp.Header.Get("Next")).To(Equal(""))
+ Expect(resp.Header.Get("Previous")).To(Equal(""))
+ Expect(resp.Header.Get("RemainingRecords")).To(Equal(""))
+ Expect(resp.Header.Get("TotalRecords")).To(Equal(strconv.Itoa(len(jobInfoSlice420))))
+
+ },
+
+ Entry("given empty filter and sorter", emptyFilter, emptySorter, currQuery),
+
+ Entry("given filled filter and sorter", filledFilter2, filledSorter2, currQuery),
+
+ Entry("given filled filter and empty sorter", filledFilter1, emptySorter, currQuery),
+
+ Entry("given empty filter and filled sorter", emptyFilter, filledSorter1, currQuery),
+ )
+ }
+ })
+
+ Context("server receives correct request and has pagination turned on", func() {
+ jobInfoAll := createJobInfoSlice(100)
+ globalLimit := []int32{70, 100, 111}
+ for _, currgl := range globalLimit {
+ for _, curr := range queryPaginatorOK {
+ // expected behaviour - handler passes query parameters to JM.
+ // this should impact data returned by JM. It is not reflected in the
+ // below mock of JM as it is out of scope of server unit tests.
+ DescribeTable("should respond with all jobs",
+ func(jobInfo []weles.JobInfo,
+ filter weles.JobFilter,
+ sorter weles.JobSorter,
+ paginator weles.JobPagination,
+ query string, gl int32) {
+
+ apiDefaults.PageLimit = gl
+
+ if !strings.Contains(query, "limit") {
+ paginator.Limit = apiDefaults.PageLimit
+ }
+
+ listInfo := weles.ListInfo{
+ TotalRecords: uint64(len(jobInfo)),
+ RemainingRecords: 0}
+
+ mockJobManager.EXPECT().ListJobs(
+ filter, sorter, paginator).Return(
+ jobInfo, listInfo, nil)
+
+ reqBody := filterSorterReqBody(filter, sorter, JSON)
+ req := createRequest(reqBody, "", query, JSON, JSON)
+
+ client := testserver.Client()
+ resp, err := client.Do(req)
+ Expect(err).ToNot(HaveOccurred())
+
+ defer resp.Body.Close()
+ respBody, err := ioutil.ReadAll(resp.Body)
+ Expect(err).ToNot(HaveOccurred())
+
+ checkReceivedJobInfo(respBody, jobInfo, JSON)
+
+ Expect(resp.StatusCode).To(Equal(200))
+ // Next and Previous headers are ignored here as they are tested in other context.
+ Expect(resp.Header.Get("RemainingRecords")).To(Equal(""))
+ Expect(resp.Header.Get("TotalRecords")).To(
+ Equal(strconv.Itoa(len(jobInfo))))
+ },
+ Entry("given empty request, when JM has less jobs than page size",
+ jobInfoAll[:40], emptyFilter, emptySorter, curr.paginator, curr.query, currgl),
+
+ Entry("given filled filter, when JM returns less jobs than Default Page size",
+ jobInfoAll[10:67], filledFilter3, emptySorter, curr.paginator, curr.query, currgl),
+
+ Entry("given filled filter, when JM returns same amount of filtered jobs as Default Page Size",
+ jobInfoAll, filledFilter1, filledSorter2, curr.paginator, curr.query, currgl),
+ )
+ }
+ }
+ })
+
+ Context("Pagination on", func() {
+ jobInfoAll := createJobInfoSlice(400)
+ DescribeTable("paginating forward",
+ func(jobInfo []weles.JobInfo,
+ startingPageNo int,
+ filter weles.JobFilter,
+ sorter weles.JobSorter) {
+
+ apiDefaults.PageLimit = 100
+
+ //prepare data for first call
+ jobInfoStartingPage := jobInfo[(startingPageNo-1)*
+ int(apiDefaults.PageLimit) : startingPageNo*
+ int(apiDefaults.PageLimit)] //first page of data
+
+ startingPageQuery := ""
+
+ paginator := weles.JobPagination{Limit: apiDefaults.PageLimit}
+ paginator.Forward = true
+ if startingPageNo != 1 {
+ paginator.JobID = jobInfoStartingPage[0].JobID
+ startingPageQuery = "?after=" +
+ jobInfo[(startingPageNo-1)*int(apiDefaults.PageLimit)].JobID.String()
+ }
+
+ listInfo := weles.ListInfo{TotalRecords: uint64(len(jobInfo)), RemainingRecords: uint64(len(jobInfo) - startingPageNo*len(jobInfoStartingPage))}
+
+ first := mockJobManager.EXPECT().ListJobs(filter, sorter, paginator).Return(jobInfoStartingPage, listInfo, nil)
+
+ reqBody := filterSorterReqBody(filter, sorter, JSON)
+ req := createRequest(reqBody, "", startingPageQuery, JSON, JSON)
+ req.Close = true
+ client := testserver.Client()
+ resp, err := client.Do(req)
+ Expect(err).ToNot(HaveOccurred())
+
+ respBody, err := ioutil.ReadAll(resp.Body)
+ Expect(err).ToNot(HaveOccurred())
+
+ checkReceivedJobInfo(respBody, jobInfoStartingPage, JSON)
+ Expect(resp.StatusCode).To(Equal(206))
+
+ Expect(resp.Header.Get("Next")).To(Equal("/api/v1/jobs/list" + "?after=" + jobInfoStartingPage[apiDefaults.PageLimit-1].JobID.String()))
+ prevCheck := ""
+ if startingPageNo != 1 {
+ prevCheck = "/api/v1/jobs/list" + "?before=" + jobInfoStartingPage[0].JobID.String()
+ }
+
+ Expect(resp.Header.Get("Previous")).To(Equal(prevCheck))
+ Expect(resp.Header.Get("RemainingRecords")).To(Equal(strconv.Itoa(len(jobInfo) - (startingPageNo * len(jobInfoStartingPage)))))
+ Expect(resp.Header.Get("TotalRecords")).To(Equal(strconv.Itoa(len(jobInfo))))
+
+ nextPage := resp.Header.Get("Next")
+ resp.Body.Close()
+ testserver.CloseClientConnections()
+
+ //prepare data for second call based on previous
+ var jobInfo2 []weles.JobInfo
+ var secondReturnCode int
+ if (len(jobInfo) - startingPageNo*int(apiDefaults.PageLimit)) <= int(apiDefaults.PageLimit) {
+
+ jobInfo2 = jobInfo[startingPageNo*int(apiDefaults.PageLimit):] //next page is not full
+ secondReturnCode = 200
+ } else {
+ jobInfo2 = jobInfo[startingPageNo*int(apiDefaults.PageLimit) : (startingPageNo+1)*int(apiDefaults.PageLimit)] //last page is full
+ secondReturnCode = 206
+ }
+
+ paginator2 := weles.JobPagination{Limit: apiDefaults.PageLimit, Forward: true, JobID: jobInfoStartingPage[int(apiDefaults.PageLimit)-1].JobID}
+ listInfo2 := weles.ListInfo{TotalRecords: listInfo.TotalRecords}
+
+ if tmp := len(jobInfo) - (startingPageNo+1)*int(apiDefaults.PageLimit); tmp < 0 {
+ listInfo2.RemainingRecords = 0
+ } else {
+ listInfo2.RemainingRecords = uint64(tmp)
+ }
+ //filter and sorter should stay the same.
+ mockJobManager.EXPECT().ListJobs(filter, sorter, paginator2).Return(jobInfo2, listInfo2, nil).After(first)
+
+ client2 := testserver.Client()
+ req2 := createRequest(reqBody, nextPage, "", JSON, JSON)
+ req2.Close = true
+ resp2, err := client2.Do(req2)
+ Expect(err).ToNot(HaveOccurred())
+
+ defer resp2.Body.Close()
+ respBody2, err := ioutil.ReadAll(resp2.Body)
+ Expect(err).ToNot(HaveOccurred())
+
+ checkReceivedJobInfo(respBody2, jobInfo2, JSON)
+
+ Expect(resp2.StatusCode).To(Equal(secondReturnCode))
+
+ if secondReturnCode == 200 {
+ Expect(resp2.Header.Get("Next")).To(Equal(""))
+ prevCheck = "/api/v1/jobs/list" + "?before=" + jobInfo2[0].JobID.String()
+ Expect(resp2.Header.Get("Previous")).To(Equal(prevCheck))
+ } else {
+ prevCheck = "/api/v1/jobs/list" + "?before=" + jobInfo2[0].JobID.String()
+ nextCheck := "/api/v1/jobs/list" + "?after=" + jobInfo2[int(apiDefaults.PageLimit)-1].JobID.String()
+ Expect(resp2.Header.Get("Next")).To(Equal(nextCheck))
+ Expect(resp2.Header.Get("Previous")).To(Equal(prevCheck))
+ }
+ if tmp := strconv.Itoa(len(jobInfo) - startingPageNo*len(jobInfoStartingPage) - len(jobInfo2)); tmp != "0" {
+ Expect(resp2.Header.Get("RemainingRecords")).To(Equal(tmp))
+ } else {
+
+ Expect(resp2.Header.Get("RemainingRecords")).To(Equal(""))
+ }
+ Expect(resp2.Header.Get("TotalRecords")).To(Equal(strconv.Itoa(len(jobInfo))))
+
+ },
+ Entry("1->2/2", // from 1 to 2 out of 2 (pages)
+ jobInfoAll[:170], 1, emptyFilter, emptySorter),
+ Entry("1->2/3",
+ jobInfoAll[:270], 1, emptyFilter, emptySorter),
+ Entry("2->3/3",
+ jobInfoAll[:300], 2, emptyFilter, emptySorter),
+ Entry("2->3/4",
+ jobInfoAll[:350], 2, emptyFilter, emptySorter),
+ )
+ DescribeTable("paginating backward",
+ func(jobInfo []weles.JobInfo,
+ startingPageNo int, pages int,
+ filter weles.JobFilter,
+ sorter weles.JobSorter) {
+
+ apiDefaults.PageLimit = 100
+ paginator := weles.JobPagination{Limit: apiDefaults.PageLimit}
+ //prepare data for first call
+ jobInfoStartingPage := []weles.JobInfo{}
+ var startingPageQuery string
+ listInfo := weles.ListInfo{}
+ if startingPageNo == pages {
+ jobInfoStartingPage = jobInfo[(startingPageNo-1)*
+ int(apiDefaults.PageLimit) : len(jobInfo)-1]
+ paginator.Forward = true
+ paginator.JobID = jobInfoStartingPage[len(jobInfoStartingPage)-1].JobID
+ startingPageQuery = "?after=" + paginator.JobID.String()
+ listInfo = weles.ListInfo{
+ TotalRecords: uint64(len(jobInfo)),
+ RemainingRecords: 0}
+
+ } else {
+ jobInfoStartingPage = jobInfo[(startingPageNo)*int(apiDefaults.PageLimit) : (startingPageNo+1)*
+ int(apiDefaults.PageLimit)] //first page of data
+ paginator.Forward = false
+ paginator.JobID = jobInfo[(startingPageNo*int(apiDefaults.PageLimit))-1].JobID
+ startingPageQuery = "?before=" + paginator.JobID.String()
+ listInfo = weles.ListInfo{
+ TotalRecords: uint64(len(jobInfo)),
+ RemainingRecords: uint64(len(jobInfo) - (int(apiDefaults.PageLimit) * startingPageNo))}
+
+ }
+
+ first := mockJobManager.EXPECT().ListJobs(filter, sorter, paginator).Return(jobInfoStartingPage, listInfo, nil)
+
+ reqBody := filterSorterReqBody(filter, sorter, JSON)
+ req := createRequest(reqBody, "", startingPageQuery, JSON, JSON)
+ req.Close = true
+ client := testserver.Client()
+ resp, err := client.Do(req)
+ Expect(err).ToNot(HaveOccurred())
+
+ respBody, err := ioutil.ReadAll(resp.Body)
+ Expect(err).ToNot(HaveOccurred())
+
+ checkReceivedJobInfo(respBody, jobInfoStartingPage, JSON)
+
+ if startingPageNo == pages {
+ Expect(resp.StatusCode).To(Equal(200))
+ Expect(resp.Header.Get("Previous")).To(Equal("/api/v1/jobs/list?before=" + jobInfoStartingPage[0].JobID.String()))
+ Expect(resp.Header.Get("Next")).To(Equal(""))
+ Expect(resp.Header.Get("TotalRecords")).To(Equal(strconv.Itoa(len(jobInfo))))
+ Expect(resp.Header.Get("RemainingRecords")).To(Equal(""))
+ } else {
+ Expect(resp.StatusCode).To(Equal(206))
+ Expect(resp.Header.Get("Previous")).To(Equal("/api/v1/jobs/list?before=" + jobInfoStartingPage[0].JobID.String()))
+ Expect(resp.Header.Get("Next")).To(Equal("/api/v1/jobs/list?after=" + jobInfoStartingPage[len(jobInfoStartingPage)-1].JobID.String()))
+ Expect(resp.Header.Get("TotalRecords")).To(Equal(strconv.Itoa(len(jobInfo))))
+ Expect(resp.Header.Get("RemainingRecords")).To(Equal(strconv.FormatUint(listInfo.RemainingRecords, 10)))
+ }
+
+ prevPage := resp.Header.Get("Previous")
+
+ resp.Body.Close()
+ testserver.CloseClientConnections()
+
+ //prepare data for second call based on previous
+
+ jobInfo2 := []weles.JobInfo{}
+ paginator2 := weles.JobPagination{Limit: apiDefaults.PageLimit, Forward: false, JobID: jobInfoStartingPage[0].JobID}
+ listInfo2 := weles.ListInfo{TotalRecords: listInfo.TotalRecords}
+ if startingPageNo == pages {
+ jobInfo2 = jobInfo[(startingPageNo-2)*int(apiDefaults.PageLimit) : (startingPageNo-1)*int(apiDefaults.PageLimit)]
+ if startingPageNo-1 == 1 {
+ listInfo2.RemainingRecords = 0
+ } else {
+ listInfo2.RemainingRecords = uint64((pages - (startingPageNo - 1)) * int(apiDefaults.PageLimit))
+ }
+ } else {
+ jobInfo2 = jobInfo[(startingPageNo-1)*int(apiDefaults.PageLimit) : startingPageNo*int(apiDefaults.PageLimit)]
+ listInfo2.RemainingRecords = uint64(apiDefaults.PageLimit)
+ }
+
+ mockJobManager.EXPECT().ListJobs(filter, sorter, paginator2).Return(jobInfo2, listInfo2, nil).After(first)
+ client2 := testserver.Client()
+ req2 := createRequest(reqBody, prevPage, "", JSON, JSON)
+ req2.Close = true
+ resp2, err := client2.Do(req2)
+ Expect(err).ToNot(HaveOccurred())
+
+ defer resp2.Body.Close()
+ respBody2, err := ioutil.ReadAll(resp2.Body)
+ Expect(err).ToNot(HaveOccurred())
+
+ checkReceivedJobInfo(respBody2, jobInfo2, JSON)
+ if startingPageNo == pages {
+
+ if startingPageNo-1 == 1 {
+ Expect(resp2.StatusCode).To(Equal(200))
+ Expect(resp2.Header.Get("RemainingRecords")).To(Equal(""))
+ Expect(resp2.Header.Get("Previous")).To(Equal(""))
+
+ } else {
+ Expect(resp2.StatusCode).To(Equal(206))
+ Expect(resp2.Header.Get("RemainingRecords")).To(Equal(strconv.FormatInt(int64(apiDefaults.PageLimit), 10)))
+ Expect(resp2.Header.Get("Previous")).To(Equal("/api/v1/jobs/list?before=" + jobInfo2[0].JobID.String()))
+ }
+ } else {
+ Expect(resp2.StatusCode).To(Equal(206))
+ Expect(resp2.Header.Get("RemainingRecords")).To(Equal(strconv.FormatInt(int64(apiDefaults.PageLimit), 10)))
+ }
+ Expect(resp2.Header.Get("Next")).To(Equal("/api/v1/jobs/list?after=" + jobInfo2[len(jobInfo2)-1].JobID.String()))
+ Expect(resp2.Header.Get("TotalRecords")).To(Equal(strconv.Itoa(len(jobInfo))))
+ },
+ // ginkgo does not like It clauses with the same name. To avoid conflicts with
+ // tests of listing artifacts, each it clause not referring to jobs exlicitly
+ // is prefixed with j:
+ Entry("j: 2->1/2", //meaning: from 1 to 2 out of 2 (pages)
+ jobInfoAll[:170], 2, 2, emptyFilter, emptySorter),
+ Entry("j: 2->1/3",
+ jobInfoAll[:270], 2, 3, emptyFilter, emptySorter),
+ Entry("j: 3->2/4",
+ jobInfoAll[:350], 3, 4, emptyFilter, emptySorter),
+ Entry("j: 3->2/3",
+ jobInfoAll[:300], 3, 3, emptyFilter, emptySorter),
+ )
+ })
+
+ Context("There is an error", func() {
+ DescribeTable("Server should respond with error from JobManager",
+ func(pageLimit int, aviJobs int, filter weles.JobFilter, sorter weles.JobSorter, statusCode int, jmerr error) {
+ apiDefaults.PageLimit = int32(pageLimit)
+ jobInfo := createJobInfoSlice(aviJobs)
+ paginator := weles.JobPagination{Limit: apiDefaults.PageLimit}
+ if pageLimit == 0 {
+ paginator.Forward = false
+ } else {
+ paginator.Forward = true
+ }
+ listInfo := weles.ListInfo{TotalRecords: uint64(aviJobs), RemainingRecords: 0}
+
+ mockJobManager.EXPECT().ListJobs(filter, sorter, paginator).Return(jobInfo, listInfo, jmerr)
+ reqBody := filterSorterReqBody(filter, sorter, JSON)
+ client := testserver.Client()
+ req := createRequest(reqBody, "", "", JSON, JSON)
+ resp, err := client.Do(req)
+ Expect(err).ToNot(HaveOccurred())
+
+ defer resp.Body.Close()
+ respBody, err := ioutil.ReadAll(resp.Body)
+ Expect(err).ToNot(HaveOccurred())
+
+ checkReceivedErr(respBody, jmerr, JSON)
+
+ Expect(resp.StatusCode).To(Equal(statusCode))
+ Expect(resp.Header.Get("Next")).To(Equal(""))
+ Expect(resp.Header.Get("Previous")).To(Equal(""))
+ Expect(resp.Header.Get("TotalRecords")).To(Equal(""))
+ Expect(resp.Header.Get("RemainingRecords")).To(Equal(""))
+
+ },
+ Entry("404 status, Job not found error when server has 0 jobs avaliable,pagination off",
+ 0, 0, emptyFilter, emptySorter, 404, weles.ErrJobNotFound),
+ Entry("404 status, Job not found error when server has 100 jobs but none fulfilling filter, pagination off",
+ 0, 100, filledFilter1, emptySorter, 404, weles.ErrJobNotFound),
+ Entry("500 status, JobManager unexpected error when server has 100 jobs, pagination off",
+ 0, 100, emptyFilter, emptySorter, 500, errors.New("This is some errors string")),
+ Entry("404 status, Job not found error when server has 0 jobs avaliable,pagination on",
+ 100, 0, emptyFilter, emptySorter, 404, weles.ErrJobNotFound),
+ Entry("404 status, Job not found error when server has 100 jobs but none fulfilling filter, pagination on",
+ 100, 100, filledFilter1, emptySorter, 404, weles.ErrJobNotFound),
+ Entry("500 status, JobManager unexpected error when server has 100 jobs, pagination on",
+ 100, 100, emptyFilter, emptySorter, 500, errors.New("This is some errors string")),
+ )
+ })
+
+ DescribeTable("error returned by server due to both before and after query params set",
+ func(defaultPageLimit int32, query string, acceptH string, contentH string, filter weles.JobFilter, sorter weles.JobSorter) {
+ apiDefaults.PageLimit = defaultPageLimit
+
+ reqBody := filterSorterReqBody(filter, sorter, contentH)
+ req := createRequest(reqBody, "", query, contentH, acceptH)
+
+ client := testserver.Client()
+ resp, err := client.Do(req)
+ Expect(err).ToNot(HaveOccurred())
+ defer resp.Body.Close()
+
+ respBody, err := ioutil.ReadAll(resp.Body)
+ Expect(err).ToNot(HaveOccurred())
+ checkReceivedErr(respBody, weles.ErrBeforeAfterNotAllowed, acceptH)
+
+ Expect(resp.StatusCode).To(Equal(400))
+ Expect(resp.Header.Get("Next")).To(Equal(""))
+ Expect(resp.Header.Get("Previous")).To(Equal(""))
+ Expect(resp.Header.Get("TotalRecords")).To(Equal(""))
+ Expect(resp.Header.Get("RemainingRecords")).To(Equal(""))
+
+ },
+ Entry("json, pagination off",
+ int32(0), "?before=10&after=20", JSON, JSON, emptyFilter, emptySorter),
+ Entry("json, pagination on",
+ int32(100), "?before=10&after=20", JSON, JSON, emptyFilter, emptySorter),
+ )
+
+ })
+})
diff --git a/server/managers.go b/server/managers.go
new file mode 100644
index 0000000..ea78ee2
--- /dev/null
+++ b/server/managers.go
@@ -0,0 +1,37 @@
+// Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+package server
+
+import (
+ "git.tizen.org/tools/weles"
+)
+
+// Managers provide implementation of JobManager and ArtifactManager interfaces.
+type Managers struct {
+ JM weles.JobManager
+ AM weles.ArtifactManager
+}
+
+// APIDefaults contains interface implementations (Managers) and default values (set via CLI flags) for the API.
+type APIDefaults struct {
+ Managers *Managers
+ PageLimit int32
+}
+
+// NewManagers creates managers struct and assigns JobManager and ArtifactManager implementation
+// to it.
+func NewManagers(jm weles.JobManager, am weles.ArtifactManager) (m *Managers) {
+ return &Managers{JM: jm, AM: am}
+}
diff --git a/server/operations/artifacts/artifact_lister.go b/server/operations/artifacts/artifact_lister.go
new file mode 100644
index 0000000..863358d
--- /dev/null
+++ b/server/operations/artifacts/artifact_lister.go
@@ -0,0 +1,75 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package artifacts
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the generate command
+
+import (
+ "net/http"
+
+ middleware "github.com/go-openapi/runtime/middleware"
+)
+
+// ArtifactListerHandlerFunc turns a function with the right signature into a artifact lister handler
+type ArtifactListerHandlerFunc func(ArtifactListerParams) middleware.Responder
+
+// Handle executing the request and returning a response
+func (fn ArtifactListerHandlerFunc) Handle(params ArtifactListerParams) middleware.Responder {
+ return fn(params)
+}
+
+// ArtifactListerHandler interface for that can handle valid artifact lister params
+type ArtifactListerHandler interface {
+ Handle(ArtifactListerParams) middleware.Responder
+}
+
+// NewArtifactLister creates a new http.Handler for the artifact lister operation
+func NewArtifactLister(ctx *middleware.Context, handler ArtifactListerHandler) *ArtifactLister {
+ return &ArtifactLister{Context: ctx, Handler: handler}
+}
+
+/*ArtifactLister swagger:route POST /artifacts/list artifacts artifactLister
+
+List artifacts with filter and sort features
+
+ArtifactLister returns information on filtered Weles artifacts.
+
+*/
+type ArtifactLister struct {
+ Context *middleware.Context
+ Handler ArtifactListerHandler
+}
+
+func (o *ArtifactLister) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+ route, rCtx, _ := o.Context.RouteInfo(r)
+ if rCtx != nil {
+ r = rCtx
+ }
+ var Params = NewArtifactListerParams()
+
+ if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
+ o.Context.Respond(rw, r, route.Produces, route, err)
+ return
+ }
+
+ res := o.Handler.Handle(Params) // actually handle the request
+
+ o.Context.Respond(rw, r, route.Produces, route, res)
+
+}
diff --git a/server/operations/artifacts/artifact_lister_parameters.go b/server/operations/artifacts/artifact_lister_parameters.go
new file mode 100644
index 0000000..384bc98
--- /dev/null
+++ b/server/operations/artifacts/artifact_lister_parameters.go
@@ -0,0 +1,182 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package artifacts
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/runtime"
+ "github.com/go-openapi/runtime/middleware"
+ "github.com/go-openapi/swag"
+
+ strfmt "github.com/go-openapi/strfmt"
+
+ weles "git.tizen.org/tools/weles"
+)
+
+// NewArtifactListerParams creates a new ArtifactListerParams object
+// no default values defined in spec.
+func NewArtifactListerParams() ArtifactListerParams {
+
+ return ArtifactListerParams{}
+}
+
+// ArtifactListerParams contains all the bound params for the artifact lister operation
+// typically these are obtained from a http.Request
+//
+// swagger:parameters ArtifactLister
+type ArtifactListerParams struct {
+
+ // HTTP Request Object
+ HTTPRequest *http.Request `json:"-"`
+
+ /*ID of the last element from previous page.
+ In: query
+ */
+ After *int64
+ /*Artifact Filter and Sort object.
+ In: body
+ */
+ ArtifactFilterAndSort *weles.ArtifactFilterAndSort
+ /*ID of first element from next page.
+ In: query
+ */
+ Before *int64
+ /*Custom page limit. Denotes number of ArtifactInfo structures that will be returned.
+ In: query
+ */
+ Limit *int32
+}
+
+// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
+// for simple values it will use straight method calls.
+//
+// To ensure default values, the struct must have been initialized with NewArtifactListerParams() beforehand.
+func (o *ArtifactListerParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
+ var res []error
+
+ o.HTTPRequest = r
+
+ qs := runtime.Values(r.URL.Query())
+
+ qAfter, qhkAfter, _ := qs.GetOK("after")
+ if err := o.bindAfter(qAfter, qhkAfter, route.Formats); err != nil {
+ res = append(res, err)
+ }
+
+ if runtime.HasBody(r) {
+ defer r.Body.Close()
+ var body weles.ArtifactFilterAndSort
+ if err := route.Consumer.Consume(r.Body, &body); err != nil {
+ res = append(res, errors.NewParseError("artifactFilterAndSort", "body", "", err))
+ } else {
+ // validate body object
+ if err := body.Validate(route.Formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) == 0 {
+ o.ArtifactFilterAndSort = &body
+ }
+ }
+ }
+ qBefore, qhkBefore, _ := qs.GetOK("before")
+ if err := o.bindBefore(qBefore, qhkBefore, route.Formats); err != nil {
+ res = append(res, err)
+ }
+
+ qLimit, qhkLimit, _ := qs.GetOK("limit")
+ if err := o.bindLimit(qLimit, qhkLimit, route.Formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+// bindAfter binds and validates parameter After from query.
+func (o *ArtifactListerParams) bindAfter(rawData []string, hasKey bool, formats strfmt.Registry) error {
+ var raw string
+ if len(rawData) > 0 {
+ raw = rawData[len(rawData)-1]
+ }
+
+ // Required: false
+ // AllowEmptyValue: false
+ if raw == "" { // empty values pass all other validations
+ return nil
+ }
+
+ value, err := swag.ConvertInt64(raw)
+ if err != nil {
+ return errors.InvalidType("after", "query", "int64", raw)
+ }
+ o.After = &value
+
+ return nil
+}
+
+// bindBefore binds and validates parameter Before from query.
+func (o *ArtifactListerParams) bindBefore(rawData []string, hasKey bool, formats strfmt.Registry) error {
+ var raw string
+ if len(rawData) > 0 {
+ raw = rawData[len(rawData)-1]
+ }
+
+ // Required: false
+ // AllowEmptyValue: false
+ if raw == "" { // empty values pass all other validations
+ return nil
+ }
+
+ value, err := swag.ConvertInt64(raw)
+ if err != nil {
+ return errors.InvalidType("before", "query", "int64", raw)
+ }
+ o.Before = &value
+
+ return nil
+}
+
+// bindLimit binds and validates parameter Limit from query.
+func (o *ArtifactListerParams) bindLimit(rawData []string, hasKey bool, formats strfmt.Registry) error {
+ var raw string
+ if len(rawData) > 0 {
+ raw = rawData[len(rawData)-1]
+ }
+
+ // Required: false
+ // AllowEmptyValue: false
+ if raw == "" { // empty values pass all other validations
+ return nil
+ }
+
+ value, err := swag.ConvertInt32(raw)
+ if err != nil {
+ return errors.InvalidType("limit", "query", "int32", raw)
+ }
+ o.Limit = &value
+
+ return nil
+}
diff --git a/server/operations/artifacts/artifact_lister_responses.go b/server/operations/artifacts/artifact_lister_responses.go
new file mode 100644
index 0000000..8033622
--- /dev/null
+++ b/server/operations/artifacts/artifact_lister_responses.go
@@ -0,0 +1,417 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package artifacts
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/runtime"
+ "github.com/go-openapi/swag"
+
+ weles "git.tizen.org/tools/weles"
+)
+
+// ArtifactListerOKCode is the HTTP code returned for type ArtifactListerOK
+const ArtifactListerOKCode int = 200
+
+/*ArtifactListerOK OK
+
+swagger:response artifactListerOK
+*/
+type ArtifactListerOK struct {
+ /*URI to request next page of data. Please note that the same body must be used as in initial request.
+
+
+ */
+ Next string `json:"Next"`
+ /*URI to request next page of data. Please note that the same body must be used as in initial request.
+
+
+ */
+ Previous string `json:"Previous"`
+ /*count of records currently fulfilling the requested ArtifactFilter. Please note that this value may change when requesting for the same data at a different moment in time.
+
+
+ */
+ TotalRecords uint64 `json:"TotalRecords"`
+
+ /*
+ In: Body
+ */
+ Payload []*weles.ArtifactInfo `json:"body,omitempty"`
+}
+
+// NewArtifactListerOK creates ArtifactListerOK with default headers values
+func NewArtifactListerOK() *ArtifactListerOK {
+
+ return &ArtifactListerOK{}
+}
+
+// WithNext adds the next to the artifact lister o k response
+func (o *ArtifactListerOK) WithNext(next string) *ArtifactListerOK {
+ o.Next = next
+ return o
+}
+
+// SetNext sets the next to the artifact lister o k response
+func (o *ArtifactListerOK) SetNext(next string) {
+ o.Next = next
+}
+
+// WithPrevious adds the previous to the artifact lister o k response
+func (o *ArtifactListerOK) WithPrevious(previous string) *ArtifactListerOK {
+ o.Previous = previous
+ return o
+}
+
+// SetPrevious sets the previous to the artifact lister o k response
+func (o *ArtifactListerOK) SetPrevious(previous string) {
+ o.Previous = previous
+}
+
+// WithTotalRecords adds the totalRecords to the artifact lister o k response
+func (o *ArtifactListerOK) WithTotalRecords(totalRecords uint64) *ArtifactListerOK {
+ o.TotalRecords = totalRecords
+ return o
+}
+
+// SetTotalRecords sets the totalRecords to the artifact lister o k response
+func (o *ArtifactListerOK) SetTotalRecords(totalRecords uint64) {
+ o.TotalRecords = totalRecords
+}
+
+// WithPayload adds the payload to the artifact lister o k response
+func (o *ArtifactListerOK) WithPayload(payload []*weles.ArtifactInfo) *ArtifactListerOK {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the artifact lister o k response
+func (o *ArtifactListerOK) SetPayload(payload []*weles.ArtifactInfo) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *ArtifactListerOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ // response header Next
+
+ next := o.Next
+ if next != "" {
+ rw.Header().Set("Next", next)
+ }
+
+ // response header Previous
+
+ previous := o.Previous
+ if previous != "" {
+ rw.Header().Set("Previous", previous)
+ }
+
+ // response header TotalRecords
+
+ totalRecords := swag.FormatUint64(o.TotalRecords)
+ if totalRecords != "" {
+ rw.Header().Set("TotalRecords", totalRecords)
+ }
+
+ rw.WriteHeader(200)
+ payload := o.Payload
+ if payload == nil {
+ payload = make([]*weles.ArtifactInfo, 0, 50)
+ }
+
+ if err := producer.Produce(rw, payload); err != nil {
+ panic(err) // let the recovery middleware deal with this
+ }
+
+}
+
+// ArtifactListerPartialContentCode is the HTTP code returned for type ArtifactListerPartialContent
+const ArtifactListerPartialContentCode int = 206
+
+/*ArtifactListerPartialContent Partial Content
+
+swagger:response artifactListerPartialContent
+*/
+type ArtifactListerPartialContent struct {
+ /*URI to request next page of data. Please note that the same body must be used as in initial request.
+
+
+ */
+ Next string `json:"Next"`
+ /*URI to request next page of data. Please note that the same body must be used as in initial request.
+
+
+ */
+ Previous string `json:"Previous"`
+ /*number of records after current page. Please note that this value may change when requesting for the same data at a different moment in time.
+
+
+ */
+ RemainingRecords uint64 `json:"RemainingRecords"`
+ /*count of records currently fulfilling the requested ArtifactFilter. Please note that this value may change when requesting for the same data at a different moment in time.
+
+
+ */
+ TotalRecords uint64 `json:"TotalRecords"`
+
+ /*
+ In: Body
+ */
+ Payload []*weles.ArtifactInfo `json:"body,omitempty"`
+}
+
+// NewArtifactListerPartialContent creates ArtifactListerPartialContent with default headers values
+func NewArtifactListerPartialContent() *ArtifactListerPartialContent {
+
+ return &ArtifactListerPartialContent{}
+}
+
+// WithNext adds the next to the artifact lister partial content response
+func (o *ArtifactListerPartialContent) WithNext(next string) *ArtifactListerPartialContent {
+ o.Next = next
+ return o
+}
+
+// SetNext sets the next to the artifact lister partial content response
+func (o *ArtifactListerPartialContent) SetNext(next string) {
+ o.Next = next
+}
+
+// WithPrevious adds the previous to the artifact lister partial content response
+func (o *ArtifactListerPartialContent) WithPrevious(previous string) *ArtifactListerPartialContent {
+ o.Previous = previous
+ return o
+}
+
+// SetPrevious sets the previous to the artifact lister partial content response
+func (o *ArtifactListerPartialContent) SetPrevious(previous string) {
+ o.Previous = previous
+}
+
+// WithRemainingRecords adds the remainingRecords to the artifact lister partial content response
+func (o *ArtifactListerPartialContent) WithRemainingRecords(remainingRecords uint64) *ArtifactListerPartialContent {
+ o.RemainingRecords = remainingRecords
+ return o
+}
+
+// SetRemainingRecords sets the remainingRecords to the artifact lister partial content response
+func (o *ArtifactListerPartialContent) SetRemainingRecords(remainingRecords uint64) {
+ o.RemainingRecords = remainingRecords
+}
+
+// WithTotalRecords adds the totalRecords to the artifact lister partial content response
+func (o *ArtifactListerPartialContent) WithTotalRecords(totalRecords uint64) *ArtifactListerPartialContent {
+ o.TotalRecords = totalRecords
+ return o
+}
+
+// SetTotalRecords sets the totalRecords to the artifact lister partial content response
+func (o *ArtifactListerPartialContent) SetTotalRecords(totalRecords uint64) {
+ o.TotalRecords = totalRecords
+}
+
+// WithPayload adds the payload to the artifact lister partial content response
+func (o *ArtifactListerPartialContent) WithPayload(payload []*weles.ArtifactInfo) *ArtifactListerPartialContent {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the artifact lister partial content response
+func (o *ArtifactListerPartialContent) SetPayload(payload []*weles.ArtifactInfo) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *ArtifactListerPartialContent) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ // response header Next
+
+ next := o.Next
+ if next != "" {
+ rw.Header().Set("Next", next)
+ }
+
+ // response header Previous
+
+ previous := o.Previous
+ if previous != "" {
+ rw.Header().Set("Previous", previous)
+ }
+
+ // response header RemainingRecords
+
+ remainingRecords := swag.FormatUint64(o.RemainingRecords)
+ if remainingRecords != "" {
+ rw.Header().Set("RemainingRecords", remainingRecords)
+ }
+
+ // response header TotalRecords
+
+ totalRecords := swag.FormatUint64(o.TotalRecords)
+ if totalRecords != "" {
+ rw.Header().Set("TotalRecords", totalRecords)
+ }
+
+ rw.WriteHeader(206)
+ payload := o.Payload
+ if payload == nil {
+ payload = make([]*weles.ArtifactInfo, 0, 50)
+ }
+
+ if err := producer.Produce(rw, payload); err != nil {
+ panic(err) // let the recovery middleware deal with this
+ }
+
+}
+
+// ArtifactListerBadRequestCode is the HTTP code returned for type ArtifactListerBadRequest
+const ArtifactListerBadRequestCode int = 400
+
+/*ArtifactListerBadRequest Bad Request
+
+swagger:response artifactListerBadRequest
+*/
+type ArtifactListerBadRequest struct {
+
+ /*
+ In: Body
+ */
+ Payload *weles.ErrResponse `json:"body,omitempty"`
+}
+
+// NewArtifactListerBadRequest creates ArtifactListerBadRequest with default headers values
+func NewArtifactListerBadRequest() *ArtifactListerBadRequest {
+
+ return &ArtifactListerBadRequest{}
+}
+
+// WithPayload adds the payload to the artifact lister bad request response
+func (o *ArtifactListerBadRequest) WithPayload(payload *weles.ErrResponse) *ArtifactListerBadRequest {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the artifact lister bad request response
+func (o *ArtifactListerBadRequest) SetPayload(payload *weles.ErrResponse) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *ArtifactListerBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.WriteHeader(400)
+ if o.Payload != nil {
+ payload := o.Payload
+ if err := producer.Produce(rw, payload); err != nil {
+ panic(err) // let the recovery middleware deal with this
+ }
+ }
+}
+
+// ArtifactListerNotFoundCode is the HTTP code returned for type ArtifactListerNotFound
+const ArtifactListerNotFoundCode int = 404
+
+/*ArtifactListerNotFound Not Found
+
+swagger:response artifactListerNotFound
+*/
+type ArtifactListerNotFound struct {
+
+ /*
+ In: Body
+ */
+ Payload *weles.ErrResponse `json:"body,omitempty"`
+}
+
+// NewArtifactListerNotFound creates ArtifactListerNotFound with default headers values
+func NewArtifactListerNotFound() *ArtifactListerNotFound {
+
+ return &ArtifactListerNotFound{}
+}
+
+// WithPayload adds the payload to the artifact lister not found response
+func (o *ArtifactListerNotFound) WithPayload(payload *weles.ErrResponse) *ArtifactListerNotFound {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the artifact lister not found response
+func (o *ArtifactListerNotFound) SetPayload(payload *weles.ErrResponse) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *ArtifactListerNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.WriteHeader(404)
+ if o.Payload != nil {
+ payload := o.Payload
+ if err := producer.Produce(rw, payload); err != nil {
+ panic(err) // let the recovery middleware deal with this
+ }
+ }
+}
+
+// ArtifactListerInternalServerErrorCode is the HTTP code returned for type ArtifactListerInternalServerError
+const ArtifactListerInternalServerErrorCode int = 500
+
+/*ArtifactListerInternalServerError Internal Server error
+
+swagger:response artifactListerInternalServerError
+*/
+type ArtifactListerInternalServerError struct {
+
+ /*
+ In: Body
+ */
+ Payload *weles.ErrResponse `json:"body,omitempty"`
+}
+
+// NewArtifactListerInternalServerError creates ArtifactListerInternalServerError with default headers values
+func NewArtifactListerInternalServerError() *ArtifactListerInternalServerError {
+
+ return &ArtifactListerInternalServerError{}
+}
+
+// WithPayload adds the payload to the artifact lister internal server error response
+func (o *ArtifactListerInternalServerError) WithPayload(payload *weles.ErrResponse) *ArtifactListerInternalServerError {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the artifact lister internal server error response
+func (o *ArtifactListerInternalServerError) SetPayload(payload *weles.ErrResponse) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *ArtifactListerInternalServerError) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.WriteHeader(500)
+ if o.Payload != nil {
+ payload := o.Payload
+ if err := producer.Produce(rw, payload); err != nil {
+ panic(err) // let the recovery middleware deal with this
+ }
+ }
+}
diff --git a/server/operations/artifacts/artifact_lister_urlbuilder.go b/server/operations/artifacts/artifact_lister_urlbuilder.go
new file mode 100644
index 0000000..e2971e0
--- /dev/null
+++ b/server/operations/artifacts/artifact_lister_urlbuilder.go
@@ -0,0 +1,138 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package artifacts
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the generate command
+
+import (
+ "errors"
+ "net/url"
+ golangswaggerpaths "path"
+
+ "github.com/go-openapi/swag"
+)
+
+// ArtifactListerURL generates an URL for the artifact lister operation
+type ArtifactListerURL struct {
+ After *int64
+ Before *int64
+ Limit *int32
+
+ _basePath string
+ // avoid unkeyed usage
+ _ struct{}
+}
+
+// WithBasePath sets the base path for this url builder, only required when it's different from the
+// base path specified in the swagger spec.
+// When the value of the base path is an empty string
+func (o *ArtifactListerURL) WithBasePath(bp string) *ArtifactListerURL {
+ o.SetBasePath(bp)
+ return o
+}
+
+// SetBasePath sets the base path for this url builder, only required when it's different from the
+// base path specified in the swagger spec.
+// When the value of the base path is an empty string
+func (o *ArtifactListerURL) SetBasePath(bp string) {
+ o._basePath = bp
+}
+
+// Build a url path and query string
+func (o *ArtifactListerURL) Build() (*url.URL, error) {
+ var result url.URL
+
+ var _path = "/artifacts/list"
+
+ _basePath := o._basePath
+ if _basePath == "" {
+ _basePath = "/api/v1"
+ }
+ result.Path = golangswaggerpaths.Join(_basePath, _path)
+
+ qs := make(url.Values)
+
+ var after string
+ if o.After != nil {
+ after = swag.FormatInt64(*o.After)
+ }
+ if after != "" {
+ qs.Set("after", after)
+ }
+
+ var before string
+ if o.Before != nil {
+ before = swag.FormatInt64(*o.Before)
+ }
+ if before != "" {
+ qs.Set("before", before)
+ }
+
+ var limit string
+ if o.Limit != nil {
+ limit = swag.FormatInt32(*o.Limit)
+ }
+ if limit != "" {
+ qs.Set("limit", limit)
+ }
+
+ result.RawQuery = qs.Encode()
+
+ return &result, nil
+}
+
+// Must is a helper function to panic when the url builder returns an error
+func (o *ArtifactListerURL) Must(u *url.URL, err error) *url.URL {
+ if err != nil {
+ panic(err)
+ }
+ if u == nil {
+ panic("url can't be nil")
+ }
+ return u
+}
+
+// String returns the string representation of the path with query string
+func (o *ArtifactListerURL) String() string {
+ return o.Must(o.Build()).String()
+}
+
+// BuildFull builds a full url with scheme, host, path and query string
+func (o *ArtifactListerURL) BuildFull(scheme, host string) (*url.URL, error) {
+ if scheme == "" {
+ return nil, errors.New("scheme is required for a full url on ArtifactListerURL")
+ }
+ if host == "" {
+ return nil, errors.New("host is required for a full url on ArtifactListerURL")
+ }
+
+ base, err := o.Build()
+ if err != nil {
+ return nil, err
+ }
+
+ base.Scheme = scheme
+ base.Host = host
+ return base, nil
+}
+
+// StringFull returns the string representation of a complete url
+func (o *ArtifactListerURL) StringFull(scheme, host string) string {
+ return o.Must(o.BuildFull(scheme, host)).String()
+}
diff --git a/server/operations/jobs/job_canceler.go b/server/operations/jobs/job_canceler.go
new file mode 100644
index 0000000..159ed8f
--- /dev/null
+++ b/server/operations/jobs/job_canceler.go
@@ -0,0 +1,75 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package jobs
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the generate command
+
+import (
+ "net/http"
+
+ middleware "github.com/go-openapi/runtime/middleware"
+)
+
+// JobCancelerHandlerFunc turns a function with the right signature into a job canceler handler
+type JobCancelerHandlerFunc func(JobCancelerParams) middleware.Responder
+
+// Handle executing the request and returning a response
+func (fn JobCancelerHandlerFunc) Handle(params JobCancelerParams) middleware.Responder {
+ return fn(params)
+}
+
+// JobCancelerHandler interface for that can handle valid job canceler params
+type JobCancelerHandler interface {
+ Handle(JobCancelerParams) middleware.Responder
+}
+
+// NewJobCanceler creates a new http.Handler for the job canceler operation
+func NewJobCanceler(ctx *middleware.Context, handler JobCancelerHandler) *JobCanceler {
+ return &JobCanceler{Context: ctx, Handler: handler}
+}
+
+/*JobCanceler swagger:route POST /jobs/{JobID}/cancel jobs jobCanceler
+
+Cancel existing job
+
+JobCanceler stops execution of Job identified by JobID.
+
+*/
+type JobCanceler struct {
+ Context *middleware.Context
+ Handler JobCancelerHandler
+}
+
+func (o *JobCanceler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+ route, rCtx, _ := o.Context.RouteInfo(r)
+ if rCtx != nil {
+ r = rCtx
+ }
+ var Params = NewJobCancelerParams()
+
+ if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
+ o.Context.Respond(rw, r, route.Produces, route, err)
+ return
+ }
+
+ res := o.Handler.Handle(Params) // actually handle the request
+
+ o.Context.Respond(rw, r, route.Produces, route, res)
+
+}
diff --git a/server/operations/jobs/job_canceler_parameters.go b/server/operations/jobs/job_canceler_parameters.go
new file mode 100644
index 0000000..9dc10c1
--- /dev/null
+++ b/server/operations/jobs/job_canceler_parameters.go
@@ -0,0 +1,93 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package jobs
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/runtime/middleware"
+ "github.com/go-openapi/swag"
+
+ strfmt "github.com/go-openapi/strfmt"
+)
+
+// NewJobCancelerParams creates a new JobCancelerParams object
+// no default values defined in spec.
+func NewJobCancelerParams() JobCancelerParams {
+
+ return JobCancelerParams{}
+}
+
+// JobCancelerParams contains all the bound params for the job canceler operation
+// typically these are obtained from a http.Request
+//
+// swagger:parameters JobCanceler
+type JobCancelerParams struct {
+
+ // HTTP Request Object
+ HTTPRequest *http.Request `json:"-"`
+
+ /*
+ Required: true
+ In: path
+ */
+ JobID uint64
+}
+
+// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
+// for simple values it will use straight method calls.
+//
+// To ensure default values, the struct must have been initialized with NewJobCancelerParams() beforehand.
+func (o *JobCancelerParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
+ var res []error
+
+ o.HTTPRequest = r
+
+ rJobID, rhkJobID, _ := route.Params.GetOK("JobID")
+ if err := o.bindJobID(rJobID, rhkJobID, route.Formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+// bindJobID binds and validates parameter JobID from path.
+func (o *JobCancelerParams) bindJobID(rawData []string, hasKey bool, formats strfmt.Registry) error {
+ var raw string
+ if len(rawData) > 0 {
+ raw = rawData[len(rawData)-1]
+ }
+
+ // Required: true
+ // Parameter is provided by construction from the route
+
+ value, err := swag.ConvertUint64(raw)
+ if err != nil {
+ return errors.InvalidType("JobID", "path", "uint64", raw)
+ }
+ o.JobID = value
+
+ return nil
+}
diff --git a/server/operations/jobs/job_canceler_responses.go b/server/operations/jobs/job_canceler_responses.go
new file mode 100644
index 0000000..556049c
--- /dev/null
+++ b/server/operations/jobs/job_canceler_responses.go
@@ -0,0 +1,185 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package jobs
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/runtime"
+
+ weles "git.tizen.org/tools/weles"
+)
+
+// JobCancelerNoContentCode is the HTTP code returned for type JobCancelerNoContent
+const JobCancelerNoContentCode int = 204
+
+/*JobCancelerNoContent No Content
+
+swagger:response jobCancelerNoContent
+*/
+type JobCancelerNoContent struct {
+}
+
+// NewJobCancelerNoContent creates JobCancelerNoContent with default headers values
+func NewJobCancelerNoContent() *JobCancelerNoContent {
+
+ return &JobCancelerNoContent{}
+}
+
+// WriteResponse to the client
+func (o *JobCancelerNoContent) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
+
+ rw.WriteHeader(204)
+}
+
+// JobCancelerForbiddenCode is the HTTP code returned for type JobCancelerForbidden
+const JobCancelerForbiddenCode int = 403
+
+/*JobCancelerForbidden Forbidden
+
+swagger:response jobCancelerForbidden
+*/
+type JobCancelerForbidden struct {
+
+ /*
+ In: Body
+ */
+ Payload *weles.ErrResponse `json:"body,omitempty"`
+}
+
+// NewJobCancelerForbidden creates JobCancelerForbidden with default headers values
+func NewJobCancelerForbidden() *JobCancelerForbidden {
+
+ return &JobCancelerForbidden{}
+}
+
+// WithPayload adds the payload to the job canceler forbidden response
+func (o *JobCancelerForbidden) WithPayload(payload *weles.ErrResponse) *JobCancelerForbidden {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the job canceler forbidden response
+func (o *JobCancelerForbidden) SetPayload(payload *weles.ErrResponse) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *JobCancelerForbidden) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.WriteHeader(403)
+ if o.Payload != nil {
+ payload := o.Payload
+ if err := producer.Produce(rw, payload); err != nil {
+ panic(err) // let the recovery middleware deal with this
+ }
+ }
+}
+
+// JobCancelerNotFoundCode is the HTTP code returned for type JobCancelerNotFound
+const JobCancelerNotFoundCode int = 404
+
+/*JobCancelerNotFound Not Found
+
+swagger:response jobCancelerNotFound
+*/
+type JobCancelerNotFound struct {
+
+ /*
+ In: Body
+ */
+ Payload *weles.ErrResponse `json:"body,omitempty"`
+}
+
+// NewJobCancelerNotFound creates JobCancelerNotFound with default headers values
+func NewJobCancelerNotFound() *JobCancelerNotFound {
+
+ return &JobCancelerNotFound{}
+}
+
+// WithPayload adds the payload to the job canceler not found response
+func (o *JobCancelerNotFound) WithPayload(payload *weles.ErrResponse) *JobCancelerNotFound {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the job canceler not found response
+func (o *JobCancelerNotFound) SetPayload(payload *weles.ErrResponse) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *JobCancelerNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.WriteHeader(404)
+ if o.Payload != nil {
+ payload := o.Payload
+ if err := producer.Produce(rw, payload); err != nil {
+ panic(err) // let the recovery middleware deal with this
+ }
+ }
+}
+
+// JobCancelerInternalServerErrorCode is the HTTP code returned for type JobCancelerInternalServerError
+const JobCancelerInternalServerErrorCode int = 500
+
+/*JobCancelerInternalServerError Internal Server error
+
+swagger:response jobCancelerInternalServerError
+*/
+type JobCancelerInternalServerError struct {
+
+ /*
+ In: Body
+ */
+ Payload *weles.ErrResponse `json:"body,omitempty"`
+}
+
+// NewJobCancelerInternalServerError creates JobCancelerInternalServerError with default headers values
+func NewJobCancelerInternalServerError() *JobCancelerInternalServerError {
+
+ return &JobCancelerInternalServerError{}
+}
+
+// WithPayload adds the payload to the job canceler internal server error response
+func (o *JobCancelerInternalServerError) WithPayload(payload *weles.ErrResponse) *JobCancelerInternalServerError {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the job canceler internal server error response
+func (o *JobCancelerInternalServerError) SetPayload(payload *weles.ErrResponse) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *JobCancelerInternalServerError) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.WriteHeader(500)
+ if o.Payload != nil {
+ payload := o.Payload
+ if err := producer.Produce(rw, payload); err != nil {
+ panic(err) // let the recovery middleware deal with this
+ }
+ }
+}
diff --git a/server/operations/jobs/job_canceler_urlbuilder.go b/server/operations/jobs/job_canceler_urlbuilder.go
new file mode 100644
index 0000000..8223e7a
--- /dev/null
+++ b/server/operations/jobs/job_canceler_urlbuilder.go
@@ -0,0 +1,116 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package jobs
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the generate command
+
+import (
+ "errors"
+ "net/url"
+ golangswaggerpaths "path"
+ "strings"
+
+ "github.com/go-openapi/swag"
+)
+
+// JobCancelerURL generates an URL for the job canceler operation
+type JobCancelerURL struct {
+ JobID uint64
+
+ _basePath string
+ // avoid unkeyed usage
+ _ struct{}
+}
+
+// WithBasePath sets the base path for this url builder, only required when it's different from the
+// base path specified in the swagger spec.
+// When the value of the base path is an empty string
+func (o *JobCancelerURL) WithBasePath(bp string) *JobCancelerURL {
+ o.SetBasePath(bp)
+ return o
+}
+
+// SetBasePath sets the base path for this url builder, only required when it's different from the
+// base path specified in the swagger spec.
+// When the value of the base path is an empty string
+func (o *JobCancelerURL) SetBasePath(bp string) {
+ o._basePath = bp
+}
+
+// Build a url path and query string
+func (o *JobCancelerURL) Build() (*url.URL, error) {
+ var result url.URL
+
+ var _path = "/jobs/{JobID}/cancel"
+
+ jobID := swag.FormatUint64(o.JobID)
+ if jobID != "" {
+ _path = strings.Replace(_path, "{JobID}", jobID, -1)
+ } else {
+ return nil, errors.New("JobID is required on JobCancelerURL")
+ }
+
+ _basePath := o._basePath
+ if _basePath == "" {
+ _basePath = "/api/v1"
+ }
+ result.Path = golangswaggerpaths.Join(_basePath, _path)
+
+ return &result, nil
+}
+
+// Must is a helper function to panic when the url builder returns an error
+func (o *JobCancelerURL) Must(u *url.URL, err error) *url.URL {
+ if err != nil {
+ panic(err)
+ }
+ if u == nil {
+ panic("url can't be nil")
+ }
+ return u
+}
+
+// String returns the string representation of the path with query string
+func (o *JobCancelerURL) String() string {
+ return o.Must(o.Build()).String()
+}
+
+// BuildFull builds a full url with scheme, host, path and query string
+func (o *JobCancelerURL) BuildFull(scheme, host string) (*url.URL, error) {
+ if scheme == "" {
+ return nil, errors.New("scheme is required for a full url on JobCancelerURL")
+ }
+ if host == "" {
+ return nil, errors.New("host is required for a full url on JobCancelerURL")
+ }
+
+ base, err := o.Build()
+ if err != nil {
+ return nil, err
+ }
+
+ base.Scheme = scheme
+ base.Host = host
+ return base, nil
+}
+
+// StringFull returns the string representation of a complete url
+func (o *JobCancelerURL) StringFull(scheme, host string) string {
+ return o.Must(o.BuildFull(scheme, host)).String()
+}
diff --git a/server/operations/jobs/job_creator.go b/server/operations/jobs/job_creator.go
new file mode 100644
index 0000000..42010f0
--- /dev/null
+++ b/server/operations/jobs/job_creator.go
@@ -0,0 +1,75 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package jobs
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the generate command
+
+import (
+ "net/http"
+
+ middleware "github.com/go-openapi/runtime/middleware"
+)
+
+// JobCreatorHandlerFunc turns a function with the right signature into a job creator handler
+type JobCreatorHandlerFunc func(JobCreatorParams) middleware.Responder
+
+// Handle executing the request and returning a response
+func (fn JobCreatorHandlerFunc) Handle(params JobCreatorParams) middleware.Responder {
+ return fn(params)
+}
+
+// JobCreatorHandler interface for that can handle valid job creator params
+type JobCreatorHandler interface {
+ Handle(JobCreatorParams) middleware.Responder
+}
+
+// NewJobCreator creates a new http.Handler for the job creator operation
+func NewJobCreator(ctx *middleware.Context, handler JobCreatorHandler) *JobCreator {
+ return &JobCreator{Context: ctx, Handler: handler}
+}
+
+/*JobCreator swagger:route POST /jobs jobs jobCreator
+
+Add new job
+
+adds new Job in Weles using recipe passed in YAML format.
+
+*/
+type JobCreator struct {
+ Context *middleware.Context
+ Handler JobCreatorHandler
+}
+
+func (o *JobCreator) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+ route, rCtx, _ := o.Context.RouteInfo(r)
+ if rCtx != nil {
+ r = rCtx
+ }
+ var Params = NewJobCreatorParams()
+
+ if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
+ o.Context.Respond(rw, r, route.Produces, route, err)
+ return
+ }
+
+ res := o.Handler.Handle(Params) // actually handle the request
+
+ o.Context.Respond(rw, r, route.Produces, route, res)
+
+}
diff --git a/server/operations/jobs/job_creator_parameters.go b/server/operations/jobs/job_creator_parameters.go
new file mode 100644
index 0000000..0bdef7f
--- /dev/null
+++ b/server/operations/jobs/job_creator_parameters.go
@@ -0,0 +1,94 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package jobs
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "io"
+ "mime/multipart"
+ "net/http"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/runtime"
+ "github.com/go-openapi/runtime/middleware"
+)
+
+// NewJobCreatorParams creates a new JobCreatorParams object
+// no default values defined in spec.
+func NewJobCreatorParams() JobCreatorParams {
+
+ return JobCreatorParams{}
+}
+
+// JobCreatorParams contains all the bound params for the job creator operation
+// typically these are obtained from a http.Request
+//
+// swagger:parameters JobCreator
+type JobCreatorParams struct {
+
+ // HTTP Request Object
+ HTTPRequest *http.Request `json:"-"`
+
+ /*is Job description yaml file.
+ Required: true
+ In: formData
+ */
+ Yamlfile io.ReadCloser
+}
+
+// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
+// for simple values it will use straight method calls.
+//
+// To ensure default values, the struct must have been initialized with NewJobCreatorParams() beforehand.
+func (o *JobCreatorParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
+ var res []error
+
+ o.HTTPRequest = r
+
+ if err := r.ParseMultipartForm(32 << 20); err != nil {
+ if err != http.ErrNotMultipart {
+ return errors.New(400, "%v", err)
+ } else if err := r.ParseForm(); err != nil {
+ return errors.New(400, "%v", err)
+ }
+ }
+
+ yamlfile, yamlfileHeader, err := r.FormFile("yamlfile")
+ if err != nil {
+ res = append(res, errors.New(400, "reading file %q failed: %v", "yamlfile", err))
+ } else if err := o.bindYamlfile(yamlfile, yamlfileHeader); err != nil {
+ // Required: true
+ res = append(res, err)
+ } else {
+ o.Yamlfile = &runtime.File{Data: yamlfile, Header: yamlfileHeader}
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+// bindYamlfile binds file parameter Yamlfile.
+//
+// The only supported validations on files are MinLength and MaxLength
+func (o *JobCreatorParams) bindYamlfile(file multipart.File, header *multipart.FileHeader) error {
+ return nil
+}
diff --git a/server/operations/jobs/job_creator_responses.go b/server/operations/jobs/job_creator_responses.go
new file mode 100644
index 0000000..a7c73cc
--- /dev/null
+++ b/server/operations/jobs/job_creator_responses.go
@@ -0,0 +1,204 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package jobs
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/runtime"
+
+ weles "git.tizen.org/tools/weles"
+)
+
+// JobCreatorCreatedCode is the HTTP code returned for type JobCreatorCreated
+const JobCreatorCreatedCode int = 201
+
+/*JobCreatorCreated Created
+
+swagger:response jobCreatorCreated
+*/
+type JobCreatorCreated struct {
+
+ /*
+ In: Body
+ */
+ Payload weles.JobID `json:"body,omitempty"`
+}
+
+// NewJobCreatorCreated creates JobCreatorCreated with default headers values
+func NewJobCreatorCreated() *JobCreatorCreated {
+
+ return &JobCreatorCreated{}
+}
+
+// WithPayload adds the payload to the job creator created response
+func (o *JobCreatorCreated) WithPayload(payload weles.JobID) *JobCreatorCreated {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the job creator created response
+func (o *JobCreatorCreated) SetPayload(payload weles.JobID) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *JobCreatorCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.WriteHeader(201)
+ payload := o.Payload
+ if err := producer.Produce(rw, payload); err != nil {
+ panic(err) // let the recovery middleware deal with this
+ }
+
+}
+
+// JobCreatorUnsupportedMediaTypeCode is the HTTP code returned for type JobCreatorUnsupportedMediaType
+const JobCreatorUnsupportedMediaTypeCode int = 415
+
+/*JobCreatorUnsupportedMediaType Unsupported media type
+
+swagger:response jobCreatorUnsupportedMediaType
+*/
+type JobCreatorUnsupportedMediaType struct {
+
+ /*
+ In: Body
+ */
+ Payload *weles.ErrResponse `json:"body,omitempty"`
+}
+
+// NewJobCreatorUnsupportedMediaType creates JobCreatorUnsupportedMediaType with default headers values
+func NewJobCreatorUnsupportedMediaType() *JobCreatorUnsupportedMediaType {
+
+ return &JobCreatorUnsupportedMediaType{}
+}
+
+// WithPayload adds the payload to the job creator unsupported media type response
+func (o *JobCreatorUnsupportedMediaType) WithPayload(payload *weles.ErrResponse) *JobCreatorUnsupportedMediaType {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the job creator unsupported media type response
+func (o *JobCreatorUnsupportedMediaType) SetPayload(payload *weles.ErrResponse) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *JobCreatorUnsupportedMediaType) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.WriteHeader(415)
+ if o.Payload != nil {
+ payload := o.Payload
+ if err := producer.Produce(rw, payload); err != nil {
+ panic(err) // let the recovery middleware deal with this
+ }
+ }
+}
+
+// JobCreatorUnprocessableEntityCode is the HTTP code returned for type JobCreatorUnprocessableEntity
+const JobCreatorUnprocessableEntityCode int = 422
+
+/*JobCreatorUnprocessableEntity Unprocessable entity
+
+swagger:response jobCreatorUnprocessableEntity
+*/
+type JobCreatorUnprocessableEntity struct {
+
+ /*
+ In: Body
+ */
+ Payload *weles.ErrResponse `json:"body,omitempty"`
+}
+
+// NewJobCreatorUnprocessableEntity creates JobCreatorUnprocessableEntity with default headers values
+func NewJobCreatorUnprocessableEntity() *JobCreatorUnprocessableEntity {
+
+ return &JobCreatorUnprocessableEntity{}
+}
+
+// WithPayload adds the payload to the job creator unprocessable entity response
+func (o *JobCreatorUnprocessableEntity) WithPayload(payload *weles.ErrResponse) *JobCreatorUnprocessableEntity {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the job creator unprocessable entity response
+func (o *JobCreatorUnprocessableEntity) SetPayload(payload *weles.ErrResponse) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *JobCreatorUnprocessableEntity) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.WriteHeader(422)
+ if o.Payload != nil {
+ payload := o.Payload
+ if err := producer.Produce(rw, payload); err != nil {
+ panic(err) // let the recovery middleware deal with this
+ }
+ }
+}
+
+// JobCreatorInternalServerErrorCode is the HTTP code returned for type JobCreatorInternalServerError
+const JobCreatorInternalServerErrorCode int = 500
+
+/*JobCreatorInternalServerError Internal Server error
+
+swagger:response jobCreatorInternalServerError
+*/
+type JobCreatorInternalServerError struct {
+
+ /*
+ In: Body
+ */
+ Payload *weles.ErrResponse `json:"body,omitempty"`
+}
+
+// NewJobCreatorInternalServerError creates JobCreatorInternalServerError with default headers values
+func NewJobCreatorInternalServerError() *JobCreatorInternalServerError {
+
+ return &JobCreatorInternalServerError{}
+}
+
+// WithPayload adds the payload to the job creator internal server error response
+func (o *JobCreatorInternalServerError) WithPayload(payload *weles.ErrResponse) *JobCreatorInternalServerError {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the job creator internal server error response
+func (o *JobCreatorInternalServerError) SetPayload(payload *weles.ErrResponse) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *JobCreatorInternalServerError) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.WriteHeader(500)
+ if o.Payload != nil {
+ payload := o.Payload
+ if err := producer.Produce(rw, payload); err != nil {
+ panic(err) // let the recovery middleware deal with this
+ }
+ }
+}
diff --git a/server/operations/jobs/job_creator_urlbuilder.go b/server/operations/jobs/job_creator_urlbuilder.go
new file mode 100644
index 0000000..3ea4a6f
--- /dev/null
+++ b/server/operations/jobs/job_creator_urlbuilder.go
@@ -0,0 +1,102 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package jobs
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the generate command
+
+import (
+ "errors"
+ "net/url"
+ golangswaggerpaths "path"
+)
+
+// JobCreatorURL generates an URL for the job creator operation
+type JobCreatorURL struct {
+ _basePath string
+}
+
+// WithBasePath sets the base path for this url builder, only required when it's different from the
+// base path specified in the swagger spec.
+// When the value of the base path is an empty string
+func (o *JobCreatorURL) WithBasePath(bp string) *JobCreatorURL {
+ o.SetBasePath(bp)
+ return o
+}
+
+// SetBasePath sets the base path for this url builder, only required when it's different from the
+// base path specified in the swagger spec.
+// When the value of the base path is an empty string
+func (o *JobCreatorURL) SetBasePath(bp string) {
+ o._basePath = bp
+}
+
+// Build a url path and query string
+func (o *JobCreatorURL) Build() (*url.URL, error) {
+ var result url.URL
+
+ var _path = "/jobs"
+
+ _basePath := o._basePath
+ if _basePath == "" {
+ _basePath = "/api/v1"
+ }
+ result.Path = golangswaggerpaths.Join(_basePath, _path)
+
+ return &result, nil
+}
+
+// Must is a helper function to panic when the url builder returns an error
+func (o *JobCreatorURL) Must(u *url.URL, err error) *url.URL {
+ if err != nil {
+ panic(err)
+ }
+ if u == nil {
+ panic("url can't be nil")
+ }
+ return u
+}
+
+// String returns the string representation of the path with query string
+func (o *JobCreatorURL) String() string {
+ return o.Must(o.Build()).String()
+}
+
+// BuildFull builds a full url with scheme, host, path and query string
+func (o *JobCreatorURL) BuildFull(scheme, host string) (*url.URL, error) {
+ if scheme == "" {
+ return nil, errors.New("scheme is required for a full url on JobCreatorURL")
+ }
+ if host == "" {
+ return nil, errors.New("host is required for a full url on JobCreatorURL")
+ }
+
+ base, err := o.Build()
+ if err != nil {
+ return nil, err
+ }
+
+ base.Scheme = scheme
+ base.Host = host
+ return base, nil
+}
+
+// StringFull returns the string representation of a complete url
+func (o *JobCreatorURL) StringFull(scheme, host string) string {
+ return o.Must(o.BuildFull(scheme, host)).String()
+}
diff --git a/server/operations/jobs/job_lister.go b/server/operations/jobs/job_lister.go
new file mode 100644
index 0000000..002c4c5
--- /dev/null
+++ b/server/operations/jobs/job_lister.go
@@ -0,0 +1,75 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package jobs
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the generate command
+
+import (
+ "net/http"
+
+ middleware "github.com/go-openapi/runtime/middleware"
+)
+
+// JobListerHandlerFunc turns a function with the right signature into a job lister handler
+type JobListerHandlerFunc func(JobListerParams) middleware.Responder
+
+// Handle executing the request and returning a response
+func (fn JobListerHandlerFunc) Handle(params JobListerParams) middleware.Responder {
+ return fn(params)
+}
+
+// JobListerHandler interface for that can handle valid job lister params
+type JobListerHandler interface {
+ Handle(JobListerParams) middleware.Responder
+}
+
+// NewJobLister creates a new http.Handler for the job lister operation
+func NewJobLister(ctx *middleware.Context, handler JobListerHandler) *JobLister {
+ return &JobLister{Context: ctx, Handler: handler}
+}
+
+/*JobLister swagger:route POST /jobs/list jobs jobLister
+
+List jobs with filter and sort features
+
+JobLister returns information on filtered Weles Jobs.
+
+*/
+type JobLister struct {
+ Context *middleware.Context
+ Handler JobListerHandler
+}
+
+func (o *JobLister) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+ route, rCtx, _ := o.Context.RouteInfo(r)
+ if rCtx != nil {
+ r = rCtx
+ }
+ var Params = NewJobListerParams()
+
+ if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
+ o.Context.Respond(rw, r, route.Produces, route, err)
+ return
+ }
+
+ res := o.Handler.Handle(Params) // actually handle the request
+
+ o.Context.Respond(rw, r, route.Produces, route, res)
+
+}
diff --git a/server/operations/jobs/job_lister_parameters.go b/server/operations/jobs/job_lister_parameters.go
new file mode 100644
index 0000000..abfbd17
--- /dev/null
+++ b/server/operations/jobs/job_lister_parameters.go
@@ -0,0 +1,182 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package jobs
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/runtime"
+ "github.com/go-openapi/runtime/middleware"
+ "github.com/go-openapi/swag"
+
+ strfmt "github.com/go-openapi/strfmt"
+
+ weles "git.tizen.org/tools/weles"
+)
+
+// NewJobListerParams creates a new JobListerParams object
+// no default values defined in spec.
+func NewJobListerParams() JobListerParams {
+
+ return JobListerParams{}
+}
+
+// JobListerParams contains all the bound params for the job lister operation
+// typically these are obtained from a http.Request
+//
+// swagger:parameters JobLister
+type JobListerParams struct {
+
+ // HTTP Request Object
+ HTTPRequest *http.Request `json:"-"`
+
+ /*JobID of the last element from previous page.
+ In: query
+ */
+ After *uint64
+ /*JobID of first element from next page.
+ In: query
+ */
+ Before *uint64
+ /*Job Filter and Sort object.
+ In: body
+ */
+ JobFilterAndSort *weles.JobFilterAndSort
+ /*Custom page limit. Denotes number of JobInfo structures that will be returned.
+ In: query
+ */
+ Limit *int32
+}
+
+// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
+// for simple values it will use straight method calls.
+//
+// To ensure default values, the struct must have been initialized with NewJobListerParams() beforehand.
+func (o *JobListerParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
+ var res []error
+
+ o.HTTPRequest = r
+
+ qs := runtime.Values(r.URL.Query())
+
+ qAfter, qhkAfter, _ := qs.GetOK("after")
+ if err := o.bindAfter(qAfter, qhkAfter, route.Formats); err != nil {
+ res = append(res, err)
+ }
+
+ qBefore, qhkBefore, _ := qs.GetOK("before")
+ if err := o.bindBefore(qBefore, qhkBefore, route.Formats); err != nil {
+ res = append(res, err)
+ }
+
+ if runtime.HasBody(r) {
+ defer r.Body.Close()
+ var body weles.JobFilterAndSort
+ if err := route.Consumer.Consume(r.Body, &body); err != nil {
+ res = append(res, errors.NewParseError("jobFilterAndSort", "body", "", err))
+ } else {
+ // validate body object
+ if err := body.Validate(route.Formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) == 0 {
+ o.JobFilterAndSort = &body
+ }
+ }
+ }
+ qLimit, qhkLimit, _ := qs.GetOK("limit")
+ if err := o.bindLimit(qLimit, qhkLimit, route.Formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+// bindAfter binds and validates parameter After from query.
+func (o *JobListerParams) bindAfter(rawData []string, hasKey bool, formats strfmt.Registry) error {
+ var raw string
+ if len(rawData) > 0 {
+ raw = rawData[len(rawData)-1]
+ }
+
+ // Required: false
+ // AllowEmptyValue: false
+ if raw == "" { // empty values pass all other validations
+ return nil
+ }
+
+ value, err := swag.ConvertUint64(raw)
+ if err != nil {
+ return errors.InvalidType("after", "query", "uint64", raw)
+ }
+ o.After = &value
+
+ return nil
+}
+
+// bindBefore binds and validates parameter Before from query.
+func (o *JobListerParams) bindBefore(rawData []string, hasKey bool, formats strfmt.Registry) error {
+ var raw string
+ if len(rawData) > 0 {
+ raw = rawData[len(rawData)-1]
+ }
+
+ // Required: false
+ // AllowEmptyValue: false
+ if raw == "" { // empty values pass all other validations
+ return nil
+ }
+
+ value, err := swag.ConvertUint64(raw)
+ if err != nil {
+ return errors.InvalidType("before", "query", "uint64", raw)
+ }
+ o.Before = &value
+
+ return nil
+}
+
+// bindLimit binds and validates parameter Limit from query.
+func (o *JobListerParams) bindLimit(rawData []string, hasKey bool, formats strfmt.Registry) error {
+ var raw string
+ if len(rawData) > 0 {
+ raw = rawData[len(rawData)-1]
+ }
+
+ // Required: false
+ // AllowEmptyValue: false
+ if raw == "" { // empty values pass all other validations
+ return nil
+ }
+
+ value, err := swag.ConvertInt32(raw)
+ if err != nil {
+ return errors.InvalidType("limit", "query", "int32", raw)
+ }
+ o.Limit = &value
+
+ return nil
+}
diff --git a/server/operations/jobs/job_lister_responses.go b/server/operations/jobs/job_lister_responses.go
new file mode 100644
index 0000000..54c3556
--- /dev/null
+++ b/server/operations/jobs/job_lister_responses.go
@@ -0,0 +1,417 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package jobs
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/runtime"
+ "github.com/go-openapi/swag"
+
+ weles "git.tizen.org/tools/weles"
+)
+
+// JobListerOKCode is the HTTP code returned for type JobListerOK
+const JobListerOKCode int = 200
+
+/*JobListerOK OK
+
+swagger:response jobListerOK
+*/
+type JobListerOK struct {
+ /*URI to request next page of data. Please note that the same body must be used as in initial request.
+
+
+ */
+ Next string `json:"Next"`
+ /*URI to request previous page of data. Please note that the same body must be used as in initial request.
+
+
+ */
+ Previous string `json:"Previous"`
+ /*count of records currently fulfilling the requested JobFilter. Please note that this value may change when requesting for the same data at a different moment in time.
+
+
+ */
+ TotalRecords uint64 `json:"TotalRecords"`
+
+ /*
+ In: Body
+ */
+ Payload []*weles.JobInfo `json:"body,omitempty"`
+}
+
+// NewJobListerOK creates JobListerOK with default headers values
+func NewJobListerOK() *JobListerOK {
+
+ return &JobListerOK{}
+}
+
+// WithNext adds the next to the job lister o k response
+func (o *JobListerOK) WithNext(next string) *JobListerOK {
+ o.Next = next
+ return o
+}
+
+// SetNext sets the next to the job lister o k response
+func (o *JobListerOK) SetNext(next string) {
+ o.Next = next
+}
+
+// WithPrevious adds the previous to the job lister o k response
+func (o *JobListerOK) WithPrevious(previous string) *JobListerOK {
+ o.Previous = previous
+ return o
+}
+
+// SetPrevious sets the previous to the job lister o k response
+func (o *JobListerOK) SetPrevious(previous string) {
+ o.Previous = previous
+}
+
+// WithTotalRecords adds the totalRecords to the job lister o k response
+func (o *JobListerOK) WithTotalRecords(totalRecords uint64) *JobListerOK {
+ o.TotalRecords = totalRecords
+ return o
+}
+
+// SetTotalRecords sets the totalRecords to the job lister o k response
+func (o *JobListerOK) SetTotalRecords(totalRecords uint64) {
+ o.TotalRecords = totalRecords
+}
+
+// WithPayload adds the payload to the job lister o k response
+func (o *JobListerOK) WithPayload(payload []*weles.JobInfo) *JobListerOK {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the job lister o k response
+func (o *JobListerOK) SetPayload(payload []*weles.JobInfo) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *JobListerOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ // response header Next
+
+ next := o.Next
+ if next != "" {
+ rw.Header().Set("Next", next)
+ }
+
+ // response header Previous
+
+ previous := o.Previous
+ if previous != "" {
+ rw.Header().Set("Previous", previous)
+ }
+
+ // response header TotalRecords
+
+ totalRecords := swag.FormatUint64(o.TotalRecords)
+ if totalRecords != "" {
+ rw.Header().Set("TotalRecords", totalRecords)
+ }
+
+ rw.WriteHeader(200)
+ payload := o.Payload
+ if payload == nil {
+ payload = make([]*weles.JobInfo, 0, 50)
+ }
+
+ if err := producer.Produce(rw, payload); err != nil {
+ panic(err) // let the recovery middleware deal with this
+ }
+
+}
+
+// JobListerPartialContentCode is the HTTP code returned for type JobListerPartialContent
+const JobListerPartialContentCode int = 206
+
+/*JobListerPartialContent Partial Content
+
+swagger:response jobListerPartialContent
+*/
+type JobListerPartialContent struct {
+ /*URI to request next page of data. Please note that the same body must be used as in initial request.
+
+
+ */
+ Next string `json:"Next"`
+ /*URI to request previous page of data. Please note that the same body must be used as in initial request.
+
+
+ */
+ Previous string `json:"Previous"`
+ /*number of records after current page. Please note that this value may change when requesting for the same data at a different moment in time.
+
+
+ */
+ RemainingRecords uint64 `json:"RemainingRecords"`
+ /*count of records currently fulfilling requested JobFilter. Please note that this value may change when requesting for the same data at a different moment in time.
+
+
+ */
+ TotalRecords uint64 `json:"TotalRecords"`
+
+ /*
+ In: Body
+ */
+ Payload []*weles.JobInfo `json:"body,omitempty"`
+}
+
+// NewJobListerPartialContent creates JobListerPartialContent with default headers values
+func NewJobListerPartialContent() *JobListerPartialContent {
+
+ return &JobListerPartialContent{}
+}
+
+// WithNext adds the next to the job lister partial content response
+func (o *JobListerPartialContent) WithNext(next string) *JobListerPartialContent {
+ o.Next = next
+ return o
+}
+
+// SetNext sets the next to the job lister partial content response
+func (o *JobListerPartialContent) SetNext(next string) {
+ o.Next = next
+}
+
+// WithPrevious adds the previous to the job lister partial content response
+func (o *JobListerPartialContent) WithPrevious(previous string) *JobListerPartialContent {
+ o.Previous = previous
+ return o
+}
+
+// SetPrevious sets the previous to the job lister partial content response
+func (o *JobListerPartialContent) SetPrevious(previous string) {
+ o.Previous = previous
+}
+
+// WithRemainingRecords adds the remainingRecords to the job lister partial content response
+func (o *JobListerPartialContent) WithRemainingRecords(remainingRecords uint64) *JobListerPartialContent {
+ o.RemainingRecords = remainingRecords
+ return o
+}
+
+// SetRemainingRecords sets the remainingRecords to the job lister partial content response
+func (o *JobListerPartialContent) SetRemainingRecords(remainingRecords uint64) {
+ o.RemainingRecords = remainingRecords
+}
+
+// WithTotalRecords adds the totalRecords to the job lister partial content response
+func (o *JobListerPartialContent) WithTotalRecords(totalRecords uint64) *JobListerPartialContent {
+ o.TotalRecords = totalRecords
+ return o
+}
+
+// SetTotalRecords sets the totalRecords to the job lister partial content response
+func (o *JobListerPartialContent) SetTotalRecords(totalRecords uint64) {
+ o.TotalRecords = totalRecords
+}
+
+// WithPayload adds the payload to the job lister partial content response
+func (o *JobListerPartialContent) WithPayload(payload []*weles.JobInfo) *JobListerPartialContent {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the job lister partial content response
+func (o *JobListerPartialContent) SetPayload(payload []*weles.JobInfo) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *JobListerPartialContent) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ // response header Next
+
+ next := o.Next
+ if next != "" {
+ rw.Header().Set("Next", next)
+ }
+
+ // response header Previous
+
+ previous := o.Previous
+ if previous != "" {
+ rw.Header().Set("Previous", previous)
+ }
+
+ // response header RemainingRecords
+
+ remainingRecords := swag.FormatUint64(o.RemainingRecords)
+ if remainingRecords != "" {
+ rw.Header().Set("RemainingRecords", remainingRecords)
+ }
+
+ // response header TotalRecords
+
+ totalRecords := swag.FormatUint64(o.TotalRecords)
+ if totalRecords != "" {
+ rw.Header().Set("TotalRecords", totalRecords)
+ }
+
+ rw.WriteHeader(206)
+ payload := o.Payload
+ if payload == nil {
+ payload = make([]*weles.JobInfo, 0, 50)
+ }
+
+ if err := producer.Produce(rw, payload); err != nil {
+ panic(err) // let the recovery middleware deal with this
+ }
+
+}
+
+// JobListerBadRequestCode is the HTTP code returned for type JobListerBadRequest
+const JobListerBadRequestCode int = 400
+
+/*JobListerBadRequest Bad Request
+
+swagger:response jobListerBadRequest
+*/
+type JobListerBadRequest struct {
+
+ /*
+ In: Body
+ */
+ Payload *weles.ErrResponse `json:"body,omitempty"`
+}
+
+// NewJobListerBadRequest creates JobListerBadRequest with default headers values
+func NewJobListerBadRequest() *JobListerBadRequest {
+
+ return &JobListerBadRequest{}
+}
+
+// WithPayload adds the payload to the job lister bad request response
+func (o *JobListerBadRequest) WithPayload(payload *weles.ErrResponse) *JobListerBadRequest {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the job lister bad request response
+func (o *JobListerBadRequest) SetPayload(payload *weles.ErrResponse) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *JobListerBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.WriteHeader(400)
+ if o.Payload != nil {
+ payload := o.Payload
+ if err := producer.Produce(rw, payload); err != nil {
+ panic(err) // let the recovery middleware deal with this
+ }
+ }
+}
+
+// JobListerNotFoundCode is the HTTP code returned for type JobListerNotFound
+const JobListerNotFoundCode int = 404
+
+/*JobListerNotFound Not Found
+
+swagger:response jobListerNotFound
+*/
+type JobListerNotFound struct {
+
+ /*
+ In: Body
+ */
+ Payload *weles.ErrResponse `json:"body,omitempty"`
+}
+
+// NewJobListerNotFound creates JobListerNotFound with default headers values
+func NewJobListerNotFound() *JobListerNotFound {
+
+ return &JobListerNotFound{}
+}
+
+// WithPayload adds the payload to the job lister not found response
+func (o *JobListerNotFound) WithPayload(payload *weles.ErrResponse) *JobListerNotFound {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the job lister not found response
+func (o *JobListerNotFound) SetPayload(payload *weles.ErrResponse) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *JobListerNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.WriteHeader(404)
+ if o.Payload != nil {
+ payload := o.Payload
+ if err := producer.Produce(rw, payload); err != nil {
+ panic(err) // let the recovery middleware deal with this
+ }
+ }
+}
+
+// JobListerInternalServerErrorCode is the HTTP code returned for type JobListerInternalServerError
+const JobListerInternalServerErrorCode int = 500
+
+/*JobListerInternalServerError Internal Server error
+
+swagger:response jobListerInternalServerError
+*/
+type JobListerInternalServerError struct {
+
+ /*
+ In: Body
+ */
+ Payload *weles.ErrResponse `json:"body,omitempty"`
+}
+
+// NewJobListerInternalServerError creates JobListerInternalServerError with default headers values
+func NewJobListerInternalServerError() *JobListerInternalServerError {
+
+ return &JobListerInternalServerError{}
+}
+
+// WithPayload adds the payload to the job lister internal server error response
+func (o *JobListerInternalServerError) WithPayload(payload *weles.ErrResponse) *JobListerInternalServerError {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the job lister internal server error response
+func (o *JobListerInternalServerError) SetPayload(payload *weles.ErrResponse) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *JobListerInternalServerError) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.WriteHeader(500)
+ if o.Payload != nil {
+ payload := o.Payload
+ if err := producer.Produce(rw, payload); err != nil {
+ panic(err) // let the recovery middleware deal with this
+ }
+ }
+}
diff --git a/server/operations/jobs/job_lister_urlbuilder.go b/server/operations/jobs/job_lister_urlbuilder.go
new file mode 100644
index 0000000..d4d0b58
--- /dev/null
+++ b/server/operations/jobs/job_lister_urlbuilder.go
@@ -0,0 +1,138 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package jobs
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the generate command
+
+import (
+ "errors"
+ "net/url"
+ golangswaggerpaths "path"
+
+ "github.com/go-openapi/swag"
+)
+
+// JobListerURL generates an URL for the job lister operation
+type JobListerURL struct {
+ After *uint64
+ Before *uint64
+ Limit *int32
+
+ _basePath string
+ // avoid unkeyed usage
+ _ struct{}
+}
+
+// WithBasePath sets the base path for this url builder, only required when it's different from the
+// base path specified in the swagger spec.
+// When the value of the base path is an empty string
+func (o *JobListerURL) WithBasePath(bp string) *JobListerURL {
+ o.SetBasePath(bp)
+ return o
+}
+
+// SetBasePath sets the base path for this url builder, only required when it's different from the
+// base path specified in the swagger spec.
+// When the value of the base path is an empty string
+func (o *JobListerURL) SetBasePath(bp string) {
+ o._basePath = bp
+}
+
+// Build a url path and query string
+func (o *JobListerURL) Build() (*url.URL, error) {
+ var result url.URL
+
+ var _path = "/jobs/list"
+
+ _basePath := o._basePath
+ if _basePath == "" {
+ _basePath = "/api/v1"
+ }
+ result.Path = golangswaggerpaths.Join(_basePath, _path)
+
+ qs := make(url.Values)
+
+ var after string
+ if o.After != nil {
+ after = swag.FormatUint64(*o.After)
+ }
+ if after != "" {
+ qs.Set("after", after)
+ }
+
+ var before string
+ if o.Before != nil {
+ before = swag.FormatUint64(*o.Before)
+ }
+ if before != "" {
+ qs.Set("before", before)
+ }
+
+ var limit string
+ if o.Limit != nil {
+ limit = swag.FormatInt32(*o.Limit)
+ }
+ if limit != "" {
+ qs.Set("limit", limit)
+ }
+
+ result.RawQuery = qs.Encode()
+
+ return &result, nil
+}
+
+// Must is a helper function to panic when the url builder returns an error
+func (o *JobListerURL) Must(u *url.URL, err error) *url.URL {
+ if err != nil {
+ panic(err)
+ }
+ if u == nil {
+ panic("url can't be nil")
+ }
+ return u
+}
+
+// String returns the string representation of the path with query string
+func (o *JobListerURL) String() string {
+ return o.Must(o.Build()).String()
+}
+
+// BuildFull builds a full url with scheme, host, path and query string
+func (o *JobListerURL) BuildFull(scheme, host string) (*url.URL, error) {
+ if scheme == "" {
+ return nil, errors.New("scheme is required for a full url on JobListerURL")
+ }
+ if host == "" {
+ return nil, errors.New("host is required for a full url on JobListerURL")
+ }
+
+ base, err := o.Build()
+ if err != nil {
+ return nil, err
+ }
+
+ base.Scheme = scheme
+ base.Host = host
+ return base, nil
+}
+
+// StringFull returns the string representation of a complete url
+func (o *JobListerURL) StringFull(scheme, host string) string {
+ return o.Must(o.BuildFull(scheme, host)).String()
+}
diff --git a/server/operations/weles_api.go b/server/operations/weles_api.go
new file mode 100644
index 0000000..a997979
--- /dev/null
+++ b/server/operations/weles_api.go
@@ -0,0 +1,344 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "fmt"
+ "net/http"
+ "strings"
+
+ errors "github.com/go-openapi/errors"
+ loads "github.com/go-openapi/loads"
+ runtime "github.com/go-openapi/runtime"
+ middleware "github.com/go-openapi/runtime/middleware"
+ security "github.com/go-openapi/runtime/security"
+ spec "github.com/go-openapi/spec"
+ strfmt "github.com/go-openapi/strfmt"
+ "github.com/go-openapi/swag"
+
+ "git.tizen.org/tools/weles/server/operations/artifacts"
+ "git.tizen.org/tools/weles/server/operations/jobs"
+)
+
+// NewWelesAPI creates a new Weles instance
+func NewWelesAPI(spec *loads.Document) *WelesAPI {
+ return &WelesAPI{
+ handlers: make(map[string]map[string]http.Handler),
+ formats: strfmt.Default,
+ defaultConsumes: "application/json",
+ defaultProduces: "application/json",
+ customConsumers: make(map[string]runtime.Consumer),
+ customProducers: make(map[string]runtime.Producer),
+ ServerShutdown: func() {},
+ spec: spec,
+ ServeError: errors.ServeError,
+ BasicAuthenticator: security.BasicAuth,
+ APIKeyAuthenticator: security.APIKeyAuth,
+ BearerAuthenticator: security.BearerAuth,
+ JSONConsumer: runtime.JSONConsumer(),
+ MultipartformConsumer: runtime.DiscardConsumer,
+ JSONProducer: runtime.JSONProducer(),
+ ArtifactsArtifactListerHandler: artifacts.ArtifactListerHandlerFunc(func(params artifacts.ArtifactListerParams) middleware.Responder {
+ return middleware.NotImplemented("operation ArtifactsArtifactLister has not yet been implemented")
+ }),
+ JobsJobCancelerHandler: jobs.JobCancelerHandlerFunc(func(params jobs.JobCancelerParams) middleware.Responder {
+ return middleware.NotImplemented("operation JobsJobCanceler has not yet been implemented")
+ }),
+ JobsJobCreatorHandler: jobs.JobCreatorHandlerFunc(func(params jobs.JobCreatorParams) middleware.Responder {
+ return middleware.NotImplemented("operation JobsJobCreator has not yet been implemented")
+ }),
+ JobsJobListerHandler: jobs.JobListerHandlerFunc(func(params jobs.JobListerParams) middleware.Responder {
+ return middleware.NotImplemented("operation JobsJobLister has not yet been implemented")
+ }),
+ }
+}
+
+/*WelesAPI This is a Weles server. You can find out more about Weles at [http://tbd.tbd](http://tbd.tbd). */
+type WelesAPI struct {
+ spec *loads.Document
+ context *middleware.Context
+ handlers map[string]map[string]http.Handler
+ formats strfmt.Registry
+ customConsumers map[string]runtime.Consumer
+ customProducers map[string]runtime.Producer
+ defaultConsumes string
+ defaultProduces string
+ Middleware func(middleware.Builder) http.Handler
+
+ // BasicAuthenticator generates a runtime.Authenticator from the supplied basic auth function.
+ // It has a default implemention in the security package, however you can replace it for your particular usage.
+ BasicAuthenticator func(security.UserPassAuthentication) runtime.Authenticator
+ // APIKeyAuthenticator generates a runtime.Authenticator from the supplied token auth function.
+ // It has a default implemention in the security package, however you can replace it for your particular usage.
+ APIKeyAuthenticator func(string, string, security.TokenAuthentication) runtime.Authenticator
+ // BearerAuthenticator generates a runtime.Authenticator from the supplied bearer token auth function.
+ // It has a default implemention in the security package, however you can replace it for your particular usage.
+ BearerAuthenticator func(string, security.ScopedTokenAuthentication) runtime.Authenticator
+
+ // JSONConsumer registers a consumer for a "application/json" mime type
+ JSONConsumer runtime.Consumer
+ // MultipartformConsumer registers a consumer for a "multipart/form-data" mime type
+ MultipartformConsumer runtime.Consumer
+
+ // JSONProducer registers a producer for a "application/json" mime type
+ JSONProducer runtime.Producer
+
+ // ArtifactsArtifactListerHandler sets the operation handler for the artifact lister operation
+ ArtifactsArtifactListerHandler artifacts.ArtifactListerHandler
+ // JobsJobCancelerHandler sets the operation handler for the job canceler operation
+ JobsJobCancelerHandler jobs.JobCancelerHandler
+ // JobsJobCreatorHandler sets the operation handler for the job creator operation
+ JobsJobCreatorHandler jobs.JobCreatorHandler
+ // JobsJobListerHandler sets the operation handler for the job lister operation
+ JobsJobListerHandler jobs.JobListerHandler
+
+ // ServeError is called when an error is received, there is a default handler
+ // but you can set your own with this
+ ServeError func(http.ResponseWriter, *http.Request, error)
+
+ // ServerShutdown is called when the HTTP(S) server is shut down and done
+ // handling all active connections and does not accept connections any more
+ ServerShutdown func()
+
+ // Custom command line argument groups with their descriptions
+ CommandLineOptionsGroups []swag.CommandLineOptionsGroup
+
+ // User defined logger function.
+ Logger func(string, ...interface{})
+}
+
+// SetDefaultProduces sets the default produces media type
+func (o *WelesAPI) SetDefaultProduces(mediaType string) {
+ o.defaultProduces = mediaType
+}
+
+// SetDefaultConsumes returns the default consumes media type
+func (o *WelesAPI) SetDefaultConsumes(mediaType string) {
+ o.defaultConsumes = mediaType
+}
+
+// SetSpec sets a spec that will be served for the clients.
+func (o *WelesAPI) SetSpec(spec *loads.Document) {
+ o.spec = spec
+}
+
+// DefaultProduces returns the default produces media type
+func (o *WelesAPI) DefaultProduces() string {
+ return o.defaultProduces
+}
+
+// DefaultConsumes returns the default consumes media type
+func (o *WelesAPI) DefaultConsumes() string {
+ return o.defaultConsumes
+}
+
+// Formats returns the registered string formats
+func (o *WelesAPI) Formats() strfmt.Registry {
+ return o.formats
+}
+
+// RegisterFormat registers a custom format validator
+func (o *WelesAPI) RegisterFormat(name string, format strfmt.Format, validator strfmt.Validator) {
+ o.formats.Add(name, format, validator)
+}
+
+// Validate validates the registrations in the WelesAPI
+func (o *WelesAPI) Validate() error {
+ var unregistered []string
+
+ if o.JSONConsumer == nil {
+ unregistered = append(unregistered, "JSONConsumer")
+ }
+
+ if o.MultipartformConsumer == nil {
+ unregistered = append(unregistered, "MultipartformConsumer")
+ }
+
+ if o.JSONProducer == nil {
+ unregistered = append(unregistered, "JSONProducer")
+ }
+
+ if o.ArtifactsArtifactListerHandler == nil {
+ unregistered = append(unregistered, "artifacts.ArtifactListerHandler")
+ }
+
+ if o.JobsJobCancelerHandler == nil {
+ unregistered = append(unregistered, "jobs.JobCancelerHandler")
+ }
+
+ if o.JobsJobCreatorHandler == nil {
+ unregistered = append(unregistered, "jobs.JobCreatorHandler")
+ }
+
+ if o.JobsJobListerHandler == nil {
+ unregistered = append(unregistered, "jobs.JobListerHandler")
+ }
+
+ if len(unregistered) > 0 {
+ return fmt.Errorf("missing registration: %s", strings.Join(unregistered, ", "))
+ }
+
+ return nil
+}
+
+// ServeErrorFor gets a error handler for a given operation id
+func (o *WelesAPI) ServeErrorFor(operationID string) func(http.ResponseWriter, *http.Request, error) {
+ return o.ServeError
+}
+
+// AuthenticatorsFor gets the authenticators for the specified security schemes
+func (o *WelesAPI) AuthenticatorsFor(schemes map[string]spec.SecurityScheme) map[string]runtime.Authenticator {
+
+ return nil
+
+}
+
+// Authorizer returns the registered authorizer
+func (o *WelesAPI) Authorizer() runtime.Authorizer {
+
+ return nil
+
+}
+
+// ConsumersFor gets the consumers for the specified media types
+func (o *WelesAPI) ConsumersFor(mediaTypes []string) map[string]runtime.Consumer {
+
+ result := make(map[string]runtime.Consumer)
+ for _, mt := range mediaTypes {
+ switch mt {
+
+ case "application/json":
+ result["application/json"] = o.JSONConsumer
+
+ case "multipart/form-data":
+ result["multipart/form-data"] = o.MultipartformConsumer
+
+ }
+
+ if c, ok := o.customConsumers[mt]; ok {
+ result[mt] = c
+ }
+ }
+ return result
+
+}
+
+// ProducersFor gets the producers for the specified media types
+func (o *WelesAPI) ProducersFor(mediaTypes []string) map[string]runtime.Producer {
+
+ result := make(map[string]runtime.Producer)
+ for _, mt := range mediaTypes {
+ switch mt {
+
+ case "application/json":
+ result["application/json"] = o.JSONProducer
+
+ }
+
+ if p, ok := o.customProducers[mt]; ok {
+ result[mt] = p
+ }
+ }
+ return result
+
+}
+
+// HandlerFor gets a http.Handler for the provided operation method and path
+func (o *WelesAPI) HandlerFor(method, path string) (http.Handler, bool) {
+ if o.handlers == nil {
+ return nil, false
+ }
+ um := strings.ToUpper(method)
+ if _, ok := o.handlers[um]; !ok {
+ return nil, false
+ }
+ if path == "/" {
+ path = ""
+ }
+ h, ok := o.handlers[um][path]
+ return h, ok
+}
+
+// Context returns the middleware context for the weles API
+func (o *WelesAPI) Context() *middleware.Context {
+ if o.context == nil {
+ o.context = middleware.NewRoutableContext(o.spec, o, nil)
+ }
+
+ return o.context
+}
+
+func (o *WelesAPI) initHandlerCache() {
+ o.Context() // don't care about the result, just that the initialization happened
+
+ if o.handlers == nil {
+ o.handlers = make(map[string]map[string]http.Handler)
+ }
+
+ if o.handlers["POST"] == nil {
+ o.handlers["POST"] = make(map[string]http.Handler)
+ }
+ o.handlers["POST"]["/artifacts/list"] = artifacts.NewArtifactLister(o.context, o.ArtifactsArtifactListerHandler)
+
+ if o.handlers["POST"] == nil {
+ o.handlers["POST"] = make(map[string]http.Handler)
+ }
+ o.handlers["POST"]["/jobs/{JobID}/cancel"] = jobs.NewJobCanceler(o.context, o.JobsJobCancelerHandler)
+
+ if o.handlers["POST"] == nil {
+ o.handlers["POST"] = make(map[string]http.Handler)
+ }
+ o.handlers["POST"]["/jobs"] = jobs.NewJobCreator(o.context, o.JobsJobCreatorHandler)
+
+ if o.handlers["POST"] == nil {
+ o.handlers["POST"] = make(map[string]http.Handler)
+ }
+ o.handlers["POST"]["/jobs/list"] = jobs.NewJobLister(o.context, o.JobsJobListerHandler)
+
+}
+
+// Serve creates a http handler to serve the API over HTTP
+// can be used directly in http.ListenAndServe(":8000", api.Serve(nil))
+func (o *WelesAPI) Serve(builder middleware.Builder) http.Handler {
+ o.Init()
+
+ if o.Middleware != nil {
+ return o.Middleware(builder)
+ }
+ return o.context.APIHandler(builder)
+}
+
+// Init allows you to just initialize the handler cache, you can then recompose the middleware as you see fit
+func (o *WelesAPI) Init() {
+ if len(o.handlers) == 0 {
+ o.initHandlerCache()
+ }
+}
+
+// RegisterConsumer allows you to add (or override) a consumer for a media type.
+func (o *WelesAPI) RegisterConsumer(mediaType string, consumer runtime.Consumer) {
+ o.customConsumers[mediaType] = consumer
+}
+
+// RegisterProducer allows you to add (or override) a producer for a media type.
+func (o *WelesAPI) RegisterProducer(mediaType string, producer runtime.Producer) {
+ o.customProducers[mediaType] = producer
+}
diff --git a/server/server.go b/server/server.go
new file mode 100644
index 0000000..25a265b
--- /dev/null
+++ b/server/server.go
@@ -0,0 +1,615 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package server
+
+import (
+ "context"
+ "crypto/tls"
+ "crypto/x509"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "net"
+ "net/http"
+ "os"
+ "os/signal"
+ "strconv"
+ "sync"
+ "sync/atomic"
+ "syscall"
+ "time"
+
+ "github.com/go-openapi/runtime/flagext"
+ "github.com/go-openapi/swag"
+ flag "github.com/spf13/pflag"
+ "golang.org/x/net/netutil"
+
+ "git.tizen.org/tools/weles/server/operations"
+)
+
+const (
+ schemeHTTP = "http"
+ schemeHTTPS = "https"
+ schemeUnix = "unix"
+)
+
+var defaultSchemes []string
+
+func init() {
+ defaultSchemes = []string{
+ schemeHTTP,
+ }
+}
+
+var (
+ enabledListeners []string
+ cleanupTimout time.Duration
+ maxHeaderSize flagext.ByteSize
+
+ socketPath string
+
+ host string
+ port int
+ listenLimit int
+ keepAlive time.Duration
+ readTimeout time.Duration
+ writeTimeout time.Duration
+
+ tlsHost string
+ tlsPort int
+ tlsListenLimit int
+ tlsKeepAlive time.Duration
+ tlsReadTimeout time.Duration
+ tlsWriteTimeout time.Duration
+ tlsCertificate string
+ tlsCertificateKey string
+ tlsCACertificate string
+)
+
+func init() {
+ maxHeaderSize = flagext.ByteSize(1000000)
+
+ flag.StringSliceVar(&enabledListeners, "scheme", defaultSchemes, "the listeners to enable, this can be repeated and defaults to the schemes in the swagger spec")
+ flag.DurationVar(&cleanupTimout, "cleanup-timeout", 10*time.Second, "grace period for which to wait before shutting down the server")
+ flag.Var(&maxHeaderSize, "max-header-size", "controls the maximum number of bytes the server will read parsing the request header's keys and values, including the request line. It does not limit the size of the request body")
+
+ flag.StringVar(&socketPath, "socket-path", "/var/run/todo-list.sock", "the unix socket to listen on")
+
+ flag.StringVar(&host, "host", "localhost", "the IP to listen on")
+ flag.IntVar(&port, "port", 0, "the port to listen on for insecure connections, defaults to a random value")
+ flag.IntVar(&listenLimit, "listen-limit", 0, "limit the number of outstanding requests")
+ flag.DurationVar(&keepAlive, "keep-alive", 3*time.Minute, "sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)")
+ flag.DurationVar(&readTimeout, "read-timeout", 30*time.Second, "maximum duration before timing out read of the request")
+ flag.DurationVar(&writeTimeout, "write-timeout", 30*time.Second, "maximum duration before timing out write of the response")
+
+ flag.StringVar(&tlsHost, "tls-host", "localhost", "the IP to listen on")
+ flag.IntVar(&tlsPort, "tls-port", 0, "the port to listen on for secure connections, defaults to a random value")
+ flag.StringVar(&tlsCertificate, "tls-certificate", "", "the certificate to use for secure connections")
+ flag.StringVar(&tlsCertificateKey, "tls-key", "", "the private key to use for secure conections")
+ flag.StringVar(&tlsCACertificate, "tls-ca", "", "the certificate authority file to be used with mutual tls auth")
+ flag.IntVar(&tlsListenLimit, "tls-listen-limit", 0, "limit the number of outstanding requests")
+ flag.DurationVar(&tlsKeepAlive, "tls-keep-alive", 3*time.Minute, "sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)")
+ flag.DurationVar(&tlsReadTimeout, "tls-read-timeout", 30*time.Second, "maximum duration before timing out read of the request")
+ flag.DurationVar(&tlsWriteTimeout, "tls-write-timeout", 30*time.Second, "maximum duration before timing out write of the response")
+}
+
+func stringEnvOverride(orig string, def string, keys ...string) string {
+ for _, k := range keys {
+ if os.Getenv(k) != "" {
+ return os.Getenv(k)
+ }
+ }
+ if def != "" && orig == "" {
+ return def
+ }
+ return orig
+}
+
+func intEnvOverride(orig int, def int, keys ...string) int {
+ for _, k := range keys {
+ if os.Getenv(k) != "" {
+ v, err := strconv.Atoi(os.Getenv(k))
+ if err != nil {
+ fmt.Fprintln(os.Stderr, k, "is not a valid number")
+ os.Exit(1)
+ }
+ return v
+ }
+ }
+ if def != 0 && orig == 0 {
+ return def
+ }
+ return orig
+}
+
+// NewServer creates a new api weles server but does not configure it
+func NewServer(api *operations.WelesAPI) *Server {
+ s := new(Server)
+
+ s.EnabledListeners = enabledListeners
+ s.CleanupTimeout = cleanupTimout
+ s.MaxHeaderSize = maxHeaderSize
+ s.SocketPath = socketPath
+ s.Host = stringEnvOverride(host, "", "HOST")
+ s.Port = intEnvOverride(port, 0, "PORT")
+ s.ListenLimit = listenLimit
+ s.KeepAlive = keepAlive
+ s.ReadTimeout = readTimeout
+ s.WriteTimeout = writeTimeout
+ s.TLSHost = stringEnvOverride(tlsHost, s.Host, "TLS_HOST", "HOST")
+ s.TLSPort = intEnvOverride(tlsPort, 0, "TLS_PORT")
+ s.TLSCertificate = stringEnvOverride(tlsCertificate, "", "TLS_CERTIFICATE")
+ s.TLSCertificateKey = stringEnvOverride(tlsCertificateKey, "", "TLS_PRIVATE_KEY")
+ s.TLSCACertificate = stringEnvOverride(tlsCACertificate, "", "TLS_CA_CERTIFICATE")
+ s.TLSListenLimit = tlsListenLimit
+ s.TLSKeepAlive = tlsKeepAlive
+ s.TLSReadTimeout = tlsReadTimeout
+ s.TLSWriteTimeout = tlsWriteTimeout
+ s.shutdown = make(chan struct{})
+ s.api = api
+ s.interrupt = make(chan os.Signal, 1)
+ return s
+}
+
+// ConfigureAPI configures the API and handlers.
+func (s *Server) ConfigureAPI() {
+ if s.api != nil {
+ s.handler = configureAPI(s.api)
+ }
+}
+
+// ConfigureFlags configures the additional flags defined by the handlers. Needs to be called before the parser.Parse
+func (s *Server) ConfigureFlags() {
+ if s.api != nil {
+ configureFlags(s.api)
+ }
+}
+
+// Server for the weles API
+type Server struct {
+ EnabledListeners []string
+ CleanupTimeout time.Duration
+ MaxHeaderSize flagext.ByteSize
+
+ SocketPath string
+ domainSocketL net.Listener
+
+ Host string
+ Port int
+ ListenLimit int
+ KeepAlive time.Duration
+ ReadTimeout time.Duration
+ WriteTimeout time.Duration
+ httpServerL net.Listener
+
+ TLSHost string
+ TLSPort int
+ TLSCertificate string
+ TLSCertificateKey string
+ TLSCACertificate string
+ TLSListenLimit int
+ TLSKeepAlive time.Duration
+ TLSReadTimeout time.Duration
+ TLSWriteTimeout time.Duration
+ httpsServerL net.Listener
+
+ api *operations.WelesAPI
+ handler http.Handler
+ hasListeners bool
+ shutdown chan struct{}
+ shuttingDown int32
+ interrupted bool
+ interrupt chan os.Signal
+ chanLock sync.RWMutex
+}
+
+// Logf logs message either via defined user logger or via system one if no user logger is defined.
+func (s *Server) Logf(f string, args ...interface{}) {
+ if s.api != nil && s.api.Logger != nil {
+ s.api.Logger(f, args...)
+ } else {
+ log.Printf(f, args...)
+ }
+}
+
+// Fatalf logs message either via defined user logger or via system one if no user logger is defined.
+// Exits with non-zero status after printing
+func (s *Server) Fatalf(f string, args ...interface{}) {
+ if s.api != nil && s.api.Logger != nil {
+ s.api.Logger(f, args...)
+ os.Exit(1)
+ } else {
+ log.Fatalf(f, args...)
+ }
+}
+
+// SetAPI configures the server with the specified API. Needs to be called before Serve
+func (s *Server) SetAPI(api *operations.WelesAPI) {
+ if api == nil {
+ s.api = nil
+ s.handler = nil
+ return
+ }
+
+ s.api = api
+ s.api.Logger = log.Printf
+ s.handler = configureAPI(api)
+}
+
+func (s *Server) hasScheme(scheme string) bool {
+ schemes := s.EnabledListeners
+ if len(schemes) == 0 {
+ schemes = defaultSchemes
+ }
+
+ for _, v := range schemes {
+ if v == scheme {
+ return true
+ }
+ }
+ return false
+}
+
+// Serve the api
+func (s *Server) Serve() (err error) {
+ if !s.hasListeners {
+ if err = s.Listen(); err != nil {
+ return err
+ }
+ }
+
+ // set default handler, if none is set
+ if s.handler == nil {
+ if s.api == nil {
+ return errors.New("can't create the default handler, as no api is set")
+ }
+
+ s.SetHandler(s.api.Serve(nil))
+ }
+
+ var wg sync.WaitGroup
+ quitting := make(chan struct{})
+ once := new(sync.Once)
+ signalNotify(s.interrupt)
+ go handleInterrupt(once, s, quitting)
+
+ if s.hasScheme(schemeUnix) {
+ domainSocket := new(http.Server)
+ domainSocket.MaxHeaderBytes = int(s.MaxHeaderSize)
+ domainSocket.Handler = s.handler
+ if int64(s.CleanupTimeout) > 0 {
+ domainSocket.IdleTimeout = s.CleanupTimeout
+ }
+
+ configureServer(domainSocket, "unix", string(s.SocketPath))
+
+ wg.Add(2)
+ s.Logf("Serving weles at unix://%s", s.SocketPath)
+ go func(l net.Listener) {
+ defer wg.Done()
+ if err := domainSocket.Serve(l); err != nil {
+ s.Fatalf("%v", err)
+ }
+ s.Logf("Stopped serving weles at unix://%s", s.SocketPath)
+ }(s.domainSocketL)
+ go s.handleShutdown(&wg, domainSocket)
+ }
+
+ if s.hasScheme(schemeHTTP) {
+ httpServer := new(http.Server)
+ httpServer.MaxHeaderBytes = int(s.MaxHeaderSize)
+ httpServer.ReadTimeout = s.ReadTimeout
+ httpServer.WriteTimeout = s.WriteTimeout
+ httpServer.SetKeepAlivesEnabled(int64(s.KeepAlive) > 0)
+ if s.ListenLimit > 0 {
+ s.httpServerL = netutil.LimitListener(s.httpServerL, s.ListenLimit)
+ }
+
+ if int64(s.CleanupTimeout) > 0 {
+ httpServer.IdleTimeout = s.CleanupTimeout
+ }
+
+ httpServer.Handler = s.handler
+
+ configureServer(httpServer, "http", s.httpServerL.Addr().String())
+
+ wg.Add(2)
+ s.Logf("Serving weles at http://%s", s.httpServerL.Addr())
+ go func(l net.Listener) {
+ defer wg.Done()
+ if err := httpServer.Serve(l); err != nil {
+ s.Fatalf("%v", err)
+ }
+ s.Logf("Stopped serving weles at http://%s", l.Addr())
+ }(s.httpServerL)
+ go s.handleShutdown(&wg, httpServer)
+ }
+
+ if s.hasScheme(schemeHTTPS) {
+ httpsServer := new(http.Server)
+ httpsServer.MaxHeaderBytes = int(s.MaxHeaderSize)
+ httpsServer.ReadTimeout = s.TLSReadTimeout
+ httpsServer.WriteTimeout = s.TLSWriteTimeout
+ httpsServer.SetKeepAlivesEnabled(int64(s.TLSKeepAlive) > 0)
+ if s.TLSListenLimit > 0 {
+ s.httpsServerL = netutil.LimitListener(s.httpsServerL, s.TLSListenLimit)
+ }
+ if int64(s.CleanupTimeout) > 0 {
+ httpsServer.IdleTimeout = s.CleanupTimeout
+ }
+ httpsServer.Handler = s.handler
+
+ // Inspired by https://blog.bracebin.com/achieving-perfect-ssl-labs-score-with-go
+ httpsServer.TLSConfig = &tls.Config{
+ // Causes servers to use Go's default ciphersuite preferences,
+ // which are tuned to avoid attacks. Does nothing on clients.
+ PreferServerCipherSuites: true,
+ // Only use curves which have assembly implementations
+ // https://github.com/golang/go/tree/master/src/crypto/elliptic
+ CurvePreferences: []tls.CurveID{tls.CurveP256},
+ // Use modern tls mode https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility
+ NextProtos: []string{"http/1.1", "h2"},
+ // https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-_Only_Support_Strong_Protocols
+ MinVersion: tls.VersionTLS12,
+ // These ciphersuites support Forward Secrecy: https://en.wikipedia.org/wiki/Forward_secrecy
+ CipherSuites: []uint16{
+ tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+ tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+ tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ },
+ }
+
+ if s.TLSCertificate != "" && s.TLSCertificateKey != "" {
+ httpsServer.TLSConfig.Certificates = make([]tls.Certificate, 1)
+ httpsServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(s.TLSCertificate, s.TLSCertificateKey)
+ }
+
+ if s.TLSCACertificate != "" {
+ caCert, caCertErr := ioutil.ReadFile(s.TLSCACertificate)
+ if caCertErr != nil {
+ log.Fatal(caCertErr)
+ }
+ caCertPool := x509.NewCertPool()
+ caCertPool.AppendCertsFromPEM(caCert)
+ httpsServer.TLSConfig.ClientCAs = caCertPool
+ httpsServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert
+ }
+
+ configureTLS(httpsServer.TLSConfig)
+ httpsServer.TLSConfig.BuildNameToCertificate()
+
+ if err != nil {
+ return err
+ }
+
+ if len(httpsServer.TLSConfig.Certificates) == 0 {
+ if s.TLSCertificate == "" {
+ if s.TLSCertificateKey == "" {
+ s.Fatalf("the required flags `--tls-certificate` and `--tls-key` were not specified")
+ }
+ s.Fatalf("the required flag `--tls-certificate` was not specified")
+ }
+ if s.TLSCertificateKey == "" {
+ s.Fatalf("the required flag `--tls-key` was not specified")
+ }
+ }
+
+ configureServer(httpsServer, "https", s.httpsServerL.Addr().String())
+
+ wg.Add(2)
+ s.Logf("Serving weles at https://%s", s.httpsServerL.Addr())
+ go func(l net.Listener) {
+ defer wg.Done()
+ if err := httpsServer.Serve(l); err != nil {
+ s.Fatalf("%v", err)
+ }
+ s.Logf("Stopped serving weles at https://%s", l.Addr())
+ }(tls.NewListener(s.httpsServerL, httpsServer.TLSConfig))
+ go s.handleShutdown(&wg, httpsServer)
+ }
+
+ wg.Wait()
+ return nil
+}
+
+// Listen creates the listeners for the server
+func (s *Server) Listen() error {
+ if s.hasListeners { // already done this
+ return nil
+ }
+
+ if s.hasScheme(schemeHTTPS) {
+ // Use http host if https host wasn't defined
+ if s.TLSHost == "" {
+ s.TLSHost = s.Host
+ }
+ // Use http listen limit if https listen limit wasn't defined
+ if s.TLSListenLimit == 0 {
+ s.TLSListenLimit = s.ListenLimit
+ }
+ // Use http tcp keep alive if https tcp keep alive wasn't defined
+ if int64(s.TLSKeepAlive) == 0 {
+ s.TLSKeepAlive = s.KeepAlive
+ }
+ // Use http read timeout if https read timeout wasn't defined
+ if int64(s.TLSReadTimeout) == 0 {
+ s.TLSReadTimeout = s.ReadTimeout
+ }
+ // Use http write timeout if https write timeout wasn't defined
+ if int64(s.TLSWriteTimeout) == 0 {
+ s.TLSWriteTimeout = s.WriteTimeout
+ }
+ }
+
+ if s.hasScheme(schemeUnix) {
+ domSockListener, err := net.Listen("unix", string(s.SocketPath))
+ if err != nil {
+ return err
+ }
+ s.domainSocketL = domSockListener
+ }
+
+ if s.hasScheme(schemeHTTP) {
+ listener, err := net.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port)))
+ if err != nil {
+ return err
+ }
+
+ h, p, err := swag.SplitHostPort(listener.Addr().String())
+ if err != nil {
+ return err
+ }
+ s.Host = h
+ s.Port = p
+ s.httpServerL = listener
+ }
+
+ if s.hasScheme(schemeHTTPS) {
+ tlsListener, err := net.Listen("tcp", net.JoinHostPort(s.TLSHost, strconv.Itoa(s.TLSPort)))
+ if err != nil {
+ return err
+ }
+
+ sh, sp, err := swag.SplitHostPort(tlsListener.Addr().String())
+ if err != nil {
+ return err
+ }
+ s.TLSHost = sh
+ s.TLSPort = sp
+ s.httpsServerL = tlsListener
+ }
+
+ s.hasListeners = true
+ return nil
+}
+
+// Shutdown server and clean up resources
+func (s *Server) Shutdown() error {
+ if atomic.LoadInt32(&s.shuttingDown) != 0 {
+ s.Logf("already shutting down")
+ return nil
+ }
+ close(s.shutdown)
+ return nil
+}
+
+func (s *Server) handleShutdown(wg *sync.WaitGroup, server *http.Server) {
+ defer wg.Done()
+ ctx, cancel := context.WithTimeout(context.TODO(), 15*time.Second)
+ defer cancel()
+
+ <-s.shutdown
+ if err := server.Shutdown(ctx); err != nil {
+ // Error from closing listeners, or context timeout:
+ s.Logf("HTTP server Shutdown: %v", err)
+ } else {
+ atomic.AddInt32(&s.shuttingDown, 1)
+ select {
+ case <-ctx.Done():
+ if err := ctx.Err(); err != nil {
+ s.Logf("Error %s", err)
+ }
+ default:
+ done := make(chan error)
+ defer close(done)
+ go func() {
+ <-ctx.Done()
+ done <- ctx.Err()
+ }()
+ go func() {
+ //done <- s.api.Shutdown(ctx)
+ s.api.ServerShutdown()
+ done <- errors.New("API shut down")
+ }()
+ if err := <-done; err != nil {
+ s.Logf("Error %s", err)
+ }
+ }
+ }
+ return
+}
+
+// GetHandler returns a handler useful for testing
+func (s *Server) GetHandler() http.Handler {
+ return s.handler
+}
+
+// SetHandler allows for setting a http handler on this server
+func (s *Server) SetHandler(handler http.Handler) {
+ s.handler = handler
+}
+
+// UnixListener returns the domain socket listener
+func (s *Server) UnixListener() (net.Listener, error) {
+ if !s.hasListeners {
+ if err := s.Listen(); err != nil {
+ return nil, err
+ }
+ }
+ return s.domainSocketL, nil
+}
+
+// HTTPListener returns the http listener
+func (s *Server) HTTPListener() (net.Listener, error) {
+ if !s.hasListeners {
+ if err := s.Listen(); err != nil {
+ return nil, err
+ }
+ }
+ return s.httpServerL, nil
+}
+
+// TLSListener returns the https listener
+func (s *Server) TLSListener() (net.Listener, error) {
+ if !s.hasListeners {
+ if err := s.Listen(); err != nil {
+ return nil, err
+ }
+ }
+ return s.httpsServerL, nil
+}
+
+func handleInterrupt(once *sync.Once, s *Server, quitting chan struct{}) {
+ once.Do(func() {
+ for _ = range s.interrupt {
+ if s.interrupted {
+ s.Logf("Server already shutting down")
+ continue
+ }
+ s.interrupted = true
+ s.Logf("Shutting down... ")
+ close(quitting)
+
+ if err := s.httpServerL.Close(); err != nil {
+ s.Logf("Error: %s", err)
+ }
+ if err := s.httpsServerL.Close(); err != nil {
+ s.Logf("Error: %s", err)
+ }
+ if err := s.domainSocketL.Close(); err != nil {
+ s.Logf("Error: %s", err)
+ }
+ }
+ })
+}
+
+func signalNotify(interrupt chan<- os.Signal) {
+ signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
+}
diff --git a/server/server_suite_test.go b/server/server_suite_test.go
new file mode 100644
index 0000000..c81272f
--- /dev/null
+++ b/server/server_suite_test.go
@@ -0,0 +1,84 @@
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+package server_test
+
+import (
+ "math/rand"
+ "net/http/httptest"
+ "testing"
+ "time"
+
+ "github.com/go-openapi/loads"
+ "github.com/go-openapi/strfmt"
+ "github.com/golang/mock/gomock"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ "github.com/tideland/golib/audit"
+
+ "git.tizen.org/tools/weles"
+ "git.tizen.org/tools/weles/mock"
+ "git.tizen.org/tools/weles/server"
+ "git.tizen.org/tools/weles/server/operations"
+)
+
+const (
+ JSON = "application/json"
+ OMIT = "omit"
+)
+
+func TestServer(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "Server Suite")
+}
+
+func testServerSetup() (mockCtrl *gomock.Controller, mockJobManager *mock.MockJobManager, mockArtifactManager *mock.MockArtifactManager, apiDefaults *server.APIDefaults, testserver *httptest.Server) {
+ mockCtrl = gomock.NewController(GinkgoT())
+ mockJobManager = mock.NewMockJobManager(mockCtrl)
+ mockArtifactManager = mock.NewMockArtifactManager(mockCtrl)
+ swaggerSpec, _ := loads.Analyzed(server.SwaggerJSON, "")
+ api := operations.NewWelesAPI(swaggerSpec)
+ srv := server.NewServer(api)
+ apiDefaults = &server.APIDefaults{Managers: server.NewManagers(mockJobManager, mockArtifactManager)}
+ srv.WelesConfigureAPI(apiDefaults)
+ testserver = httptest.NewServer(srv.GetHandler())
+ return
+}
+
+// createJobInfoSlice is a function to create random data for tests of JobLister
+func createJobInfoSlice(sliceLenght int) (ret []weles.JobInfo) {
+ // checking for errors omitted due to fixed input.
+ dateTimeIter, _ := time.Parse("Mon Jan 2 15:04:05 -0700 MST 2006", "Tue Jan 2 15:04:05 +0100 CET 1900")
+ durationIncrement, _ := time.ParseDuration("+25h")
+ durationIncrement2, _ := time.ParseDuration("+100h")
+ jobInfo := make([]weles.JobInfo, sliceLenght)
+ gen := audit.NewGenerator(rand.New(rand.NewSource(time.Now().UTC().UnixNano())))
+ for i, _ := range jobInfo {
+ tmp := weles.JobInfo{}
+ createdTime := gen.Time(time.Local, dateTimeIter, durationIncrement)
+ tmp.Created = strfmt.DateTime(createdTime)
+ tmp.Updated = strfmt.DateTime(gen.Time(time.Local, createdTime, durationIncrement2))
+ tmp.Info = gen.Sentence()
+ tmp.Name = gen.Word()
+ tmp.Status = weles.JobStatus(gen.OneStringOf(string(weles.JobStatusNEW),
+ string(weles.JobStatusPARSING), string(weles.JobStatusDOWNLOADING),
+ string(weles.JobStatusWAITING), string(weles.JobStatusRUNNING),
+ string(weles.JobStatusCOMPLETED), string(weles.JobStatusFAILED),
+ string(weles.JobStatusCANCELED)))
+ tmp.JobID = weles.JobID(i + 1)
+ dateTimeIter = dateTimeIter.Add(durationIncrement)
+ jobInfo[i] = tmp
+ }
+ return jobInfo
+}
diff --git a/server/test_sample.yml b/server/test_sample.yml
new file mode 100755
index 0000000..58e6336
--- /dev/null
+++ b/server/test_sample.yml
@@ -0,0 +1,12 @@
+# Contents of this file are not an example.
+# This is just a text file for tests.
+---
+timeout: 18000
+job_name: kvm-test
+device_type: kvm
+target: kvm01
+actions:
+- command: deploy_linaro_image
+ parameters:
+ image: file:///path/to/kvm.img
+- command: boot_linaro_image
diff --git a/sort_order.go b/sort_order.go
new file mode 100644
index 0000000..f650ceb
--- /dev/null
+++ b/sort_order.go
@@ -0,0 +1,83 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+//
+
+package weles
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "encoding/json"
+
+ strfmt "github.com/go-openapi/strfmt"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/validate"
+)
+
+// SortOrder denotes direction of sorting of weles jobs or artifacts.
+//
+// * Ascending - from oldest to newest.
+//
+// * Descending - from newest to oldest.
+//
+// swagger:model SortOrder
+type SortOrder string
+
+const (
+
+ // SortOrderAscending captures enum value "Ascending"
+ SortOrderAscending SortOrder = "Ascending"
+
+ // SortOrderDescending captures enum value "Descending"
+ SortOrderDescending SortOrder = "Descending"
+)
+
+// for schema
+var sortOrderEnum []interface{}
+
+func init() {
+ var res []SortOrder
+ if err := json.Unmarshal([]byte(`["Ascending","Descending"]`), &res); err != nil {
+ panic(err)
+ }
+ for _, v := range res {
+ sortOrderEnum = append(sortOrderEnum, v)
+ }
+}
+
+func (m SortOrder) validateSortOrderEnum(path, location string, value SortOrder) error {
+ if err := validate.Enum(path, location, value, sortOrderEnum); err != nil {
+ return err
+ }
+ return nil
+}
+
+// Validate validates this sort order
+func (m SortOrder) Validate(formats strfmt.Registry) error {
+ var res []error
+
+ // value enum
+ if err := m.validateSortOrderEnum("", "body", m); err != nil {
+ return err
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
diff --git a/swagger.yml b/swagger.yml
new file mode 100644
index 0000000..3781afe
--- /dev/null
+++ b/swagger.yml
@@ -0,0 +1,559 @@
+swagger: '2.0'
+info:
+ description: >-
+ This is a Weles server. You can find out more about Weles at
+ [http://tbd.tbd](http://tbd.tbd).
+ version: 0.0.0
+ title: Weles
+ termsOfService: 'http://tbd.tbd/terms/'
+ contact:
+ email: tbd@tbd.com
+ license:
+ name: Apache 2.0
+ url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+host: 'localhost:8088'
+basePath: /api/v1
+consumes:
+ - application/json
+produces:
+ - application/json
+tags:
+ - name: jobs
+ description: Info and management of Weles jobs.
+ - name: artifacts
+ description: Info about all artifacts used by Weles jobs.
+schemes:
+ - http
+paths:
+ /jobs:
+ post:
+ tags:
+ - jobs
+ summary: Add new job
+ description: adds new Job in Weles using recipe passed in YAML format.
+ operationId: JobCreator
+ consumes:
+ - multipart/form-data
+ parameters:
+ - in: formData
+ name: yamlfile
+ type: file
+ required: true
+ description: is Job description yaml file.
+ produces:
+ - application/json
+ responses:
+ '201':
+ description: Created
+ schema:
+ $ref: '#/definitions/JobID'
+ '415':
+ $ref: '#/responses/UnsupportedMediaType'
+ '422':
+ $ref: '#/responses/UnprocessableEntity'
+ '500':
+ $ref: '#/responses/InternalServer'
+ '/jobs/{JobID}/cancel':
+ post:
+ tags:
+ - jobs
+ summary: Cancel existing job
+ description: JobCanceler stops execution of Job identified by JobID.
+ operationId: JobCanceler
+ consumes:
+ - application/json
+ produces:
+ - application/json
+ parameters:
+ - in: path
+ required: true
+ name: JobID
+ type: integer
+ format: uint64
+ responses:
+ '204':
+ description: No Content
+ '404':
+ $ref: '#/responses/NotFound'
+ '403':
+ $ref: '#/responses/Forbidden'
+ '500':
+ $ref: '#/responses/InternalServer'
+ /jobs/list:
+ post:
+ tags:
+ - jobs
+ summary: List jobs with filter and sort features
+ description: JobLister returns information on filtered Weles Jobs.
+ operationId: JobLister
+ consumes:
+ - application/json
+ produces:
+ - application/json
+ parameters:
+ - in: body
+ name: jobFilterAndSort
+ description: Job Filter and Sort object.
+ required: false
+ schema:
+ $ref: '#/definitions/JobFilterAndSort'
+ - in: query
+ name: after
+ description: JobID of the last element from previous page.
+ type: integer
+ format: uint64
+ - in: query
+ name: before
+ description: JobID of first element from next page.
+ type: integer
+ format: uint64
+ - in: query
+ name: limit
+ description: Custom page limit. Denotes number of JobInfo structures that will be returned.
+ type: integer
+ format: int32
+ responses:
+ '200':
+ description: OK
+ schema:
+ type: array
+ items:
+ $ref: '#/definitions/JobInfo'
+ headers:
+ Previous:
+ type: string
+ format: URI
+ description: >
+ URI to request previous page of data. Please note that the same body must be used as in initial request.
+ Next:
+ type: string
+ format: URI
+ description: >
+ URI to request next page of data. Please note that the same body must be used as in initial request.
+ TotalRecords:
+ type: integer
+ format: uint64
+ description: >
+ count of records currently fulfilling the requested JobFilter. Please note that this value may change when requesting for the same data at a different moment in time.
+ '206':
+ description: Partial Content
+ schema:
+ type: array
+ items:
+ $ref: '#/definitions/JobInfo'
+ headers:
+ Previous:
+ type: string
+ format: URI
+ description: >
+ URI to request previous page of data. Please note that the same body must be used as in initial request.
+ Next:
+ type: string
+ format: URI
+ description: >
+ URI to request next page of data. Please note that the same body must be used as in initial request.
+ TotalRecords:
+ type: integer
+ format: uint64
+ description: >
+ count of records currently fulfilling requested JobFilter. Please note that this value may change when requesting for the same data at a different moment in time.
+ RemainingRecords:
+ type: integer
+ format: uint64
+ description: >
+ number of records after current page. Please note that this value may change when requesting for the same data at a different moment in time.
+ '400':
+ $ref: '#/responses/BadRequest'
+ '404':
+ $ref: '#/responses/NotFound'
+ '500':
+ $ref: '#/responses/InternalServer'
+ /artifacts/list:
+ post:
+ tags:
+ - artifacts
+ summary: List artifacts with filter and sort features
+ description: ArtifactLister returns information on filtered Weles artifacts.
+ operationId: ArtifactLister
+ consumes:
+ - application/json
+ produces:
+ - application/json
+ parameters:
+ - in: body
+ name: artifactFilterAndSort
+ description: Artifact Filter and Sort object.
+ required: false
+ schema:
+ $ref: '#/definitions/ArtifactFilterAndSort'
+ - in: query
+ name: after
+ description: ID of the last element from previous page.
+ type: integer
+ format: int64
+ - in: query
+ name: before
+ description: ID of first element from next page.
+ type: integer
+ format: int64
+ - in: query
+ name: limit
+ description: Custom page limit. Denotes number of ArtifactInfo structures that will be returned.
+ type: integer
+ format: int32
+ responses:
+ '200':
+ description: OK
+ schema:
+ type: array
+ items:
+ $ref: '#/definitions/ArtifactInfo'
+ headers:
+ Previous:
+ type: string
+ format: URI
+ description: >
+ URI to request next page of data. Please note that the same body must be used as in initial request.
+ Next:
+ type: string
+ format: URI
+ description: >
+ URI to request next page of data. Please note that the same body must be used as in initial request.
+ TotalRecords:
+ type: integer
+ format: uint64
+ description: >
+ count of records currently fulfilling the requested ArtifactFilter. Please note that this value may change when requesting for the same data at a different moment in time.
+ '206':
+ description: Partial Content
+ schema:
+ type: array
+ items:
+ $ref: '#/definitions/ArtifactInfo'
+ headers:
+ Previous:
+ type: string
+ format: URI
+ description: >
+ URI to request next page of data. Please note that the same body must be used as in initial request.
+ Next:
+ type: string
+ format: URI
+ description: >
+ URI to request next page of data. Please note that the same body must be used as in initial request.
+ TotalRecords:
+ type: integer
+ format: uint64
+ description: >
+ count of records currently fulfilling the requested ArtifactFilter. Please note that this value may change when requesting for the same data at a different moment in time.
+ RemainingRecords:
+ type: integer
+ format: uint64
+ description: >
+ number of records after current page. Please note that this value may change when requesting for the same data at a different moment in time.
+ '400':
+ $ref: '#/responses/BadRequest'
+ '404':
+ $ref: '#/responses/NotFound'
+ '500':
+ $ref: '#/responses/InternalServer'
+responses:
+ BadRequest:
+ description: Bad Request
+ schema:
+ $ref: '#/definitions/ErrResponse'
+ NotFound:
+ description: Not Found
+ schema:
+ $ref: '#/definitions/ErrResponse'
+ Forbidden:
+ description: Forbidden
+ schema:
+ $ref: '#/definitions/ErrResponse'
+ UnsupportedMediaType:
+ description: Unsupported media type
+ schema:
+ $ref: '#/definitions/ErrResponse'
+ UnprocessableEntity:
+ description: Unprocessable entity
+ schema:
+ $ref: '#/definitions/ErrResponse'
+ InternalServer:
+ description: Internal Server error
+ schema:
+ $ref: '#/definitions/ErrResponse'
+definitions:
+ JobID:
+ description: is a unique identifier for Weles Job.
+ type: integer
+ format: uint64
+ JobStatus:
+ description: |
+ specifies state of the Job.
+
+ * NEW - The new Job has been created.
+
+ * PARSING - Provided yaml file is being parsed and interpreted.
+
+ * DOWNLOADING - Images and/or files required for the test are being downloaded.
+
+ * WAITING - Job is waiting for Boruta worker.
+
+ * RUNNING - Job is being executed.
+
+ * COMPLETED - Job is completed. This is terminal state.
+
+ * FAILED - Job execution has failed. This is terminal state.
+
+ * CANCELED -Job has been canceled with API call. This is terminal state.
+
+ type: string
+ enum:
+ - NEW
+ - PARSING
+ - DOWNLOADING
+ - WAITING
+ - RUNNING
+ - COMPLETED
+ - FAILED
+ - CANCELED
+ JobInfo:
+ description: contains information about a Job available for public API.
+ type: object
+ properties:
+ jobID:
+ $ref: '#/definitions/JobID'
+ description: is a unique Job identifier
+ name:
+ type: string
+ description: is the Job name acquired from yaml file during Job creation.
+ created:
+ type: string
+ format: date-time
+ description: is the Job creation time in UTC.
+ updated:
+ type: string
+ format: date-time
+ description: is the time of latest Jobs' status modification.
+ status:
+ $ref: '#/definitions/JobStatus'
+ description: specifies current state of the Job.
+ info:
+ type: string
+ description: provides additional information about current state, e.g. cause of failure
+ JobFilter:
+ description: is used to filter Weles Jobs.
+ type: object
+ properties:
+ JobID:
+ type: array
+ items:
+ $ref: '#/definitions/JobID'
+ Name:
+ type: array
+ items:
+ type: string
+ CreatedAfter:
+ type: string
+ format: date-time
+ CreatedBefore:
+ type: string
+ format: date-time
+ UpdatedAfter:
+ type: string
+ format: date-time
+ UpdatedBefore:
+ type: string
+ format: date-time
+ Status:
+ type: array
+ items:
+ $ref: '#/definitions/JobStatus'
+ Info:
+ type: array
+ items:
+ type: string
+ JobSortBy:
+ description: |
+ denotes key for sorting Jobs list. Jobs are sorted by ID (Ascending) by default.
+ You can sort jobs additionaly by
+
+ * CreatedDate - sorting by date of creation of the weles job.
+
+ * UpdatedDate - sorting by date of update of the weles job.
+
+ * JobStatus - sorting by the Job Status. Descending order will sort in the order JobStatuses are listed in the docs (from NEW at the start to CANCELED at the end). Ascending will reverse this order.
+ When sorting is applied, and there are many jobs with the same date/status, they will be sorted by JobID (Ascending)
+ type: string
+ enum:
+ - CreatedDate
+ - UpdatedDate
+ - JobStatus
+ SortOrder:
+ description: |
+ denotes direction of sorting of weles jobs or artifacts.
+
+ * Ascending - from oldest to newest.
+
+ * Descending - from newest to oldest.
+
+ type: string
+ enum:
+ - Ascending
+ - Descending
+ JobSorter:
+ description: |
+ defines the key for sorting as well as direction of sorting.
+ type: object
+ properties:
+ SortBy:
+ $ref: '#/definitions/JobSortBy'
+ SortOrder:
+ $ref: '#/definitions/SortOrder'
+ JobFilterAndSort:
+ description: Data for filtering and sorting Weles Jobs lists.
+ type: object
+ properties:
+ Filter:
+ $ref: '#/definitions/JobFilter'
+ Sorter:
+ $ref: '#/definitions/JobSorter'
+ ArtifactType:
+ description: |
+ denotes type and function of an artifact.
+
+ * IMAGE - image file.
+
+ * RESULT - all outputs, files built during tests, etc.
+
+ * TEST - additional files uploaded by user for conducting test.
+
+ * YAML - yaml file describing Weles Job.
+
+ type: string
+ enum:
+ - IMAGE
+ - RESULT
+ - TEST
+ - YAML
+ ArtifactPath:
+ description: describes path to artifact in ArtifactDB filesystem.
+ type: string
+ ArtifactStatus:
+ description: |
+ describes artifact status and availability.
+
+ * DOWNLOADING - artifact is currently being downloaded.
+
+ * READY - artifact has been downloaded and is ready to use.
+
+ * FAILED - file is not available for use (e.g. download failed).
+
+ * PENDING - artifact download has not started yet.
+
+ type: string
+ enum:
+ - DOWNLOADING
+ - READY
+ - FAILED
+ - PENDING
+ ArtifactURI:
+ description: is used to identify artifact's source.
+ type: string
+ format: uri
+ ArtifactAlias:
+ description: is an alternative name of an artifact.
+ type: string
+ ArtifactDescription:
+ description: contains information needed to create new artifact in ArtifactDB.
+ type: object
+ properties:
+ JobID:
+ $ref: '#/definitions/JobID'
+ description: specifies Job for which artifact was created.
+ Type:
+ $ref: '#/definitions/ArtifactType'
+ Alias:
+ $ref: '#/definitions/ArtifactAlias'
+ URI:
+ $ref: '#/definitions/ArtifactURI'
+ ArtifactInfo:
+ description: describes single artifact stored in ArtifactDB.
+ type: object
+ allOf:
+ - $ref: '#/definitions/ArtifactDescription'
+ properties:
+ Path:
+ $ref: '#/definitions/ArtifactPath'
+ Status:
+ $ref: '#/definitions/ArtifactStatus'
+ Timestamp:
+ description: is date of creating the artifact.
+ type: string
+ format: date-time
+ ID:
+ description: unique identification of the artifact.
+ type: integer
+ format: int64
+ x-go-custom-tag: "db:\",primarykey, autoincrement\""
+ ArtifactFilter:
+ description: is used to filter results from ArtifactDB.
+ type: object
+ properties:
+ JobID:
+ type: array
+ items:
+ $ref: '#/definitions/JobID'
+ Type:
+ type: array
+ items:
+ $ref: '#/definitions/ArtifactType'
+ Status:
+ type: array
+ items:
+ $ref: '#/definitions/ArtifactStatus'
+ Alias:
+ type: array
+ items:
+ $ref: '#/definitions/ArtifactAlias'
+ ErrResponse:
+ description: >-
+ is a standard error response containing information about the
+ error. It consists of error type and message.
+ type: object
+ properties:
+ type:
+ type: string
+ message:
+ type: string
+ ArtifactSortBy:
+ description: >
+ denotes the key for sorting list of all artifacts.
+
+ * ID - sorting by artifact ID.
+
+ type: string
+ enum:
+ - ID
+ ArtifactSorter:
+ description: |
+ defines the key for sorting as well as direction of sorting.
+ When ArtifactSorter is empty, artifacts are sorted by ID, Ascending.
+ type: object
+ properties:
+ SortBy:
+ $ref: '#/definitions/ArtifactSortBy'
+ SortOrder:
+ $ref: '#/definitions/SortOrder'
+ ArtifactFilterAndSort:
+ description: Data for filtering and sorting Weles Jobs lists.
+ type: object
+ properties:
+ Filter:
+ $ref: '#/definitions/ArtifactFilter'
+ Sorter:
+ $ref: '#/definitions/ArtifactSorter'
+externalDocs:
+ description: TBD
+ url: 'http://TBD.tbd'
+
diff --git a/weles.go b/weles.go
index 66055ec..43d1c08 100644
--- a/weles.go
+++ b/weles.go
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,3 @@
// Package weles represents the base of the Weles system.
package weles
-
-// JobID is a unique identifier for Weles Job.
-type JobID uint64
diff --git a/weles_suite_test.go b/weles_suite_test.go
new file mode 100644
index 0000000..86c5caf
--- /dev/null
+++ b/weles_suite_test.go
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package weles
+
+import (
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+
+ "testing"
+)
+
+func TestWeles(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "Weles Suite")
+}