Crashing of Datagerry while creating a new object throught API in Go

Bug report

Summary: There is a problem in DataGerry. When requesting the creation of an object, DataGerry crashes.

Steps to reproduce:

  1. Make a request to DataGerry API from a library written in Go.

  2. Specify the necessary parameters.

err := dag.CreateObject(datagerry.CreateObjectRequestArgs{
    TypeID:   employer_type_id,
    AuthorID: author_id,
    Active:   true,
    Fields: []datagerry.CreateObjectRequestField{
        {Name: env.DataGerryEmployersTypeNameFieldName, Value: name},
        {Name: env.DataGerryEmployersTypeEmailFieldName, Value: email},
        {Name: env.DataGerryEmployersTypeCompanyFieldName, Value: company_id},
    },
})
  1. Wait for the response. Returns 500 error
{
  "description": "The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.",
  "joke": "Are you nuts?",
  "message": "",
  "response": "Internal Server Error: http://192.168.77.99/rest/objects/",
  "status": 500
}
  1. Observe the crashing and unable to use DataGerry: img

There is an error from docker container


There is a needed code of library

schemas.go

package datagerry

import "time"

type CreateObjectRequestField struct {
	Name  string      `validate:"required"`
	Value interface{} `validate:"omitempty"`
}

type CreateObjectRequestArgs struct {
	TypeID       int                        `json:"type_id" validate:"required"`
	Status       bool                       `json:"status" validate:"omitempty"`
	Version      string                     `json:"version" validate:"omitempty"`
	AuthorID     int                        `json:"author_id" validate:"required"`
	CreationTime *time.Time                 `json:"creation_time" validate:"omitempty"`
	LastEditTime *time.Time                 `json:"last_edit_time" validate:"omitempty"`
	Active       bool                       `json:"active" validate:"omitempty"`
	Fields       []CreateObjectRequestField `json:"fields" validate:"required"`
}

api.go

package datagerry

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"time"
)

type DataGerryAPI struct {
	Login                 string
	Password              string
	Endpoint              string
	BearerToken           string
	BearerTokenValidUntil int64
}

type Query struct {
	Match map[string]string `json:"$match"`
}

func (d *DataGerryAPI) UpdateBearerToken() (string, error) {
	if d.BearerTokenValidUntil > time.Now().Unix() {
		return d.BearerToken, nil
	}

	req, err := http.NewRequest("POST", d.Endpoint+"/auth/login", nil)
	if err != nil {
		return "", err
	}

	jsonData := map[string]string{"user_name": d.Login, "password": d.Password}
	jsonValue, err := json.Marshal(jsonData)
	if err != nil {
		return "", err
	}

	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Accept", "application/json")

	req.Body = io.NopCloser(bytes.NewBuffer(jsonValue))

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return "", err
	}

	errorFromCode := HTTPCodeToError(resp.StatusCode)
	if errorFromCode != nil {
		return "", errorFromCode
	}

	var authResponse DataGerryAuthResponse
	err = json.NewDecoder(resp.Body).Decode(&authResponse)
	if err != nil {
		return "", err
	}

	d.BearerToken = authResponse.Token
	d.BearerTokenValidUntil = int64(authResponse.TokenExpire)

	return d.BearerToken, nil
}

func (d *DataGerryAPI) CreateObject(object CreateObjectRequestArgs) error {

	jsonValue, err := json.Marshal(object)
	if err != nil {
		return err
	}
	req, err := http.NewRequest("POST", d.Endpoint+"/objects/", bytes.NewBuffer(jsonValue))
	if err != nil {
		return err
	}

	token, err := d.UpdateBearerToken()
	if err != nil {
		return err
	}

	req.Header.Add("Authorization", "Bearer "+token)
	req.Header.Add("Content-Type", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	fmt.Println(string(body))

	errorFromCode := HTTPCodeToError(resp.StatusCode)
	if errorFromCode != nil {
		return errorFromCode
	}

	return nil
}

func (d *DataGerryAPI) GetObjectsByPublicID(public_id_of_type string) (DataGerryObjectsResponse, error) {
	var objects DataGerryObjectsResponse

	req, err := http.NewRequest("GET", d.Endpoint+"/objects/", nil)
	if err != nil {
		return objects, err
	}

	q := req.URL.Query()
	q.Add("filter", fmt.Sprintf(`{"type_id": %s}`, public_id_of_type))
	req.URL.RawQuery = q.Encode()

	token, err := d.UpdateBearerToken()
	if err != nil {
		return objects, err
	}

	req.Header.Add("Authorization", "Bearer "+token)

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return objects, err
	}

	errorFromCode := HTTPCodeToError(resp.StatusCode)
	if errorFromCode != nil {
		return objects, errorFromCode
	}

	err = json.NewDecoder(resp.Body).Decode(&objects)
	if err != nil {
		return objects, err
	}

	return objects, nil
}

errors.go

package datagerry

// 400 Bad Request – Resource could not be inserted.
type DataGerryResourceCouldNotBeInsertedError struct {
	Message string `json:"message"`
}

func (e *DataGerryResourceCouldNotBeInsertedError) Error() string {
	return e.Message
}

// 403 Forbidden – No right to update a existing object of this type.
type DataGerryNoRightToUpdateAExistingObjectOfThisTypeError struct {
	Message string `json:"message"`
}

func (e *DataGerryNoRightToUpdateAExistingObjectOfThisTypeError) Error() string {
	return e.Message
}

