Skip to content

Commit

Permalink
feat: improve js error
Browse files Browse the repository at this point in the history
Signed-off-by: Guilhem Fanton <guilhem.fanton@gmail.com>
  • Loading branch information
gfanton committed Dec 29, 2020
1 parent cd0129a commit 09cab5b
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 59 deletions.
56 changes: 56 additions & 0 deletions js/packages/grpc-bridge/rpc/error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { errcode, account } from '@berty-tech/api'
import { reduce } from 'lodash'

class GRPCError extends Error {
public EOF: boolean
public OK: boolean
public Code: errcode.ErrCode
public GrpcCode: account.GRPCErrCode

private error: account.Error

constructor(e: account.IError | null | undefined) {
if (!e) {
// this should not happen, but should not break the app either.
// instead simply create a empty error and warn about this
console.warn(`GRPCError: (${e}) grpc error provided, empty error returned`)
e = account.Error.create({})
}

const error = account.Error.create(e)
super(error.message)

this.error = error
this.Code = error.errorCode
this.GrpcCode = error.grpcErrorCode

this.OK =
error.grpcErrorCode === account.GRPCErrCode.OK &&
error.errorCode === errcode.ErrCode.Undefined
this.EOF =
error.grpcErrorCode === account.GRPCErrCode.CANCELED ||
(error.grpcErrorCode === account.GRPCErrCode.UNKNOWN && error.message === 'EOF')
}

public details(): errcode.ErrDetails {
if (this.error.errorDetails) {
return errcode.ErrDetails.create(this.error.errorDetails)
}

return errcode.ErrDetails.create({})
}

public errCode(): errcode.ErrCode {
return this.Code
}

public grpcErrorCode(): account.GRPCErrCode {
return this.GrpcCode
}

public hasErrCode(error: errcode.ErrCode): boolean {
return reduce(this.error.errorDetails?.codes, (ac, v) => ac && v == error, false) || false
}
}

export default GRPCError
102 changes: 43 additions & 59 deletions js/packages/grpc-bridge/rpc/rpc.bridge.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,20 @@
import { Service } from '..'
import rpcNative from './rpc.native'
import beapi from '@berty-tech/api'
import { getServiceName, EOF } from './utils'
import { account } from '@berty-tech/api'
import { getServiceName } from './utils'
import * as pbjs from 'protobufjs'
import { ServiceClientType } from '../welsh-clients.gen'
import GRPCError from './error'

const getErrorFromResponse = <M extends pbjs.Method>(
method: M,
response: beapi.account.ClientStreamRecv.IReply,
) => {
if (response.error) {
if (
response.error.grpcErrorCode === beapi.account.GRPCErrCode.CANCELED ||
(response.error.grpcErrorCode === beapi.account.GRPCErrCode.UNKNOWN &&
response.error.message === 'EOF')
) {
return EOF
}

if (response.error.errorCode || 0 > 0) {
return new Error(`${method.name} error: ${response.error.message}`)
}

if (response.error.grpcErrorCode || 0 > 0) {
const name = beapi.account.GRPCErrCode[response.error.grpcErrorCode || 0]
return new Error(`${method.name} error: GRPC_${name}(${response.error.grpcErrorCode})`)
}

return new Error(`unknown error: ${response.error.message}`)
}

return null
}
const ErrStreamClientAlreadyStarted = new GRPCError({
grpcErrorCode: account.GRPCErrCode.CANCELED,
message: 'client stream not started or has been closed',
})

const makeStreamClient = <M extends pbjs.Method>(
streamid: string,
method: M,
accountClient: ServiceClientType<beapi.account.AccountService>,
accountClient: ServiceClientType<account.AccountService>,
) => {
const eventEmitter = {
events: [] as ((...a: unknown[]) => void)[],
Expand All @@ -54,25 +32,27 @@ const makeStreamClient = <M extends pbjs.Method>(
payload,
})

const err = getErrorFromResponse(method, response)
if (err) {
throw err
// check for error
if (response.error) {
const grpcerr = new GRPCError(response.error)
if (!grpcerr.OK) {
throw grpcerr
}
}
},
async start() {
if (this.started) {
throw new Error('client stream already started or has been closed')
throw ErrStreamClientAlreadyStarted
}
this.started = true

var response: beapi.account.ClientStreamRecv.IReply
var response: account.ClientStreamRecv.IReply

for (;;) {
response = await accountClient.clientStreamRecv({ streamId: streamid })

const err = getErrorFromResponse(method, response)

if (err) {
this._publish(null, err)
const grpcerr = new GRPCError(response.error)
if (!grpcerr.OK) {
this._publish(null, grpcerr)
return
}

Expand All @@ -81,26 +61,30 @@ const makeStreamClient = <M extends pbjs.Method>(
},
async stop() {
if (!this.started) {
throw new Error('client stream not started or has been closed')
throw ErrStreamClientAlreadyStarted
}

const response = await accountClient.clientStreamClose({ streamId: streamid })
const err = getErrorFromResponse(method, response)
if (err) {
throw err
const grpcerr = new GRPCError(response.error)
if (!grpcerr.OK) {
this._publish(null, grpcerr)
return
}

return
},
async stopAndRecv() {
if (this.started) {
throw new Error('client stream not started or has been closed')
throw ErrStreamClientAlreadyStarted
}

const response = await accountClient.clientStreamCloseAndRecv({ streamId: streamid })

const err = getErrorFromResponse(method, response)
if (err) {
throw err
const grpcerr = new GRPCError(response.error)
if (!grpcerr.OK) {
this._publish(null, grpcerr)
return
}

return method.resolvedResponseType?.decode(response.payload)
},
}
Expand All @@ -112,7 +96,7 @@ const makeStreamClient = <M extends pbjs.Method>(
}
}

const unary = (accountClient: ServiceClientType<beapi.account.AccountService>) => async <
const unary = (accountClient: ServiceClientType<account.AccountService>) => async <
M extends pbjs.Method
>(
method: M,
Expand All @@ -128,15 +112,15 @@ const unary = (accountClient: ServiceClientType<beapi.account.AccountService>) =
payload: request,
// metadata: {}, // @TODO: pass metdate object
})
const err = getErrorFromResponse(method, response)
if (err !== null) {
throw err
const grpcerr = new GRPCError(response.error)
if (!grpcerr.OK) {
throw grpcerr
}

return response.payload
}

const stream = (accountClient: ServiceClientType<beapi.account.AccountService>) => async <
const stream = (accountClient: ServiceClientType<account.AccountService>) => async <
M extends pbjs.Method
>(
method: M,
Expand All @@ -156,18 +140,18 @@ const stream = (accountClient: ServiceClientType<beapi.account.AccountService>)
// metadata: {},
})

const err = getErrorFromResponse(method, response)
if (err !== null) {
throw err
const grpcerr = new GRPCError(response.error)
if (!grpcerr.OK) {
throw grpcerr
}

return makeStreamClient(response.streamId, method, accountClient)
}

const client = (accountClient: ServiceClientType<beapi.account.AccountService>) => ({
const client = (accountClient: ServiceClientType<account.AccountService>) => ({
unaryCall: unary(accountClient),
streamCall: stream(accountClient),
})

const accountClient = Service(beapi.account.AccountService, rpcNative)
const accountClient = Service(account.AccountService, rpcNative)
export default client(accountClient)

0 comments on commit 09cab5b

Please sign in to comment.