Skip to content

More things you can do with Laminar with examples

Shubham Prashar edited this page Apr 14, 2022 · 1 revision

This page tells you about things you can do with Laminar along with appropriate examples

Example 1 - Add a single record directly mapped to a single table

Things covered

  • Addition of a record
  • How to define the input and mapping it's data types
  • Handling of enums in the table
  • mutation type is 'I'. Available mutation types are
    • 'I' Insert
    • 'S' Insert
    • 'U' Update
    • 'D' Delete
[
   {
      "title" : "AddCustomer",
      "req_name" : "",
      "res_name" : "",
      "decl_req" : 1,
      "decl_res" : 1,
      "decl_grpc" : 1,
      "decl_grapql" : 1,
      "sql_stmt" : "insert into Customer (name,govt_id_type,govt_id,gender,PhoneNo) values (?,?,?,?,?);",
      "sql_params" : "name:Customer.name,govt_id_type:Customer.govt_id_type,govt_id:Customer.govt_id,gender:Customer.gender,PhoneNo:Customer.PhoneNo",
      "sql_uniquekey" : 0,
      "impl_dao" : 1,
      "impl_grpc" : 1,
      "null_sql_replace" : 1,
      "sql_replace" : "",
      "impl_reacrjs" : 0,
      "req_override" : "",
      "res_override" : "",
      "mutation" : "I",
      "oauth_public" : 0,
      "oauth_claims" : "",
      "status" : 1
  }
]

Running the microservice

