Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

106 Commits

Folders and files

Repository files navigation

Assignment 2:

Countries Dashboard Service

Project info

  • This Project is a web application that allows users to create, read, update, and delete dashboard configurations.
  • It retrieves populated dashboards from external APIs (REST Countries, Open-Meteo, Currency API) and supports registering, deleting, and viewing webhooks for events. The webhooks are triggered automatically when a dashboard is created, updated, or deleted.
  • The service is deployed using Docker and OpenStack, and persists data using Firestore.

Team members

Member Responsibilities
Sofiya Yasim Ibrahim Ali notifications_handler and corresponding structs, and connecting trigger webhooks to registration_handler as well as testing.
Katerina Radchenko Docker, dashboards_handler and corresponding structs, as well as integration with external APIs, registration_handler.
Katharina Kivle status_handler and corresponding structs and registration_handler as well as testing.

Features

  • Register, update, delete, and view dashboard configurations
  • Retrieve populated dashboards with external data
  • Webhook registration for:
    • REGISTER (new dashboard)
    • CHANGE (dashboard update)
    • DELETE (dashboard removal)
    • INVOKE (dashboard viewed)

Endpoints

User friendly homepage:

Endpoint:

Method: GET
Path: /

Endpoint 'Registrations': Registering dashboard configuration

Register new dashboard configuration:

Endpoint:

Method: POST
Path: /dashboard/v1/registrations/

Example body:

{
   "country": "Norway",                                    
   "isoCode": "NO",                                         
   "features": {
                  "temperature": true,                     
                  "precipitation": true,                   
                  "capital": true,                          
                  "coordinates": true,                      
                  "population": true,                       
                  "area": true,                             
                  "targetCurrencies": ["EUR", "USD", "SEK"]
               }
}

Example response:

{
    "id": "ZaAJF0ywF7XFDJRPsfwi",
    "lastChange": "20250407 13:02"
}

View all registered dashboard configurations:

Endpoint:

Method: GET
Path: /dashboard/v1/registrations/

Example response:

[
    {
        "id": "0xrM5VxUqt9QAkUlM2Az",
        "country": "Norway",
        "isoCode": "no",
        "features": {
            "temperature": true,
            "precipitation": true,
            "capital": true,
            "coordinates": true,
            "population": true,
            "area": true,
            "targetCurrencies": [
                "USD",
                "SEK"
            ]
        },
        "lastChange": "20250408 19:41"
    },
    {
        "id": "MUGKLhX4aX8R8tsVXk3i",
        "country": "Sweden",
        "isoCode": "se",
        "features": {
            "temperature": true,
            "precipitation": true,
            "capital": true,
            "coordinates": true,
            "population": true,
            "area": true,
            "targetCurrencies": [
                "EUR",
                "NOK",
                "SEK"
            ]
        },
        "lastChange": "20250403 14:02"
    },
    {
        "id": "ZaAJF0ywF7XFDJRPsfwi",
        "country": "Norway",
        "isoCode": "no",
        "features": {
            "temperature": false,
            "precipitation": true,
            "capital": true,
            "coordinates": true,
            "population": true,
            "area": true,
            "targetCurrencies": [
                "EUR",
                "USD",
                "SEK"
            ]
        },
        "lastChange": "20250407 23:02"
    },
    {
        "id": "b3RiUziT0eCMohDRPxJH",
        "country": "Belgia",
        "isoCode": "be",
        "features": {
            "temperature": false,
            "precipitation": true,
            "capital": true,
            "coordinates": false,
            "population": true,
            "area": true,
            "targetCurrencies": [
                "EUR",
                "USD",
                "SEK"
            ]
        },
        "lastChange": "20250408 20:08"
    }...
]

View a specific registered dashboard configuration:

Endpoint:

Method: GET
Path: /dashboard/v1/registrations/{id}

Query Parameters:

id: the ID associated with the specific configuration.

Example request:

Method: GET
Path: /dashboard/v1/registrations/b3RiUziT0eCMohDRPxJH

Example response:

