Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding apk code challenge artifacts #2612

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions code-challenge/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# WSO2 APK Code Challenge ToDo Service

## Docker Image

docker pull sampathrajapakse/todo-service:latest

## Docker Image Creation

Use the above provided docker image or you can build using following commands.

docker buildx create --use

docker buildx inspect --bootstrap

docker buildx build --platform linux/amd64,linux/arm64 -t sampathrajapakse/todo-service:latest --push .

## Deploy in K8s Cluster

kubectl create ns apk

kubectl apply -f k8s-artifacts/deployment.yaml -n apk

## Access the pod locally

kubectl port-forward pod/todo-app-<random-id> 8080:8080 -n apk

## Invoke the services

### Retrieve all to-dos

curl http://localhost:8080/todos

### Create a new to-do

curl -X POST http://localhost:8080/todos \
-H "Content-Type: application/json" \
-d '{"task": "Buy groceries", "done": false}'

### Retrieve a single to-do

curl http://localhost:8080/todos/1

### Update an existing to-do

curl -X PUT http://localhost:8080/todos/1 \
-H "Content-Type: application/json" \
-d '{"task": "Buy groceries", "done": true}'

### Delete a to-do

curl -X DELETE http://localhost:8080/todos/1

### Register a user with user-reg header

curl -X POST http://localhost:8080/register \
-H "user-reg: sampath"
41 changes: 41 additions & 0 deletions code-challenge/k8s-artifacts/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: todo-app
labels:
app: todo-app
spec:
replicas: 1
selector:
matchLabels:
app: todo-app
template:
metadata:
labels:
app: todo-app
spec:
containers:
- name: todo-app
image: sampathrajapakse/todo-service:latest
ports:
- containerPort: 8080
resources:
limits:
memory: "128Mi"
cpu: "500m"
requests:
memory: "64Mi"
cpu: "250m"
---
apiVersion: v1
kind: Service
metadata:
name: todo-app-service
spec:
selector:
app: todo-app
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
144 changes: 144 additions & 0 deletions code-challenge/open-api/oas.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
openapi: 3.0.3
info:
title: To-Do API
description: A simple to-do service API
version: 1.0.0
servers:
- url: http://todo-app-service:80
description: Local development server
paths:
/todos:
get:
summary: Retrieve all to-dos
description: Returns a list of all to-do items
responses:
'200':
description: A list of to-do items
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/ToDo'
post:
summary: Create a new to-do
description: Adds a new to-do item
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/NewToDo'
responses:
'201':
description: To-do created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/ToDo'
'400':
description: Invalid input
/todos/{id}:
get:
summary: Retrieve a single to-do item
description: Returns the details of a specific to-do item by its ID
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: The requested to-do item
content:
application/json:
schema:
$ref: '#/components/schemas/ToDo'
'404':
description: To-do not found
put:
summary: Update a to-do item
description: Updates an existing to-do item by its ID
parameters:
- name: id
in: path
required: true
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/NewToDo'
responses:
'200':
description: To-do updated successfully
content:
application/json:
schema:
$ref: '#/components/schemas/ToDo'
'400':
description: Invalid input
'404':
description: To-do not found
delete:
summary: Delete a to-do item
description: Deletes a to-do item by its ID
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'204':
description: To-do deleted successfully
'404':
description: To-do not found
/register:
post:
summary: Register a user
description: Registers a new user and expects a custom header `user-reg`
parameters:
- name: user-reg
in: header
required: true
schema:
type: string
responses:
'201':
description: User registered successfully
'400':
description: Missing or invalid header
components:
schemas:
ToDo:
type: object
properties:
id:
type: integer
description: Unique identifier of the to-do item
task:
type: string
description: The task description
done:
type: boolean
description: Status of the to-do (completed or not)
required:
- id
- task
- done
NewToDo:
type: object
properties:
task:
type: string
description: The task description
done:
type: boolean
description: Status of the to-do (completed or not)
required:
- task
- done
23 changes: 23 additions & 0 deletions code-challenge/todo-app/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Build Stage
FROM golang:1.22 AS build

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . ./

RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .

# Deployment Stage
FROM alpine:3.19.1

WORKDIR /root/

COPY --from=build /app/main .

# Expose the application port
EXPOSE 8080

CMD ["./main"]
5 changes: 5 additions & 0 deletions code-challenge/todo-app/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/wso2/apk/code-challenge/todo-app

go 1.22.6

require github.com/gorilla/mux v1.8.1
2 changes: 2 additions & 0 deletions code-challenge/todo-app/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
102 changes: 102 additions & 0 deletions code-challenge/todo-app/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package main

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

"github.com/gorilla/mux"
)

type Todo struct {
ID int `json:"id"`
Task string `json:"task"`
Done bool `json:"done"`
}

var todos []Todo
var nextID = 1

// Get all to-dos
func getTodos(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(todos)
}

// Create a new to-do
func createTodo(w http.ResponseWriter, r *http.Request) {
var todo Todo
_ = json.NewDecoder(r.Body).Decode(&todo)
todo.ID = nextID
nextID++
todos = append(todos, todo)
json.NewEncoder(w).Encode(todo)
}

// Get a single to-do by ID
func getTodoByID(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id, _ := strconv.Atoi(params["id"])
for _, todo := range todos {
if todo.ID == id {
json.NewEncoder(w).Encode(todo)
return
}
}
http.Error(w, "To-do not found", http.StatusNotFound)
}

// Update a to-do by ID
func updateTodo(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id, _ := strconv.Atoi(params["id"])
for index, todo := range todos {
if todo.ID == id {
todos = append(todos[:index], todos[index+1:]...)
var updatedTodo Todo
_ = json.NewDecoder(r.Body).Decode(&updatedTodo)
updatedTodo.ID = id
todos = append(todos, updatedTodo)
json.NewEncoder(w).Encode(updatedTodo)
return
}
}
http.Error(w, "To-do not found", http.StatusNotFound)
}

// Delete a to-do by ID
func deleteTodo(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id, _ := strconv.Atoi(params["id"])
for index, todo := range todos {
if todo.ID == id {
todos = append(todos[:index], todos[index+1:]...)
json.NewEncoder(w).Encode(todos)
return
}
}
http.Error(w, "To-do not found", http.StatusNotFound)
}

// Register a user
func registerUser(w http.ResponseWriter, r *http.Request) {
regHeader := r.Header.Get("user-reg")
if regHeader == "" {
http.Error(w, "Missing user-reg header", http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusCreated)
w.Write([]byte("User registered with header: " + regHeader))
}

func main() {
router := mux.NewRouter()
router.HandleFunc("/todos", getTodos).Methods("GET")
router.HandleFunc("/todos", createTodo).Methods("POST")
router.HandleFunc("/todos/{id}", getTodoByID).Methods("GET")
router.HandleFunc("/todos/{id}", updateTodo).Methods("PUT")
router.HandleFunc("/todos/{id}", deleteTodo).Methods("DELETE")
router.HandleFunc("/register", registerUser).Methods("POST")

log.Fatal(http.ListenAndServe(":8080", router))
}
Loading