Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

938/mk/create receiver #986

Merged
merged 26 commits into from
Jan 24, 2023
Merged

938/mk/create receiver #986

merged 26 commits into from
Jan 24, 2023

Conversation

mkurapov
Copy link
Contributor

@mkurapov mkurapov commented Jan 17, 2023

Changes proposed in this pull request

  • Adding CreateReceiver mutation to GraphQL admin API, which allows creating a remote (or local) incoming payment

Context

Checklist

  • Related issues linked using fixes #number
  • Tests added/updated
  • Documentation added
  • Make sure that all checks pass

@github-actions github-actions bot added pkg: backend Changes in the backend package. pkg: map Changes in mock-account-provider package type: source Changes business logic type: tests Testing related labels Jan 17, 2023
const { paymentPointerUrl } = args
const grant = await getGrant(deps, paymentPointerUrl, [
AccessAction.Create,
AccessAction.ReadAll
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to have a GetRemoteIncomingPayment query in the Admin API in addition to this "Create" one?

Copy link
Contributor Author

@mkurapov mkurapov Jan 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created separate issue, I'll mark it as nice-to-have:

CC @wilsonianb

openPaymentsClient: AuthenticatedClient
}

export async function createRemoteIncomingPaymentService(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a separate PR, I want the ReceiverService to use this service to fetch remote incoming payments, since it has some similar logic currently. (ReceiverService determines whether an incoming payment (or connection) is local and remote, and fetches either)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the mutation was instead CreateReceiver?
I like the idea of making it clear what the purpose of this is.
We could add a ReceiverService.create (which could create locally or remotely) but still move remote management to RemoteIncomingPaymentService if we wanted.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔

  • Are you suggesting we also remove CreateIncomingPayment and consolidate both remote and incoming payment creation with CreateReceiver?
  • If not, is it confusing having both CreateIncomingPayment and CreateReceiver? Could someone get confused why are they different at first glance?

Copy link
Contributor

@wilsonianb wilsonianb Jan 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking we have both

  • CreateIncomingPayment for a user to get paid
  • CreateReceiver for a user to pay someone

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does fit in well with the quote creation, receiver attribute as well.

RemoteIncomingPayment GraphQL type can also be also a Receiver instead, going to make that change

@@ -0,0 +1,92 @@
import { v4 as uuid } from 'uuid'
Copy link
Contributor Author

@mkurapov mkurapov Jan 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took this from the open-payments package, since I wanted to mock the results from the open-paymentsclient calls. I could also export these mocks directly from the library if that feels like a better solution

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm good with exported mocks

@mkurapov mkurapov marked this pull request as ready for review January 17, 2023 19:58

const grantOptions = {
authServer: paymentPointer.authServer,
accessType: 'incoming-payment' as const,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was hoping 👆 would allow 👇 but 😞

Suggested change
accessType: 'incoming-payment' as const,
accessType: AccessType.IncomingPayment,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does allow AccessType.IncomingPayment, but I still need to do as incoming-payment` when making the call for the grant request below

@@ -0,0 +1,92 @@
import { v4 as uuid } from 'uuid'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm good with exported mocks

openPaymentsClient: AuthenticatedClient
}

export async function createRemoteIncomingPaymentService(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the mutation was instead CreateReceiver?
I like the idea of making it clear what the purpose of this is.
We could add a ReceiverService.create (which could create locally or remotely) but still move remote management to RemoteIncomingPaymentService if we wanted.

* feat(backend): add create method for receiverService

* chore(backend): createRemoteIncomingPayment mutation -> createReceiver mutation

* chore(backend): update ReceiverService tests to use mocks when fetching remote incoming payments

* chore(backend): use open-payments mocks

* chore(backend): fix receiver tests

* chore(backend): update receiver tests

* chore(backend): couple more updated tests
@mkurapov mkurapov changed the title 938/mk/create remote incoming payment 938/mk/create receiver Jan 19, 2023
@@ -245,7 +245,7 @@ export class IncomingPayment
ilpStreamConnection: Connection
}): OpenPaymentsIncomingPayment {
return {
id: this.id,
id: this.url,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Copying comment from other PR for visibility):

OpenPayment API IncomingPayments return the full url instead of just the uuid. This reflects what is done in the IncomingPaymentRoutes, but standardizes it when converting between IncomingPayment model and what Open Payments expects

@@ -155,6 +157,8 @@ export interface AppServices {
paymentPointerKeyRoutes: Promise<PaymentPointerKeyRoutes>
paymentPointerRoutes: Promise<PaymentPointerRoutes>
incomingPaymentService: Promise<IncomingPaymentService>
remoteIncomingPaymentService: Promise<RemoteIncomingPaymentService>
receiverService: Promise<ReceiverService>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not new, just adding so we get proper typing when we resolve this dependency

Comment on lines +127 to +129
return incomingPaymentOrError.toOpenPaymentsType({
ilpStreamConnection: connection
})
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once I split out the IncomingPayment types in open-payments with and without the ilpStreamConnection

The corresponding connection fetching will (should?) be optional

@mkurapov
Copy link
Contributor Author

Will also update the Postman collection once this is merged in

packages/backend/src/graphql/schema.graphql Outdated Show resolved Hide resolved
}
}

async function getGrant(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we somehow consolidate this and

async function getIncomingPaymentGrant(

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, for sure. I was going to do that as a follow up so the PR was manageable

export interface RemoteIncomingPaymentService {
create(
args: CreateRemoteIncomingPaymentArgs
): Promise<OpenPaymentsIncomingPayment>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
): Promise<OpenPaymentsIncomingPayment>
): Promise<OpenPaymentsIncomingPayment | IncomingPaymentError>

to be able to return IncomingPaymentError.UnknownPaymentPointer (instead of throwing) and return the corresponding code and message in the createReceiver resolver

export enum IncomingPaymentError {
UnknownPaymentPointer = 'UnknownPaymentPointer',

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea

Copy link
Contributor Author

@mkurapov mkurapov Jan 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, do you mean

// ReceiverService
async function createLocalIncomingPayment(
  deps: ServiceDependencies,
  args: CreateReceiverArgs,
  paymentPointer: PaymentPointer
): Promise<OpenPaymentsIncomingPayment | IncomingPaymentError> <---

instead? RemotePaymentService can only return the OpenPaymentsIncomingPayment since it uses the client

edit: I see what you mean, although I'd have to pass it all the way through and edit receiverService.create return type as well 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if having a custom Error object with something like a userMessage property would be a better approach

Copy link
Contributor

@wilsonianb wilsonianb Jan 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I think it needs to be in RemotePaymentService.
The payment pointer lookup in getGrant should somehow bubble up an error to allow the resolver to return 4XX.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if having a custom Error object with something like a userMessage property would be a better approach

That'd probably work too. I'm just thinking through the lens of the other services' *Errors.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, I'll give it a try 👍

const errorMessage = 'Could not get connection for local incoming payment'
deps.logger.error({ incomingPaymentOrError }, errorMessage)

throw new Error(errorMessage)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I can also return an additional type of ReceiverError here as well, together with L93?

Comment on lines 23 to 31
export const receiverErrorToCode = (error: ReceiverError): number =>
isIncomingPaymentError(error)
? incomingPaymentErrorToCode[error]
: remoteIncomingPaymentErrorToCode[error]

export const receiverErrorToMessage = (error: ReceiverError): string =>
isIncomingPaymentError(error)
? incomingPaymentErrorToMessage[error]
: remoteIncomingPaymentErrorToMessage[error]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could have also just spread the two errorToMessage/errorToCode objects into a new receiver errorToMessage/errorToCode object, but thought this was easier to follow

wilsonianb
wilsonianb previously approved these changes Jan 23, 2023
packages/backend/src/open_payments/receiver/errors.ts Outdated Show resolved Hide resolved
? serializeAmount(incomingAmount)
: undefined
})
) as Receiver
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be worth adding a createReceiver (here or in tests/receiver) that returns a guaranteed Receiver?

Copy link
Contributor Author

@mkurapov mkurapov Jan 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be better to have Receiver.fromIncomingPayment/fromConnectionto throw errors instead of returning undefined? It'll allow us to return error messages back to the caller

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 that or return Receiver | ReceiverError 😬

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ended up going with just throwing, will likely be changing out how the Receiver class works anyway if we want to go with type Receiver = IncomingPayment | Connection

wilsonianb
wilsonianb previously approved these changes Jan 23, 2023
expect(receiver).toBeUndefined()
const connection = connectionService.get(incomingPayment) as Connection

;(connection.ilpAddress as string) = 'not base 64 encoded'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this you or pnpm format?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pnpm format

@mkurapov mkurapov merged commit d40c2e6 into main Jan 24, 2023
@mkurapov mkurapov deleted the 938/mk/create-remote-incoming-payment branch January 24, 2023 00:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pkg: backend Changes in the backend package. pkg: map Changes in mock-account-provider package type: source Changes business logic type: tests Testing related
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow creating remote incoming payments via GraphQL API
2 participants