{
        "id": "b3RiUziT0eCMohDRPxJH",
        "country": "Belgia",
        "isoCode": "be",
        "features": {
            "temperature": false,
            "precipitation": true,
            "capital": true,
            "coordinates": false,
            "population": true,
            "area": true,
            "targetCurrencies": [
                "EUR",
                "USD",
                "SEK"
            ]
        },
        "lastChange": "20250408 20:08"
    }

Replace a specific registered dashboard configuration:

Endpoint:

Method: PUT
Path: /dashboard/v1/registrations/{id}

Query Parameters:

id: the ID associated with the specific configuration.

Example request:

Path: /dashboard/v1/registrations/b3RiUziT0eCMohDRPxJH

Example body:

{
   "country": "Belgia",
        "isoCode": "be",
        "features": {
            "temperature": true, // Changed value
            "precipitation": true,
            "capital": true,
            "coordinates": false,
            "population": true,
            "area": true,
            "targetCurrencies": [
                "EUR",
                "USD",
                "SEK"
            ]
        }
}

Delete a specific registered dashboard configuration:

Endpoint:

Method: DELETE
Path: /dashboard/v1/registrations/{id}

Query Parameters:

id: the ID associated with the specific configuration.

Example request:

/dashboard/v1/registrations/b3RiUziT0eCMohDRPxJH

Endpoint 'Dashboards': Retrieve populated dashboard:

Retrieving a specific populated dashboard:

Endpoint:

Method: GET
Path: /dashboard/v1/dashboards/{id}

Query Parameters:

id: the ID associated with the specific configuration.

Example request:

/dashboard/v1/dashboards/ZaAJF0ywF7XFDJRPsfwi

Response:

{
    "country": "Norway",
    "isoCode": "no",
    "features": {
        "temperature": null,
        "precipitation": 0.02261904761904763,
        "capital": "Oslo",
        "coordinates": {
            "latitude": 62,
            "longitude": 10
        },
        "population": 5379475,
        "area": 323802,
        "targetCurrencies": {
            "EUR": 0.083447,
            "SEK": 0.916623,
            "USD": 0.091509
        }
    },
    "lastRetrieval": "20250410 00:19"
}

Endpoint 'Notifications': Managing webhooks for event notifications

Registration of Webhook:

Endpoint:

Method: POST
Path: /dashboard/v1/notifications/

Example body:

{
   "url": "https://webhook.site/b5e5e555-f590-4ecc-93ac-bf12990665c1",
   "country": "NO",
   "event": "INVOKE"
}

Example response:

{
    "id":"cFgY08RFKv1mBGhMLLRN"
}

Deletion of Webhook:

Endpoint:

Method: DELETE
Path: /dashboard/v1/notifications/{id}

Query Parameters:

id: the ID for the webhook registration.

Example request:

/dashboard/v1/notifications/cFgY08RFKv1mBGhMLLRN

Example response:

{
    "message": "Webhook deleted successfully"
}

View specific registered webhook:

Endpoint:

Method: GET
Path: /dashboard/v1/notifications/{id}

Query Parameters:

id: the ID for the webhook registration.

Example request:

/dashboard/v1/notifications/8MFqluTvqj9DM9xg4uGY

Example response:

{
    "id": "8MFqluTvqj9DM9xg4uGY",
    "url": "https://webhook.site/b5e5e555-f590-4ecc-93ac-bf12990665c1",
    "country": "NO",
    "event": "CHANGE"
}

View all registered webhooks:

Endpoint:

Method: GET
Path: /dashboard/v1/notifications/

Example request:

/dashboard/v1/notifications/

Example response:

[
    {
        "id": "8MFqluTvqj9DM9xg4uGY",
        "url": "https://webhook.site/b5e5e555-f590-4ecc-93ac-bf12990665c1",
        "country": "NO",
        "event": "CHANGE"
    },
    {
        "id": "9b80WsAjAavjXb3FgXsm",
        "url": "https://webhook.site/b5e5e555-f590-4ecc-93ac-bf12990665c1",
        "country": "NO",
        "event": "DELETE"
    },
    {
        "id": "XETAPbr3CSRcklkK6LBe",
        "url": "https://webhook.site/b5e5e555-f590-4ecc-93ac-bf12990665c1",
        "country": "NO",
        "event": "INVOKE"
    }
]

