nim twirp service experiment

This commit is contained in:
Oskar Thoren 2019-09-03 10:25:03 +02:00
parent d7ab72bcbd
commit 85be50d903
No known key found for this signature in database
GPG Key ID: B2ECCFD3BC2EF77E
3 changed files with 353 additions and 0 deletions

View File

@ -98,3 +98,7 @@ Actually use them, and check enum and dispatching etc.
Put in Makefile
nimble install https://github.com/PMunch/protobuf-nim
### Protobuf service generation
Install `nimtwirp`, then:
`nimtwirp_build -I:. --out:. protocol.proto `

View File

@ -0,0 +1,237 @@
# Generated by protoc_gen_nim. Do not edit!
import base64
import intsets
import json
import strutils
import nimpb/nimpb
import nimpb/json as nimpb_json
type
vac_cas_CASRequest* = ref vac_cas_CASRequestObj
vac_cas_CASRequestObj* = object of Message
id: string
vac_cas_CASResponse* = ref vac_cas_CASResponseObj
vac_cas_CASResponseObj* = object of Message
id: string
data: string
proc newvac_cas_CASResponse*(): vac_cas_CASResponse
proc newvac_cas_CASResponse*(data: string): vac_cas_CASResponse
proc newvac_cas_CASResponse*(data: seq[byte]): vac_cas_CASResponse
proc writevac_cas_CASResponse*(stream: Stream, message: vac_cas_CASResponse)
proc readvac_cas_CASResponse*(stream: Stream): vac_cas_CASResponse
proc sizeOfvac_cas_CASResponse*(message: vac_cas_CASResponse): uint64
proc toJson*(message: vac_cas_CASResponse): JsonNode
proc parsevac_cas_CASResponse*(obj: JsonNode): vac_cas_CASResponse
proc newvac_cas_CASRequest*(): vac_cas_CASRequest
proc newvac_cas_CASRequest*(data: string): vac_cas_CASRequest
proc newvac_cas_CASRequest*(data: seq[byte]): vac_cas_CASRequest
proc writevac_cas_CASRequest*(stream: Stream, message: vac_cas_CASRequest)
proc readvac_cas_CASRequest*(stream: Stream): vac_cas_CASRequest
proc sizeOfvac_cas_CASRequest*(message: vac_cas_CASRequest): uint64
proc toJson*(message: vac_cas_CASRequest): JsonNode
proc parsevac_cas_CASRequest*(obj: JsonNode): vac_cas_CASRequest
proc fullyQualifiedName*(T: typedesc[vac_cas_CASResponse]): string = "vac.cas.CASResponse"
proc readvac_cas_CASResponseImpl(stream: Stream): Message = readvac_cas_CASResponse(stream)
proc writevac_cas_CASResponseImpl(stream: Stream, msg: Message) = writevac_cas_CASResponse(stream, vac_cas_CASResponse(msg))
proc toJsonvac_cas_CASResponseImpl(msg: Message): JsonNode = toJson(vac_cas_CASResponse(msg))
proc fromJsonvac_cas_CASResponseImpl(node: JsonNode): Message = parsevac_cas_CASResponse(node)
proc vac_cas_CASResponseProcs*(): MessageProcs =
result.readImpl = readvac_cas_CASResponseImpl
result.writeImpl = writevac_cas_CASResponseImpl
result.toJsonImpl = toJsonvac_cas_CASResponseImpl
result.fromJsonImpl = fromJsonvac_cas_CASResponseImpl
proc newvac_cas_CASResponse*(): vac_cas_CASResponse =
new(result)
initMessage(result[])
result.procs = vac_cas_CASResponseProcs()
result.id = ""
result.data = ""
proc clearid*(message: vac_cas_CASResponse) =
message.id = ""
proc setid*(message: vac_cas_CASResponse, value: string) =
message.id = value
proc id*(message: vac_cas_CASResponse): string {.inline.} =
message.id
proc `id=`*(message: vac_cas_CASResponse, value: string) {.inline.} =
setid(message, value)
proc cleardata*(message: vac_cas_CASResponse) =
message.data = ""
proc setdata*(message: vac_cas_CASResponse, value: string) =
message.data = value
proc data*(message: vac_cas_CASResponse): string {.inline.} =
message.data
proc `data=`*(message: vac_cas_CASResponse, value: string) {.inline.} =
setdata(message, value)
proc sizeOfvac_cas_CASResponse*(message: vac_cas_CASResponse): uint64 =
if len(message.id) > 0:
result = result + sizeOfTag(2, WireType.LengthDelimited)
result = result + sizeOfString(message.id)
if len(message.data) > 0:
result = result + sizeOfTag(3, WireType.LengthDelimited)
result = result + sizeOfString(message.data)
result = result + sizeOfUnknownFields(message)
proc writevac_cas_CASResponse*(stream: Stream, message: vac_cas_CASResponse) =
if len(message.id) > 0:
protoWriteString(stream, message.id, 2)
if len(message.data) > 0:
protoWriteString(stream, message.data, 3)
writeUnknownFields(stream, message)
proc readvac_cas_CASResponse*(stream: Stream): vac_cas_CASResponse =
result = newvac_cas_CASResponse()
while not atEnd(stream):
let
tag = readTag(stream)
wireType = wireType(tag)
case fieldNumber(tag)
of 0:
raise newException(InvalidFieldNumberError, "Invalid field number: 0")
of 2:
expectWireType(wireType, WireType.LengthDelimited)
setid(result, protoReadString(stream))
of 3:
expectWireType(wireType, WireType.LengthDelimited)
setdata(result, protoReadString(stream))
else: readUnknownField(stream, result, tag)
proc toJson*(message: vac_cas_CASResponse): JsonNode =
result = newJObject()
if len(message.id) > 0:
result["id"] = %message.id
if len(message.data) > 0:
result["data"] = %message.data
proc parsevac_cas_CASResponse*(obj: JsonNode): vac_cas_CASResponse =
result = newvac_cas_CASResponse()
var node: JsonNode
if obj.kind != JObject:
raise newException(nimpb_json.ParseError, "object expected")
node = getJsonField(obj, "id", "id")
if node != nil and node.kind != JNull:
setid(result, parseString(node))
node = getJsonField(obj, "data", "data")
if node != nil and node.kind != JNull:
setdata(result, parseString(node))
proc serialize*(message: vac_cas_CASResponse): string =
let
ss = newStringStream()
writevac_cas_CASResponse(ss, message)
result = ss.data
proc newvac_cas_CASResponse*(data: string): vac_cas_CASResponse =
let
ss = newStringStream(data)
result = readvac_cas_CASResponse(ss)
proc newvac_cas_CASResponse*(data: seq[byte]): vac_cas_CASResponse =
let
ss = newStringStream(cast[string](data))
result = readvac_cas_CASResponse(ss)
proc fullyQualifiedName*(T: typedesc[vac_cas_CASRequest]): string = "vac.cas.CASRequest"
proc readvac_cas_CASRequestImpl(stream: Stream): Message = readvac_cas_CASRequest(stream)
proc writevac_cas_CASRequestImpl(stream: Stream, msg: Message) = writevac_cas_CASRequest(stream, vac_cas_CASRequest(msg))
proc toJsonvac_cas_CASRequestImpl(msg: Message): JsonNode = toJson(vac_cas_CASRequest(msg))
proc fromJsonvac_cas_CASRequestImpl(node: JsonNode): Message = parsevac_cas_CASRequest(node)
proc vac_cas_CASRequestProcs*(): MessageProcs =
result.readImpl = readvac_cas_CASRequestImpl
result.writeImpl = writevac_cas_CASRequestImpl
result.toJsonImpl = toJsonvac_cas_CASRequestImpl
result.fromJsonImpl = fromJsonvac_cas_CASRequestImpl
proc newvac_cas_CASRequest*(): vac_cas_CASRequest =
new(result)
initMessage(result[])
result.procs = vac_cas_CASRequestProcs()
result.id = ""
proc clearid*(message: vac_cas_CASRequest) =
message.id = ""
proc setid*(message: vac_cas_CASRequest, value: string) =
message.id = value
proc id*(message: vac_cas_CASRequest): string {.inline.} =
message.id
proc `id=`*(message: vac_cas_CASRequest, value: string) {.inline.} =
setid(message, value)
proc sizeOfvac_cas_CASRequest*(message: vac_cas_CASRequest): uint64 =
if len(message.id) > 0:
result = result + sizeOfTag(2, WireType.LengthDelimited)
result = result + sizeOfString(message.id)
result = result + sizeOfUnknownFields(message)
proc writevac_cas_CASRequest*(stream: Stream, message: vac_cas_CASRequest) =
if len(message.id) > 0:
protoWriteString(stream, message.id, 2)
writeUnknownFields(stream, message)
proc readvac_cas_CASRequest*(stream: Stream): vac_cas_CASRequest =
result = newvac_cas_CASRequest()
while not atEnd(stream):
let
tag = readTag(stream)
wireType = wireType(tag)
case fieldNumber(tag)
of 0:
raise newException(InvalidFieldNumberError, "Invalid field number: 0")
of 2:
expectWireType(wireType, WireType.LengthDelimited)
setid(result, protoReadString(stream))
else: readUnknownField(stream, result, tag)
proc toJson*(message: vac_cas_CASRequest): JsonNode =
result = newJObject()
if len(message.id) > 0:
result["id"] = %message.id
proc parsevac_cas_CASRequest*(obj: JsonNode): vac_cas_CASRequest =
result = newvac_cas_CASRequest()
var node: JsonNode
if obj.kind != JObject:
raise newException(nimpb_json.ParseError, "object expected")
node = getJsonField(obj, "id", "id")
if node != nil and node.kind != JNull:
setid(result, parseString(node))
proc serialize*(message: vac_cas_CASRequest): string =
let
ss = newStringStream()
writevac_cas_CASRequest(ss, message)
result = ss.data
proc newvac_cas_CASRequest*(data: string): vac_cas_CASRequest =
let
ss = newStringStream(data)
result = readvac_cas_CASRequest(ss)
proc newvac_cas_CASRequest*(data: seq[byte]): vac_cas_CASRequest =
let
ss = newStringStream(cast[string](data))
result = readvac_cas_CASRequest(ss)

