Migrate to version 2
This commit is contained in:
parent
4ac3625f45
commit
96f8dfa35e
21 changed files with 777 additions and 746 deletions
134
data/apiclient.go
Normal file
134
data/apiclient.go
Normal 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
|
||||
}
|
||||
*/
|
310
data/data.go
310
data/data.go
|
@ -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
|
||||
}
|
||||
*/
|
||||
|
|
Reference in a new issue