It was my intention to use the HTTP status codes both in the header and the body of the two response structs. Bu that without setting the status code twice as function parameter and again for the struct to avoid redundancy.
The parameter response of JSON() is an interface to allow both structs to be accepted. The compiler throws the following exception:
response.Status undefined (type interface {} has no field or method Status)
because the response field must not have a status attribute. Is there an alternative way to avoid setting the status code twice?
type Response struct {
Status int `json:"status"`
Data interface{} `json:"data"`
}
type ErrorResponse struct {
Status int `json:"status"`
Errors []string `json:"errors"`
}
func JSON(rw http.ResponseWriter, response interface{}) {
payload, _ := json.MarshalIndent(response, "", " ")
rw.WriteHeader(response.Status)
...
}
The type response in rw.WriteHeader(response.Status) is interface{}. In Go, you need to explicitly assert the type of the underlying struct and then access the field:
func JSON(rw http.ResponseWriter, response interface{}) {
payload, _ := json.MarshalIndent(response, "", " ")
switch r := response.(type) {
case ErrorResponse:
rw.WriteHeader(r.Status)
case Response:
rw.WriteHeader(r.Status)
}
...
}
A better and the preferred way to do this however is to define a common interface for your responses, that has a method for getting the status of the response:
type Statuser interface {
Status() int
}
// You need to rename the fields to avoid name collision.
func (r Response) Status() int { return r.ResStatus }
func (r ErrorResponse) Status() int { return r.ResStatus }
func JSON(rw http.ResponseWriter, response Statuser) {
payload, _ := json.MarshalIndent(response, "", " ")
rw.WriteHeader(response.Status())
...
}
And it's better to rename Response to DataResponse and ResponseInterface to Response, IMO.
Interfaces don't have attributes, so you need to extract the struct from the interface. To do this you use a type assertion
if response, ok := response.(ErrorResponse); ok {
rw.WriteHeader(response.Status)
...
Related
I have a function to talk to my REST server as follows
func send<T: Decodable>(_ request: HTTPSClient.Request) async throws -> T {
do {
let (data, status): (Data, HTTPSClient.StatusCode) = try await request.send()
if status.responseType != .success { // success means 2xx
throw try JSONDecoder().decode(CustomError.self, from: data)
}
return try JSONDecoder().decode(T.self, from: data)
} catch {
// some error handling here
}
}
And is called as follows
public struct API1Response: Codable {
// some fields
}
...
let response: API1Response = try await self.send(httpsRequest)
Now I have a special use case where the response needs to be JSON decoded into different structs based on the HTTP response status code (2xx).
For example, if the response code is 200 OK, it needs to be decoded into struct APIOKResponse. If the response code is 202 Accepted, it needs to be decoded into struct APIAcceptedResponse and so on.
I want to write a similar function as above which can support multiple response types
I have written the below function, it does not throw any compilation errors
func send<T: Decodable>(_ request: HTTPSClient.Request, _ types: [HTTPSClient.StatusCode: T.Type]) async throws -> T {
do {
let (data, status): (Data, HTTPSClient.StatusCode) = try await request.send()
if status.responseType != .success { // success means 2xx
throw try JSONDecoder().decode(CustomError.self, from: data)
}
guard let t = types[status] else {
throw ClientError.unknownResponse
}
return try JSONDecoder().decode(t.self, from: data)
} catch {
// some error handling here
}
}
I don't understand how to call this though. Tried below
struct APIAcceptedResponse: Codable {
// some fields
}
struct APIOKResponse: Codable {
// some fields
}
...
let response = try await self.send(httpsRequest, [.accepted: APIAcceptedResponse, .ok: APIOKResponse])
// and
let response = try await self.send(httpsRequest, [.accepted: APIAcceptedResponse.self, .ok: APIOKResponse.self])
But in both cases it shows error
Cannot convert value of type 'APIAcceptedResponse.Type' to expected dictionary value type 'APIOKResponse.Type'
Is there something I am doing wrong in the send function itself?
If not, how to call it?
Is this is something can be achieved even?
This cannot be solved with generics. T has to be of a certain type. So you cannot provide an array of multiple different types the function could choose from. Also this type has to be known at compile time. You canĀ“t choose it depending on your response.
As an alternative you could use protocols.
A simple example:
protocol ApiResponse: Decodable{
// common properties of all responses
}
struct ApiOkResponse: Codable, ApiResponse{
}
struct ApiErrorResponse: Codable, ApiResponse{
}
func getType(_ statusCode: Int) -> ApiResponse.Type{
if statusCode == 200{
return ApiOkResponse.self
} else {
return ApiErrorResponse.self
}
}
func send() throws -> ApiResponse{
let data = Data()
let responseStatusCode = 200
let type = getType(responseStatusCode)
return try JSONDecoder().decode(type, from: data)
}
Is there any way to auto generate UUID with GORM while saving the object in DB in go?
I am having experience with ROR migrations, where ID would be auto generated and PK by default.
Here is my code
Todo.go
package model
type Todo struct {
ID string `json:"id"`
Text string `json:"text"`
Done bool `json:"done"`
}
schema.resolvers.go
func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) {
db, _ := database.connect()
defer db.Close()
todo := &model.Todo{
Text: input.Text,
ID: fmt.Sprintf("%d", rand.Int()), // I don't want to provide ID like this
}
db.Create(&todo)
...
}
models_gen.go
# GraphQL schema example
#
# https://gqlgen.com/getting-started/
type Todo {
id: ID!
text: String!
done: Boolean!
}
input NewTodo {
text: String!
userId: String!
}
type Mutation {
createTodo(input: NewTodo!): Todo!
}
Any help would be really appreciated.
Assuming you're using gorm v2, you can use hooks to achieve this. The documentation about hooks is here
In particular, you can have a look at the BeforeCreate hook. Applied on a model, it provides a container for a function to be run before the creation in the database as its name state.
The GORM documentation also provides an example to generate a UUID when we create a user entity in a database with Gorm, which seems to be what you're looking for:
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
u.UUID = uuid.New()
if !u.IsValid() {
err = errors.New("can't save invalid data")
}
return
}
GORM Hooks is the most obvious solution to generate UUID when creating an object.
func (t *Todo) BeforeCreate(tx *gorm.DB) (err error) {
t.ID = uuid.New().String()
return
}
But if you want to generate UUID for every object, you could define a base struct and embed it into object structs.
type Base struct {
ID string `json:"id"`
}
func (b *Base) BeforeCreate(tx *gorm.DB) (err error) {
b.ID = uuid.New().String()
return
}
type Todo struct {
Base
Text string `json:"text"`
Done bool `json:"done"`
}
type User struct {
Base
Name string `json:"name"`
}
Or just use the GORM plugin next to generate UUID for objects.
import "github.com/invzhi/next"
plugin := NewPlugin()
// register uuid generate function
plugin.Register("uuid", func(_, zero bool) (interface{}, error) {
if !zero {
return nil, SkipField
}
return uuid.New().String()
})
_ = db.Use(plugin)
type Todo struct {
ID string `gorm:"type:varchar(20);column:id;next:uuid"` // next:uuid meaning generate uuid when create
Text string `gorm:"type:varchar(100);column:text`
Done bool `gorm:"type:boolean;column:done`
}
type User struct {
ID string `gorm:"type:varchar(20);column:id;next:uuid"` // next:uuid meaning generate uuid when create
Name string `gorm:"type:varchar(100);column:name"`
}
Is there currently a way for a generic class/struct to have no data type?
For example I have these two sets of api results.
result 1
{
"status": 200,
"message": "Success"
}
result 2
{
"status": 200,
"message": "Success",
"result": <Some Result Here>
}
Here's my example data model
public struct APIResult<Result> {
public let status: Int
public let message: String
public let result: Result?
// I want some kind of init like this
// note: I do data parsing a different way this is just an example
init(status: Int, message: String, result: Result?) {
self.status = status
self.message = message
self.result = result
}
}
Is there a way for result 1 to have its own custom initializer like how other datatypes are made for example
extension APIResult where Result: SomeClass {
init(...) {
...
}
}
extension APIResult where Result == SomeStruct {
init(...) {
...
}
}
Note: I know there is a different approach to this by creating a protocol for APIResult which only contains status and message, I'm just asking if there is a different way without doing so.
I've just remembered that actually Swift has actually built-in EmptyResult class, and it's called Void or () (Proper CS name is Unit type).
So, your extension should be like this:
extension APIResult where Result == () {
init(status: Int, message: String) {
self.init(status: status, message: message, result: nil)
}
}
I want to create function that accept 'dynamic array struct' and use it to map a data from database *mgodb
type Cats struct {
Meow string
}
func getCatsPagination() {
mapStructResult("Animality","Cat_Col", Cats)
}
type Dogs struct {
Bark string
}
func getDogsPagination() {
mapStructResult("Animality","Dog_Col", Dogs)
}
func mapStructResult(db string, collection string, model interface{}) {
result := []model{} //gets an error here
err := con.Find(param).Limit(int(limit)).Skip(int(offset)).All(&result) // map any database result to 'any' struct provided
if err != nil {
log.Fatal(err)
}
}
and gets an error as "model is not a type", why is it?
any answer will be highly appreciated !
Pass the ready slice to the mapStructResult function.
type Cats struct {
Meow string
}
func getCatsPagination() {
cats := []Cats{}
mapStructResult("Animality", "Cat_Col", &cats)
}
type Dogs struct {
Bark string
}
func getDogsPagination() {
dogs := []Dogs{}
mapStructResult("Animality", "Dog_Col", &dogs)
}
func mapStructResult(db string, collection string, model interface{}) {
err := con.Find(param).Limit(int(limit)).Skip(int(offset)).All(result) // map any database result to 'any' struct provided
if err != nil {
log.Fatal(err)
}
}
First of all there is no term in go called dynamic struct. Struct fields are declared before using them and cannot be changed. We can use bson type to handle the data. Bson type is like map[string]interface{} used to save the data dynamically.
func mapStructResult(db string, collection string, model interface{}) {
var result []bson.M // bson.M works like map[string]interface{}
err := con.Find(param).Limit(int(limit)).Skip(int(offset)).All(&result) // map any database result to 'any' struct provided
if err != nil {
log.Fatal(err)
}
}
For more information on bson type. Look this godoc for BSON
I have a Get() function:
func Get(url string) *Response {
res, err := http.Get(url)
if err != nil {
return &Response{}
}
// res.Body != nil when err == nil
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Fatalf("ReadAll: %v", err)
}
reflect.TypeOf(body)
return &Response{sync.Mutex(),string(body), res.StatusCode}
}
as well as a Read() function:
func Read(url string, timeout time.Duration) (res *Response) {
done := make(chan bool)
go func() {
res = Get(url)
done <- true
}()
select { // As soon as either
case <-done: // done is sent on the channel or
case <-time.After(timeout): // timeout
res = &Response{"Gateway timeout\n", 504}
}
return
}
the Response type returned by the functions is defined as:
type Response struct {
Body string
StatusCode int
}
This read function makes use of the Get() function and also implements a timeout. The problem is that a data race can occur if the timeout occurs and the Get() response is written to res at the same time in Read().
I have a plan for how to solve this. It is to use Mutex. To do this, I would add a field to the Response struct:
type Response struct {
mu sync.Mutex
Body string
StatusCode int
}
so that the Response can be locked. However, I'm not sure how to fix this in the other parts of the code.
My attempt looks like this, for the Get():
func Get(url string) *Response {
res, err := http.Get(url)
if err != nil {
return &Response{}
}
// res.Body != nil when err == nil
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Fatalf("ReadAll: %v", err)
}
reflect.TypeOf(body)
return &Response{sync.Mutex(),string(body), res.StatusCode} // This line is changed.
}
and for the Read():
func Read(url string, timeout time.Duration) (res *Response) {
done := make(chan bool)
res = &Response{sync.Mutex()} // this line has been added
go func() {
res = Get(url)
done <- true
}()
select {
case <-done:
case <-time.After(timeout):
res.mu.Lock()
res = &Response{sync.Mutex(), "Gateway timeout\n", 504} // And mutex was added here.
}
defer res.mu.Unlock()
return
}
This "solution" generates these errors:
./client.go:54: missing argument to conversion to sync.Mutex: sync.Mutex()
./client.go:63: missing argument to conversion to sync.Mutex: sync.Mutex()
./client.go:63: too few values in struct initializer
./client.go:73: missing argument to conversion to sync.Mutex: sync.Mutex()
./client.go:95: cannot use "Service unavailable\n" (type string) as type sync.Mutex in field value
./client.go:95: cannot use 503 (type int) as type string in field value
./client.go:95: too few values in struct initializer
What is the correct way of using Mutex in this case?
While your answer with Volker's guidance is good, you might want to consider using a non default http.Client so that you can set a Timeout on the client making the request (then you don't have to worry about handling the timeouts yourself).
I followed Volker's suggestion and used a channel to solve the problem.
func Read(url string, timeout time.Duration) (res *Response) {
done := make(chan bool) // A channel
resChan := make(chan *Response)
go func() {
resChan <- Get(url)
done <- true
}()
select {
case <-done:
res = &Response{}
case <-time.After(timeout):
res = &Response{"Gateway timeout\n", 504}
}
return
}
Now, there can be no simultaneous writes to res. It's going to be either the timeout or the returned value of Get(url).