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

API for Asset CRUD Operations and Authentication Middleware #14

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
27 changes: 27 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Binaries for programs and plugins
*.exe
*.dll
*.so
*.dylib

# Dependency directories (remove the comment below if you are using dep)
# vendor/

# Compiled Object files, Static and Dynamic libs (remove the comment below if you are using go modules)
*.o
*.a
*.lo
*.la
*.os
*.so
*.so.*
*.dylib
*.test
*.prof
*.exe

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out
18 changes: 18 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM golang:1.22.3-bullseye

Check failure on line 1 in Dockerfile

View check run for this annotation

Wiz GWI / Wiz IaC Scanner

Missing User Instruction

Rule ID: 0b5e0683-5a06-4bcd-ac73-28249add06df Severity: High Resource: FROM={{golang:1.22.3-bullseye}} A user should be specified in the dockerfile, otherwise the image will run as root
Raw output
Expected: The 'Dockerfile' should contain the 'USER' instruction
Found: The 'Dockerfile' does not contain any 'USER' instruction

Check notice on line 1 in Dockerfile

View check run for this annotation

Wiz GWI / Wiz IaC Scanner

Healthcheck Instruction Missing

Rule ID: 704ee966-67b2-4219-871f-12a7e5126cb1 Severity: Low Resource: FROM={{golang:1.22.3-bullseye}} Ensure that HEALTHCHECK is being used. The HEALTHCHECK instruction tells Docker how to test a container to check that it is still working
Raw output
Expected: Dockerfile should contain instruction 'HEALTHCHECK'
Found: Dockerfile doesn't contain instruction 'HEALTHCHECK'
# Set the Current Working Directory inside the container
WORKDIR /app

# Copy the source from the current directory to the Working Directory inside the container
COPY . .

# Download all the dependencies
RUN go mod download

# Install the package
RUN go build -o main .

# This container exposes port 8080 to the outside world
EXPOSE 8080

# Run the executable
CMD ["./main"]
168 changes: 150 additions & 18 deletions README.md
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job with the readme, very descriptive 👍

Original file line number Diff line number Diff line change
@@ -1,31 +1,163 @@
# GlobalWebIndex Engineering Challenge
# User Favorites API

## Introduction
This is a sample Go application that provides an API for managing user favorites. Users can register, login, and manage their favorite assets (charts, insights, and audiences). The API uses JWT for authentication.

This challenge is designed to give you the opportunity to demonstrate your abilities as a software engineer and specifically your knowledge of the Go language.
## Project Structure

On the surface the challenge is trivial to solve, however you should choose to add features or capabilities which you feel demonstrate your skills and knowledge the best. For example, you could choose to optimise for performance and concurrency, you could choose to add a robust security layer or ensure your application is highly available. Or all of these.
```
user-favorites-api/
├── Dockerfile
├── docker-compose.yml
├── go.mod
├── go.sum
├── handlers/
│ └── handlers.go
├── main.go
├── models/
│ └── models.go
├── router/
│ └── router.go
├── store/
│ └── store.go
├── middleware/
│ └── jwt.go
├── db_scripts/
│ └── dbsetup.sql
└── README.md
```

Of course, usually we would choose to solve any given requirement with the simplest possible solution, however that is not the spirit of this challenge.
## Setup

## Challenge
### Prerequisites

Let's say that in GWI platform all of our users have access to a huge list of assets. We want our users to have a peronal list of favourites, meaning assets that favourite or “star” so that they have them in their frontpage dashboard for quick access. An asset can be one the following
* Chart (that has a small title, axes titles and data)
* Insight (a small piece of text that provides some insight into a topic, e.g. "40% of millenials spend more than 3hours on social media daily")
* Audience (which is a series of characteristics, for that exercise lets focus on gender (Male, Female), birth country, age groups, hours spent daily on social media, number of purchases last month)
e.g. Males from 24-35 that spent more than 3 hours on social media daily.
- Docker
- Docker Compose

Build a web server which has some endpoint to receive a user id and return a list of all the user’s favourites. Also we want endpoints that would add an asset to favourites, remove it, or edit its description. Assets obviously can share some common attributes (like their description) but they also have completely different structure and data. It’s up to you to decide the structure and we are not looking for something overly complex here (especially for the cases of audiences). There is no need to have/deploy/create an actual database although we would like to discuss about storage options and data representations.
### Running the Application

Note that users have no limit on how many assets they want on their favourites so your service will need to provide a reasonable response time.
1. Ensure you have Docker and Docker Compose installed.
2. Build and start the application using Docker Compose:

A working server application with functional API is required, along with a clear readme.md. Useful and passing tests would be also be viewed favourably
```sh
docker-compose up --build --remove-orphans
```

It is appreciated, though not required, if a Dockerfile is included.
This command will build the Go application image, start the PostgreSQL database, and then start the Go application. The application will be available at `http://localhost:8080`, and the PostgreSQL database will be available at `localhost:5432` with the credentials specified.

## Submission
## API Endpoints

Just create a fork from the current repo and send it to us!
### User Registration

Good luck, potential colleague!
- **URL:** `/register`
- **Method:** `POST`
- **Request Body:**

