--- /dev/null
+/* Copyright (c) 2017 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 v1 provides HTTP API of Weles.
+//
+// Through this API, clients may:
+//
+// * Create a Weles Job
+//
+// * Cancel a Weles Job
+//
+// * List and filter Weles Jobs
+//
+// * List and filter Weles Artifacts
+package v1
+
+import (
+ "git.tizen.org/tools/weles"
+ "github.com/dimfeld/httptreemux"
+)
+
+// API provides HTTP API handlers.
+type API struct {
+ router *httptreemux.TreeMux
+ jm weles.JobManager
+ am weles.ArtifactManager
+ cfg apiConfig
+}
+
+// apiConfig contains configuration for API Handlers.
+type apiConfig struct {
+ maxYamlSize int64
+}
+
+// initRouter initializes the routes of the Weles API.
+func (api *API) initRouter() {
+ //TODO(aalexnaderr) check groups and contexts in the httptreemux package
+
+ api.router.POST("/api/v1/jobs/", api.JobCreator)
+ api.router.POST("/api/v1/jobs/:jobID/cancel", api.JobCanceler)
+ api.router.GET("/api/v1/jobs/", api.JobFetcher)
+ api.router.POST("/api/v1/jobs/filter", api.JobFetcher)
+
+ api.router.GET("/api/v1/artifacts/", api.ArtifactsLister)
+ api.router.POST("/api/v1/artifacts/filter", api.ArtifactsLister)
+}
+
+// NewAPI defines possible routes.
+func NewAPI(router *httptreemux.TreeMux, jm weles.JobManager, am weles.ArtifactManager) (api *API) {
+ api = new(API)
+ api.router = router
+ api.jm = jm
+ api.am = am
+ // 10mb should be more than enough for yaml file
+ // this should be moved to the initApiConfig function when there will be more configs.
+ api.cfg.maxYamlSize = 10 << 20
+ api.initRouter()
+
+ return api
+}
--- /dev/null
+/* Copyright (c) 2017 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 v1
+
+import (
+ "encoding/json"
+ "errors"
+ "io/ioutil"
+ "net/http"
+ "strconv"
+
+ "git.tizen.org/tools/weles"
+)
+
+type jobIDpack struct {
+ weles.JobID `json:"jobid"`
+}
+
+type jobIDfilter struct {
+ Jobids []weles.JobID `json:"jobid"`
+}
+
+// errHandler is error handler function that returns error in the HTTP Bad Request response when
+// error is not nil. After the errHandler is executed no further writes to the request should
+// be done.
+func errHandler(w http.ResponseWriter, err error) bool {
+ // TODO(aalexanderr) errors should be analysed to set proper http status e.g. when there is
+ // no file supplied by user, StatusNotFound should be returned.
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return true
+ }
+ return false
+}
+
+// JobCreator takes yaml file from http POST request:
+// - bodycontenttype: multipart/form-data
+// - field name: "uploadfile"
+// and passes it as byte slice to CreateJob function.
+// CreateJob function returns JobId which is passed to the requestor in JSON response
+func (api *API) JobCreator(w http.ResponseWriter, r *http.Request, _ map[string]string) {
+ if errHandler(w, r.ParseMultipartForm(api.cfg.maxYamlSize)) {
+ return
+ }
+
+ file, _, err := r.FormFile("uploadfile")
+ if errHandler(w, err) {
+ return
+ }
+ //TODO file.Close error should be passed to logger which currently does not exist thus
+ // an assumption is made that correctly opened file returned by standard library will
+ // always close.
+ defer file.Close()
+
+ byteContainer, err := ioutil.ReadAll(file)
+ if errHandler(w, err) {
+ return
+ }
+
+ jobid, err := api.jm.CreateJob(byteContainer)
+ if errHandler(w, err) {
+ return
+ }
+
+ response, err := json.Marshal(&jobIDpack{jobid})
+ if errHandler(w, err) {
+ return
+ }
+ w.Header().Set("Content-Type", "application/json; charset=UTF-8")
+ w.WriteHeader(http.StatusCreated)
+ w.Write(response)
+
+}
+
+// JobCanceler takes jobID and passes it to CancelJob function.
+func (api *API) JobCanceler(w http.ResponseWriter, r *http.Request, p map[string]string) {
+ jobidStr, ok := p["jobID"]
+ if !ok {
+ errHandler(w, errors.New("jobID parameter is not filled"))
+ return
+ }
+ jobidUint, err := strconv.ParseUint(jobidStr, 10, 64)
+ if errHandler(w, err) {
+ return
+ }
+ if errHandler(w, api.jm.CancelJob(weles.JobID(jobidUint))) {
+ return
+ }
+ w.WriteHeader(http.StatusOK)
+}
+
+// JobFetcher will return info on
+// - all jobs available when receiving GET request
+// - jobs with specified JobIDs when they are JSON encoded in the POST request body
+func (api *API) JobFetcher(w http.ResponseWriter, r *http.Request, _ map[string]string) {
+ var jobInfos []weles.JobInfo
+ switch r.Method {
+ case http.MethodGet:
+ var err error
+ jobInfos, err = api.jm.ListJobs(nil)
+ if errHandler(w, err) {
+ return
+ }
+ case http.MethodPost:
+ body, err := ioutil.ReadAll(r.Body)
+ if errHandler(w, err) {
+ return
+ }
+ var jobids jobIDfilter
+ err = json.Unmarshal(body, &jobids)
+ if errHandler(w, err) {
+ return
+ }
+ jobInfos, err = api.jm.ListJobs(jobids.Jobids)
+ if errHandler(w, err) {
+ return
+ }
+
+ }
+ data, err := json.Marshal(&jobInfos)
+ if errHandler(w, err) {
+ return
+ }
+ w.Header().Set("Content-Type", "application/json; charset=UTF-8")
+ w.WriteHeader(http.StatusOK)
+ w.Write(data)
+}
+
+// ArtifactsLister will return info on:
+// - all artifacts when receiving GET request
+// - filtered artifacts according to specified filters in POST request
+func (api *API) ArtifactsLister(w http.ResponseWriter, r *http.Request, _ map[string]string) {
+ var filter weles.ArtifactFilter
+ var artifactInfos []weles.ArtifactInfo
+ switch r.Method {
+ case http.MethodGet:
+
+ case http.MethodPost:
+ body, err := ioutil.ReadAll(r.Body)
+ if errHandler(w, err) {
+ return
+ }
+ err = json.Unmarshal(body, &filter)
+ if errHandler(w, err) {
+ return
+ }
+ }
+ artifactInfos, err := api.am.ListArtifact(filter)
+ if errHandler(w, err) {
+ return
+ }
+ data, err := json.Marshal(artifactInfos)
+ if errHandler(w, err) {
+ return
+ }
+ w.Header().Set("Content-Type", "application/json; charset=UTF-8")
+ w.WriteHeader(http.StatusOK)
+ w.Write(data)
+}
--- /dev/null
+// Package mockup is used for the purpose of development of api
+// before the interfaces are implemented in the appropriate packages.
+// After the interfaces will be implemented, rebase will be performed
+// and this mockup package will be deleted.
+package mockup
+
+import (
+ "fmt"
+
+ "git.tizen.org/tools/weles"
+)
+
+// JobManagerMock is a mockup struct that will implement JobManager interface.
+type JobManagerMock struct {
+}
+
+// NewJobManagerMock is function that creates JobManagerMock object.
+func NewJobManagerMock() weles.JobManager {
+ return &JobManagerMock{}
+}
+
+// CreateJob is a mockup function implementing JobManager interface.
+func (mock *JobManagerMock) CreateJob(yaml []byte) (weles.JobID, error) {
+ return 123, nil
+}
+
+// CancelJob is a mockup function implementing JobManager interface.
+func (mock *JobManagerMock) CancelJob(j weles.JobID) (err error) {
+ if j == 1234 {
+ err = nil
+ } else {
+ err = fmt.Errorf("Wrong ID")
+ }
+
+ return err
+}
+
+// ListJobs is a mockup function implementing JobManager interface.
+func (mock *JobManagerMock) ListJobs(jid []weles.JobID) ([]weles.JobInfo, error) {
+ if jid == nil {
+ return []weles.JobInfo{
+ {JobID: 123, Name: "JobNamee", Info: "asddd"},
+ {JobID: 1234, Name: "JobNamee", Info: "asddd"},
+ }, nil
+ }
+
+ return []weles.JobInfo{
+ {JobID: 1, Name: "JobNamee", Info: "asddd"},
+ {JobID: 123, Name: "JobNam3ee", Info: "ssasddd"},
+ {JobID: 1245, Name: "JobName5e", Info: "asddd"},
+ }, nil
+}
+
+// ArtifactManagerMock is a mockup struct that will implement ArtifactManagerMock interface.
+type ArtifactManagerMock struct {
+}
+
+// NewArtifactManagerMock is a function that creates ArtifactManagerMock struct.
+func NewArtifactManagerMock() weles.ArtifactManager {
+ return &ArtifactManagerMock{}
+}
+
+// ListArtifact is a mockup function implementing ArtifactManager interface.
+func (mock *ArtifactManagerMock) ListArtifact(filter weles.ArtifactFilter) ([]weles.ArtifactInfo, error) {
+ return nil, nil
+}
+
+// PushArtifact is a mockup function implementing ArtifactManager interface.
+func (mock *ArtifactManagerMock) PushArtifact(artifact weles.ArtifactDescription, ch chan weles.ArtifactStatusChange) (weles.ArtifactPath, error) {
+ return "", nil
+}
+
+// CreateArtifact is a mockup function implementing ArtifactManager interface.
+func (mock *ArtifactManagerMock) CreateArtifact(artifact weles.ArtifactDescription) (weles.ArtifactPath, error) {
+ return "", nil
+}
+
+// GetArtifactInfo is a mockup function implementing ArtifactManager interface.
+func (mock *ArtifactManagerMock) GetArtifactInfo(path weles.ArtifactPath) (weles.ArtifactInfo, error) {
+ ai := new(weles.ArtifactInfo)
+
+ return *ai, nil
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 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 (
+ "log"
+ "net/http"
+
+ "git.tizen.org/tools/weles/server/api/v1"
+ "git.tizen.org/tools/weles/server/mockup"
+ "github.com/dimfeld/httptreemux"
+)
+
+func main() {
+ router := httptreemux.New()
+ jm := mockup.NewJobManagerMock()
+ am := mockup.NewArtifactManagerMock()
+ //TODO pass pointer not copy
+ _ = v1.NewAPI(router, jm, am)
+
+ log.Fatal(http.ListenAndServe(":8080", router))
+}