How can I write a nested Slice in go into a postgres DB?

Viewed 123

This might look like a stupid question but i am struggling hard, to get this working. Here are a few details, I want to share with you, so you might have a better understanding of what I mean. First of all, i'm just trying to learn coding and I try to implement what I learnt in a small project for myself. Please bear with me :)

Current Setup

I am fetching an external API Endpoint, where the json looks like this:

{
    "orders": [
        {
            "input": {
                "amount": "1",
                "currency": "EUR",
                "type": "bank_account",
                "iban": "DEXXX"
            },
            "output": {
                "amount": "2",
                "currency": "CHF",
                "type": "paypal",
                "paypal_recipient": "example@example.org"
            },
            "id": "8e150ca6",
            "timestamp_created": "2021-06-06T20:34:19.827Z",
            "timestamp_executed": "2021-06-07T06:43:57Z",
            "timestamp_awaiting_payment_since": "2021-06-06T20:34:28.921Z",
            "timestamp_payment_received": "2021-06-07T06:43:57Z",
            "payment_details": {
                "iban": "DEXXX",
                "recipient_name": "Company1",
                "recipient_postal_address": [
                    "Maxstreet",
                    "1000 Berlin                                                                  ",
                    "Germany"
                ],
                "reference": "Order XY",
                "swift_bic": "BCNNXX",
                "type": "bank_account"
            },
            "price_breakdown": {
                "customer_trading_fee": {
                    "amount": "1.56",
                    "currency": "EUR"
                },
                "non-verified_fee": {
                    "amount": "0.79",
                    "currency": "EUR"
                },
                "partner_fee": {
                    "amount": "3.4",
                    "currency": "EUR"
                }
            }
        },
        {
            "input": {.....
        },
    "pagination": {
        "current": 1,
        "first": 1,
        "last": 1
    }
}

I have several Structs, which does reprint, how the json is built:

    Type OrderStructDB struct {
    gorm.Model
    OrderID                       string    `gorm:"unique;not null"`
    TimestampCreated              time.Time `json:"timestamp_created"`
    TimestampCancelled            time.Time `json:"timestamp_cancelled"`
    TimestampAwaitingPaymentSince time.Time `json:"timestamp_awaiting_payment_since"`
    InputStructID                 uint      `gorm:"foreignKey:InputStruct"`
    InputStruct                   InputStruct
    OutputStructID                uint `gorm:"foreignKey:OutputStruct"`
    OutputStruct                  OutputStruct
    PriceBreakdownStructID        uint `gorm:"foreignKey:PriceBreakdownStruct"`
    PriceBreakdownStruct          PriceBreakdownStruct
    //PaymentDetailsStructID        uint `gorm:"foreignKey:PaymentDetailsStruct"`
    //PaymentDetailsStruct          PaymentDetailsStruct

}

type OrderStruct struct {
    Input                         InputStruct          `json:"input" ,gorm:"foreignKey:ID"`
    Output                        OutputStruct         `json:"output" ,gorm:"foreignKey:ID"`
    OrderID                       string               `json:"id" ,gorm:"unique;not null"`
    TimestampCreated              time.Time            `json:"timestamp_created"`
    TimestampCancelled            time.Time            `json:"timestamp_cancelled"`
    TimestampAwaitingPaymentSince time.Time            `json:"timestamp_awaiting_payment_since"`
    PaymentDetails                PaymentDetailsStruct `json:"payment_details" ,gorm:"embedded;embeddedPrefix:paymentdetails_,foreignKey:ID"`
    PriceBreakdown                PriceBreakdownStruct `json:"price_breakdown" ,gorm:"embedded;embeddedPrefix:pricebreakdown_,foreignKey:ID"`
    MessageToSign                 MessageToSignStruct  `json:"message_to_sign,omitempty"`

    //PaymentDetailsStructID        uint `gorm:"foreignKey:PaymentDetailsStruct"`
    //PaymentDetailsStruct          PaymentDetailsStruct

}

type InputStruct struct {
    Amount   string `json:"amount"`
    Currency string `json:"currency"`
    Type     string `json:"type"`
    Iban     string `json:"iban"`
    gorm.Model
}
type OutputStruct struct {
    Amount        string `json:"amount"`
    Currency      string `json:"currency"`
    Type          string `json:"type"`
    CryptoAddress string `json:"crypto_address"`
    gorm.Model
}
type PaymentDetailsStruct struct {
    Iban                   string   `json:"iban"`
    RecipientName          string   `json:"recipient_name"`
    RecipientPostalAddress []string `json:"recipient_postal_address"`
    Reference              string   `json:"reference"`
    SwiftBic               string   `json:"swift_bic"`
    Type                   string   `json:"type"`
}
type CustomerTradingFeeStruct struct {
    Amount   string `json:"amount"`
    Currency string `json:"currency"`
    gorm.Model
}
type PriceBreakdownStruct struct {
    CustomerTradingFeeID uint                     `gorm:"foreignKey:CustomerTradingFee"`
    CustomerTradingFee   CustomerTradingFeeStruct `json:"customer_trading_fee" ,gorm:"references:PriceBreakdownID"`
    NonVerifiedFeeID     uint                     `gorm:"foreignKey:NonVerifiedFee"`
    NonVerifiedFee       NonVerifiedFeeStruct     `json:"non-verified_fee" ,gorm:"references:PriceBreakdownID"`
    PartnerFeeID         uint                     `gorm:"foreignKey:ParnterFee"`
    PartnerFee           PartnerFeeStruct         `json:"partner_fee" ,gorm:"references:PriceBreakdownID"`
    gorm.Model
}

type MessageToSignStruct struct {
    Body                   string `json:"body"`
    SignatureSubmissionURL string `json:"signature_submission_url"`
}

type NonVerifiedFeeStruct struct {
    Amount   string `json:"amount"`
    Currency string `json:"currency"`
    gorm.Model
}

type PartnerFeeStruct struct {
    Amount   string `json:"amount"`
    Currency string `json:"currency"`
    gorm.Model
}

type OrdersStruct struct {
    Orders     []OrderStruct    `json:"orders"`
    Pagination PaginationStruct `json:"pagination"`
}

type PaginationStruct struct {
    Current int `json:"current"`
    First   int `json:"first"`
    Last    int `json:"last"`
}

What I have managed so far is:

  • I can Unmarshal the Rest Response into my current Structs
  • I can print all Orders on to the console
  • I can Query a single Order and map it directly into "OrderStruct"
  • I can map "OrderStruct" into "OrderStructDB" and write that single Order into the Database (It also works with the database relations so far)

Where I Am struggling

I'm really not sure, how i can use the "OrdersStruct", with a nested Slice of "OrderStruct" and write the same details to the database as described in "OrderStructDB". I tried a few variants but i couldn't get it working after hours.

Here's what the codepart looks like for getting a single order and write it to the db:

func getOrder() (Order OrderStruct) {

    conf := getToken()
    client := conf.Client(context.Background())

    res, err := client.Get("https://api.endpoint/v1/orders/baa1f5f4")
    if err != nil {
        log.Fatal(err.Error())
    }
    body, err := ioutil.ReadAll(res.Body)
    if err != nil {
        log.Fatal(err.Error())
    }

    var formattedBody OrderStruct
    err = json.Unmarshal(body, &formattedBody)
    if err != nil {
        log.Fatal(err.Error())
    }

    // Create DB Entry Logic and Struct
    // Connect DB
    db := helpers.ConnectDB()
    order := OrderStructDB{
        OrderID:                       formattedBody.OrderID,
        InputStruct:                   formattedBody.Input,
        OutputStruct:                  formattedBody.Output,
        PriceBreakdownStruct:          formattedBody.PriceBreakdown,
        TimestampCreated:              formattedBody.TimestampCreated,
        TimestampCancelled:            formattedBody.TimestampCancelled,
        TimestampAwaitingPaymentSince: formattedBody.TimestampAwaitingPaymentSince,
    }

    err = db.Create(&order).Error
    if err, ok := err.(*pq.Error); ok && err.Code.Name() == "unique_violation" {
        helpers.HandleErr(err)
        // handle error
    }

    defer db.Close()

    return formattedBody
}

And here's the snippet, on where I fetch all the orders, but have not managed to get it into the database. I've deleted all attempts, trying to get it into the db tho:

func getOrders() (Orders OrdersStruct) {

    conf := getToken()
    client := conf.Client(context.Background())

    res, err := client.Get("https://api.endpoint/v1/orders")
    if err != nil {
        log.Fatal(err.Error())
    }

    body, err := ioutil.ReadAll(res.Body)
    if err != nil {
        log.Fatal(err.Error())
    }

    var formattedBody OrdersStruct
    err = json.Unmarshal(body, &formattedBody)
    helpers.HandleErr(err)

    // Connect DB
    db := helpers.ConnectDB()

    //Define the StructMapping, my problem lies probably here. How to map the details of slice struct into a struct, i can map into the databse


    err = db.Create(&orders).Error
    if err, ok := err.(*pq.Error); ok && err.Code.Name() == "unique_violation" {
        helpers.HandleErr(err)
        // handle error
    }

    defer db.Close()

    return formattedBody
}

I've added a small comment to the section, i believe i have the error. I want to put the elements of the OrdersStruct into the OrderStructDB, as I want the information stored to be the same as the single order one. But I can't manage to get it nearly working. I tried several methods like:

    orders := OrderStructDB{
        OrderID:                       formattedBody.Orders.OrderID,
        InputStruct:                   formattedBody.Orders.Input,
        OutputStruct:                  formattedBody.Orders.Output,
        PriceBreakdownStruct:          formattedBody.Orders.PriceBreakdown,
        TimestampCreated:              formattedBody.Orders.TimestampCreated,
        TimestampCancelled:            formattedBody.Orders.TimestampCancelled,
        TimestampAwaitingPaymentSince: formattedBody.Orders.TimestampAwaitingPaymentSince,
    }

But i keep failing here. I am not sure, what is the best way to achieve that. Do I maybe need to create also a new OrdersStructDB Type. I thought of bulk inserting, but do I maybe need to loop through the slice and create my new slice, without the "top-level-elements" of the json?

I'd be so thankful to get a few insights and hints, on where my brain is stopping to work, where i am thinking in the wrong direction.

Happy to share also more Code if that would be helpful for you.

Thanks and thankfully,

a guy who tries to learn to code

0 Answers
Related