Migrate to version 2

This commit is contained in:
Victor Lacasse-Beaudoin 2023-05-29 17:58:23 -04:00
parent 4ac3625f45
commit 96f8dfa35e
21 changed files with 777 additions and 746 deletions

134
data/apiclient.go Normal file
View file

@ -0,0 +1,134 @@
package data
import (
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
)
type ApiClient struct {
Key string
Host string
Port int
Protocol string
}
func NewApiClient(key, host, protocol string, port int) *ApiClient {
return &ApiClient{
Key: key,
Host: host,
Port: port,
Protocol: protocol,
}
}
func (a *ApiClient) Call(method, route string, requestBody io.Reader, useKey bool) (*http.Response, error) {
var response *http.Response
endpoint := fmt.Sprintf("%s://%s:%d%s",
a.Protocol, a.Host, a.Port, route,
)
/*
//TODO
log.Println("endpoint: ", endpoint)
*/
// Create client
client := &http.Client{}
// Create request
request, err := http.NewRequest(method, endpoint, requestBody)
if err != nil {
return response, err
}
if useKey {
if a.Key == "" {
return response, fmt.Errorf("Call to API required a key but none was provided. See --help for instructions on providing an API key.")
}
request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", a.Key))
}
if requestBody != nil {
request.Header.Add("Content-Type", "application/json")
}
// Fetch Request
response, err = client.Do(request)
if err != nil {
return response, err
}
return response, nil
}
// GetV4 allows checking for API v4 server health
func (a *ApiClient) GetV4() (string, error) {
var getV4Response struct {
Message string `json:"message"`
}
response, err := a.Call(http.MethodGet, "/v4", nil, true)
if err != nil {
return getV4Response.Message, err
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return getV4Response.Message, err
}
if err := json.Unmarshal(body, &getV4Response); err != nil {
return getV4Response.Message, err
}
if getV4Response.Message == "" {
return getV4Response.Message, errors.New("Could not confirm that API server is up, no response message")
}
return getV4Response.Message, nil
}
/*
func (a *ApiClient) GetMembre(membreID string) (models.Membre, error) {
var getMembreResponse struct {
Message string `json:"message"`
Data struct {
Membre models.Membre `json:"membre"`
} `json:"data"`
}
if membreID == "" {
return getMembreResponse.Data.Membre, errors.New("Veuillez fournir un numéro étudiant à rechercher")
}
response, err := a.Call(http.MethodGet, fmt.Sprintf("/v4/membres/%s", membreID), nil, true)
if err != nil {
return getMembreResponse.Data.Membre, err
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return getMembreResponse.Data.Membre, err
}
if err := json.Unmarshal(body, &getMembreResponse); err != nil {
return getMembreResponse.Data.Membre, err
}
if getMembreResponse.Data.Membre == *new(models.Membre) {
return getMembreResponse.Data.Membre, fmt.Errorf("Ce numéro étudiant ne correspond à aucunE membre")
}
return getMembreResponse.Data.Membre, nil
}
*/

View file

