From d62941f36922ae42bba00b199ddfce7d2ec858ae Mon Sep 17 00:00:00 2001
From: Mathilde Hertaas <maimh@stud.ntnu.no>
Date: Sat, 1 Mar 2025 17:45:28 +0100
Subject: [PATCH] cities is now included in info endpoint, user can spesify but
 not over 10

---
 main.go | 264 +++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 195 insertions(+), 69 deletions(-)

diff --git a/main.go b/main.go
index e50c9e5..c90d8ac 100644
--- a/main.go
+++ b/main.go
@@ -4,132 +4,258 @@ import (
 	"encoding/json"
 	"fmt"
 	"net/http"
+	"time"
 	"strings"
+	"strconv"
 )
 
-//countryinfo/v1/info/{country_code}{?limit=10}
-	//given country, 2-letter country codes (ISO 3166-2).
-	//limit is the number of cities that are listed in the response.
+var serviceStartTime time.Time
 
-// Struktur for å lagre data om landene i info endepunktet
+// Funksjon for å hente statuskode fra et API
+func getAPIStatus(url string) (int, error) {
+	resp, err := http.Get(url)
+	if err != nil {
+		return 0, err
+	}
+	defer resp.Body.Close()
+
+	// Returner HTTP-statuskoden
+	return resp.StatusCode, nil
+}
+
+// Funksjon for å beregne oppetid
+func getUptime() int64 {
+	return int64(time.Since(serviceStartTime).Seconds())
+}
+
+// Handler for Diagnostics endpoint
+func diagnosticsHandler(w http.ResponseWriter, r *http.Request) {
+	// Hent statuskoder fra eksterne API-er
+	countriesNowStatus, err := getAPIStatus("http://129.241.150.113:3500/api/v0.1/countries")
+	if err != nil {
+		http.Error(w, "Error fetching CountriesNow status", http.StatusInternalServerError)
+		return
+	}
+
+	restCountriesStatus, err := getAPIStatus("http://129.241.150.113:8080/v3.1/all") //funker med all
+	if err != nil {
+		http.Error(w, "Error fetching RestCountries status", http.StatusInternalServerError)
+		return
+	}
+
+	// Bygg responsen med statusinformasjon
+	response := map[string]interface{}{
+		"countriesnowapi": fmt.Sprintf("%d", countriesNowStatus),
+		"restcountriesapi": fmt.Sprintf("%d", restCountriesStatus),
+		"version":          "v1", // Versjon av API
+		"uptime":           getUptime(), // Oppetid i sekunder
+	}
+
+	// Sett Content-Type til application/json
+	w.Header().Set("Content-Type", "application/json")
+	
+	// Returner JSON-responsen
+	json.NewEncoder(w).Encode(response)
+}
+
+type Flags struct{
+	PNG string `json:"png"`
+}
+
+// Struktur for data fra REST Countries API
 type CountryInfo struct {
 	Name struct {
-		Common   string `json:"common"`
-		Official string `json:"official"`
-	} `json:"name"`
-	Capital  []string          `json:"capital"`
-	Region   string            `json:"region"`
+		Common   string         `json:"common"`
+		Official string         `json:"official"`
+	}                           `json:"name"`
+	Capital   []string          `json:"capital"`
 	Languages map[string]string `json:"languages"`
+	Continents []string          `json:"continents"`
+	Borders    []string         `json:"borders"`
+	Population int              `json:"population"`
+	Flags Flags                `json:"flags"`
 }
 
+type CityInfo struct {
+	Cities []string `json:"cities"`
+}
 
-//Når du kjører koden blir du sendt til en hjemmeside siden forespørsel spesifikasjoner ikke er gitt enda
-	//info om hvordan bruke API
-	func homeHandler(w http.ResponseWriter, r *http.Request) {
-		w.Header().Set("Content-Type", "application/json")
-		// Strukturert respons for bedre lesbarhet
-		response := map[string]interface{}{
-		"WELCOME": "Welcome to the Country Info API",
-			"endpoints": map[string]interface{}{
-				"Endpoint 1": map[string]string{
-					"Description": "Get information about a country using its ISO 3166-1 alpha-2 code.",
-					"URL":         "http://localhost:8080/countryinfo/v1/info/{country_code}{?limit=10}",
-					"Example":     "http://localhost:8080/countryinfo/v1/info/NO",
-				},
-				"Endpoint 2": map[string]interface{}{
-					"Description": "TO BE IMPLEMENTED",
-				
-				},
-			
-			},
-		}
-	
-		json.NewEncoder(w).Encode(response)
-	}
 
