mojaloop / mojaloop-specification

This repo contains the specification document set of the Open API for FSP Interoperability
https://docs.mojaloop.io/api
Other
20 stars 40 forks source link

Start investigating the possibility of a v2.0 FSPIOP API specification #118

Closed pedrosousabarreto closed 1 year ago

pedrosousabarreto commented 1 year ago

Draft request / to be later changed to comply with template:

My proposal is that we start thinking about what can be done to simplify our API, and perhaps publish it as v2.0 while still supporting v1.1. If this makes sense, then vNext, when released could ship with the current FSPIOP v1.1 as well as the new 2.0.

Starting points for discussion:

henrka commented 1 year ago

Thanks @pedrosousabarreto,

The URI /participants should represent either FSPs/Participants or participant's parties, but never both

Can you please elaborate a bit more what you on what you are after?

Currently, we have the following setup:

The main differences between /parties and /participants are:

The ideal scenario is that if we are trying to fetch or manipulate Party records, then we should use the URI "/parties", and in the case of FSPs/Participants the URI "/participants"

If you want to fetch information about a Party (an account holder), then you use GET /parties/{type}/{identifier}. There is no possibility to manipulate anything about a Party using the FSPIOP API, as that information is stored in their respective FSPs.

If you want to manipulate or fetch which Participant (an FSP) that an account holder is located in, then you use POST /participants (manipulate) or GET /participants/{type}/{identifier} (fetch).

(Ideally the manipulate should use HTTP method PUT instead, but to have a consistent architecture in the API PUTs are always used for callbacks, please see HTTP Methods and HTTP sequence flow for more information)

BTW, the standard method to address the problem of children of a resource is to use subcollections, like so /participants/:participant_id/parties

Yes, that might be the standard, but then you need to know which participant id that should be used to retrieve the party, so just following a standard way of addressing might give you other issues. The setup that we have today allows an FSP to directly send a GET /parties/{type}/{identifier} request without any participant information. The actual use case that an integrator is trying to achieve might not even care at all about the participant.

Additionally, a request like that seems more valid in a centralized setup, e.g. using a Mojaloop Switch, than the decentralized API that the FSPIOP API is meant to be (supporting bilateral setups in addition to supporting it with a Switch).

Ideally the FSPIOP api should not use resources that have another

I guess what you are after here is that party information should be under the participant resource? Please see above for reasoning.

Simplify the usage of HTTP headers, in some cases our API definition requires them to be set where a body property also includes the same value, this is mostly a problem in bulk requests

It would help a lot if you could provide some concrete examples. But yes, there are some duplications at some places, due to for example that some information is needed in the API for routing, but the same information is also needed to comply with Interledger.

The specificity of accept and content-type on requests is not adding any value, to my knowledge we don't support multiple input or output formats, we should consider simplifying and use only "application/json".

With "to my knowledge we don't support multiple input or output formats", are you referring specifically to the Mojaloop Switch? The API is not limited to the capabilities of Mojaloop. For example we are using it for other interoperability purposes even without a Switch.

The content-type and accept header used for version negotiating was at least used in Mowali. Our product supported multiple versions at the same time, while the other product only supported a single version. Version negotiation allowed interoperability between these two so that our product could send exactly what was expected by the other product. In theory, other products could use another version to us without us having to configure anything. Is this not providing value to you?

Having the version in the URI is another alternative, but that is definitely not RESTful: URIs should represent resources and not versions.

Use of Interledger v4 if this makes sense

Yes, ideally we should move away from Interledger v1. There are some other change requests related to this (see for example #13 and #14), but there hasn't really been any push towards changing.

elnyry-sam-k commented 1 year ago

Thanks for creating the issue @pedrosousabarreto , appreciate it and thanks Henrik for the initial response.

Few quick comments:

  1. For the issue of /participants resource being used on both FSPIOP and Admin APIs - I think we should pursue that on the Admin/Operations API side, where we already have an open request (the one you referenced from 2019). And because it affects a smaller number of adopters (DFSP implementations should far outnumber Switch implementations).
  2. The question of supporting API versions should be left to schemes and implementations. The CCB doesn't/shouldn't make decisions such as deprecating certain versions unless there are known critical issues.
  3. This is more of a suggestion - maybe we can discuss more on the DA (as it is not APIs / CCB/SIG related) - I think it is better for a first version of an implementation to support one version fully as "release-able / publish-able / prod-quality", whether it is v1.1 or v2.0 but not both. Once there's a stable implementation for one version, it can be enhanced to support a future version (if v1.1 is implemented first) or provide backwards compatibility if needed (if v2.0 is implemented first).
  4. Interledger v4 usage - I think merits a separate issue of its own to discuss it further to analyze what changes, trade-offs are involved and what implementation changes will be needed because of it.
MichaelJBRichards commented 1 year ago

Just to summarise where I think we are now on the ILP packet:

The ILP V4 packet could be incorporated in the API now and without changes to the API, since its structure is opaque to the API definition (it's just a binary string). Although the V4 packet contains more information than earlier versions, it still does not contain sufficient information to substitute for the body of a POST /transfers request. Our current pattern says that, rather than expecting to find some of the information required to process a request in the ILP packet and some elsewhere, it would be simpler for participants to look for it all in the same place: which currently has to be the body of the message. It's also simpler because no decoding is required.

All of which has led us to the following position: we're not against moving from ILP V1 to ILP V4, but there are no obvious benefits to doing so; and, in any case, doing so would require some changes to documentation but not to the API itself.

I'd also like to address the question of field duplication between the header and the body of the request. I sometimes hear it said that the FSPIOP-Source and FSPIOP-Destination fields in the header duplicate information in the body of the request. This is not, however, the case in fact, for (at least) the following reasons:

It may be, of course, that you had other duplications in mind. In that case, I hope that you will raise them.

pedrosousabarreto commented 1 year ago

Hi @henrka,

Have to say I'm not a REST purist, but if you have a "/participants" URI, it must represent participants resource, i.e., should contain participant records/entities.

An additional problem is that the /participants URI does not represent participants in the FSPIOP API but does so in the Admin API. FSPIOP API:

Reaching sub collections and filter/search patterns:

The standard way of interpreting requests like GET /participants/{Type}/{ID}/{SubId} is to assume {Type} is an identifier of participants and {id} is a child collection of the identified participant.

Looks like when we use the /participants URI in the FSPIOP API, we're not targeting participants, but party to participant associations. Suggestion: In the FSPAPI chagen the /participants URI to /partyAssociations and use a more standard approach to filtering and searching:

The standard way of querying a resource is using query parameters, not artificial URLs that do not represent the resource structure - same for sorting, pagination or limits - which we don't use at all.

The current different between POST /participants (bulk) and POST /participants/{Type}/{ID} also makes no sense to me, especially since in the case of the latter, the Type and ID are also provided in the body of the post. Simply have the POST /participants (or as I suggest /partyAssociations) receive an array of associations, the same request can now be used with a single association of a bulk association.

If no one else thinks this is confusing or not per standards then I'll rest my case.

I get that most, if not all, of these decisions were made for a valid reason, but if that reason is no longer valid and we have the opportunity of creating a new cleaner API, why not take it with vNext, where we can still support 1.1 and 2.0 at the same time.

Regarding the content-type and accept header, the argument you make is that in theory it adds value. Well, I wage that in practice it adds necessary code and complexity for no practical value. If no one else uses it of sees value in it, maybe it belongs in a fork.

MichaelJBRichards commented 1 year ago

One further comment on the question of complexity. I had some discussions with LAV (which tells you how long ago they were...) about simplifying the API. The route that we started to go down was that the PM structure would enable us effectively to behave like Stripe: that is, to produce multiple user-facing APIs ad lib. These APIs would all map on to the same underlying API structure and would encapsulate the (necessary) complexities of the secure interchange of Mojaloop messages, but they would represent things to the external world in a way that looked simple to its (possibly quite small) class of users. Is there a reason why that wouldn't work just as well, and with less upheaval?

pedrosousabarreto commented 1 year ago

Hi Michael, it's a valid strategy.

The strategy I'm proposing is a different one; it focuses on removing complexity instead of adding a layer of simplification on top of the existing complexity - which is in itself increasing complexity by adding another layer of component(s).

pedrosousabarreto commented 1 year ago

I'd also like to propose that we create a Node.js Mojaloop client lib for vNext, instead of another API that wraps lower level APIs. This would serve the same purpose of the skd-adapter, but as a simple client library that implementers can use in their code and not a deployable service.

Maybe followup that one with other versions of the client lib for other tech stacks, like Java or .net.

Food for thought!

pedrosousabarreto commented 1 year ago

It looks like our official documentation agrees with my thinking, both in regards to versioning and resources naming.

Refs:

Versioning

API URIs should include a version identifier in the format vM as a leading path element (where "M" is the Major component of the multi-part version number). The API and its version identifier element must conform to the semantic versioning8 2.0 specification for API versioning.

A client must specify the Major version number in each request. It is not possible for a client to express a requirement for a specific minor version.

The full API version number is specified in the response header (TBD) for all successful and error responses.

While an API version contract will be influenced by Major, minor, and patch levels, only the Major version number is a production API binding element-that is, a production client cannot request a particular minor version or patch level and a production server will not accept a URI request that specifies these extra elements.

However, in pre-production environments, it is anticipated that some combination of minor, patch, pre-release, and metadata suffixes would be supported in client requests (as defined in semver [3]) and may be expressed in pre-production URIs to assist with development and integration scenarios.

URIs Define Resources

A well-designed URI pattern makes an API easy to consume, discover, and extend, just as a carefully designed API does in a traditional programming language. Pure REST disdains this principle in favor of HATEOAS. But pragmatic REST follows a normal pattern for URI definitions to improve human understanding, even if HATEOAS principles are employed for discovery.

URI paths that refer to a collection of objects should consist of a plural noun, e.g. /customers, to refer to a set of customers. When a collection can have only one instance, the singular noun should be used to avoid confusion. E.g. GET /transfers/:id/fulfillment is correct, since there is only one fulfillment object per identified transfer.

URI paths that refer to a single object should consist of a plural noun (representing the collection), followed by a predefined unique identifier. E.g., /customers/123456 to refer to the specific customer with number 123456. The identifier must be unique within the containing collection and persist for the life of the object within that collection. IDs must not be ordinal values-ordinal retrieval of objects from a collection is possible using query parameters on the collection URI.

URI paths may have a prefix to identify the environment, version, or other context of the resource. Nothing should follow the identifying path but collections and object references.

URI path and query segment identifiers should be chosen from the Roman character set, [0-9A-Za-z]. Use camelCase to define the elements of the URI path. Do not use snake_case.

For the avoidance of doubt, "_" (underscore) and "-" (hyphen) should not be used in URI path or query segment identifiers.

This probably seems a bit parochial. The purpose is to find a well-defined URI format that is consistent with wide-spread practice, easy to define, predictable, and that maps to native environments and conventions. It isn't going to satisfy everyone. Here is reasoning behind this constraint:

CapitalCase and camelCase are the defacto standard for NodeJS and JavaScript and are a common constraint in URI definition: URI path segments are often mapped to JS internal resources and so conforming to JS naming conventions makes sense.

Field names in JSON and SQL should also follow this convention since they are often automatically mapped into variable name space and can be referenced in URIs as path or query segment identifiers.

We should also avoid the use of "$" unless it is required by a library (e.g. JQuery). IBM JCL has passed away; let it rest in peace. There are better scope control tools to separate name spaces than introducing non-roman symbols.

We should avoid "-" (hyphen) in path segment and query parameter names as it does not map into variable names, SQL, or JSON field name identifiers.

Underscore characters must be escaped in markdown source by prefixing each with a "\" character.

Snake_case has been reported to be slightly easier to read than camelCase in variable names, but it actually does not improve readability of URIs, as it visually interferes with path and query segment delimiters making it difficult to visually parse them. And when URIs are underlined in presentation, the underscores become illegible. URI Parameters

Use a standard and predictable set of optional parameters in a consistent way.

A set of standard query parameters should be used for collections to enable caller control over how much of the collection they see. E.g. "count" to determine how many objects to return, "start" to determine where to start counting in the result set, and "q" as a generic free-form search query. We will define the standard set of parameters as we go and will apply them consistently.

henrka commented 1 year ago

Thanks @pedrosousabarreto,

Have to say I'm not a REST purist, but if you have a "/participants" URI, it must represent participants resource, i.e., should contain participant records/entities.

We are not stating that we are fully REST-compliant, and this sounds like a thing you would do if you had an API stating that it was fully REST-compliant.

An additional problem is that the /participants URI does not represent participants in the FSPIOP API but does so in the Admin API.

I can't speak for what is available in the Admin API, but as far as I know the FSPIOP API has never had a goal of resembling what is available in the Admin API. FSPIOP API has been around since 2017 and is not tightly coupled with the Mojaloop Switch. It can be (and is) used without Mojaloop.

There is no use case for creating an actual participant in FSPIOP API, what you are creating is an account holder identified by a type and identifier is available under this participant.

Suggestion: In the FSPAPI chagen the /participants URI to /partyAssociations and use a more standard approach to filtering and searching:

We are not providing a standard API that allows you to filter and search account holders. To avoid leakage of information you need to provide both a type and identifier. For example you don't want other FSPs to know how many account holders you have identified by MSISDN.

Using query parameters for this is wrong in my opinion. Query string processing is more error-prone than a defined URI. This is not a front-end API that should be used in some GUI. This is a pure back-end API between FSPs, with an optional Switch and ALS between. It is not intended to be used in other ways. It should never be used for sorting or filtering like you could do in a GUI. You will never just use a type, or just an identifier. Both are required.

Regarding using resource name /partyAssociations instead of /participants, I understand the reasoning behind it and I would have seen it as a good alternative in 2017-18. Now I don't really see the cost benefit of making the change. I think most people understands the use of the resource /participants instead of /partyAssociations, especially if they read the documentation for the API.

The current different between POST /participants (bulk) and POST /participants/{Type}/{ID} also makes no sense to me, especially since in the case of the latter, the Type and ID are also provided in the body of the post.

That is not true, the type and the identifier is not provided in the body of the POST /participants/{Type}/{ID}. If it did, then I understand that it would make no sense.

Simply have the POST /participants (or as I suggest /partyAssociations) receive an array of associations, the same request can now be used with a single association of a bulk association.

There is an open change request that among other things discusses having just a POST /participants, please see #115. POST /participants/{Type}/{ID} was added as the normal use case is that you just create or update the participant for one single account holder. Using the bulk version is more or less only valid when you as an FSP start a new system and want to set all your existing account holders as belonging to you.

Regarding the content-type and accept header, the argument you make is that in theory it adds value. Well, I wage that in practice it adds necessary code and complexity for no practical value. If no one else uses it of sees value in it, maybe it belongs in a fork

I'm sorry, but is being used in production just theory to you? It had a very practical value to us and the other system, as we didn't have to do any updates to support multiple versions. I hope that can be considered value to you?

A scheme could also mandate exactly which versions that should be used, which avoids any real content-negotiation needed, hence you could avoid the code and complexity.

henrka commented 1 year ago

Thanks @pedrosousabarreto,

It looks like our official documentation agrees with my thinking, both in regards to versioning and resources naming.

This document was written in 2016 if I understand it correctly. It was not used as input when the FSPIOP API was designed (just for information).

I recommend the last section from the link.

We May Need to Give REST a Rest As we design the interconnection APIs between components and between participating systems, we may find API requirements that don't precisely match the Pragmatic REST pattern defined here. We will evaluate these case-by-case and make the best choice to support the project goals.

pedrosousabarreto commented 1 year ago

I'm afraid I was not very competent in communicating the motivation behind this proposal.

My proposal is, as per the title, is: "Start investigating the possibility of a v2.0 FSPIOP API specification".

What I'd like us to do is to use vNext as an opportunity, to think about evolving the future of Mojaloop and not have the usual entrenched and defensive reaction trying to justify the past. It's alright, no one is criticising anyone, I'm merely trying to add value for the future.

Now, with Mojaloop vNext, as intentionally designed by the reference architecture, we can have multiple external APIs; this means that we can have a 2.0 FSPIOP API side by side with a 1.1 FSPIOP API and even a future ISO20022 API.

Again: I am not suggesting we break anything!!!

henrka commented 1 year ago

Thanks @pedrosousabarreto,

My proposal is, as per the title, is: "Start investigating the possibility of a v2.0 FSPIOP API specification".

Great, the current candidate items for version 2.0 is available in #65. New proposals are most welcome as change requests!

What I'd like us to do is to use vNext as an opportunity, to think about evolving the future of Mojaloop and not have the usual entrenched and defensive reaction trying to justify the past. It's alright, no one is criticising anyone, I'm merely trying to add value for the future.

Just to be clear, I don't have anything at all against evolving the API in a way that provides actual business value. But, in my mind we also need to defend existing implementers in such a way that they feel compelled to update to a version 2.0. For example, changing the resource name /participants to /partyAssociations because it might not be fully REST-compliant doesn't, in my mind, justify the cost of the change for existing implementors (even if it might be small).

I'm very happy that you bring up things that you feel are not good today, so that we can discuss them.

MichaelJBRichards commented 1 year ago

Just to comment - I think repeating @elnyry-sam-k: we can, of course, support multiple external APIs in the current version as well. I assume that this does not mean multiple external APIs in the same scheme - @pedrosousabarreto , can you confirm? and if that is what you meant, how you envisage it working...

pedrosousabarreto commented 1 year ago

I have no opinion on that, I'm merely proposing a possibility. How do you see FSPIOP and ISO20022 working in the same scheme?

henrka commented 1 year ago

That sounds like a good possibility if Mojaloop could support that, for example in a country there might be banks that prefer an ISO 20022 API, and mobile money operators that already have existing support for the FSPIOP API.

MichaelJBRichards commented 1 year ago

Weeell... You could support @henrka's scenario in one of (at least) two ways. You could have two interop schemes, one for banks which uses ISO and one for MMSs that uses, say, FSPIOP; and have a Mojaloop meta-scheme which interoperates between them via CNPs. Or, you could have everybody belong to the same scheme and manage signing across API formats via a canonicalisation function like the one I talked about at the convening. As long as the regulator would support it, natch.

pedrosousabarreto commented 1 year ago

I think this is one of the most important questions we need to answer. I assumed that with the reference architecture, with its internal canonical model (ie, internal API) and multiple external APIs doing protocol translation we were addressing it; as Michael is saying we can have a canonical non-repudiation mechanism. But looks like more details need sorting, perhaps Interledger v4 can help.

What is the established strategy today for a Mojaloop implementer in regards to an upgrade path if the FSPIOP protocol evolves? How does that implementer upgrade the switch to benefit from latest features that might come with a new version or a different protocol? I assume it's not ideal that a scheme has to stay forever in the same protocol and version, or, requires all of its participants to do a full stop, upgrade and re-connect phase exercise.

As per the reference architecture, Mojaloop vNext needs to support multiple external APIs and versions.

henrka commented 1 year ago

What is the established strategy today for a Mojaloop implementer in regards to an upgrade path if the FSPIOP protocol evolves? How does that implementer upgrade the switch to benefit from latest features that might come with a new version or a different protocol? I assume it's not ideal that a scheme has to stay forever in the same protocol and version, or, requires all of its participants to do a full stop, upgrade and re-connect phase exercise.

I can only answer from the FSPIOP API viewpoint, and that is what we have the version negotiation for. The version negotiation is meant to allow different versions. In theory, and what we showed in practice for Mowali, is that you can use/support multiple versions at the same time.

henrka commented 1 year ago

Or, you could have everybody belong to the same scheme and manage signing across API formats via a canonicalisation function like the one I talked about at the convening. As long as the regulator would support it, natch.

An improved signing feature using canonicalization sounds like an interesting feature to have. At least a signing feature that is not as strict as today with the exact content..

MichaelJBRichards commented 1 year ago

On the upgrade strategy, I think @elnyry-sam-k is the man you want. If I recall correctly, he and Lewis put together the Mojaloop strategy for versioning and upgrading.

elnyry-sam-k commented 1 year ago

On the upgrade strategy, I think @elnyry-sam-k is the man you want. If I recall correctly, he and Lewis put together the Mojaloop strategy for versioning and upgrading.

Glad to see that you remember it Michael, here it is: https://github.com/mojaloop/documentation-artifacts/blob/master/presentations/April%202020%20Community%20Event/Presentations/mojaloop_versioning_pi10.pptx.pdf . But these are the options, Business/Product decision about which one(s) is/are acceptable need to made..

henrka commented 1 year ago

@pedrosousabarreto, may I propose that we close this generic issue and that you instead create separate issues for each thing that you would like to change? For example where the improved signing feature using canonicalization is one issue. This should make it easier to follow the discussions.

henrka commented 1 year ago

I'm closing this generic issue in the favor of separate issues for each specific change, as discussed in FSPIOP API SIG meetings and also mentioned in comment https://github.com/mojaloop/mojaloop-specification/issues/118#issuecomment-1558852203. @pedrosousabarreto, you are more than welcome to create new specific issues.

Issues that are accepted to be in version 2.0 will then be added to #65.