Skip to content

Commit

Permalink
Merge pull request #32 from vibrato/swagger
Browse files Browse the repository at this point in the history
Add swagger json and UI to application
  • Loading branch information
TWinsnes authored Mar 5, 2019
2 parents d7401fc + b77b86f commit 4683e63
Show file tree
Hide file tree
Showing 9 changed files with 320 additions and 157 deletions.
14 changes: 13 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
FROM golang:alpine AS build

RUN apk add --no-cache curl git
RUN apk add --no-cache curl git gcc linux-headers musl-dev

RUN curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh

ARG GO_SWAGGER_VERSION=0.18.0
ARG SWAGGER_UI_VERSION=3.20.9

RUN curl -sfL -o /usr/local/bin/swagger https://github.com/go-swagger/go-swagger/releases/download/v$GO_SWAGGER_VERSION/swagger_linux_amd64 \
&& chmod +x /usr/local/bin/swagger \
&& curl -sfL https://github.com/swagger-api/swagger-ui/archive/v$SWAGGER_UI_VERSION.tar.gz | tar xz -C /tmp/ \
&& mv /tmp/swagger-ui-$SWAGGER_UI_VERSION /tmp/swagger \
&& sed -i 's#"https://petstore\.swagger\.io/v2/swagger\.json"#"./swagger.json"#g' /tmp/swagger/dist/index.html

WORKDIR $GOPATH/src/github.com/vibrato/TechTestApp

COPY Gopkg.toml Gopkg.lock $GOPATH/src/github.com/vibrato/TechTestApp/
Expand All @@ -13,6 +22,7 @@ RUN dep ensure -vendor-only -v
COPY . .

RUN go build -o /TechTestApp
RUN swagger generate spec -o /swagger.json

FROM alpine:latest

Expand All @@ -21,6 +31,8 @@ WORKDIR /TechTestApp
COPY assets ./assets
COPY conf.toml ./conf.toml

COPY --from=build /tmp/swagger/dist ./assets/swagger
COPY --from=build /swagger.json ./assets/swagger/swagger.json
COPY --from=build /TechTestApp TechTestApp