+// Struktur for kombinert respons
+type CombinedInfo struct {
+	Name       string            `json:"name"`
+	Continents   []string         `json:"continenents"`
+	Population int               `json:"population"`
+	Languages  map[string]string `json:"languages"`
+	Borders    []string          `json:"borders"`
+	Flags      string            `json:"flags"`
+	Capital    string            `json:"capital"`
+	Cities     string          `json:"cities"`
+}
 
-// En funksjon som henter data fra det eksterne API-et ved landkode
+// Henter landdata fra REST Countries API
 func getCountryDataByCode(countryCode string) (*CountryInfo, error) {
-
-	//bygge en URL for API kallet
 	url := fmt.Sprintf("http://129.241.150.113:8080/v3.1/alpha/%s", countryCode)
-
-	//sender en http forespørsel
 	resp, err := http.Get(url)
 	if err != nil {
 		return nil, err
 	}
 	defer resp.Body.Close()
 
-
-	// Sjekk om API-kallet var vellykket (statuskode 200)
 	if resp.StatusCode != http.StatusOK {
 		return nil, fmt.Errorf("error retrieving country data, status code: %d", resp.StatusCode)
 	}
 
-	// Håndterer en http feil, som om landet ikke finnes
 	var countries []CountryInfo
 	if err := json.NewDecoder(resp.Body).Decode(&countries); err != nil {
 		return nil, err
 	}
 
-	// Hvis vi ikke får noen treff
 	if len(countries) == 0 {
 		return nil, fmt.Errorf("country not found for code: %s", countryCode)
 	}
 
-	// Returner det første elementet i listen siden forventer bare ett treff)
 	return &countries[0], nil
 }
 