@ -1,97 +1,293 @@
package data
import (
"errors"
"log"
"fmt"
"github.com/spf13/viper"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"git.agecem.com/agecem/bottin-agenda/models"
_ "github.com/jackc/pgx/stdlib"
"github.com/jmoiron/sqlx"
)
var db *gorm.DB
type Membre struct {
gorm.Model
NumEtud string `mapper:"num_etud" json:"num_etud"`
Nom string `mapper:"-" json:"-"`
// DataClient is a postgres client based on sqlx
type DataClient struct {
PostgresConnection PostgresConnection
DB sqlx.DB
}
func OpenDatabase() error {
var err error
type PostgresConnection struct {
User string
Password string
Database string
Host string
Port int
SSL bool
}
var dialector gorm.Dialector
func NewDataClient(connection PostgresConnection) (*DataClient, error) {
client := &DataClient{PostgresConnection: connection}
switch t := viper.GetString("db.type"); t {
case "sqlite":
log.Println("Using driver gorm.io/driver/sqlite")
connectionString := fmt.Sprintf("postgres://%s:%s@%s:%d/%s",
client.PostgresConnection.User,
client.PostgresConnection.Password,
client.PostgresConnection.Host,
client.PostgresConnection.Port,
client.PostgresConnection.Database,
)
db_sqlite_path := viper.GetString("db.sqlite.path")
db, err := sqlx.Connect("pgx", connectionString)
if err != nil {
return nil, err
}
if db_sqlite_path == "" {
log.Fatal("No valid database file found in `--db-sqlite-path` or `db.sqlite.path`.")
client.DB = *db
return client, nil
}
func (d *DataClient) Seed() (int64, error) {
result, err := d.DB.Exec(models.Schema)
if err != nil {
return 0, err
}
rows, err := result.RowsAffected()
if err != nil {
return rows, err
}
return rows, nil
}
/*
// InsertMembres inserts a slice of Membre into a database, returning the amount inserted and any error encountered
func (d *DataClient) InsertMembres(membres []models.Membre) (int64, error) {
var rowsInserted int64
tx, err := d.DB.Beginx()
if err != nil {
tx.Rollback()
return rowsInserted, err
}
for _, membre := range membres {
if membre.ID == "" {
tx.Rollback()
return 0, errors.New("Cannot insert membre with no membre_id")
}
result, err := tx.NamedExec("INSERT INTO membres (id, last_name, first_name, prefered_name, programme_id) VALUES (:id, :last_name, :first_name, :prefered_name, :programme_id);", &membre)
if err != nil {
tx.Rollback()
return 0, err
}
log.Println("Using database file:", db_sqlite_path)
rows, err := result.RowsAffected()
if err != nil {
tx.Rollback()
return 0, err
}
dialector = sqlite.Open(db_sqlite_path)
default:
log.Fatalf("Unrecognized database driver requested (%s).\n", t)
rowsInserted += rows
}
db, err = gorm.Open(dialector, &gorm.Config{})
err = tx.Commit()
if err != nil {
return err
return rowsInserted, err
}
sqlDB, err := db.DB()
if err != nil {
return err
}
return sqlDB.Ping()
return rowsInserted, nil
}
func MigrateDatabase() error {
err := db.AutoMigrate(&Membre{})
return err
func (d *DataClient) InsertProgrammes(programmes []models.Programme) (int64, error) {
var rowsInserted int64
tx, err := d.DB.Beginx()
if err != nil {
tx.Rollback()
return rowsInserted, err
}
for _, programme := range programmes {
if programme.ID == "" {
tx.Rollback()
return 0, errors.New("Cannot insert programme with no programme_id")
}
result, err := tx.NamedExec("INSERT INTO programmes (id, titre) VALUES (:id, :titre);", &programme)
if err != nil {
tx.Rollback()
return 0, err
}
rows, err := result.RowsAffected()
if err != nil {
tx.Rollback()
return 0, err
}
rowsInserted += rows
}
err = tx.Commit()
if err != nil {
return rowsInserted, err
}
return rowsInserted, nil
}
func ReadMembre(num_etud string) (Membre, error) {
var membre Membre
result := db.Limit(1).Find(&membre, "num_etud = ?", num_etud)
func (d *DataClient) GetMembre(membreID string) (models.Membre, error) {
var membre models.Membre
if result.Error != nil {
return membre, result.Error
rows, err := d.DB.Queryx("SELECT * FROM membres WHERE id = $1 LIMIT 1;", membreID)
if err != nil {
return membre, err
}
for rows.Next() {
err := rows.StructScan(&membre)
if err != nil {
return membre, err
}
}
if membre.ID == "" {
return membre, fmt.Errorf("No membre by that id was found")
}
return membre, nil
}
func InsertMembre(membre *Membre) (error, uint) {
// Reset ID before insertion
membre.ID = 0
// Insert membre into database
result := db.Create(&membre)
if result.Error != nil {
return result.Error, 0
func (d *DataClient) UpdateMembreName(membreID, newName string) (int64, error) {
result, err := d.DB.Exec("UPDATE membres SET prefered_name = $1 WHERE id = $2;", newName, membreID)
if err != nil {
return 0, err
}
return nil, membre.ID
rows, err := result.RowsAffected()
if err != nil {
return rows, err
}
return rows, nil
}
*/
/*
func (d *DataClient) Insert(assets []models.Asset) (id int64, err error) {
// Check for minimal required info
for _, asset := range assets {
if asset.Description == "" {
err = errors.New("Cannot insert: At least one asset has no `description` set.")
return
}
}
tx := d.DB.MustBegin()
for _, asset := range assets {
_, err = tx.NamedExec("INSERT INTO assets (description, status, created_at) VALUES (:description, :status, current_timestamp)", asset)
if err != nil {
return
}
}
err = tx.Commit()
return
}
func InsertMembres(membres []*Membre, batch_size int) error {
if len(membres) == 0 {
return errors.New("Cannot insert empty batch of membres.")
func (d *DataClient) List() ([]models.Asset, error) {
// Query the database, storing results in a []Person (wrapped in []interface{})
assets := []models.Asset{}
err := d.DB.Select(&assets, "SELECT * FROM assets WHERE deleted_at IS NULL LIMIT 1000")
if err != nil {
return nil, err
}
for _, membre := range membres {
membre.ID = 0
}
return assets, nil
}
db.CreateInBatches(&membres, batch_size)
// RecordEvent allows inserting into events when an asset or a tag is modified
// or deleted.
func (d *DataClient) RecordEvent(assetID, tagID int64, content string) error {
event := models.Event{
AssetID: assetID,
TagID: tagID,
Content: content,
}
_, err := d.DB.NamedExec("INSERT INTO events (asset_id, tag_id, at, content) VALUES (:asset_id, :tag_id, current_timestamp, :content);", event)
if err != nil {
return err
}
return nil
}
func (d *DataClient) Delete(assetIDs []int64) ([]int64, error) {
var rows []int64
tx := d.DB.MustBegin()
for _, assetID := range assetIDs {
result, err := d.DB.Exec("UPDATE assets SET deleted_at = current_timestamp WHERE id = $1 AND deleted_at IS NULL;", assetID)
if err != nil {
return rows, err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return rows, err
}
if rowsAffected != 0 {
rows = append(rows, assetID)
}
}
err := tx.Commit()
if err != nil {
return rows, err
}
for _, assetID := range assetIDs {
d.RecordEvent(assetID, -1, fmt.Sprintf("Asset %d deleted.", assetID))
}
return rows, nil
}
func (d *DataClient) UpdateAssetDescription(assetID int64, description string) (int64, error) {
result, err := d.DB.Exec("UPDATE assets SET description = $1 WHERE id = $2", description, assetID)
if err != nil {
return 0, err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return 0, err
}
if rowsAffected != 0 {
return 0, errors.New("Nothing to do")
}
return rowsAffected, nil
}
func (d *DataClient) UpdateAssetStatus(assetID int64, status string) (int64, error) {
result, err := d.DB.Exec("UPDATE assets SET status = $1 WHERE id = $2", status, assetID)
if err != nil {
return 0, err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return 0, err
}
if rowsAffected != 0 {
return 0, errors.New("Nothing to do")
}
return rowsAffected, nil
}
*/