View File

@ -0,0 +1,112 @@
import asyncdispatch
import asynchttpserver
import httpclient
import json
import strutils
import protocol_pb
import nimtwirp/nimtwirp
import nimtwirp/errors
const
CASPrefix* = "/twirp/vac.cas.CAS/"
type
CAS* = ref CASObj
CASObj* = object of RootObj
GetImpl*: proc (service: CAS, param: vac_cas_CASRequest): Future[vac_cas_CASResponse] {.gcsafe, closure.}
PostImpl*: proc (service: CAS, param: vac_cas_CASRequest): Future[vac_cas_CASResponse] {.gcsafe, closure.}
proc Get*(service: CAS, param: vac_cas_CASRequest): Future[vac_cas_CASResponse] {.async.} =
if service.GetImpl == nil:
raise newTwirpError(TwirpUnimplemented, "Get is not implemented")
result = await service.GetImpl(service, param)
proc Post*(service: CAS, param: vac_cas_CASRequest): Future[vac_cas_CASResponse] {.async.} =
if service.PostImpl == nil:
raise newTwirpError(TwirpUnimplemented, "Post is not implemented")
result = await service.PostImpl(service, param)
proc newCAS*(): CAS =
new(result)
proc handleRequest*(service: CAS, req: Request): Future[nimtwirp.Response] {.async.} =
let (contentType, methodName) = validateRequest(req, CASPrefix)
if methodName == "Get":
var inputMsg: vac_cas_CASRequest
if contentType == "application/protobuf":
inputMsg = newvac_cas_CASRequest(req.body)
elif contentType == "application/json":
let node = parseJson(req.body)
inputMsg = parsevac_cas_CASRequest(node)
let outputMsg = await Get(service, inputMsg)
if contentType == "application/protobuf":
return nimtwirp.newResponse(serialize(outputMsg))
elif contentType == "application/json":
return nimtwirp.newResponse(toJson(outputMsg))
elif methodName == "Post":
var inputMsg: vac_cas_CASRequest
if contentType == "application/protobuf":
inputMsg = newvac_cas_CASRequest(req.body)
elif contentType == "application/json":
let node = parseJson(req.body)
inputMsg = parsevac_cas_CASRequest(node)
let outputMsg = await Post(service, inputMsg)
if contentType == "application/protobuf":
return nimtwirp.newResponse(serialize(outputMsg))
elif contentType == "application/json":
return nimtwirp.newResponse(toJson(outputMsg))
else:
raise newTwirpError(TwirpBadRoute, "unknown method")
type
CASClient* = ref object of nimtwirp.Client
proc newCASClient*(address: string, kind = ClientKind.Protobuf): CASClient =
new(result)
result.client = newHttpClient()
result.kind = kind
case kind
of ClientKind.Protobuf:
result.client.headers = newHttpHeaders({"Content-Type": "application/protobuf"})
of ClientKind.Json:
result.client.headers = newHttpHeaders({"Content-Type": "application/json"})
result.address = address
proc Get*(client: CASClient, req: vac_cas_CASRequest): vac_cas_CASResponse =
var body: string
case client.kind
of ClientKind.Protobuf:
body = serialize(req)
of ClientKind.Json:
body = $toJson(req)
let resp = request(client, CASPrefix, "Get", body)
case client.kind
of ClientKind.Protobuf:
result = newvac_cas_CASResponse(resp.body)
of ClientKind.Json:
result = parsevac_cas_CASResponse(parseJson(resp.body))
proc Post*(client: CASClient, req: vac_cas_CASRequest): vac_cas_CASResponse =
var body: string
case client.kind
of ClientKind.Protobuf:
body = serialize(req)
of ClientKind.Json:
body = $toJson(req)
let resp = request(client, CASPrefix, "Post", body)
case client.kind
of ClientKind.Protobuf:
result = newvac_cas_CASResponse(resp.body)
of ClientKind.Json:
result = parsevac_cas_CASResponse(parseJson(resp.body))