+func getCitiesByCountry(countryName string) ([]string, error) {
+    url := fmt.Sprintf("https://countriesnow.space/api/v0.1/countries/cities/q?country=%s", countryName)
+    resp, err := http.Get(url)
+    if err != nil {
+        return nil, err
+    }
+    defer resp.Body.Close()
 
-//oprett en handler for /countryinfo/v1/info/{country_code}
-// leser landkoden fra URL
-// slår opp dataene
-// REturnerer dem som JSON
-func handler(w http.ResponseWriter, r *http.Request) {
-	// Hent landkode fra path
+    if resp.StatusCode != http.StatusOK {
+        return nil, fmt.Errorf("error retrieving cities, status code: %d", resp.StatusCode)
+    }
+
+    var responseBody map[string]interface{}
+    if err := json.NewDecoder(resp.Body).Decode(&responseBody); err != nil {
+        return nil, err
+    }
+
+
+    if cities, ok := responseBody["data"].([]interface{}); ok {
+        var cityList []string
+        for _, city := range cities {
+            if cityStr, ok := city.(string); ok {
+                cityList = append(cityList, cityStr)
+            }
+        }
+
+        // Hvis ingen byer er funnet, returner en feilmelding
+        if len(cityList) == 0 {
+            fmt.Println("No cities found in the response.")
+        }
+
+        return cityList, nil
+    }
+
+    return nil, fmt.Errorf("no cities found for country %s", countryName)
+}
+
+func countryInfoHandler(w http.ResponseWriter, r *http.Request) {
 	parts := strings.Split(r.URL.Path, "/")
 	if len(parts) < 5 {
-		http.Error(w, "Invalid format. Please use the format /countryinfo/v1/info/{countryCode}{?limit=10}. Example: /countryinfo/v1/info/NO", http.StatusBadRequest)
+		http.Error(w, "Invalid format. Use: /countryinfo/v1/info/{countryCode}", http.StatusBadRequest)
 		return
 	}
-	countryCode := parts[4] // Landkoden er den 5. delen av pathen (fra 0)
+	countryCode := parts[4]
 
-	// Hent data fra API ved landkode
-	//country inneholder data om alt går bra
+	// Hent landdata fra REST Countries API
 	country, err := getCountryDataByCode(countryCode)
-	//err inneholder feil hvis noe går galt, ikke finner landkode
 	if err != nil {
-		http.Error(w, fmt.Sprintf(
-			"Error retrieving data for country code: %s. Check if the country code is correct.\nExample: /countryinfo/v1/info/NO\nError: %s",
-			countryCode, err.Error(),
-		), http.StatusInternalServerError)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+
+		// Hent antall byer som skal vises fra URL-parameteren (default til 10)
+	// F.eks. /countryinfo/v1/info/NO?cities=5
+	cityCount := 10 // Standardverdi
+	cityCountStr := r.URL.Query().Get("cities") // Hent "cities" parameteren
+	if cityCountStr != "" {
+		// Prøv å konvertere parameteren til et heltall
+		if count, err := strconv.Atoi(cityCountStr); err == nil && count > 0 {
+			cityCount = count
+		}
+	}
+
+	// Hent byene fra countriesnow API
+	cities, err := getCitiesByCountry(country.Name.Common)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
 	}
 
+	// Hvis det er flere enn det brukeren ønsker, begrens listen
+	if len(cities) > cityCount {
+		cities = cities[:cityCount]
+	}
 
+	// Feilsøking: Hvis ingen byer ble funnet
+	if len(cities) == 0 {
+		fmt.Printf("No cities found for %s\n", country.Name.Common)
+	}
 
-	// Returner JSON-respons
+	// Begrens byene til maks 10
+	if len(cities) > 10 {
+		cities = cities[:10]
+	}
+
+	// Bygg kombinert respons
+	response := CombinedInfo{
+		Name:       country.Name.Common,
+		Continents: country.Continents,
+		Population: country.Population,
+		Languages:  country.Languages,
+		Borders:    country.Borders,
+		Flags:      country.Flags.PNG,
+		Capital:    "",
+		Cities:     strings.Join(cities, ", "), // Legg til de 10 første byene som en kommaseparert liste
+	}
+
+	if len(country.Capital) > 0 {
+		response.Capital = country.Capital[0]
+	}
+
+	// Sett Content-Type til application/json
 	w.Header().Set("Content-Type", "application/json")
-	json.NewEncoder(w).Encode(country)
+	json.NewEncoder(w).Encode(response)
 }
 
-
-// kaller de ulike funkjsonene basert på hvilken URL som sender forespørsel
 func main() {
 
-	http.HandleFunc("/",homeHandler)
-	//Rute for info om land
-	http.HandleFunc("/countryinfo/v1/info/", handler) 
+//starttidspunkt for tjenesten
+	serviceStartTime = time.Now()
+
+	http.HandleFunc("/status/", diagnosticsHandler)
+
+	http.HandleFunc("/", homeHandler)
+	http.HandleFunc("/countryinfo/v1/info/", countryInfoHandler)
 
-	//info i terminal
 	fmt.Println("Server running on port 8080...")
-	
 	if err := http.ListenAndServe(":8080", nil); err != nil {
-		// Logg feilen og avslutt programmet dersom serveren ikke kan starte
 		fmt.Printf("Server failed to start: %s\n", err)
 	}
 }
 
-//URL for å teste info for norge
-//http://localhost:8080/countryinfo/v1/info/No
\ No newline at end of file
+func homeHandler(w http.ResponseWriter, r *http.Request) {
+	w.Header().Set("Content-Type", "application/json")
+	response := map[string]interface{}{
+		"WELCOME": "Welcome to the Country Info API",
+		"endpoints": map[string]interface{}{
+			"Get Country Info": map[string]string{
+				"Description": "Get country details including cities, population, and more.",
+				"URL Format":         "http://localhost:8080/countryinfo/v1/info/{country_code}{?limit=10}",
+				"Example":     "http://localhost:8080/countryinfo/v1/info/NO?cities=5",
+			},
+			"Get status info" : map[string]string{
+				"Description" : "Get information about the API statuses",
+				"URL" : "http://localhost:8080/status/",
+			},
+		},
+	}
+
+	json.NewEncoder(w).Encode(response)
+}
-- 
GitLab