Skip to content

Commit 4683e63

Browse files
authored
Merge pull request #32 from vibrato/swagger
Add swagger json and UI to application
2 parents d7401fc + b77b86f commit 4683e63

File tree

9 files changed

+320
-157
lines changed

9 files changed

+320
-157
lines changed

Dockerfile

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
FROM golang:alpine AS build
22

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

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

7+
ARG GO_SWAGGER_VERSION=0.18.0
8+
ARG SWAGGER_UI_VERSION=3.20.9
9+
10+
RUN curl -sfL -o /usr/local/bin/swagger https://github.com/go-swagger/go-swagger/releases/download/v$GO_SWAGGER_VERSION/swagger_linux_amd64 \
11+
&& chmod +x /usr/local/bin/swagger \
12+
&& curl -sfL https://github.com/swagger-api/swagger-ui/archive/v$SWAGGER_UI_VERSION.tar.gz | tar xz -C /tmp/ \
13+
&& mv /tmp/swagger-ui-$SWAGGER_UI_VERSION /tmp/swagger \
14+
&& sed -i 's#"https://petstore\.swagger\.io/v2/swagger\.json"#"./swagger.json"#g' /tmp/swagger/dist/index.html
15+
716
WORKDIR $GOPATH/src/github.com/vibrato/TechTestApp
817

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

1524
RUN go build -o /TechTestApp
25+
RUN swagger generate spec -o /swagger.json
1626

1727
FROM alpine:latest
1828

@@ -21,6 +31,8 @@ WORKDIR /TechTestApp
2131
COPY assets ./assets
2232
COPY conf.toml ./conf.toml
2333

34+
COPY --from=build /tmp/swagger/dist ./assets/swagger
35+
COPY --from=build /swagger.json ./assets/swagger/swagger.json
2436
COPY --from=build /TechTestApp TechTestApp
2537

2638
ENTRYPOINT [ "./TechTestApp", "serve" ]