*  protoc --go_out=zerotouch/golang/proto --go-grpc_out=zerotouch/golang/proto zerotouch/golang/proto/*.proto -I=/Users/kishannigam/go/src/code.nurture.farm/Core/FarmService
* cd zerotouch/golang
* sh run_local.sh

Example 2 - Bulk Add

Things covered

  • Addition of multiple records
[
    {
        "title" : "AddProduct",
        "req_name" : "",
        "res_name" : "",
        "decl_req" : 1,
        "decl_res" : 1,
        "decl_grpc" : 1,
        "decl_grapql" : 1,
        "sql_stmt" : "insert into Product (name,description) values (?,?);",
        "sql_params" : "name:Product.name,description:string",
        "sql_uniquekey" : 0,
        "impl_dao" : 1,
        "impl_grpc" : 1,
        "null_sql_replace" : 1,
        "sql_replace" : "",
        "impl_reacrjs" : 0,
        "req_override" : "",
        "res_override" : "",
        "mutation" : "I",
        "oauth_public" : 0,
        "oauth_claims" : "",
        "status" : 1
    }
]

For golang

*  protoc --go_out=zerotouch/golang/proto --go-grpc_out=zerotouch/golang/proto zerotouch/golang/proto/*.proto -I=/Users/kishannigam/go/src/code.nurture.farm/Core/FarmService
* cd zerotouch/golang
* sh run_local.sh

Example 3 - Validate Input request and send an Error

Validate the input of a request before processing.

For addition/update/delete 3 hooks are provided

  • onRequest
  • onResponse
  • onError

The ReqRes object holds both the request and response. On can override the request object which will be used for data insert. If the response object is populated on onRequest, immediately the response is sent to client.

We will use the onRequest to validate the request input. If it is not valid, we will return a error response.

Golang Code.

func (rc *AddProductController) OnRequest(ctx context.Context, request interface{}) (interface{}) {

	addProductRequest := request.(*fs.AddProductRequest)
	if len(addProductRequest.Description) > 256 {
		return &fs.AddProductResponse{
			Status: &fs.Status{
				Status: fs.StatusCode_INVALID_REQUEST,
				ErrorMessages: []string{
					"Description allowes 256 characters.",
				},
			},
		}
	}
	return nil
}

For GoLang:

* protoc --go_out=zerotouch/golang/proto --go-grpc_out=zerotouch/golang/proto zerotouch/golang/proto/*.proto -I=/Users/kishannigam/go/src/code.nurture.farm/Core/FarmService
* cd zerotouch/golang
* sh run_local.sh

Example 4 - Find a product with unique key constraint

Things covered

  • Get one single record
  • How to define the input and mapping it's data types
[
    {
        "title" : "FindProductById",
        "req_name" : "",
        "res_name" : "",
        "decl_req" : 1,
        "decl_res" : 1,
        "decl_grpc" : 1,
        "decl_grapql" : 0,
        "sql_stmt" : "select name,description from Product where product_id = ?;",
        "sql_params" : "productid:int32",
        "sql_uniquekey" : 0,
        "impl_dao" : 1,
        "impl_grpc" : 1,
        "null_sql_replace" : 1,
        "sql_replace" : "",
        "impl_reacrjs" : 0,
        "req_override" : "",
        "res_override" : "",
        "mutation" : "S",
        "oauth_public" : 0,
        "oauth_claims" : "",
        "status" : 1
    }
]

For GoLang:

* protoc --go_out=zerotouch/golang/proto --go-grpc_out=zerotouch/golang/proto zerotouch/golang/proto/*.proto -I=/Users/kishannigam/go/src/code.nurture.farm/Core/FarmService
* cd zerotouch/golang
* sh run_local.sh

Example 5 - Pagination of records. Requires total count to show pages

Things covered

  • Override the total count using Data access object.
  • Override onData to set the count.
[
    {
        "title" : "FindProductByNameCount",
        "req_name" : "",
        "res_name" : "",
        "decl_req" : 0,
        "decl_res" : 0,
        "decl_grpc" : 0,
        "decl_grapql" : 0,
        "sql_stmt" : "select count(name) as total from Product where name = ?;",
        "sql_params" : "name:string",
        "sql_uniquekey" : 0,
        "impl_dao" : 1,
        "impl_grpc" : 0,
        "null_sql_replace" : 1,
        "sql_replace" : "",
        "impl_reacrjs" : 0,
        "req_override" : "",
        "res_override" : "",
        "mutation" : "S",
        "oauth_public" : 0,
        "oauth_claims" : "",
        "status" : 1
    }
]
  • Disable req, res, grpc, graphql
  • Implement dao only.

onData override

  • Override onData
  • Setup count in the FindProductRes.Builder responseBuilder
  • At the end framework reads all records from data and add to response.
  • Clearing List data will have empty list in response.
  • Setting the response in ReqRes<FindProductReq, FindProductRes> currentStep will ignore any further processing including < List data
  • Usage of setReadOnTxnScope
  • Direct usage of FindProductByNameCountResponseDao

Golang Code:

func (rc *FindProductByNameAndDescController) OnData(ctx context.Context, request interface{}, response interface{}) (interface{}) {

	findProductByNameAndDescReq := request.(*fs.FindProductReq)
	findProductByNameAndDescRes := response.(*fs.FindProductRes)
	if len(findProductByNameAndDescReq.Name)>0 && len(findProductByNameAndDescReq.Description)==0 {
		var rows = entsql.Rows{}
		var args []interface{}
		args = append(args, findProductByNameAndDescReq.Name)
		query := query.QUERY_FindProductByNameCount
		err := executor.Driver.GetDriver().Query(ctx, query, args, &rows)
		if err != nil {
			logger.Error("Error could not ExecuteFindProductByNameCount", zap.Error(err))
			return &fs.FindProductRes{
				Status: &fs.Status{
					Status: fs.StatusCode_DB_FAILURE,
				},
			}
		}
		for rows.Next() {
			model := models.FindProductByNameCountResponseVO{}
			err := rows.Scan(&model.Total)
			if err != nil {
				logger.Error("Error while fetching rows for ExecuteFindProductByNameCount", zap.Error(err))
				return &fs.FindProductRes{
					Status: &fs.Status{
						Status: fs.StatusCode_DB_FAILURE,
					},
				}
			}
			findProductByNameAndDescRes.Count = cast.ToInt32(model.Total.Int64)
		}
	}
 	return findProductByNameAndDescRes
}

For GoLang:

* protoc --go_out=zerotouch/golang/proto --go-grpc_out=zerotouch/golang/proto zerotouch/golang/proto/*.proto -I=/Users/kishannigam/go/src/code.nurture.farm/Core/FarmService
* cd zerotouch/golang
* sh run_local.sh

Example 6 - Enter multiple data types in a single transaction (e.g. order detail with multiple items)

Things covered

  • In a single transaction insert to multiple tables
  • Use the auto ID generated from upstream inserts in downstream inserts.
  • Mix Batch with Single record insert
  • Do it in transaction scope

  • Order Detail - Declare request, response and implement dao.
[
    {
        "title" : "AddOrderDetail",
        "req_name" : "",
        "res_name" : "",
        "decl_req" : 1,
        "decl_res" : 1,
        "decl_grpc" : 0,
        "decl_grapql" : 0,
        "sql_stmt" : "insert into OrderDetail (customer_id, order_status) values (?, ?);",
        "sql_params" : "customer_id:OrderDetail.customer_id,order_status:OrderDetail.order_status",
        "sql_uniquekey" : 0,
        "impl_dao" : 1,
        "impl_grpc" : 1,
        "null_sql_replace" : 1,
        "sql_replace" : "",
        "impl_reacrjs" : 0,
        "req_override" : "",
        "res_override" : "",
        "mutation" : "I",
        "oauth_public" : 0,
        "oauth_claims" : "",
        "status" : 1
    }
]
  • Order Items - Declare request, response and implement dao.
[
    {
        "title" : "AddOrderItem",
        "req_name" : "",
        "res_name" : "",
        "decl_req" : 1,
        "decl_res" : 1,
        "decl_grpc" : 0,
        "decl_grapql" : 0,
        "sql_stmt" : "insert into OrderItem (order_id, product_id, quantity) values (?, ?, ?);",
        "sql_params" : "order_id:OrderItem.order_id,product_id:OrderItem.product_id,quantity:OrderItem.quantity",
        "sql_uniquekey" : 0,
        "impl_dao" : 1,
        "impl_grpc" : 0,
        "null_sql_replace" : 1,
        "sql_replace" : "",
        "impl_reacrjs" : 0,
        "req_override" : "",
        "res_override" : "",
        "mutation" : "I",
        "oauth_public" : 0,
        "oauth_claims" : "",
        "status" : 1
    }
]
  • Generate proto messages
  • Override request to take multiple messages
  • Don't implement grpc, Hooks need to be implemented.
[
    {
        "title" : "EnterOrder",
        "req_name" : "EnterOrderReq",
        "res_name" : "EnterOrderRes",
        "decl_req" : 1,
        "decl_res" : 1,
        "decl_grpc" : 1,
        "decl_grapql" : 0,
        "sql_stmt" : "",
        "sql_params" : "",
        "sql_uniquekey" : 0,
        "impl_dao" : 1,
        "impl_grpc" : 0,
        "null_sql_replace" : 1,
        "sql_replace" : "",
        "impl_reacrjs" : 0,
        "req_override" : "AddOrderDetailRequest  orderDetail = 5;  BulkAddOrderItemRequest  orderItems = 6;",
        "res_override" : "",
        "mutation" : "I",
        "oauth_public" : 0,
        "oauth_claims" : "",
        "status" : 1
    }
]

Golang Code:

func (rc *EnterOrderController) OnRequest(ctx context.Context, request *fs.EnterOrderReq) (*fs.EnterOrderRes) {

	enterOrderRequest := request

	var response fs.EnterOrderRes
	response.Status = &common.RequestStatusResult{
			Status: common.RequestStatus_SUCCESS,
	}

	response.Count = 1
	_, txErr:= executor.Driver.TransactionRunner(ctx, "OnRequestEnterOrder", func(ctx context.Context, txName string, tx dialect.Tx) (res executor.TransactionResult, err error){

		model := mappers.MakeAddOrderDetailRequestVO(enterOrderRequest.OrderDetail)
		args := executor.AddOrderDetailArgs(model)
		var rows sql.Result
		err = tx.Exec(ctx, query.QUERY_AddOrderDetail, args, &rows)
		if err != nil {
			logger.Error("Error could not AddOrderDetail", zap.Error(err))
			return nil, err
		}
		insertedId, err := rows.LastInsertId()
		if err != nil {
			logger.Error("Error could not get lastInsertedId for AddOrderDetail", zap.Error(err))
			return nil, err
		}
		response.RecordId = cast.ToString(insertedId)

		for _, enterOrderItemRequest := range enterOrderRequest.OrderItems {

			enterOrderItemRequest.OrderId = cast.ToInt32(insertedId)
			model := mappers.MakeAddOrderItemRequestVO(enterOrderItemRequest)
			args := executor.AddOrderItemArgs(model)

			var rows sql.Result
			query := query.QUERY_AddOrderItem

			err := tx.Exec(ctx, query, args, &rows)
			if err != nil {
				logger.Error("Error could not ExecuteAddOrderItemRequest", zap.Error(err))
				return nil, err
			}

			_, err = rows.LastInsertId()
			if err != nil {
				logger.Error("Error could not get lastInsertedId for AddOrderItemRequest", zap.Error(err))
				return nil, err
			}
		}


		return nil, nil
	})

	if txErr != nil {
		response.Status = &common.RequestStatusResult{
			Status: common.RequestStatus_INTERNAL_ERROR,
			ErrorCode: common.ErrorCode_DATABASE_ERROR,
		}
		response.Count = 0
		response.RecordId = ""
		return &response
	}

	return &response
}

Steps to generate and run code :

For GoLang:

* protoc --go_out=zerotouch/golang/proto --go-grpc_out=zerotouch/golang/proto zerotouch/golang/proto/*.proto -I=/Users/kishannigam/go/src/code.nurture.farm/Core/FarmService
* cd zerotouch/golang
* sh run_local.sh

Example 7 - Serve in a heirarchial query

Things covered

  • Ses the column alias or column name to determine the key name in the message.
  • If an alias contains dots, the proto message is selected to create nested objects.
Query

SQL

SELECT   
       BusinessEntityID As Id,  
       FirstName, LastName,  
       Title As 'Info.Title',  
       MiddleName As 'Info.MiddleName'  
   FROM Person.Person  
   limit 10
   
Result

JSON

Copy
[{
	"Id": 1,
	"FirstName": "Ken",
	"LastName": "Sanchez",
	"Info": {
		"MiddleName": "J"
	}
}, {
	"Id": 2,
	"FirstName": "Terri",
	"LastName": "Duffy",
	"Info": {
		"MiddleName": "Lee"
	}
}, {
	"Id": 3,
	"FirstName": "Roberto",
	"LastName": "Tamburello"
}, {
	"Id": 4,
	"FirstName": "Rob",
	"LastName": "Walters"
}, {
	"Id": 5,
	"FirstName": "Gail",
	"LastName": "Erickson",
	"Info": {
		"Title": "Ms.",
		"MiddleName": "A"
	}
}]
[
    {
        "title" : "GetLatestOrder",
        "req_name" : "",
        "res_name" : "",
        "decl_req" : 1,
        "decl_res" : 1,
        "decl_grpc" : 1,
        "decl_grapql" : 0,
        "sql_stmt" : "select order_id, customer_id ,order_status, created_on from OrderDetail where created_on > ?;",
        "sql_params" : "creation_time:OrderDetail.created_on",
        "sql_uniquekey" : 1,
        "impl_dao" : 1,
        "impl_grpc" : 0,
        "null_sql_replace" : 1,
        "sql_replace" : "",
        "impl_reacrjs" : 0,
        "req_override" : "",
        "res_override" : "",
        "mutation" : "S",
        "oauth_public" : 0,
        "oauth_claims" : "",
        "status" : 1
    }
]
  • Request is reused.
[
    {
        "title" : "GetLatestOrderCustomers",
        "req_name" : "GetLatestOrderRequest",
        "res_name" : "",
        "decl_req" : 0,
        "decl_res" : 1,
        "decl_grpc" : 1,
        "decl_grapql" : 0,
        "sql_stmt" : "select customer_id,name,govt_id_type,govt_id,gender,PhoneNo from Customer where customer_id in ( select customer_id from OrderDetail where created_on > ? )",
        "sql_params" : "creation_time:OrderDetail.created_on",
        "sql_uniquekey" : 0,
        "impl_dao" : 1,
        "impl_grpc" : 1,
        "null_sql_replace" : 1,
        "sql_replace" : "",
        "impl_reacrjs" : 0,
        "req_override" : "",
        "res_override" : "",
        "mutation" : "S",
        "oauth_public" : 0,
        "oauth_claims" : "",
        "status" : 1
    }
]
  • Request is reused.
  • GetLatestOrderResponseRecord.order_status are mapped to generated record and fields
[
    {
        "title" : "GetOrderCustomerMix",
        "req_name" : "GetLatestOrderRequest",
        "res_name" : "",
        "decl_req" : 0,
        "decl_res" : 1,
        "decl_grpc" : 1,
        "decl_grapql" : 0,
        "sql_stmt" : "select o.order_status as `GetLatestOrderResponseRecord.order_status`, c.name as `GetLatestOrderCustomersResponseRecord.name` from OrderDetail o, Customer c where o.customer_id = c.customer_id and o.customer_id = c.customer_id and o.created_on > ?;",
        "sql_params" : "creation_time:OrderDetail.created_on",
        "sql_uniquekey" : 0,
        "impl_dao" : 1,
        "impl_grpc" : 1,
        "null_sql_replace" : 1,
        "sql_replace" : "",
        "impl_reacrjs" : 0,
        "req_override" : "",
        "res_override" : "",
        "mutation" : "S",
        "oauth_public" : 0,
        "oauth_claims" : "",
        "status" : 1
    }
]

For GoLang:

* protoc --go_out=zerotouch/golang/proto --go-grpc_out=zerotouch/golang/proto zerotouch/golang/proto/*.proto -I=/Users/kishannigam/go/src/code.nurture.farm/Core/FarmService
* cd zerotouch/golang
* sh run_local.sh

Example 8 - Call extrnal micro service

Things covered

  • No request, response implementation
  • Complete implementation.
[
    {
        "title" : "WeatherService",
        "req_name" : "",
        "res_name" : "",
        "decl_req" : 1,
        "decl_res" : 1,
        "decl_grpc" : 1,
        "decl_grapql" : 0,
        "sql_stmt" : "",
        "sql_params" : "",
        "sql_uniquekey" : 0,
        "impl_dao" : 0,
        "impl_grpc" : 0,
        "null_sql_replace" : 1,
        "sql_replace" : "",
        "impl_reacrjs" : 0,
        "req_override" : "LatLng latlng=9; } message LatLng {double lat = 1; double lng = 2",
        "res_override" : "int32 temp = 9;",
        "mutation" : "S",
        "oauth_public" : 0,
        "oauth_claims" : "",
        "status" : 1
    }
]

Golang Code:

func (rc *WeatherServiceController) OnRequest(ctx context.Context, request interface{}) (interface{}) {

	weatherServiceRequest := request.(*fs.WeatherServiceRequest)
	logger.Info("LatLng:"+cast.ToString(weatherServiceRequest.GetLatlng().GetLat())+ ", " +cast.ToString(weatherServiceRequest.GetLatlng().GetLng()))
	return &fs.WeatherServiceResponse{
		Status: &fs.Status{
			Status: fs.StatusCode_SUCCESS,
		},
		TemparatureInDegrees: 23.23,
	}
}

Steps to generate and run code :

For Golang:

* protoc --go_out=zerotouch/golang/proto --go-grpc_out=zerotouch/golang/proto zerotouch/golang/proto/*.proto -I=/Users/kishannigam/go/src/code.nurture.farm/Core/FarmService
* cd zerotouch/golang
* sh run_local.sh

Example 8 - Run in oauth context

Things covered

  • OAuth setup
  • Running in private scope

[ { "title" : "FindProductByIds", "req_name" : "", "res_name" : "", "decl_req" : 1, "decl_res" : 1, "decl_grpc" : 1, "decl_grapql" : 0, "sql_stmt" : "select product_id,name from Product where product_id in (@productid@);", "sql_params" : "", "sql_uniquekey" : 0, "impl_dao" : 1, "impl_grpc" : 1, "null_sql_replace" : 1, "sql_replace" : "productid:repeated int32", "impl_reacrjs" : 0, "req_override" : "", "res_override" : "", "mutation" : "S", "oauth_public" : 0, "oauth_claims" : "", "status" : 1 } ]

Example 8 - Delete records using a non parameterized query

Things covered

  • Deleting of records
  • Filter on selected Ids
[
    {
        "title" : "DeleteCustomerByIds",
        "req_name" : "",
        "res_name" : "",
        "decl_req" : 1,
        "decl_res" : 1,
        "decl_grpc" : 1,
        "decl_grapql" : 0,
        "sql_stmt" : "delete from Customer where govt_id in (@govtids@);",
        "sql_params" : "",
        "sql_uniquekey" : 0,
        "impl_dao" : 1,
        "impl_grpc" : 1,
        "null_sql_replace" : 1,
        "sql_replace" : "govtids:repeated string",
        "impl_reacrjs" : 0,
        "req_override" : "",
        "res_override" : "",
        "mutation" : "D",
        "oauth_public" : 0,
        "oauth_claims" : "",
        "status" : 1
    }
]

Currently replace tag should be all small case.

For Golang:

* protoc --go_out=zerotouch/golang/proto --go-grpc_out=zerotouch/golang/proto zerotouch/golang/proto/*.proto -I=/Users/kishannigam/go/src/code.nurture.farm/Core/FarmService
* cd zerotouch/golang
* sh run_local.sh

Example 9 - Pagination of records. Requires total count to show pages

Things covered

  • Override the total count using Data access object.
  • Override onData to set the count.

  • Disable req, res, grpc, graphql
  • Implement dao only.

[
    {
        "title" : "FindProductByNameCount",
        "req_name" : "",
        "res_name" : "",
        "decl_req" : 0,
        "decl_res" : 0,
        "decl_grpc" : 0,
        "decl_grapql" : 0,
        "sql_stmt" : "select count(name) as total from Product where name = ?;",
        "sql_params" : "name:string",
        "sql_uniquekey" : 0,
        "impl_dao" : 1,
        "impl_grpc" : 0,
        "null_sql_replace" : 1,
        "sql_replace" : "",
        "impl_reacrjs" : 0,
        "req_override" : "",
        "res_override" : "",
        "mutation" : "S",
        "oauth_public" : 0,
        "oauth_claims" : "",
        "status" : 1
    }
]

onData override

  • Override onData
  • Setup count in the FindProductRes.Builder responseBuilder
  • At the end framework reads all records from data and add to response.
  • Clearing List data will have empty list in response.
  • Setting the response in ReqRes<FindProductReq, FindProductRes> currentStep will ignore any further processing including < List data
  • Usage of setReadOnTxnScope
  • Direct usage of FindProductByNameCountResponseDao

Golang Code:

func (rc *FindProductByNameAndDescController) OnData(ctx context.Context, request interface{}, response interface{}) (interface{}) {

	findProductByNameAndDescReq := request.(*fs.FindProductReq)
	findProductByNameAndDescRes := response.(*fs.FindProductRes)
	if len(findProductByNameAndDescReq.Name)>0 && len(findProductByNameAndDescReq.Description)==0 {
		var rows = entsql.Rows{}
		var args []interface{}
		args = append(args, findProductByNameAndDescReq.Name)
		query := query.QUERY_FindProductByNameCount
		err := executor.Driver.GetDriver().Query(ctx, query, args, &rows)
		if err != nil {
			logger.Error("Error could not ExecuteFindProductByNameCount", zap.Error(err))
			return &fs.FindProductRes{
				Status: &fs.Status{
					Status: fs.StatusCode_DB_FAILURE,
				},
			}
		}
		for rows.Next() {
			model := models.FindProductByNameCountResponseVO{}
			err := rows.Scan(&model.Total)
			if err != nil {
				logger.Error("Error while fetching rows for ExecuteFindProductByNameCount", zap.Error(err))
				return &fs.FindProductRes{
					Status: &fs.Status{
						Status: fs.StatusCode_DB_FAILURE,
					},
				}
			}
			findProductByNameAndDescRes.Count = cast.ToInt32(model.Total.Int64)
		}
	}
 	return findProductByNameAndDescRes
}

Steps to generate and run code :

For GoLang:

* protoc --go_out=zerotouch/golang/proto --go-grpc_out=zerotouch/golang/proto zerotouch/golang/proto/*.proto -I=/Users/kishannigam/go/src/code.nurture.farm/Core/FarmService
* cd zerotouch/golang
* sh run_local.sh

Example - Update records from table.

For GoLang:

*  protoc --go_out=zerotouch/golang/proto --go-grpc_out=zerotouch/golang/proto zerotouch/golang/proto/*.proto -I=/Users/kishannigam/go/src/code.nurture.farm/Core/FarmService
* cd zerotouch/golang
* sh run_local.sh

Clone this wiki locally