Skip to content

Commit

Permalink
documentation for request builder
Browse files Browse the repository at this point in the history
  • Loading branch information
Ptroger committed Apr 4, 2024
1 parent ca11996 commit 8b6eece
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 29 deletions.
3 changes: 1 addition & 2 deletions packages/engine-request/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"dependencies": {
"tslib": "^2.3.0",
"uuid": "^9.0.1",
"zod": "^3.22.4",
"jest": "^29.4.1"
"zod": "^3.22.4"
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
// builders.test.ts
import {
Eip712TypedData,
TransactionRequest
} from '@narval/policy-engine-shared'
import { Eip712TypedData, TransactionRequest } from '@narval/policy-engine-shared'
import { v4 } from 'uuid'
import { buildRequest } from '../request-builder'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Feed, JwtString, Prices } from '@narval/policy-engine-shared'

export default class EvaluationRequestBuilder {
approvals?: JwtString[]
prices?: Prices
feeds?: Feed<unknown>[]
private approvals?: JwtString[]
private prices?: Prices
private feeds?: Feed<unknown>[]

setApprovals(approvals: JwtString[]) {
this.approvals = approvals
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { OrganizationAction } from '../domain'
import EvaluationRequestBuilder from './evaluation-request'

export default class OrganizationBuilder extends EvaluationRequestBuilder {
action: OrganizationAction | null = null
nonce: string
export default class OrganizationBuilder {
private action: OrganizationAction | null = null
private nonce: string

setAction(action: OrganizationAction) {
this.action = action
Expand Down
11 changes: 11 additions & 0 deletions packages/engine-request/src/lib/builders/request-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@ import { Category } from '../domain'
import OrganizationBuilder from './organization-request'
import WalletRequestBuilder from './wallet-request'

/**
* Creates a request builder for either Wallet or Organization categories.
* @param category - The category of the request to build. Either 'wallet' or 'organization'.
* @returns A builder instance specific to the provided category, either WalletRequestBuilder or OrganizationBuilder.
* @example
* // For wallet-related requests
* const walletBuilder = buildRequest('wallet');
*
* // For organization-related requests
* const organizationBuilder = buildRequest('organization');
*/
export function buildRequest(category: 'wallet'): WalletRequestBuilder
export function buildRequest(category: 'organization'): OrganizationBuilder
export function buildRequest(category: Category): OrganizationBuilder | WalletRequestBuilder {
Expand Down
80 changes: 66 additions & 14 deletions packages/engine-request/src/lib/builders/wallet-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,28 @@ import {
} from '@narval/policy-engine-shared'
import { v4 } from 'uuid'
import { BuildResponse, WalletAction } from '../domain'
import EvaluationRequestBuilder from './evaluation-request'

export default class WalletRequestBuilder extends EvaluationRequestBuilder {
action: WalletAction
resourceId: string
nonce: string

/**
* Builder class for creating wallet-related requests, such as signing transactions, raw data, messages, or typed data.
*/
export default class WalletRequestBuilder {
private action: WalletAction
private resourceId: string
private nonce: string

/**
* Mandatory. Sets the action to be performed by the request.
* @param action - The specific wallet action to perform.
* @returns The builder instance specific to the action for method chaining.
* @example
* const request = buildRequest('wallet')
* .setAction('signTransaction')
* .setTransactionRequest(transactionRequest)
* @example
* const request = buildRequest('wallet')
* .setAction('signRaw')
* .setRawMessage(rawMessage)
*/
setAction(action: 'signTransaction'): SignTransactionBuilder
setAction(action: 'signRaw'): SignRawBuilder
setAction(action: 'signMessage'): SignMessageBuilder
Expand All @@ -34,11 +49,32 @@ export default class WalletRequestBuilder extends EvaluationRequestBuilder {
}
}

/**
* Optional. Sets the unique identifier for the transaction nonce.
* @default v4()
* @optional
* @param nonce - The nonce value as a string.
* @returns The builder instance for method chaining.
*/
setNonce(nonce: string) {
this.nonce = nonce
return this
}

protected getNonce() {
return this.nonce
}

protected getResourceId() {
return this.resourceId
}

/**
* Mandatory. Sets the resource identifier involved in the request, usually the wallet address.
* @param resourceId - The Ethereum wallet address as a string.
* @mandatory
* @returns The builder instance for method chaining.
*/
setResourceId(resourceId: string) {
this.resourceId = resourceId
return this
Expand All @@ -53,12 +89,16 @@ class SignTransactionBuilder extends WalletRequestBuilder {
return this
}

/**
* Constructs and returns the final request object based on the previously provided configurations.
* @returns {BuildResponse<SignTransactionAction>} The final request object if all required configurations are valid, otherwise returns an error object.
*/
build(): BuildResponse<SignTransactionAction> {
const nonce = this.nonce || v4()
const nonce = this.getNonce() || v4()
const request = {
action: WalletAction.SIGN_TRANSACTION,
nonce,
resourceId: this.resourceId,
resourceId: this.getResourceId(),
transactionRequest: this.transactionRequest
}
const res = SignTransactionAction.safeParse(request)
Expand Down Expand Up @@ -88,12 +128,16 @@ class SignRawBuilder extends WalletRequestBuilder {
return this
}

/**
* Constructs and returns the final request object based on the previously provided configurations.
* @returns {BuildResponse<SignRawAction>} The final request object if all required configurations are valid, otherwise returns an error object.
*/
build(): BuildResponse<SignRawAction> {
const nonce = this.nonce || v4()
const nonce = this.getNonce() || v4()
const request = {
action: WalletAction.SIGN_RAW,
nonce,
resourceId: this.resourceId,
resourceId: this.getResourceId(),
rawMessage: this.rawMessage
}
const res = SignRawAction.safeParse(request)
Expand All @@ -117,12 +161,16 @@ class SignMessageBuilder extends WalletRequestBuilder {
return this
}

/**
* Constructs and returns the final request object based on the previously provided configurations.
* @returns {BuildResponse<SignMessageAction>} The final request object if all required configurations are valid, otherwise returns an error object.
*/
build(): BuildResponse<SignMessageAction> {
const nonce = this.nonce || v4()
const nonce = this.getNonce() || v4()
const request = {
action: WalletAction.SIGN_MESSAGE,
nonce,
resourceId: this.resourceId,
resourceId: this.getResourceId(),
message: this.message
}
const res = SignMessageAction.safeParse(request)
Expand All @@ -146,12 +194,16 @@ class SignTypedDataBuilder extends WalletRequestBuilder {
return this
}

/**
* Constructs and returns the final request object based on the previously provided configurations.
* @returns {BuildResponse<SignTypedDataAction>} The final request object if all required configurations are valid, otherwise returns an error object.
*/
build(): BuildResponse<SignTypedDataAction> {
const nonce = this.nonce || v4()
const nonce = this.getNonce() || v4()
const request = {
action: WalletAction.SIGN_TYPED_DATA,
nonce,
resourceId: this.resourceId,
resourceId: this.getResourceId(),
typedData: this.typedData
}
const res = SignTypedDataAction.safeParse(request)
Expand Down
80 changes: 78 additions & 2 deletions packages/engine-request/src/lib/engine-request.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,79 @@
export function engineRequest(): string {
return 'engine-request'
import { EvaluationRequest, EvaluationResponse, JwtString, Request } from '@narval/policy-engine-shared'
import { Jwk, Payload, SigningAlg, hash, signJwt } from '@narval/signature'
import axios from 'axios'

export type SignConfig = {
principalCredential: Jwk
orgId: string
signingAlg?: SigningAlg
signer?: (payload: string) => Promise<string>
}

export type BuildEngineRequestInput = {
request: Request
signingConfig: SignConfig
}

const buildPayloadFromRequest = (request: Request, jwk: Jwk, orgId: string): Payload => {
return {
requestHash: hash(request),
sub: jwk.kid,
iss: orgId,
iat: new Date().getTime()
}
}

export const signPayload = async ({ payload, jwk }: { payload: Payload; jwk: Jwk }): Promise<JwtString> => {
const jwt = await signJwt(payload, jwk)
return jwt
}

export const signPayloadSafe = async ({
payload,
jwk
}: {
payload: Payload
jwk: Jwk
}): Promise<{ success: boolean; jwt: JwtString | null; error?: unknown }> => {
try {
const jwt = await signJwt(payload, jwk)
return { success: true, jwt }
} catch (error) {
return { success: false, jwt: null, error }
}
}

export const signEngineRequest = async ({ request, signingConfig }: BuildEngineRequestInput): Promise<JwtString> => {
const { principalCredential, signingAlg, signer } = signingConfig
const payload = buildPayloadFromRequest(request, principalCredential, signingConfig.orgId)
const authentication = signer
? await signJwt(payload, principalCredential, { alg: signingAlg }, signer)
: await signJwt(payload, principalCredential, { alg: signingAlg })
return authentication
}

export const signEngineRequestSafe = async ({
request,
signingConfig
}: BuildEngineRequestInput): Promise<{ success: boolean; jwt: JwtString | null; error?: unknown }> => {
try {
const authentication = await signEngineRequest({ request, signingConfig })
return { success: true, jwt: authentication }
} catch (error) {
return { success: false, jwt: null, error }
}
}

export const sendEvaluationRequest = async (
evaluationRequest: EvaluationRequest,
engineUrl: string
): Promise<EvaluationResponse> => {
const response = await axios.post<EvaluationResponse>(engineUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(evaluationRequest)
})
return response.data
}

0 comments on commit 8b6eece

Please sign in to comment.