49/100 Dias de Golang - Aplicação para buscar previsão do tempo - Parte 6
Table of Contents
#
Aplicação para buscar previsão do tempo - Parte 6
Hoje vou alterar implementar aquela execução que planejei na Parte 5.
Començando pelo main.go
. Bem simples, só chamando o Execute
da pasta cmd
.
package main
import (
"cidadeTempo/cmd"
)
func main() {
cmd.Execute()
}
Vamos para os arquivos da pasta cmd
. Esse é o arquivo principal, criado pelo Cobra. Só adicionei mais algumas descrições e exemplos de uso. Perceba aqui que o método Execute
está com a primeira letra maíuscula, pois ele será exportado, se você tentar no main.go
chamar o cmd.init()
verá que não é possível
package cmd
import (
"os"
"github.com/spf13/cobra"
)
var rootCmd = &cobra.Command{
Use: "cidadeTempo",
Short: "Consulta informações da previsão do tempo de uma cidade",
Long: `Consulta informações da previsão do tempo de uma cidade
Exemplos de uso:
cidadeTempo cidade --cidade "Curitiba"
cidadeTempo tempo --cidadeID 227`,
}
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
Tanto no cidade.go
quanto no tempo.go
removi as chamadas de API e as structs e levei para outro arquivo, com a ideia de modularizar a aplicação, separar os domínios e não repetir código.
package cmd
import (
"log"
"cidadeTempo/internal/api"
"github.com/spf13/cobra"
)
var cidade string
var cidadeCmd = &cobra.Command{
Use: "cidade",
Short: "Retorna uma listagem de cidades",
Long: `Retorna dados das cidades candidatas que possuem a string passada como parâmetro
exemplo de uso: cidadetempo --cidade Curitiba
`,
Run: func(cmd *cobra.Command, args []string) {
cidades, err := api.BuscarCidades(cidade)
if err != nil {
log.Fatalf("Erro ao buscar cidades: %v", err)
}
api.ExibirCidades(cidades)
},
}
func init() {
rootCmd.AddCommand(cidadeCmd)
cidadeCmd.Flags().StringVarP(&cidade, "cidade", "c", "Curitiba", "Cidade que você quer o ID")
}
package cmd
import (
"log"
"cidadeTempo/internal/api"
"github.com/spf13/cobra"
)
var cidadeID string
var tempoCmd = &cobra.Command{
Use: "tempo",
Short: "Exibe a previsão do tempo para uma cidade",
Long: `Exibe a previsão do tempo para uma cidade a partir do seu ID.
Exemplo de uso: cidadetempo tempo --cidadeID 227
Você pode obter o ID da cidade usando o comando "cidade".
`,
Run: func(cmd *cobra.Command, args []string) {
info, err := api.BuscarPrevisaoTempo(cidadeID)
if err != nil {
log.Fatalf("Erro ao buscar previsão do tempo: %v", err)
}
api.ExibirPrevisaoTempo(info)
},
}
func init() {
rootCmd.AddCommand(tempoCmd)
tempoCmd.Flags().StringVarP(&cidadeID, "cidadeID", "i", "1", "ID da cidade")
}
No arquivo pkg/client/client.go
fiz um client HTTP para facilitar o uso da API e não ficarmos repetindo código.
package client
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
const (
BaseURL = "https://brasilapi.com.br/api/cptec/v1"
)
type Client struct {
httpClient *http.Client
}
func NewClient() *Client {
return &Client{
httpClient: &http.Client{},
}
}
func (c *Client) Get(url string, result interface{}) error {
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return fmt.Errorf("erro ao criar request: %w", err)
}
resp, err := c.httpClient.Do(req)
if err != nil {
return fmt.Errorf("erro ao fazer request: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("erro ao ler resposta: %w", err)
}
err = json.Unmarshal(body, result)
if err != nil {
return fmt.Errorf("erro ao fazer parse do JSON: %w", err)
}
return nil
}
Na pasta internal/models
temos as structs das cidades e do tempo.
package models
type Cidade struct {
Nome string `json:"nome"`
ID int `json:"id"`
Estado string `json:"estado"`
}
type CidadeResponse []Cidade
package models
type Info struct {
Cidade string `json:"cidade"`
Estado string `json:"estado"`
AtualizadoEm string `json:"atualizado_em"`
Clima []Clima `json:"clima"`
}
type Clima struct {
Data string `json:"data"`
Condicao string `json:"condicao"`
CondicaoDesc string `json:"condicao_desc"`
Min int `json:"min"`
Max int `json:"max"`
IndiceUv int `json:"indice_uv"`
}
Na pasta internal/api
temos a requsição sendo feita, chamando o HTTP Client e os models
package api
import (
"fmt"
"cidadeTempo/internal/models"
"cidadeTempo/pkg/client"
)
func BuscarCidades(nomeCidade string) (models.CidadeResponse, error) {
var cidades models.CidadeResponse
c := client.NewClient()
url := fmt.Sprintf("%s/cidade/%s", client.BaseURL, nomeCidade)
err := c.Get(url, &cidades)
if err != nil {
return nil, err
}
return cidades, nil
}
func ExibirCidades(cidades models.CidadeResponse) {
fmt.Println("\nCidades encontradas:")
for _, c := range cidades {
fmt.Printf("Nome: %s, ID: %d, Estado: %s\n", c.Nome, c.ID, c.Estado)
}
}
package api
import (
"fmt"
"cidadeTempo/internal/models"
"cidadeTempo/pkg/client"
)
func BuscarPrevisaoTempo(cidadeID string) (models.Info, error) {
var info models.Info
c := client.NewClient()
url := fmt.Sprintf("%s/clima/previsao/%s", client.BaseURL, cidadeID)
err := c.Get(url, &info)
if err != nil {
return models.Info{}, err
}
return info, nil
}
func ExibirPrevisaoTempo(info models.Info) {
fmt.Printf("\nPrevisão do tempo para %s/%s\n", info.Cidade, info.Estado)
fmt.Printf("Atualizado em: %s\n\n", info.AtualizadoEm)
for _, clima := range info.Clima {
fmt.Printf("Data: %s\n", clima.Data)
fmt.Printf("Condição: %s\n", clima.CondicaoDesc)
fmt.Printf("Temperatura: Mín %d°C - Máx %d°C\n", clima.Min, clima.Max)
fmt.Printf("Índice UV: %d\n\n", clima.IndiceUv)
}
}
Ainda estou aprendendo sobre como estruturar bem o código Golang, mas achei que dessa forma ficou legal para um primeiro projeto.