assets/js/app.jsx

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ class TaskContainer extends React.Component {
77

88
this.state ={
99
tasks: [{
10-
Id: 0,
11-
Title: "Loading...",
12-
Completed: false,
13-
Priority: 0
10+
id: 0,
11+
title: "Loading...",
12+
completed: false,
13+
priority: 0
1414
}]
1515
};
1616
}
@@ -30,22 +30,22 @@ class TaskContainer extends React.Component {
3030
deleteTask(task) {
3131

3232
console.log(task)
33-
33+
3434
var filteredTasks = this.state.tasks.filter(function (item) {
35-
return (item.ID !== task.ID);
35+
return (item.id !== task.id);
3636
});
3737

3838
console.log(filteredTasks)
39-
40-
this.setState({tasks: filteredTasks});
4139

42-
fetch("/api/task/" + task.ID + "/", {
40+
this.setState({tasks: filteredTasks});
41+
42+
fetch("/api/task/" + task.id + "/", {
4343
method: "DELETE",
4444
})
4545

4646

4747
}
48-
48+
4949
render() {
5050

5151
return (
@@ -70,8 +70,8 @@ class TaskList extends React.Component {
7070
render() {
7171
return(
7272
<ul className="theList">
73-
{this.props.tasks.map(task =>
74-
<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>
73+
{this.props.tasks.map(task =>
74+
<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>
7575
)}
7676
</ul>
7777
)
@@ -86,10 +86,10 @@ class TaskForm extends React.Component {
8686
}
8787

8888
state = {
89-
Title: "",
90-
Priority: 1000,
91-
Completed: false,
92-
Id: 0,
89+
title: "",
90+
priority: 1000,
91+
completed: false,
92+
id: 0,
9393
};
9494

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

106106
const data = this.state
107-
107+
108108
console.log(data)
109109

110110
if (data.Title === "") {
@@ -119,7 +119,7 @@ class TaskForm extends React.Component {
119119
.then(data => this.props.onAddTask(data))
120120
.then(o => this.setState({Title: ""}));
121121
}
122-
122+
123123
render() {
124124
return (
125125
<form onSubmit={this.handleSubmit} className="taskForm">
@@ -132,4 +132,4 @@ class TaskForm extends React.Component {
132132

133133

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

cmd/root.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ var rootCmd = &cobra.Command{
5454
This application is used as part of testing potential candiates at Vibrato.
5555
5656
Please visit http://vibrato.com.au for more details`,
57-
Version: "0.3.7",
57+
Version: "0.4.0",
5858
}
5959

6060
// Execute adds all child commands to the root command and sets flags appropriately.

db/db.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func RebuildDb(cfg Config) error {
6969
}
7070

7171
query = fmt.Sprintf(`CREATE DATABASE %s
72-
WITH
72+
WITH
7373
OWNER = %s
7474
ENCODING = 'UTF8'
7575
LC_COLLATE = 'en_US.utf8'
@@ -194,7 +194,7 @@ func getSeedTasks() []model.Task {
194194
return tasks
195195
}
196196

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

200200
var tasks []model.Task

main.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@
1818
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1919
// THE SOFTWARE.
2020

21+
// Vibrato Tech Test Application
22+
//
23+
// This application is used as part of the Vibrato Technical Assessment.
24+
//
25+
// Version: 1.0
26+
// Contact: Thomas Winsnes<[email protected]> https://www.vibrato.com.au
27+
//
28+
// swagger:meta
2129
package main
2230

2331
import "github.com/vibrato/TechTestApp/cmd"

model/task.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,23 @@
2020

2121
package model
2222

23+
// A task
24+
// swagger:model
2325
type Task struct {
24-
ID int
25-
Priority int
26-
Title string
27-
Complete bool
26+
// the id of the task
27+
// required: true
28+
// min: 0
29+
ID int `json:"id"`
30+
31+
// Where the task fits in the list
32+
// required: true
33+
// min: 0
34+
Priority int `json:"priority"`
35+
36+
// The task name or description
37+
// required: true
38+
Title string `json:"title"`
39+
40+
// Is the task finished
41+
Complete bool `json:"complete"`
2842
}

ui/api.go

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
// Copyright © 2018 Thomas Winsnes <[email protected]>
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a copy
4+
// of this software and associated documentation files (the "Software"), to deal
5+
// in the Software without restriction, including without limitation the rights
6+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
// copies of the Software, and to permit persons to whom the Software is
8+
// furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
// THE SOFTWARE.
20+
21+
package ui
22+
23+
import (
24+
"encoding/json"
25+
"fmt"
26+
"log"
27+
"net/http"
28+
"strconv"
29+
30+
"github.com/gorilla/mux"
31+
"github.com/vibrato/TechTestApp/db"
32+
"github.com/vibrato/TechTestApp/model"
33+
)
34+
35+
// TaskID parameter.
36+
//
37+
// swagger:parameters deleteTask
38+
type TaskID struct {
39+
// The ID of the task
40+
//
41+
// in: path
42+
// min: 0
43+
// required: true
44+
ID int `json:"id"`
45+
}
46+
47+
// Sucessful Task Array Response
48+
//
49+
// swagger:response allTasks
50+
type allTasks struct {
51+
// in: body
52+
// The tasks being returned
53+
// required: true
54+
Tasks []model.Task `json:"tasks"`
55+
}
56+
57+
// swagger:route GET /api/task/ getTasks
58+
//
59+
// Fetch all tasks
60+
//
61+
// Produces:
62+
// - application/json
63+
//
64+
// Responses:
65+
// 200: allTasks
66+
//
67+
func getTasks(cfg Config) http.Handler {
68+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
69+
output, _ := db.GetAllTasks(cfg.DB)
70+
js, _ := json.Marshal(output)
71+
w.Header().Set("Content-Type", "application/json")
72+
fmt.Fprintf(w, string(js))
73+
})
74+
}
75+
76+
// Sucessful Single Task Response
77+
//
78+
// swagger:response aTask
79+
type aTask struct {
80+
// in: body
81+
// The tasks being returned
82+
// required: true
83+
Task model.Task `json:"task"`
84+
}
85+
86+
// swagger:parameters addTask
87+
type taskParameter struct {
88+
// in:body
89+
Task model.Task `json:"task"`
90+
}
91+
92+
// swagger:route POST /api/task/ addTask
93+
//
94+
// Add a new task to the list.
95+
//
96+
// Produces:
97+
// - application/json
98+
//
99+
// Responses:
100+
// 200: aTask
101+
// 400:
102+
// 500:
103+
//
104+
func addTask(cfg Config) http.Handler {
105+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
106+
decoder := json.NewDecoder(r.Body)
107+
var task model.Task
108+
109+
err := decoder.Decode(&task)
110+
111+
if err != nil {
112+
log.Println(err)
113+
http.Error(w, err.Error(), 400)
114+
return
115+
}
116+
117+
newTask, err := db.AddTask(cfg.DB, task)
118+
119+
if err != nil {
120+
log.Println(err)
121+
http.Error(w, err.Error(), 500)
122+
return
123+
}
124+
125+
js, _ := json.Marshal(newTask)
126+
w.Header().Set("Content-Type", "application/json")
127+
fmt.Fprintf(w, string(js))
128+
})
129+
}
130+
131+
// swagger:route DELETE /api/task/{id}/ deleteTask
132+
//
133+
// Delete a Task by ID
134+
//
135+
// Responses:
136+
// 204:
137+
// 404:
138+
// 500:
139+
//
140+
func deleteTask(cfg Config) http.Handler {
141+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
142+
vars := mux.Vars(r)
143+
144+
id, err := strconv.Atoi(vars["id"])
145+
146+
if err != nil {
147+
fmt.Print(err)
148+
http.Error(w, err.Error(), 500)
149+
return
150+
}
151+
152+
err = db.DeleteTask(cfg.DB, model.Task{ID: id})
153+
154+
if err != nil {
155+
fmt.Print(err)
156+
http.Error(w, err.Error(), 500)
157+
return
158+
}
159+
160+
w.WriteHeader(http.StatusNoContent)
161+
})
162+
}
163+
164+
func apiHandler(cfg Config, router *mux.Router) {
165+
router.Handle("/task/{id:[0-9]+}/", deleteTask(cfg)).Methods("DELETE")
166+
router.Handle("/task/", getTasks(cfg)).Methods("GET")
167+
router.Handle("/task/", addTask(cfg)).Methods("POST")
168+
}

0 commit comments

Comments
 (0)