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