项目作者: marcopollivier

项目描述 :
Repositório do curso feito em parceria com a @digitalinnovationone
高级语言: Go
项目地址: git://github.com/marcopollivier/dio-expert-session-finance.git
创建时间: 2020-04-21T22:33:05Z
项目社区:https://github.com/marcopollivier/dio-expert-session-finance

开源协议:MIT License

下载


dio-expert-session-finance

Pré Desenvolvimento

  1. Vamos criar um projeto no Github chamado dio-expert-session-finance

    • Depois voltamos aqui para configurar mais nosso repositório. Por enquanto vamos ter apenas arquivos básicos
      • .gitignore
      • README.md
  2. Depois do nosso projeto criado no Github, vamos criar um projeto Go básico.

    • $ mkdir dio-expert-session-finance
    • $ cd dio-expert-session-finance
    • $ go mod init github.com/marcopollivier/dio-expert-session-finance

      Esses comandos vão criar dentro da pasta dio-expert-session-finance um arquivo go.mod.
      E esse arquivo vai ser a base do nosso projeto.

      Depois disso, você já pode abrir seu projeto na IDE de sua escolha

    • GoLand
    • VSCode
    • Vim
    1. Agora que temos o nosso projeto funcionando corretamente, vamos criar um Hello, World! para
      termos certeza que tudo está de acordo com o que esperávamos.

      • Dentro da pasta dio-expert-session-finance/cmd/server/ vamos criar o arquivo main.go
      1. package main
      2. import "fmt"
      3. func main() {
      4. fmt.Print("Olá, Mundo!")
      5. }

      Com isso já vemos que nosso ambiente está ok e funcional, mas ainda não é isso que queremos exatamente, correto?

  3. Mas precisamos evoluir nosso código para começar a tomar forma de uma API.

    1. package main
    2. import (
    3. "fmt"
    4. "net/http"
    5. )
    6. func main() {
    7. http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    8. fmt.Fprintf(w, "Olá. Bem vindo a minha página!")
    9. })
    10. http.ListenAndServe(":8080", nil)
    11. }

    $ curl curl http://localhost:8080/

  4. Vamos começar a pensar no nosso modelo. Nós queremos criar um sistema pras nossas finanças pessoais.
    Para isso vamos pensar num modelo para trabalharmos.

    5.1. Nosso modelo financeiro vai ser bem simples, mas flexível o suficiente para evoluirmos
    no futuro

    • Titulo
    • Valor
    • Tipo (ENTRADA, SAIDA)
    • Data

      5.2. Vamos pensar que, antes de mais nada, queremos retornar um JSON com esse modelo.

      1. package main
      2. import (
      3. "encoding/json"
      4. "net/http"
      5. "time"
      6. )
      7. func main() {
      8. http.HandleFunc("/transactions", getTransactions)
      9. _ = http.ListenAndServe(":8080", nil)
      10. }
      11. type Transaction struct {
      12. Title string
      13. Amount float32
      14. Type int //0. entrada 1. saida
      15. CreatedAt time.Time
      16. }
      17. type Transactions []Transaction
      18. func getTransactions(w http.ResponseWriter, r *http.Request) {
      19. if r.Method != "GET" {
      20. w.WriteHeader(http.StatusMethodNotAllowed)
      21. return
      22. }
      23. w.Header().Set("Content-type", "application/json")
      24. layout := "2006-01-02T15:04:05"
      25. salaryReceived, _ := time.Parse(layout, "2020-04-05T11:45:26")
      26. paidElectricityBill, _ := time.Parse(layout, "2020-04-12T22:00:00")
      27. var transactions = Transactions{
      28. Transaction{
      29. Title: "Salário",
      30. Amount: 1200.0,
      31. Type: 0,
      32. CreatedAt: salaryReceived,
      33. },
      34. Transaction{
      35. Title: "Conta de luz",
      36. Amount: 100.0,
      37. Type: 1,
      38. CreatedAt: paidElectricityBill,
      39. },
      40. }
      41. _ = json.NewEncoder(w).Encode(transactions)
      42. }
      1. type Tction struct {
      2. Title string `json:"title"`
      3. Amount float32 `json:"amount"`
      4. Type int `json:"type"` //0. entrada 1. saida
      5. CreatedAt time.Time `json:"created_at"`
      6. }
  5. E agora vamos fazer um método de inserção (POST)

    1. http.HandleFunc("/transactions/create", createATransaction)
    2. ...
    3. func createATransaction(w http.ResponseWriter, r *http.Request) {
    4. if r.Method != "POST" {
    5. w.WriteHeader(http.StatusMethodNotAllowed)
    6. return
    7. }
    8. var res = Transactions{}
    9. var body, _ = ioutil.ReadAll(r.Body)
    10. _ = json.Unmarshal(body, &res)
    11. fmt.Println(res)
    12. fmt.Println(res[0].Title)
    13. fmt.Println(res[1].Title)
    14. }
    1. [
    2. {
    3. "title": "Salário",
    4. "amount": 1200,
    5. "type": 0,
    6. "created_at": "2020-04-05T11:45:26Z"
    7. }
    8. ]

    ```shell script
    $ curl -X POST ‘http://localhost:8080/transactions/create‘ \
    -H ‘Content-Type: application/json’ \
    -d ‘[

    1. {
    2. "title": "Salário",
    3. "amount": 1200,
    4. "type": 0,
    5. "created_at": "2020-04-05T11:45:26Z"
    6. }
    7. ]'

    ```

  6. Hora de refatorar. Vamos colocar em memória e isolar em arquivos e pacotes

  7. Vamos começar a pensar em monitoramento? Então vamos criar um arquivo de Healthcheck

    1. package actuator
    2. import (
    3. "encoding/json"
    4. "net/http"
    5. )
    6. func Health(responseWriter http.ResponseWriter, request *http.Request) {
    7. responseWriter.Header().Set("Content-Type", "application/json")
    8. profile := HealthBody{"alive"}
    9. returnBody, err := json.Marshal(profile)
    10. if err != nil {
    11. http.Error(responseWriter, err.Error(), http.StatusInternalServerError)
    12. return
    13. }
    14. _, err = responseWriter.Write(returnBody)
    15. if err != nil {
    16. http.Error(responseWriter, err.Error(), http.StatusInternalServerError)
    17. return
    18. }
    19. }
    20. type HealthBody struct {
    21. Status string
    22. }
  8. Vamos aproveitar já que criamos um util e escrever um teste unitário para ele

    1. package util
    2. import (
    3. "testing"
    4. )
    5. func TestStringToDate(testing *testing.T) {
    6. var convertedTime = StringToTime("2019-02-12T10:00:00")
    7. if convertedTime.Year() != 2019 {
    8. testing.Errorf("Converter StringToDate is failed. Expected Year %v, got %v", 2019, convertedTime.Year())
    9. }
    10. if convertedTime.Month() != 2 {
    11. testing.Errorf("Converter StringToDate is failed. Expected Month %v, got %v", 2, convertedTime.Month())
    12. }
    13. if convertedTime.Hour() != 10 {
    14. testing.Errorf("Converter StringToDate is failed. Expected Hour %v, got %v", 10, convertedTime.Hour())
    15. }
    16. }
  9. Vamos começar a pensar em um pouco de qualidade de código também

    10.1. Para fazer análise de código estática, vamos instalar a dependencia do lint

    • $ go get -u golang.org/x/lint/golint

    10.2. E vamos executar nossos primeiros comandos relacionados

    • $ go test ./...
    • $ golint ./...
  10. Vamos configurar o CircleCI

  11. Vamos colocar métricas na nossa aplicação

    1. $ go get github.com/prometheus/client_golang/prometheus
    2. $ go get github.com/prometheus/client_golang/prometheus/promauto
    3. $ go get github.com/prometheus/client_golang/prometheus/promhttp
  12. Para os passos seguintes, nós vamos fazer uma integração com um BD qualquer.
    Para isso, vamos subir uma imagem Docker do Postgres pra poder fazer o nosso teste.

    Vamos subir o BD via Docker Compose

    host: localhost
    user: postgres
    pass: postgres
    DB: diodb

    1. version: "3"
    2. services:
    3. postgres:
    4. image: postgres:9.6
    5. container_name: "postgres"
    6. environment:
    7. - POSTGRES_DB=diodb
    8. - POSTGRES_USER=postgres
    9. - TZ=GMT
    10. volumes:
    11. - "./data/postgres:/var/lib/postgresql/data"
    12. ports:
    13. - 5432:5432
    1. prepare-tests:
    2. docker-compose -f .devops/postgres.yml up -d
  13. Já com o banco acessível via Docker, vamos criar a base que utilizaremos no nosso teste

    1. CREATE TABLE transactions (
    2. id SERIAL PRIMARY KEY,
    3. title varchar(100),
    4. amount decimal,
    5. type smallint,
    6. installment smallint,
    7. created_at timestamp
    8. );
    9. insert into transactions (title, amount, type, installment, created_at)
    10. values ('Freela', '100.0', 0, 1, '2020-04-10 04:05:06');
    11. select * from transactions;
  14. Agora com a estrutra de banco criada, vamos fazer as alterações necessárias no código. E a primeira delas é baixar a
    dependência do driver do Postgres.

    Lista de SQLDrivers disponíveis

    Execute o seguinte comando dentro da pasta do projeto

    shell script $ go get -u github.com/lib/pq

  15. E esse é o código que vai manipular as informações do banco de fato

    1. package postgres
    2. import (
    3. "database/sql"
    4. "fmt"
    5. "github.com/marcopollivier/dio-expert-session-pre-class/model/transaction"
    6. _ "github.com/lib/pq"
    7. )
    8. const (
    9. host = "localhost"
    10. port = 5432
    11. user = "postgres"
    12. password = "postgres"
    13. dbname = "diodb"
    14. )
    15. func connect() *sql.DB {
    16. psqlInfo := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbname)
    17. var db, _ = sql.Open("postgres", psqlInfo)
    18. return db
    19. }
    20. func Create(transaction transaction.Transaction) int {
    21. var db = connect()
    22. defer db.Close()
    23. var sqlStatement = `INSERT INTO transactions (title, amount, type, installment, created_at)
    24. VALUES ($1, $2, $3, $4, $5)
    25. RETURNING id;`
    26. var id int
    27. _ = db.QueryRow(sqlStatement,
    28. transaction.Title,
    29. transaction.Amount,
    30. transaction.Type,
    31. transaction.Installment,
    32. transaction.CreatedAt).Scan(&id)
    33. fmt.Println("New record ID is:", id)
    34. return id
    35. }
    36. func main() {
    37. //log.Fatal(http.Init())
    38. //"Outro freela", 400.0, 0, 1, "2020-04-20 12:00:06"
    39. postgres.Create(transaction.Transaction{Title: "Outro freela", Amount: 600.0, Type: 0, Installment: 1, CreatedAt: util.StringToTime("2020-04-20T12:00:06")})
    40. }
    41. func FetchAll() transaction.Transactions {
    42. var db = connect()
    43. defer db.Close()
    44. rows, _ := db.Query("SELECT title, amount, type, installment, created_at FROM transactions")
    45. defer rows.Close()
    46. var transactionSlice []transaction.Transaction
    47. for rows.Next() {
    48. var transaction transaction.Transaction
    49. _ = rows.Scan(&transaction.Title,
    50. &transaction.Amount,
    51. &transaction.Type,
    52. &transaction.Installment,
    53. &transaction.CreatedAt)
    54. transactionSlice = append(transactionSlice, transaction)
    55. }
    56. return transactionSlice
    57. }
    58. fmt.Print(postgres.FetchAll())