Webhook Invocation (upon trigger):

Method:

Method: POST
Path: https://webhook.site/b5e5e555-f590-4ecc-93ac-bf12990665c1

Example request:

/dashboard/v1/notifications/

Example response:

{
  "country": "NO",
  "event": "INVOKE",
  "id": "XETAPbr3CSRcklkK6LBe",
  "time": "20250409 11:32"
}

Endpoint 'Status': Monitoring service availability

Endpoint:

Method: GET
Path: dashboard/v1/status/

Example response:

{
    "countries_api": 200,
    "meteo_api": 200,
    "currency_api": 200,
    "notification_db": 200,
    "webhooks": 7,
    "version": "v1",
    "uptime": 8801
}

Technologies

  • Language: Go (Golang)
  • Database: Firestore (Firebase)
  • Hosting: Docker + OpenStack (SkyHigh)
  • External APIs:
    • REST Countries
    • Open-Meteo
    • Currency API

Error handling

HTTP errors handled in the code:

200 - OK

204 - No Content

404 - Not Found

405 - Method Not Allowed

500 - Internal Server Error


Running locally (Windows PowerShell)

Option A — Real Firestore (requires your own credentials)

Set environment variable to your service account JSON. Do NOT commit this file.

$env:FIREBASE_CREDENTIALS = "C:\\path\\to\\your\\service-account.json"
# optional
$env:PORT = "8080"

cd "C:\\Users\\Bruker\\OneDrive - NTNU\\semester 4\\Cloud Technologies\\assignment-2"
go run .\main.go

Option B — Firestore Emulator (no credentials needed)

Run the Firestore emulator locally and set its host; the service will connect without keys.

# Example host; adapt to your emulator
$env:FIRESTORE_EMULATOR_HOST = "localhost:8081"

cd "C:\\Users\\Bruker\\OneDrive - NTNU\\semester 4\\Cloud Technologies\\assignment-2"
go run .\main.go

Quick sanity checks

Open in browser:

Use a REST client (or Invoke-RestMethod) for registrations/webhooks.


Deployment

Webhook site:


Docker:

This project is dockerized and deployed on OpenStack (SkyHigh).

Running locally with docker

The first step was setting up OpenStack (and the network, subnet and router), adding new rules to security groups (allowing ingress from 8080 and 22) and finally creating an instance. After connecting to the instance and installing Docker, it was time to create the Dockerfile that will be used to set up the image. Next we build the image and run it:

docker build -t assignment-2 .
docker run -d -p 8080:8080 --name assignment2 assignment-2

Testing on your machine

Use the following URL to run the service:


Testing

A range of tests have been implemented to ensure that the core functionality of the system works as expected. These tests cover handling of webhooks, creation and retrieval of dashboards, as well as system status verification.

The focus of the tests has been to:

  • Validate the use of correct HTTP methods (such as GET, POST, and DELETE).
  • Ensure the system correctly handles invalid requests, such as malformed JSON or missing IDs.
  • Check response codes and error handling to ensure the correct status is returned in various scenarios.

The tests are written using Go’s built-in testing package, along with assert for clear and expressive validation.

Example command to run the tests:

go test

The tests are located in the testing_Handler folder and cover the following components:

  • notification_Handler
  • registrations_Handler
  • status_Handler

These tests help ensure the reliability of the system by catching errors early and supporting robust, stable operation.


Security notes

  • Service account JSON files are sensitive. Store them outside the repository and reference via env vars.
  • In the portfolio ZIP, credentials are intentionally excluded.
  • Prefer the Firestore emulator for development to avoid key management.

Details/specifications

  • All time formats are in "YYYYMMDD HH:MM" format
  • Country/ISO code must be provided for valid webhook matching
  • Each webhook registration is persistent and survives service restarts

Further work and improvments:

Futher work with the assignemtnt would include more testing as well as comparing our implementation with working on JSON documents that are then transferred to the database (instead of working on the firebase "directly").

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages