/*
Lets create a minimal service with Orion

API Interface

Your service starts with the API interface, In orion we define all API interfaces as a protobuf file (stringsvc/stringproto/stringproto.proto)
	syntax = "proto3";

	package stringproto;

	service StringService{

		rpc Upper (UpperRequest) returns (UpperResponse){
		}

		rpc Count (CountRequest) returns (CountResponse) {
		}
	}

	message UpperRequest {
		string msg = 1;
	}

	message UpperResponse {
		string msg = 1;
	}

	message CountRequest{
		string msg = 1;
	}

	message CountResponse{
		int64 count = 1;
	}

Above protobuf file describes a service called "StringService" and defines the endpoints "Upper" and "Count", As we can see all our API Contracts are clearly laid out and makes it easy for other services to integrate with our service

Generating Service Code

we can execute
	protoc -I . stringproto.proto --go_out=plugins=grpc:. --orion_out=.
to generate protobuf/orion code, you will see following files are generated
	stringproto.pb.go
	stringproto.pb.orion.go

Service Lifecycle

Orion manages lifecycle of the service by ServiceFactory (stringsvc/service/init.go)
	package service

	import "github.com/carousell/Orion/orion"

	type svcFactory struct{}

	func (s *svcFactory) NewService(orion.Server) interface{} {
		return &svc{}
	}

	func (s *svcFactory) DisposeService(svc interface{}) {
		//do nothing
	}

	func GetFactory() orion.ServiceFactory {
		return &svcFactory{}
	}
"NewService" is called when a new service is initialized, "DisposeService" is called when a service is disposed.
A new service is initialized and older service is disposed every time config reloads.

Implementing Service

we need to implement the "StringServiceServer" interface generated by protoc plugin, this is where our application logic lives
(stringsvc/service/service.go)
	package service

	import (
		"context"
		"strings"

		proto "github.com/carousell/Orion/example/stringsvc/stringproto"
	)

	// svc implements proto.StringServiceServer
	type svc struct{}

	func (s *svc) Upper(ctx context.Context, req *proto.UpperRequest) (*proto.UpperResponse, error) {
		resp := new(proto.UpperResponse)
		resp.Msg = strings.ToUpper(req.GetMsg())
		return resp, nil
	}

	func (s *svc) Count(ctx context.Context, req *proto.CountRequest) (*proto.CountResponse, error) {
		resp := new(proto.CountResponse)
		resp.Count = int64(len(req.GetMsg()))
		return resp, nil
	}

Building Server

in only a few lines of code we can get a server started serving gRPC and HTTP (stringsvc/cmd/server/main.go)
	package main

	import (
		"github.com/carousell/Orion/example/stringsvc/service"
		proto "github.com/carousell/Orion/example/stringsvc/stringproto"
		"github.com/carousell/Orion/orion"
	)

	func main() {
		server := orion.GetDefaultServer("StringService")
		proto.RegisterStringServiceOrionServer(service.GetFactory(), server)
		server.Start()
		server.Wait()
	}

Running Server

we can start the server by running "go run cmd/server/main.go"

	ᐅ go run cmd/server/main.go
	2018/02/01 16:20:57 msg Reading config
	2018/02/01 16:20:57 Config config could not be read Config File "StringService" Not Found in "[. /opt/config]"
	2018/02/01 16:20:57 HTTPListnerPort 9282
	2018/02/01 16:20:57 gRPCListnerPort 9281
	2018/02/01 16:20:57 HystrixPort 9283
	2018/02/01 16:20:57 warning rollbar token is empty not initializing rollbar

	  ___  ____  ___ ___  _   _
	 / _ \|  _ \|_ _/ _ \| \ | |
	| | | | |_) || | | | |  \| |
	| |_| |  _ < | | |_| | |\  |
	 \___/|_| \_\___\___/|_| \_|

	2018/02/01 16:20:57 GRPC server starting
	2018/02/01 16:20:57 PprofPort 9284
	Mapped URLs:
		[POST] /stringservice/upper
		[POST] /stringservice/count

Orion automatically generates and initializes HTTP/gRPC servers based on our defined proto
as we can see the path for our endpoints Upper and Count are as follows
	[POST] /stringservice/upper
	[POST] /stringservice/count

Making HTTP Calls

Lets call Upper
	ᐅ curl -X POST -i -d '{"msg":"Hello World"}' http://127.0.0.1:9282/stringservice/upper
	HTTP/1.1 200 OK
	Content-Type: application/json
	Date: Thu, 01 Feb 2018 08:27:32 GMT
	Content-Length: 21

	{"msg":"HELLO WORLD"}

By default orion responds in "application/json" for HTTP calls, We can even ask for response in "protobuf" by setting the Accept HTTP header

	ᐅ curl -X POST -i -H "Accept:application/protobuf" -d '{"msg":"Hello World"}' http://127.0.0.1:9282/stringservice/upper
	HTTP/1.1 200 OK
	Content-Type: application/octet-stream
	Date: Thu, 01 Feb 2018 08:30:21 GMT
	Content-Length: 13
				// binary data omitted
	HELLO WORLD

Orion also logs response time and errors for each API call
	2018/02/01 16:27:32 path /stringservice/upper method POST error <nil> took 132.96µs
	2018/02/01 16:30:21 path /stringservice/upper method POST error <nil> took 766.984µs

Making gRPC calls

we can build a client using StringServiceClient generated by protoc grpc plugin (stringsvc/cmd/client/main.go)
	package main

	import (
		"context"
		"log"

		proto "github.com/carousell/Orion/example/stringsvc/stringproto"
		"google.golang.org/grpc"
	)

	const (
		address = "127.0.0.1:9281"
	)

	func main() {
		// establish connection
		conn, err := grpc.Dial(address, grpc.WithInsecure())
		if err != nil {
			log.Fatalf("did not connect: %v", err)
		}
		defer conn.Close()

		client := proto.NewStringServiceClient(conn)
		uppercase(client)
	}

	func uppercase(client proto.StringServiceClient) {
		r := new(proto.UpperRequest)
		r.Msg = "Hello World"
		log.Println("making gRPC calls for Upper")
		resp, err := client.Upper(context.Background(), r)
		log.Println("resp", resp)
		log.Println("error", err)
	}
On running this client we can see
	ᐅ go run cmd/client/main.go
	2018/02/01 16:50:06 making gRPC calls for Upper
	2018/02/01 16:50:06 resp msg:"HELLO WORLD"
	2018/02/01 16:50:06 error <nil>

we will see the following on server
	2018/02/01 16:50:06 method /stringproto.StringService/Upper error <nil> took 19.764µs
*/
package example

//go:generate godoc2ghmd -ex -file=README.md github.com/carousell/Orion/example