// 404 Not Found – No resource found.
type DataGerryNoResourceFoundError struct {
	Message string `json:"message"`
}

func (e *DataGerryNoResourceFoundError) Error() string {
	return e.Message
}

// 500 Internal Server Error – Something went wrong during update.
type DataGerrySomethingWentWrongDuringUpdateError struct {
	Message string `json:"message"`
}

func (e *DataGerrySomethingWentWrongDuringUpdateError) Error() string {
	return e.Message
}

func HTTPCodeToError(code int) error {
	switch code {
	case 400:
		return &DataGerryResourceCouldNotBeInsertedError{
			Message: "Resource could not be inserted.",
		}
	case 403:
		return &DataGerryNoRightToUpdateAExistingObjectOfThisTypeError{
			Message: "No right to update a existing object of this type.",
		}
	case 404:
		return &DataGerryNoResourceFoundError{
			Message: "No resource found.",
		}
	case 500:
		return &DataGerrySomethingWentWrongDuringUpdateError{
			Message: "Something went wrong during update.",
		}
	default:
		return nil
	}
}

main.go

package main

import (
	"fmt"
	"strconv"

	"github.com/###/internal/env"
	"github.com/###/pkg/datagerry"
)

func main() {

	login := env.DataGerryLogin
	password := env.DataGerryPassword
	endpoint := env.DataGerryEndpoint

	if login == "" {
		panic("DATAGERRY_LOGIN is not set")
	}

	if password == "" {
		panic("DATAGERRY_PASSWORD is not set")
	}

	if endpoint == "" {
		panic("DATAGERRY_ENDPOINT is not set")
	}

	dag := datagerry.DataGerryAPI{
		Login:    login,
		Password: password,
		Endpoint: endpoint,
	}

	var name, email, remote string
	fmt.Print("Enter Employer name: ")
	fmt.Scanln(&name)
	fmt.Print("Enter Employer email: ")
	fmt.Scanln(&email)

	companies, err := dag.GetObjectsByPublicID(env.DataGerryCompanyTypePublicID)
	if err != nil {
		panic(err)
	}

	fmt.Println("Enter Employer company: ")
	for k, company := range companies.Results {
		for _, v := range company.Fields {
			if v.Name == env.DataGerryCompanyTypeNameFieldName {
				fmt.Printf("%d. %s\n", k+1, v.Value)
			}
		}
	}
	answer := 0
	for {
		fmt.Scanln(&answer)
		if answer > 0 && answer <= len(companies.Results) {
			break
		}
		fmt.Println("Enter correct company id")
	}
	company_id := companies.Results[answer-1].PublicID
	employer_type_id, err := strconv.Atoi(env.DataGerryEmployersTypePublicID)
	if err != nil {
		panic(err)
	}

	author_id, err := strconv.Atoi(env.DataGerryAuthorID)
	if err != nil {
		panic(err)
	}

	fmt.Print("Is remote work? (y/n): ")
	fmt.Scanln(&remote)
	if remote == "y" {
		err := dag.CreateObject(datagerry.CreateObjectRequestArgs{
			TypeID:   employer_type_id,
			AuthorID: author_id,
			Active:   true,
			Fields: []datagerry.CreateObjectRequestField{
				{Name: env.DataGerryEmployersTypeNameFieldName, Value: name},
				{Name: env.DataGerryEmployersTypeEmailFieldName, Value: email},
				{Name: env.DataGerryEmployersTypeCompanyFieldName, Value: company_id},
			},
		})
		if err != nil {
			panic(err)
		}

	} else if remote == "n" {
		// Create Employer, then ask for unused table and assign with user
	}
}

.env like this

### DataGerry
DATAGERRY_LOGIN=admin
DATAGERRY_PASSWORD=admin
DATAGERRY_ENDPOINT=http://192.168.77.99/rest
DATAGERRY_AUTHOR_ID=1
## Employers type
DATAGERRY_EMPLOYERS_TYPE__PUBLIC_ID=11
DATAGERRY_EMPLOYERS_TYPE__NAME_FIELD_NAME=text-39929
DATAGERRY_EMPLOYERS_TYPE__EMAIL_FIELD_NAME=text-27614
DATAGERRY_EMPLOYERS_TYPE__COMPANY_FIELD_NAME=ref-25151
## Company type
DATAGERRY_COMPANY_TYPE__PUBLIC_ID=12
DATAGERRY_COMPANY_TYPE__NAME_FIELD_NAME=text-19742
DATAGERRY_COMPANY_TYPE__ADDRESS__STREET_FIELD_NAME=text-29607
DATAGERRY_COMPANY_TYPE__ADDRESS__HOUSE_NUMBER_FIELD_NAME=text-11283
DATAGERRY_COMPANY_TYPE__ADDRESS__LOCATION_FIELD_NAME=text-52606
DATAGERRY_COMPANY_TYPE__ADDRESS__POSTAL_CODE_FIELD_NAME=text-36017
## Table type
DATAGERRY_TABLE_TYPE__PUBLIC_ID=10
DATAGERRY_TABLE_TYPE__NUMBER_FIELD_NAME=text-72385
DATAGERRY_TABLE_TYPE__USER_ASSIGNMENT_FIELD_NAME=ref-83646
DATAGERRY_TABLE_TYPE__DESCRIPTION_FIELD_NAME=textarea-39911

Error raises when executing CreateObject method

HI @D41n3l3k00 ,
thanks for the detailed infos about the error, we will have a look at it.

BR Adnan