ENTRYPOINT [ "./TechTestApp", "serve" ]
38 changes: 19 additions & 19 deletions assets/js/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ class TaskContainer extends React.Component {

this.state ={
tasks: [{
Id: 0,
Title: "Loading...",
Completed: false,
Priority: 0
id: 0,
title: "Loading...",
completed: false,
priority: 0
}]
};
}
Expand All @@ -30,22 +30,22 @@ class TaskContainer extends React.Component {
deleteTask(task) {

console.log(task)

var filteredTasks = this.state.tasks.filter(function (item) {
return (item.ID !== task.ID);
return (item.id !== task.id);
});

console.log(filteredTasks)

this.setState({tasks: filteredTasks});

fetch("/api/task/" + task.ID + "/", {
this.setState({tasks: filteredTasks});

fetch("/api/task/" + task.id + "/", {
method: "DELETE",
})


}

render() {

return (
Expand All @@ -70,8 +70,8 @@ class TaskList extends React.Component {
render() {
return(
<ul className="theList">
{this.props.tasks.map(task =>
<li key={task.Id}><span className="delete" onClick={() => this.delete(task)}><i className="fas fa-trash"></i></span><span className="title">{task.Title}</span></li>
{this.props.tasks.map(task =>
<li key={task.id}><span className="delete" onClick={() => this.delete(task)}><i className="fas fa-trash"></i></span><span className="title">{task.title}</span></li>
)}
</ul>
)
Expand All @@ -86,10 +86,10 @@ class TaskForm extends React.Component {
}

state = {
Title: "",
Priority: 1000,
Completed: false,
Id: 0,
title: "",
priority: 1000,
completed: false,
id: 0,
};

onChange = (e) => {
Expand All @@ -104,7 +104,7 @@ class TaskForm extends React.Component {
event.preventDefault();

const data = this.state

console.log(data)

if (data.Title === "") {
Expand All @@ -119,7 +119,7 @@ class TaskForm extends React.Component {
.then(data => this.props.onAddTask(data))
.then(o => this.setState({Title: ""}));
}

render() {
return (
<form onSubmit={this.handleSubmit} className="taskForm">
Expand All @@ -132,4 +132,4 @@ class TaskForm extends React.Component {


ReactDOM.render( <TaskContainer/>, document.querySelector("#root"));

2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ var rootCmd = &cobra.Command{
This application is used as part of testing potential candiates at Vibrato.
Please visit http://vibrato.com.au for more details`,
Version: "0.3.7",
Version: "0.4.0",
}

// Execute adds all child commands to the root command and sets flags appropriately.
Expand Down
4 changes: 2 additions & 2 deletions db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func RebuildDb(cfg Config) error {
}

query = fmt.Sprintf(`CREATE DATABASE %s
WITH
WITH
OWNER = %s
ENCODING = 'UTF8'
LC_COLLATE = 'en_US.utf8'
Expand Down Expand Up @@ -194,7 +194,7 @@ func getSeedTasks() []model.Task {
return tasks
}

// GetAllTasks lists ass tasks in the database
// GetAllTasks lists all tasks in the database
func GetAllTasks(cfg Config) ([]model.Task, error) {

var tasks []model.Task
Expand Down
8 changes: 8 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

// Vibrato Tech Test Application
//
// This application is used as part of the Vibrato Technical Assessment.
//
// Version: 1.0
// Contact: Thomas Winsnes<[email protected]> https://www.vibrato.com.au
//
// swagger:meta
package main

import "github.com/vibrato/TechTestApp/cmd"
Expand Down
22 changes: 18 additions & 4 deletions model/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,23 @@

package model

// A task
// swagger:model
type Task struct {
ID int
Priority int
Title string
Complete bool
// the id of the task
// required: true
// min: 0
ID int `json:"id"`

// Where the task fits in the list
// required: true
// min: 0
Priority int `json:"priority"`

// The task name or description
// required: true
Title string `json:"title"`

// Is the task finished
Complete bool `json:"complete"`
}
168 changes: 168 additions & 0 deletions ui/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// Copyright © 2018 Thomas Winsnes <[email protected]>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package ui

import (
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"

"github.com/gorilla/mux"
"github.com/vibrato/TechTestApp/db"
"github.com/vibrato/TechTestApp/model"
)

// TaskID parameter.
//
// swagger:parameters deleteTask
type TaskID struct {
// The ID of the task
//
// in: path
// min: 0
// required: true
ID int `json:"id"`
}

// Sucessful Task Array Response
//
// swagger:response allTasks
type allTasks struct {
// in: body
// The tasks being returned
// required: true
Tasks []model.Task `json:"tasks"`
}

// swagger:route GET /api/task/ getTasks
//
// Fetch all tasks
//
// Produces:
// - application/json
//
// Responses:
// 200: allTasks
//
func getTasks(cfg Config) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
output, _ := db.GetAllTasks(cfg.DB)
js, _ := json.Marshal(output)
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, string(js))
})
}

// Sucessful Single Task Response
//
// swagger:response aTask
type aTask struct {
// in: body
// The tasks being returned
// required: true
Task model.Task `json:"task"`
}

// swagger:parameters addTask
type taskParameter struct {
// in:body
Task model.Task `json:"task"`
}

// swagger:route POST /api/task/ addTask
//
// Add a new task to the list.
//
// Produces:
// - application/json
//
// Responses:
// 200: aTask
// 400:
// 500:
//
func addTask(cfg Config) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
decoder := json.NewDecoder(r.Body)
var task model.Task

err := decoder.Decode(&task)

if err != nil {
log.Println(err)
http.Error(w, err.Error(), 400)
return
}

newTask, err := db.AddTask(cfg.DB, task)

if err != nil {
log.Println(err)
http.Error(w, err.Error(), 500)
return
}

js, _ := json.Marshal(newTask)
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, string(js))
})
}

// swagger:route DELETE /api/task/{id}/ deleteTask
//
// Delete a Task by ID
//
// Responses:
// 204:
// 404:
// 500:
//
func deleteTask(cfg Config) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)

id, err := strconv.Atoi(vars["id"])

if err != nil {
fmt.Print(err)
http.Error(w, err.Error(), 500)
return
}

err = db.DeleteTask(cfg.DB, model.Task{ID: id})

if err != nil {
fmt.Print(err)
http.Error(w, err.Error(), 500)
return
}

w.WriteHeader(http.StatusNoContent)
})
}

func apiHandler(cfg Config, router *mux.Router) {
router.Handle("/task/{id:[0-9]+}/", deleteTask(cfg)).Methods("DELETE")
router.Handle("/task/", getTasks(cfg)).Methods("GET")
router.Handle("/task/", addTask(cfg)).Methods("POST")
}
Loading

0 comments on commit 4683e63

Please sign in to comment.