Bug report
Summary: There is a problem in DataGerry. When requesting the creation of an object, DataGerry crashes.
Steps to reproduce:
-
Make a request to DataGerry API from a library written in Go.
-
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},
},
})
- 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
}
- 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