Pular para o conteúdo principal

victorstein.dev

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.