```json
{
"username": "john_doe",
"email": "[email protected]",
"password": "password123"
}
```

- **Sample Request:**

```sh
curl -X POST http://localhost:8080/register -d '{"username":"john_doe","email":"[email protected]","password":"password123"}' -H "Content-Type: application/json"
```

### User Login

- **URL:** `/login`
- **Method:** `POST`
- **Request Body:**

```json
{
"username": "john_doe",
"password": "password123"
}
```

- **Sample Request:**

```sh
curl -X POST http://localhost:8080/login -d '{"username":"john_doe","password":"password123"}' -H "Content-Type: application/json"
```

- **Response:**

```json
{
"token": "your_jwt_token_here"
}
```

### Get User Favorites

- **URL:** `/api/favorites/{userID}`
- **Method:** `GET`
- **Headers:** `Authorization: Bearer your_jwt_token_here`
- **Sample Request:**

```sh
curl -H "Authorization: Bearer your_jwt_token_here" http://localhost:8080/api/favorites/1
```

### Add Favorite

- **URL:** `/api/favorites/{userID}`
- **Method:** `POST`
- **Headers:** `Authorization: Bearer your_jwt_token_here`
- **Request Body:**

```json
{
"id": "1",
"type": "chart",
"description": "Test Chart",
"data": "Sample Data"
}
```

- **Sample Request:**

```sh
curl -X POST -H "Authorization: Bearer your_jwt_token_here" -d '{"id":"1","type":"chart","description":"Test Chart","data":"Sample Data"}' -H "Content-Type: application/json" http://localhost:8080/api/favorites/1
```

### Remove Favorite

- **URL:** `/api/favorites/{userID}/{assetID}`
- **Method:** `DELETE`
- **Headers:** `Authorization: Bearer your_jwt_token_here`
- **Sample Request:**

```sh
curl -X DELETE -H "Authorization: Bearer your_jwt_token_here" http://localhost:8080/api/favorites/1/1
```

### Edit Favorite

- **URL:** `/api/favorites/{userID}/{assetID}`
- **Method:** `PUT`
- **Headers:** `Authorization: Bearer your_jwt_token_here`
- **Request Body:**

```json
{
"description": "Updated Chart Description",
"data": "Updated Sample Data"
}
```

- **Sample Request:**

```sh
curl -X PUT -H "Authorization: Bearer your_jwt_token_here" -d '{"description":"Updated Chart Description","data":"Updated Sample Data"}' -H "Content-Type: application/json" http://localhost:8080/api/favorites/1/1
```

## Environment Variables

- `DATABASE_URL`: The URL of the PostgreSQL database. Example: `postgresql://user_favorites_user:mysecretpassword@db:5432/user_favorites_db`

Check failure on line 162 in README.md

View check run for this annotation

Wiz GWI / Wiz Secret Scanner

Secret found: Password 🤫

SHA256: 94aefb8be78b2b7c344d11d1ba8a79ef087eceb19150881f69460b8772753263 Complexity: Weak as a kitten 🐱! Entropy Bits: 75 Length: 16
- `SECRET_KEY`: The secret key used for signing JWT tokens. It should be a long and random string. Example: `my_super_secret_key_123`
16 changes: 16 additions & 0 deletions db_scripts/dbsetup.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL,
password TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS assets (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL,
asset_id VARCHAR(255) NOT NULL,
type VARCHAR(50) NOT NULL,
description TEXT NOT NULL,
data TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);
25 changes: 25 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
services:
db:
image: postgres:latest
environment:
POSTGRES_DB: user_favorites_db
POSTGRES_USER: user_favorites_user
POSTGRES_PASSWORD: mysecretpassword
volumes:
- db_data:/var/lib/postgresql/data
- ./db_scripts:/docker-entrypoint-initdb.d
ports:
- "5432:5432"

app:
build: .
environment:
DATABASE_URL: postgresql://user_favorites_user:mysecretpassword@db:5432/user_favorites_db

Check failure on line 17 in docker-compose.yml

View check run for this annotation

Wiz GWI / Wiz Secret Scanner

Secret found: Password 🤫

SHA256: 94aefb8be78b2b7c344d11d1ba8a79ef087eceb19150881f69460b8772753263 Complexity: Weak as a kitten 🐱! Entropy Bits: 75 Length: 16
SECRET_KEY: mysecretkey
ports:
- "8080:8080"
depends_on:
- db

volumes:
db_data:
18 changes: 18 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module user-favorites-api

go 1.22.3

require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible

Check failure on line 6 in go.mod

View check run for this annotation

Wiz GWI / Wiz Vulnerability Scanner

github.com/dgrijalva/jwt-go:3.2.0+incompatible

Detected Vulnerabilities: CVE-2020-26160, Severity: High, Source: https://nvd.nist.gov/vuln/detail/CVE-2020-26160 CVSS score: 7.5, CVSS exploitability score: 3.9 💥 Has public exploit 🧨 Has CISA KEV exploit
github.com/gorilla/mux v1.8.1
github.com/jackc/pgx/v5 v5.6.0
golang.org/x/crypto v0.23.0
)

require (
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/text v0.15.0 // indirect
)
32 changes: 32 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading