@mojaloop/sdk-scheme-adapter 24.8.0 → 24.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/.yarn/cache/{@mojaloop-central-services-shared-npm-18.23.2-5627dda2e3-5fb84a745d.zip → @mojaloop-central-services-shared-npm-18.24.0-34d9578f93-8c24dea9e4.zip} +0 -0
  2. package/.yarn/cache/@mojaloop-inter-scheme-proxy-cache-lib-npm-2.5.0-eeb80fe407-bf7e14ce33.zip +0 -0
  3. package/.yarn/cache/{@mojaloop-sdk-standard-components-npm-19.13.0-5ccf814779-a4c88f8c1b.zip → @mojaloop-sdk-standard-components-npm-19.14.0-0f02852725-2dcd03cbba.zip} +0 -0
  4. package/.yarn/cache/ioredis-npm-5.6.1-d69383b35a-89100a97b2.zip +0 -0
  5. package/.yarn/cache/yaml-npm-2.7.1-9e92f81b45-385f8115dd.zip +0 -0
  6. package/.yarn/install-state.gz +0 -0
  7. package/CHANGELOG.md +7 -0
  8. package/modules/api-svc/package.json +4 -4
  9. package/modules/api-svc/src/InboundServer/api.yaml +172 -133
  10. package/modules/api-svc/src/InboundServer/api_iso20022.yaml +39 -0
  11. package/modules/api-svc/src/InboundServer/api_template.yaml +170 -66
  12. package/modules/api-svc/src/InboundServer/handlers.js +26 -6
  13. package/modules/api-svc/src/InboundServer/index.js +2 -1
  14. package/modules/api-svc/src/InboundServer/middlewares.js +61 -21
  15. package/modules/api-svc/src/config.js +1 -0
  16. package/modules/api-svc/src/lib/model/InboundPingModel.js +100 -0
  17. package/modules/api-svc/src/lib/model/index.js +3 -2
  18. package/modules/api-svc/test/__mocks__/@mojaloop/sdk-standard-components.js +2 -0
  19. package/modules/api-svc/test/unit/lib/model/InboundPingModel.test.js +124 -0
  20. package/modules/outbound-command-event-handler/package.json +2 -2
  21. package/modules/outbound-domain-event-handler/package.json +1 -1
  22. package/modules/private-shared-lib/package.json +2 -2
  23. package/package.json +1 -1
  24. package/.yarn/cache/@mojaloop-central-services-logger-npm-11.7.0-bd1e0d14fc-386d668607.zip +0 -0
  25. package/.yarn/cache/@mojaloop-inter-scheme-proxy-cache-lib-npm-2.4.0-cc60317958-8e539f65c3.zip +0 -0
Binary file
package/CHANGELOG.md CHANGED
@@ -1,4 +1,11 @@
1
1
  # Changelog: [mojaloop/sdk-scheme-adapter](https://github.com/mojaloop/sdk-scheme-adapter)
2
+ ## [24.9.0](https://github.com/mojaloop/sdk-scheme-adapter/compare/v24.8.0...v24.9.0) (2025-04-22)
3
+
4
+
5
+ ### Features
6
+
7
+ * **csi-1408:** added InboundPingModel ([#574](https://github.com/mojaloop/sdk-scheme-adapter/issues/574)) ([ef84eee](https://github.com/mojaloop/sdk-scheme-adapter/commit/ef84eee0d4ca0fe3c6c84fb72cca196b4ffd707b)), closes [#573](https://github.com/mojaloop/sdk-scheme-adapter/issues/573)
8
+
2
9
  ## [24.8.0](https://github.com/mojaloop/sdk-scheme-adapter/compare/v24.7.0...v24.8.0) (2025-04-15)
3
10
 
4
11
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mojaloop/sdk-scheme-adapter-api-svc",
3
- "version": "21.0.0-snapshot.32",
3
+ "version": "21.0.0-snapshot.34",
4
4
  "description": "An adapter for connecting to Mojaloop API enabled switches.",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -21,7 +21,7 @@
21
21
  "start:debug": "node --inspect=0.0.0.0:9229 src/index.js",
22
22
  "build": "yarn run build:openapi",
23
23
  "build:openapi": "yarn run build:openapi:inbound",
24
- "build:openapi:inbound": "openapi bundle --output src/InboundServer/api.yaml --ext yaml ../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/openapi.yaml",
24
+ "build:openapi:inbound": "openapi bundle --output src/InboundServer/api.yaml --ext yaml src/InboundServer/api_template.yaml",
25
25
  "clean:dist": "rm -Rf dist",
26
26
  "lint": "eslint ./src/",
27
27
  "lint:fix": "eslint ./src/ --fix",
@@ -68,12 +68,12 @@
68
68
  "@mojaloop/central-services-error-handling": "13.0.7",
69
69
  "@mojaloop/central-services-logger": "11.8.1",
70
70
  "@mojaloop/central-services-metrics": "12.5.0",
71
- "@mojaloop/central-services-shared": "18.23.2",
71
+ "@mojaloop/central-services-shared": "18.24.0",
72
72
  "@mojaloop/event-sdk": "14.4.0",
73
73
  "@mojaloop/logging-bc-client-lib": "0.5.8",
74
74
  "@mojaloop/ml-schema-transformer-lib": "2.7.1",
75
75
  "@mojaloop/sdk-scheme-adapter-private-shared-lib": "workspace:^",
76
- "@mojaloop/sdk-standard-components": "19.13.0",
76
+ "@mojaloop/sdk-standard-components": "19.14.0",
77
77
  "ajv": "8.17.1",
78
78
  "axios": "1.8.4",
79
79
  "body-parser": "2.2.0",
@@ -58,6 +58,45 @@ paths:
58
58
  responses:
59
59
  '200':
60
60
  description: Ok
61
+ /ping:
62
+ post:
63
+ description: The HTTP request `POST /ping` is used to validate mTLS and JWS
64
+ summary: For testing mTLS and JWS
65
+ tags:
66
+ - participants
67
+ - ping
68
+ operationId: handlePostPing
69
+ requestBody:
70
+ description: The object sent in the POST/PUT `/ping` requests with validation request ID.
71
+ required: true
72
+ content:
73
+ application/json:
74
+ schema:
75
+ type: object
76
+ properties:
77
+ requestId:
78
+ $ref: '#/components/schemas/CorrelationId'
79
+ required:
80
+ - requestId
81
+ responses:
82
+ '202':
83
+ $ref: '#/components/responses/202'
84
+ '400':
85
+ $ref: '#/components/responses/400'
86
+ '401':
87
+ $ref: '#/components/responses/401'
88
+ '403':
89
+ $ref: '#/components/responses/403'
90
+ '404':
91
+ $ref: '#/components/responses/404'
92
+ '405':
93
+ $ref: '#/components/responses/405'
94
+ '406':
95
+ $ref: '#/components/responses/406'
96
+ '501':
97
+ $ref: '#/components/responses/501'
98
+ '503':
99
+ $ref: '#/components/responses/503'
61
100
  /participants:
62
101
  post:
63
102
  description: >-
@@ -3519,27 +3558,6 @@ components:
3519
3558
  description: >-
3520
3559
  The API data type UndefinedEnum is a JSON String consisting of 1 to 32
3521
3560
  uppercase characters including an underscore character (_).
3522
- ParticipantsPostRequest:
3523
- title: ParticipantsPostRequest
3524
- type: object
3525
- description: The object sent in the POST /participants request.
3526
- properties:
3527
- requestId:
3528
- $ref: '#/components/schemas/CorrelationId'
3529
- partyList:
3530
- type: array
3531
- items:
3532
- $ref: '#/components/schemas/PartyIdInfo'
3533
- minItems: 1
3534
- maxItems: 10000
3535
- description: >-
3536
- List of PartyIdInfo elements that the client would like to update or
3537
- create FSP information about.
3538
- currency:
3539
- $ref: '#/components/schemas/Currency'
3540
- required:
3541
- - requestId
3542
- - partyList
3543
3561
  ErrorCode:
3544
3562
  title: ErrorCode
3545
3563
  type: string
@@ -3582,6 +3600,27 @@ components:
3582
3600
  properties:
3583
3601
  errorInformation:
3584
3602
  $ref: '#/components/schemas/ErrorInformation'
3603
+ ParticipantsPostRequest:
3604
+ title: ParticipantsPostRequest
3605
+ type: object
3606
+ description: The object sent in the POST /participants request.
3607
+ properties:
3608
+ requestId:
3609
+ $ref: '#/components/schemas/CorrelationId'
3610
+ partyList:
3611
+ type: array
3612
+ items:
3613
+ $ref: '#/components/schemas/PartyIdInfo'
3614
+ minItems: 1
3615
+ maxItems: 10000
3616
+ description: >-
3617
+ List of PartyIdInfo elements that the client would like to update or
3618
+ create FSP information about.
3619
+ currency:
3620
+ $ref: '#/components/schemas/Currency'
3621
+ required:
3622
+ - requestId
3623
+ - partyList
3585
3624
  PartyResult:
3586
3625
  title: PartyResult
3587
3626
  type: object
@@ -4588,6 +4627,118 @@ components:
4588
4627
  maxItems: 16
4589
4628
  required:
4590
4629
  - providers
4630
+ responses:
4631
+ '200':
4632
+ description: OK
4633
+ '202':
4634
+ description: Accepted
4635
+ '400':
4636
+ description: Bad Request
4637
+ content:
4638
+ application/json:
4639
+ schema:
4640
+ $ref: '#/components/schemas/ErrorInformationResponse'
4641
+ headers:
4642
+ Content-Length:
4643
+ $ref: '#/components/headers/Content-Length'
4644
+ Content-Type:
4645
+ $ref: '#/components/headers/Content-Type'
4646
+ '401':
4647
+ description: Unauthorized
4648
+ content:
4649
+ application/json:
4650
+ schema:
4651
+ $ref: '#/components/schemas/ErrorInformationResponse'
4652
+ headers:
4653
+ Content-Length:
4654
+ $ref: '#/components/headers/Content-Length'
4655
+ Content-Type:
4656
+ $ref: '#/components/headers/Content-Type'
4657
+ '403':
4658
+ description: Forbidden
4659
+ content:
4660
+ application/json:
4661
+ schema:
4662
+ $ref: '#/components/schemas/ErrorInformationResponse'
4663
+ headers:
4664
+ Content-Length:
4665
+ $ref: '#/components/headers/Content-Length'
4666
+ Content-Type:
4667
+ $ref: '#/components/headers/Content-Type'
4668
+ '404':
4669
+ description: Not Found
4670
+ content:
4671
+ application/json:
4672
+ schema:
4673
+ $ref: '#/components/schemas/ErrorInformationResponse'
4674
+ headers:
4675
+ Content-Length:
4676
+ $ref: '#/components/headers/Content-Length'
4677
+ Content-Type:
4678
+ $ref: '#/components/headers/Content-Type'
4679
+ '405':
4680
+ description: Method Not Allowed
4681
+ content:
4682
+ application/json:
4683
+ schema:
4684
+ $ref: '#/components/schemas/ErrorInformationResponse'
4685
+ headers:
4686
+ Content-Length:
4687
+ $ref: '#/components/headers/Content-Length'
4688
+ Content-Type:
4689
+ $ref: '#/components/headers/Content-Type'
4690
+ '406':
4691
+ description: Not Acceptable
4692
+ content:
4693
+ application/json:
4694
+ schema:
4695
+ $ref: '#/components/schemas/ErrorInformationResponse'
4696
+ headers:
4697
+ Content-Length:
4698
+ $ref: '#/components/headers/Content-Length'
4699
+ Content-Type:
4700
+ $ref: '#/components/headers/Content-Type'
4701
+ '501':
4702
+ description: Not Implemented
4703
+ content:
4704
+ application/json:
4705
+ schema:
4706
+ $ref: '#/components/schemas/ErrorInformationResponse'
4707
+ headers:
4708
+ Content-Length:
4709
+ $ref: '#/components/headers/Content-Length'
4710
+ Content-Type:
4711
+ $ref: '#/components/headers/Content-Type'
4712
+ '503':
4713
+ description: Service Unavailable
4714
+ content:
4715
+ application/json:
4716
+ schema:
4717
+ $ref: '#/components/schemas/ErrorInformationResponse'
4718
+ headers:
4719
+ Content-Length:
4720
+ $ref: '#/components/headers/Content-Length'
4721
+ Content-Type:
4722
+ $ref: '#/components/headers/Content-Type'
4723
+ headers:
4724
+ Content-Length:
4725
+ required: false
4726
+ schema:
4727
+ type: integer
4728
+ description: >-
4729
+ The `Content-Length` header field indicates the anticipated size of the
4730
+ payload body. Only sent if there is a body.
4731
+
4732
+
4733
+ **Note:** The API supports a maximum size of 5242880 bytes (5
4734
+ Megabytes).
4735
+ Content-Type:
4736
+ schema:
4737
+ type: string
4738
+ required: true
4739
+ description: >-
4740
+ The `Content-Type` header indicates the specific version of the API used
4741
+ to send the payload body.
4591
4742
  parameters:
4592
4743
  Accept:
4593
4744
  name: Accept
@@ -4749,115 +4900,3 @@ components:
4749
4900
  schema:
4750
4901
  type: string
4751
4902
  description: ISO 4217 currency code for the target currency.
4752
- responses:
4753
- '200':
4754
- description: OK
4755
- '202':
4756
- description: Accepted
4757
- '400':
4758
- description: Bad Request
4759
- content:
4760
- application/json:
4761
- schema:
4762
- $ref: '#/components/schemas/ErrorInformationResponse'
4763
- headers:
4764
- Content-Length:
4765
- $ref: '#/components/headers/Content-Length'
4766
- Content-Type:
4767
- $ref: '#/components/headers/Content-Type'
4768
- '401':
4769
- description: Unauthorized
4770
- content:
4771
- application/json:
4772
- schema:
4773
- $ref: '#/components/schemas/ErrorInformationResponse'
4774
- headers:
4775
- Content-Length:
4776
- $ref: '#/components/headers/Content-Length'
4777
- Content-Type:
4778
- $ref: '#/components/headers/Content-Type'
4779
- '403':
4780
- description: Forbidden
4781
- content:
4782
- application/json:
4783
- schema:
4784
- $ref: '#/components/schemas/ErrorInformationResponse'
4785
- headers:
4786
- Content-Length:
4787
- $ref: '#/components/headers/Content-Length'
4788
- Content-Type:
4789
- $ref: '#/components/headers/Content-Type'
4790
- '404':
4791
- description: Not Found
4792
- content:
4793
- application/json:
4794
- schema:
4795
- $ref: '#/components/schemas/ErrorInformationResponse'
4796
- headers:
4797
- Content-Length:
4798
- $ref: '#/components/headers/Content-Length'
4799
- Content-Type:
4800
- $ref: '#/components/headers/Content-Type'
4801
- '405':
4802
- description: Method Not Allowed
4803
- content:
4804
- application/json:
4805
- schema:
4806
- $ref: '#/components/schemas/ErrorInformationResponse'
4807
- headers:
4808
- Content-Length:
4809
- $ref: '#/components/headers/Content-Length'
4810
- Content-Type:
4811
- $ref: '#/components/headers/Content-Type'
4812
- '406':
4813
- description: Not Acceptable
4814
- content:
4815
- application/json:
4816
- schema:
4817
- $ref: '#/components/schemas/ErrorInformationResponse'
4818
- headers:
4819
- Content-Length:
4820
- $ref: '#/components/headers/Content-Length'
4821
- Content-Type:
4822
- $ref: '#/components/headers/Content-Type'
4823
- '501':
4824
- description: Not Implemented
4825
- content:
4826
- application/json:
4827
- schema:
4828
- $ref: '#/components/schemas/ErrorInformationResponse'
4829
- headers:
4830
- Content-Length:
4831
- $ref: '#/components/headers/Content-Length'
4832
- Content-Type:
4833
- $ref: '#/components/headers/Content-Type'
4834
- '503':
4835
- description: Service Unavailable
4836
- content:
4837
- application/json:
4838
- schema:
4839
- $ref: '#/components/schemas/ErrorInformationResponse'
4840
- headers:
4841
- Content-Length:
4842
- $ref: '#/components/headers/Content-Length'
4843
- Content-Type:
4844
- $ref: '#/components/headers/Content-Type'
4845
- headers:
4846
- Content-Length:
4847
- required: false
4848
- schema:
4849
- type: integer
4850
- description: >-
4851
- The `Content-Length` header field indicates the anticipated size of the
4852
- payload body. Only sent if there is a body.
4853
-
4854
-
4855
- **Note:** The API supports a maximum size of 5242880 bytes (5
4856
- Megabytes).
4857
- Content-Type:
4858
- schema:
4859
- type: string
4860
- required: true
4861
- description: >-
4862
- The `Content-Type` header indicates the specific version of the API used
4863
- to send the payload body.
@@ -34,6 +34,45 @@ servers:
34
34
  - https
35
35
  default: https
36
36
  paths:
37
+ /ping:
38
+ post:
39
+ description: The HTTP request `POST /ping` is used to validate mTLS and JWS
40
+ summary: For testing mTLS and JWS
41
+ tags:
42
+ - participants
43
+ - ping
44
+ operationId: handlePostPing
45
+ requestBody:
46
+ description: The object sent in the POST/PUT `/ping` requests with validation request ID.
47
+ required: true
48
+ content:
49
+ application/json:
50
+ schema:
51
+ type: object
52
+ properties:
53
+ requestId:
54
+ $ref: '#/components/schemas/CorrelationId'
55
+ required:
56
+ - requestId
57
+ responses:
58
+ '202':
59
+ $ref: '#/components/responses/202'
60
+ '400':
61
+ $ref: '#/components/responses/400'
62
+ '401':
63
+ $ref: '#/components/responses/401'
64
+ '403':
65
+ $ref: '#/components/responses/403'
66
+ '404':
67
+ $ref: '#/components/responses/404'
68
+ '405':
69
+ $ref: '#/components/responses/405'
70
+ '406':
71
+ $ref: '#/components/responses/406'
72
+ '501':
73
+ $ref: '#/components/responses/501'
74
+ '503':
75
+ $ref: '#/components/responses/503'
37
76
  /interface:
38
77
  post:
39
78
  description: >-
@@ -1,71 +1,175 @@
1
- openapi: 3.0.0
1
+ openapi: 3.0.2
2
2
  info:
3
- version: '1.1'
3
+ version: '2.0-draft'
4
4
  title: Open API for FSP Interoperability (FSPIOP)
5
5
  description: >-
6
- Based on API Definition.docx updated on 2020-05-19 Version 1.1.
7
- API supports a maximum size of 65536 bytes (64 Kilobytes) in the HTTP
8
- header.
6
+ Revision date: 2023-11-23
7
+ Based on [API Definition updated on 2020-05-19 Version
8
+ 1.1](https://github.com/mojaloop/mojaloop-specification/blob/main/documents/v1.1-document-set/API%20Definition_v1.1.pdf).
9
+
10
+ This is implementation friendly version of the API definition.
11
+
12
+ It includes the below definitions needed for third-party functionality.
13
+ - AuthenticationType
14
+ - U2F enum
15
+ - AuthenticationValue
16
+ - oneOf is changed to anyOf
17
+ - new element is added U2FPinValue
18
+ - New element U2FPIN
19
+
20
+ **Note:** The API supports a maximum size of 65536 bytes (64 Kilobytes) in
21
+ the HTTP header.
9
22
  license:
10
- name: Open API for FSP Interoperability (FSPIOP)
23
+ name: CC BY-ND 4.0
24
+ url: 'https://github.com/mojaloop/mojaloop-specification/blob/main/LICENSE.md'
25
+ contact:
26
+ name: Sam Kummary
27
+ url: 'https://github.com/mojaloop/mojaloop-specification/issues'
28
+ servers:
29
+ - url: 'protocol://hostname:<port>/switch/'
30
+ variables:
31
+ protocol:
32
+ enum:
33
+ - http
34
+ - https
35
+ default: https
11
36
  paths:
12
- '/participants/{ID}/error':
13
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/participants_ID_error.yaml'
14
- '/participants/{ID}':
15
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/participants_ID.yaml'
16
- '/participants/{Type}/{ID}/error':
17
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/participants_Type_ID_error.yaml'
18
- '/participants/{Type}/{ID}/{SubId}/error':
19
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/participants_Type_ID_SubId_error.yaml'
20
- '/participants/{Type}/{ID}/{SubId}':
21
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/participants_Type_ID_SubId.yaml'
22
- '/participants/{Type}/{ID}':
23
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/participants_Type_ID.yaml'
24
- '/participants':
25
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/participants.yaml'
26
- '/parties/{Type}/{ID}':
27
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/parties_Type_ID.yaml'
28
- '/parties/{Type}/{ID}/error':
29
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/parties_Type_ID_error.yaml'
30
- '/parties/{Type}/{ID}/{SubId}':
31
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/parties_Type_ID_SubId.yaml'
32
- '/parties/{Type}/{ID}/{SubId}/error':
33
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/parties_Type_ID_SubId_error.yaml'
34
- '/transactionRequests/{ID}/error':
35
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/transactionRequests_ID_error.yaml'
36
- '/transactionRequests/{ID}':
37
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/transactionRequests_ID.yaml'
38
- '/transactionRequests':
39
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/transactionRequests.yaml'
40
- '/quotes/{ID}/error':
41
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/quotes_ID_error.yaml'
42
- '/quotes/{ID}':
43
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/quotes_ID.yaml'
44
- '/quotes':
45
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/quotes.yaml'
46
- '/authorizations/{ID}':
47
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/authorizations_ID.yaml'
48
- '/authorizations/{ID}/error':
49
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/authorizations_ID_error.yaml'
50
- '/transfers/{ID}/error':
51
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/transfers_ID_error.yaml'
52
- '/transfers/{ID}':
53
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/transfers_ID.yaml'
54
- '/transfers':
55
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/transfers.yaml'
56
- '/transactions/{ID}':
57
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/transactions_ID.yaml'
58
- '/transactions/{ID}/error':
59
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/transactions_ID_error.yaml'
60
- '/bulkQuotes/{ID}/error':
61
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/bulkQuotes_ID_error.yaml'
62
- '/bulkQuotes/{ID}':
63
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/bulkQuotes_ID.yaml'
64
- '/bulkQuotes':
65
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/bulkQuotes.yaml'
66
- '/bulkTransfers/{ID}':
67
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/bulkTransfers_ID.yaml'
68
- '/bulkTransfers':
69
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/bulkTransfers.yaml'
70
- '/bulkTransfers/{ID}/error':
71
- $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v1_1/openapi3/paths/bulkTransfers_ID_error.yaml'
37
+ /interface:
38
+ post:
39
+ description: >-
40
+ Essential path to include schema definitions that are not used so that
41
+ these definitions get included into the openapi-cli bundle api
42
+ definition so that they get converted into typescript definitions.
43
+ operationId: test
44
+ requestBody:
45
+ content:
46
+ application/json:
47
+ schema:
48
+ oneOf:
49
+ - $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/components/schemas/BinaryString.yaml'
50
+ - $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/components/schemas/BinaryString32.yaml'
51
+ - $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/components/schemas/Date.yaml'
52
+ - $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/components/schemas/Integer.yaml'
53
+ - $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/components/schemas/Name.yaml'
54
+ - $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/components/schemas/PersonalIdentifierType.yaml'
55
+ - $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/components/schemas/TokenCode.yaml'
56
+ - $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/components/schemas/Transaction.yaml'
57
+ - $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/components/schemas/UndefinedEnum.yaml'
58
+ responses:
59
+ 200:
60
+ description: Ok
61
+ /ping:
62
+ post:
63
+ description: The HTTP request `POST /ping` is used to validate mTLS and JWS
64
+ summary: For testing mTLS and JWS
65
+ tags:
66
+ - participants
67
+ - ping
68
+ operationId: handlePostPing
69
+ requestBody:
70
+ description: The object sent in the POST/PUT `/ping` requests with validation request ID.
71
+ required: true
72
+ content:
73
+ application/json:
74
+ schema:
75
+ type: object
76
+ properties:
77
+ requestId:
78
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/components/schemas/CorrelationId.yaml'
79
+ required:
80
+ - requestId
81
+ responses:
82
+ '202':
83
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/components/responses/202.yaml'
84
+ '400':
85
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/components/responses/400.yaml'
86
+ '401':
87
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/components/responses/401.yaml'
88
+ '403':
89
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/components/responses/403.yaml'
90
+ '404':
91
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/components/responses/404.yaml'
92
+ '405':
93
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/components/responses/405.yaml'
94
+ '406':
95
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/components/responses/406.yaml'
96
+ '501':
97
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/components/responses/501.yaml'
98
+ '503':
99
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/components/responses/503.yaml'
100
+ /participants:
101
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/participants.yaml'
102
+ /participants/{ID}:
103
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/participants_ID.yaml'
104
+ /participants/{ID}/error:
105
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/participants_ID_error.yaml'
106
+ /participants/{Type}/{ID}:
107
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/participants_Type_ID.yaml'
108
+ /participants/{Type}/{ID}/error:
109
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/participants_Type_ID_error.yaml'
110
+ /participants/{Type}/{ID}/{SubId}:
111
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/participants_Type_ID_SubId.yaml'
112
+ /participants/{Type}/{ID}/{SubId}/error:
113
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/participants_Type_ID_SubId_error.yaml'
114
+ /parties/{Type}/{ID}:
115
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/parties_Type_ID.yaml'
116
+ /parties/{Type}/{ID}/error:
117
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/parties_Type_ID_error.yaml'
118
+ /parties/{Type}/{ID}/{SubId}:
119
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/parties_Type_ID_SubId.yaml'
120
+ /parties/{Type}/{ID}/{SubId}/error:
121
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/parties_Type_ID_SubId_error.yaml'
122
+ /transactionRequests:
123
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/transactionRequests.yaml'
124
+ /transactionRequests/{ID}:
125
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/transactionRequests_ID.yaml'
126
+ /transactionRequests/{ID}/error:
127
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/transactionRequests_ID_error.yaml'
128
+ /quotes:
129
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/quotes.yaml'
130
+ /quotes/{ID}:
131
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/quotes_ID.yaml'
132
+ /quotes/{ID}/error:
133
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/quotes_ID_error.yaml'
134
+ /authorizations/{ID}:
135
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/authorizations_ID.yaml'
136
+ /authorizations/{ID}/error:
137
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/authorizations_ID_error.yaml'
138
+ /transfers:
139
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/transfers.yaml'
140
+ /transfers/{ID}:
141
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/transfers_ID.yaml'
142
+ /transfers/{ID}/error:
143
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/transfers_ID_error.yaml'
144
+ /transactions/{ID}:
145
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/transactions_ID.yaml'
146
+ /transactions/{ID}/error:
147
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/transactions_ID_error.yaml'
148
+ /bulkQuotes:
149
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/bulkQuotes.yaml'
150
+ /bulkQuotes/{ID}:
151
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/bulkQuotes_ID.yaml'
152
+ /bulkQuotes/{ID}/error:
153
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/bulkQuotes_ID_error.yaml'
154
+ /bulkTransfers:
155
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/bulkTransfers.yaml'
156
+ /bulkTransfers/{ID}:
157
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/bulkTransfers_ID.yaml'
158
+ /bulkTransfers/{ID}/error:
159
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/bulkTransfers_ID_error.yaml'
160
+ /fxQuotes:
161
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/fxQuotes.yaml'
162
+ /fxQuotes/{ID}:
163
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/fxQuotes_ID.yaml'
164
+ /fxQuotes/{ID}/error:
165
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/fxQuotes_ID_error.yaml'
166
+ /fxTransfers:
167
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/fxTransfers.yaml'
168
+ /fxTransfers/{ID}:
169
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/fxTransfers_ID.yaml'
170
+ /fxTransfers/{ID}/error:
171
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/fxTransfers_ID_error.yaml'
172
+ /services/FXP:
173
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/services_FXP.yaml'
174
+ /services/FXP/{SourceCurrency}/{TargetCurrency}:
175
+ $ref: '../../../../node_modules/@mojaloop/api-snippets/fspiop/v2_0/openapi3/paths/services_FXP_SourceCurrency_TargetCurrency.yaml'
@@ -34,6 +34,7 @@
34
34
  const { Enum } = require('@mojaloop/central-services-shared');
35
35
  const {
36
36
  InboundTransfersModel,
37
+ InboundPingModel,
37
38
  PartiesModel,
38
39
  QuotesModel,
39
40
  TransfersModel,
@@ -411,8 +412,8 @@ const putParticipantsByTypeAndId = async (ctx) => {
411
412
  // publish an event onto the cache for subscribers to action
412
413
  let cacheId = `${idType}_${idValue}` + (idSubValue ? `_${idSubValue}` : '');
413
414
  const message = { data };
414
-
415
- // We need to determine if this callback is a response to either a GET/POST /participants
415
+
416
+ // We need to determine if this callback is a response to either a GET/POST /participants
416
417
  // or DELETE /participants/{Type}/{ID}/{SubId} request
417
418
  const adCacheId = `ad_${cacheId}`;
418
419
  if (ctx.state.cache._callbacks[adCacheId]) {
@@ -431,8 +432,8 @@ const putParticipantsByTypeAndId = async (ctx) => {
431
432
 
432
433
 
433
434
  /**
434
- * Handles a PUT /participants/{Type}/{ID}/{SubId}/error request.
435
- * This is an error response to a GET /participants/{Type}/{ID}/{SubId} or
435
+ * Handles a PUT /participants/{Type}/{ID}/{SubId}/error request.
436
+ * This is an error response to a GET /participants/{Type}/{ID}/{SubId} or
436
437
  * DELETE /participants/{Type}/{ID}/{SubId} request
437
438
  */
438
439
  const putParticipantsByTypeAndIdError = async(ctx) => {
@@ -450,7 +451,7 @@ const putParticipantsByTypeAndIdError = async(ctx) => {
450
451
  let cacheId = `${idType}_${idValue}` + (idSubValue ? `_${idSubValue}` : '');
451
452
  const message = { data };
452
453
 
453
- // We need to determine if this callback is a response to either a GET/POST /participants
454
+ // We need to determine if this callback is a response to either a GET/POST /participants
454
455
  // or DELETE /participants/{Type}/{ID}/{SubId} request
455
456
  const adCacheId = `ad_${cacheId}`;
456
457
  if (ctx.state.cache._callbacks[adCacheId]) {
@@ -1105,6 +1106,22 @@ const createPutFxTransfersHandler = (success) => async (ctx) => {
1105
1106
  ctx.response.status = ReturnCodes.OK.CODE;
1106
1107
  };
1107
1108
 
1109
+ const handlePostPing = (ctx) => {
1110
+ const { jwsPingValidationResult, conf, logger, wso2 } = ctx.state;
1111
+ const { sourceFspId, body, headers } = extractBodyHeadersSourceFspId(ctx);
1112
+
1113
+ const model = new InboundPingModel({
1114
+ ...conf,
1115
+ resourceVersions: ctx.resourceVersions,
1116
+ logger,
1117
+ wso2,
1118
+ });
1119
+ model.postPing({ jwsPingValidationResult, sourceFspId, body, headers })
1120
+ .catch(err => logger.error('error in handlePostPing:', err));
1121
+
1122
+ prepareResponse(ctx);
1123
+ };
1124
+
1108
1125
  module.exports = {
1109
1126
  '/': {
1110
1127
  get: healthCheck
@@ -1217,5 +1234,8 @@ module.exports = {
1217
1234
  },
1218
1235
  '/fxTransfers/{ID}/error': {
1219
1236
  put: createPutFxTransfersHandler(false)
1220
- }
1237
+ },
1238
+ '/ping': {
1239
+ post: handlePostPing
1240
+ },
1221
1241
  };
@@ -125,7 +125,8 @@ class InboundApi extends EventEmitter {
125
125
  api.use(middlewares.createJwsValidator(logger, jwsVerificationKeys, jwsExclusions));
126
126
  }
127
127
 
128
- api.use(middlewares.applyState({ cache, wso2, conf, logExcludePaths }));
128
+ api.use(middlewares.applyState({ conf, cache, wso2, logExcludePaths }));
129
+ api.use(middlewares.createPingMiddleware(conf, jwsVerificationKeys));
129
130
  api.use(middlewares.createRequestValidator(validator));
130
131
  api.use(middlewares.assignFspiopIdentifier());
131
132
  if (conf.enableTestFeatures) {
@@ -25,8 +25,8 @@
25
25
  --------------
26
26
  ******/
27
27
  const { env } = require('node:process');
28
- const coBody = require('co-body');
29
28
  const { generateSlug } = require('random-word-slugs');
29
+ const coBody = require('co-body');
30
30
 
31
31
  const { Jws, Errors, common } = require('@mojaloop/sdk-standard-components');
32
32
  const { ReturnCodes } = require('@mojaloop/central-services-shared').Enum.Http;
@@ -58,7 +58,7 @@ const createErrorHandler = (logger) => async (ctx, next) => {
58
58
  await next();
59
59
  } catch (err) {
60
60
  // TODO: return a 500 here if the response has not already been sent?
61
- logger.isErrorEnabled && logger.push({ err }).error('Error caught in catchall');
61
+ logger.error('Error caught in catchall: ', err);
62
62
  }
63
63
  };
64
64
 
@@ -261,7 +261,7 @@ const createHeaderValidator = (conf) => async (
261
261
 
262
262
  // Only validate requests for the requested resources
263
263
  if (!resources.includes(resource)) {
264
- logger.info(`skip validation for ${resource}`);
264
+ logger.info(`skipping header validation for ${resource}`);
265
265
  return await next();
266
266
  }
267
267
 
@@ -347,17 +347,9 @@ const createHeaderValidator = (conf) => async (
347
347
  return;
348
348
  }
349
349
 
350
- try {
351
- ctx.request.body = await coBody.json(ctx.req, { limit: conf.fspiopApiServerMaxRequestBytes });
352
- }
353
- catch(err) {
354
- // error parsing body
355
- logger.push({ err }).error('Error parsing body');
356
- ctx.response.status = Errors.MojaloopApiErrorCodes.MALFORMED_SYNTAX.httpStatusCode;
357
- ctx.response.body = new Errors.MojaloopFSPIOPError(err, err.message, null,
358
- Errors.MojaloopApiErrorCodes.MALFORMED_SYNTAX).toApiErrorObject();
359
- return;
360
- }
350
+ const isOk = await extractRequestBody(conf, ctx);
351
+ if (!isOk) return;
352
+
361
353
  await next();
362
354
  };
363
355
 
@@ -372,7 +364,7 @@ const createHeaderValidator = (conf) => async (
372
364
  const createJwsValidator = (logger, keys, exclusions) => {
373
365
  // todo: take logger from ctx
374
366
  const jwsValidator = new Jws.validator({
375
- logger: logger,
367
+ logger,
376
368
  validationKeys: keys,
377
369
  });
378
370
  // JWS validation for incoming requests
@@ -383,22 +375,23 @@ const createJwsValidator = (logger, keys, exclusions) => {
383
375
  if (exclusions.includes('putParties')
384
376
  && ctx.request.method === 'PUT'
385
377
  && ctx.request.path.startsWith('/parties/')) {
386
- logger.isInfoEnabled && logger.info('Skipping jws validation on put parties. config flag is set');
378
+ logger.info('skipping jws validation on put parties. config flag is set');
387
379
  return await next();
388
380
  }
389
381
 
382
+ if (isPingRoute(ctx)) return await next();
383
+
390
384
  // we dont check signatures on GET requests
391
385
  // todo: validate this requirement. No state is mutated by GETs but
392
386
  // there are potential security issues if message origin is used to
393
387
  // determine permission sets i.e. what is "readable"
394
388
  if (ctx.request.method !== 'GET') {
395
389
  logger.isDebugEnabled && logger.push({ request: ctx.request, body: ctx.request.body }).debug('Validating JWS');
396
- jwsValidator.validate(ctx.request, logger);
390
+ jwsValidator.validate(ctx.request);
397
391
  }
398
392
 
399
- }
400
- catch(err) {
401
- logger.push({ err }).error('Inbound request failed JWS validation');
393
+ } catch (err) {
394
+ logger.error('Inbound request failed JWS validation', err);
402
395
 
403
396
  ctx.response.status = ReturnCodes.BADREQUEST.CODE;
404
397
  ctx.response.body = new Errors.MojaloopFSPIOPError(
@@ -445,7 +438,6 @@ const createLogger = (logger) => async (ctx, next) => {
445
438
  await next();
446
439
  };
447
440
 
448
-
449
441
  /**
450
442
  * Add validation for each inbound request
451
443
  * @param validator
@@ -516,6 +508,53 @@ const createResponseLogging = () => async (ctx, next) => {
516
508
  return await next();
517
509
  };
518
510
 
511
+ const createPingMiddleware = (config, validationKeys) => async (ctx, next) => {
512
+ if (!isPingRoute(ctx)) return await next();
513
+
514
+ const { logger } = ctx.state;
515
+ const isOk = await extractRequestBody(config, ctx);
516
+ if (!isOk) {
517
+ logger.warn('failed to extract request body');
518
+ return;
519
+ }
520
+
521
+ let result;
522
+
523
+ if (validationKeys) {
524
+ const jwsValidator = new Jws.validator({
525
+ logger,
526
+ validationKeys,
527
+ });
528
+
529
+ try {
530
+ result = jwsValidator.validate(ctx.request);
531
+ } catch (err) {
532
+ result = err;
533
+ }
534
+ }
535
+
536
+ ctx.state.jwsPingValidationResult = result;
537
+ logger.verbose(`is jwsPingValidation passed: ${result === true}`);
538
+
539
+ await next();
540
+ };
541
+
542
+ const extractRequestBody = async (conf, ctx) => {
543
+ try {
544
+ ctx.request.body = await coBody.json(ctx.req, { limit: conf.fspiopApiServerMaxRequestBytes });
545
+ return true;
546
+ } catch (err) {
547
+ // error parsing body
548
+ ctx.state.logger.error('Error parsing body: ', err);
549
+ ctx.response.status = Errors.MojaloopApiErrorCodes.MALFORMED_SYNTAX.httpStatusCode;
550
+ ctx.response.body = new Errors.MojaloopFSPIOPError(err, err.message, null,
551
+ Errors.MojaloopApiErrorCodes.MALFORMED_SYNTAX).toApiErrorObject();
552
+
553
+ }
554
+ };
555
+
556
+ const isPingRoute = (ctx) => ctx.request?.path?.startsWith('/ping');
557
+
519
558
  module.exports = {
520
559
  applyState,
521
560
  assignFspiopIdentifier,
@@ -528,5 +567,6 @@ module.exports = {
528
567
  createRequestValidator,
529
568
  createResponseBodyHandler,
530
569
  createResponseLogging,
570
+ createPingMiddleware,
531
571
  logResponse,
532
572
  };
@@ -157,6 +157,7 @@ module.exports = {
157
157
  bulkTransfersEndpoint: env.get('BULK_TRANSFERS_ENDPOINT').asString(),
158
158
  fxQuotesEndpoint: env.get('FX_QUOTES_ENDPOINT').asString(),
159
159
  fxTransfersEndpoint: env.get('FX_TRANSFERS_ENDPOINT').asString(),
160
+ pingEndpoint: env.get('PING_ENDPOINT').asString(),
160
161
  backendEndpoint: env.get('BACKEND_ENDPOINT').required().asString(),
161
162
 
162
163
  getServicesFxpResponse: env.get('GET_SERVICES_FXP_RESPONSE').default('').asArray(),
@@ -0,0 +1,100 @@
1
+ /*****
2
+ License
3
+ --------------
4
+ Copyright © 2020-2025 Mojaloop Foundation
5
+ The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
10
+
11
+ Contributors
12
+ --------------
13
+ This is the official list of the Mojaloop project contributors for this file.
14
+ Names of the original copyright holders (individuals or organizations)
15
+ should be listed with a '*' in the first column. People who have
16
+ contributed from an organization can be listed under the organization
17
+ that actually holds the copyright for their contributions (see the
18
+ Mojaloop Foundation for an example). Those individuals should have
19
+ their names indented and be marked with a '-'. Email address can be added
20
+ optionally within square brackets <email>.
21
+
22
+ * Mojaloop Foundation
23
+ * Eugen Klymniuk <eugen.klymniuk@infitx.com>
24
+
25
+ --------------
26
+ ******/
27
+
28
+ const { requests: { PingRequests }, Errors } = require('@mojaloop/sdk-standard-components');
29
+ const { Headers } = require('@mojaloop/central-services-shared').Enum.Http;
30
+
31
+ class InboundPingModel {
32
+ constructor(config) {
33
+ this.logger = config.logger.push({ component: this.constructor.name });
34
+ this.dfspId = config.dfspId;
35
+ this.pingRequests = new PingRequests({
36
+ logger: this.logger,
37
+ peerEndpoint: config.peerEndpoint,
38
+ pingEndpoint: config.pingEndpoint,
39
+ dfspId: config.dfspId,
40
+ tls: {
41
+ enabled: config.outbound.tls.mutualTLS.enabled,
42
+ creds: config.outbound.tls.creds,
43
+ },
44
+ jwsSign: config.jwsSign,
45
+ jwsSigningKey: config.jwsSigningKey,
46
+ // todo: think, if we need the rest
47
+ wso2: config.wso2,
48
+ resourceVersions: config.resourceVersions,
49
+ apiType: config.apiType,
50
+ });
51
+ }
52
+
53
+ async postPing({ jwsPingValidationResult, sourceFspId, body, headers }) {
54
+ const { requestId } = body;
55
+ const log = this.logger.child({ requestId, sourceFspId });
56
+ log.debug('postPing...', { jwsPingValidationResult, headers });
57
+
58
+ if (jwsPingValidationResult === true) {
59
+ log.verbose('ping JWS validation passed, sending PUT ping callback...');
60
+ return this.pingRequests.putPing({
61
+ requestId,
62
+ destination: sourceFspId,
63
+ headers: this.#createCallbackHeaders(headers),
64
+ });
65
+ }
66
+
67
+ const errInfo = this.#createPingError(jwsPingValidationResult);
68
+ log.info('ping JWS validation failed, sending PUT ping error callback...', { errInfo });
69
+ return this.pingRequests.putPingError({
70
+ requestId,
71
+ destination: sourceFspId,
72
+ headers: this.#createCallbackHeaders(headers),
73
+ errInfo
74
+ });
75
+ }
76
+
77
+ #createPingError(jwsPingValidationResult, destination) {
78
+ const cause = jwsPingValidationResult || new Error('JWS validationKeys are not provided');
79
+ const errMessage = 'error on JWS ping validation';
80
+ const fspiopError = new Errors.MojaloopFSPIOPError(
81
+ cause,
82
+ errMessage,
83
+ destination,
84
+ Errors.MojaloopApiErrorCodes.VALIDATION_ERROR
85
+ );
86
+ this.logger.warn(`${errMessage}: ${cause.message}`);
87
+
88
+ return fspiopError.toApiErrorObject();
89
+ }
90
+
91
+ #createCallbackHeaders(headers) {
92
+ return {
93
+ ...headers,
94
+ [Headers.FSPIOP.DESTINATION]: headers[Headers.FSPIOP.SOURCE],
95
+ [Headers.FSPIOP.SOURCE]: this.dfspId
96
+ };
97
+ }
98
+ }
99
+
100
+ module.exports = InboundPingModel;
@@ -26,8 +26,8 @@
26
26
  ******/
27
27
  'use strict';
28
28
 
29
-
30
29
  const InboundTransfersModel = require('./InboundTransfersModel');
30
+ const InboundPingModel = require('./InboundPingModel');
31
31
  const OutboundTransfersModel = require('./OutboundTransfersModel');
32
32
  const OutboundBulkQuotesModel = require('./OutboundBulkQuotesModel');
33
33
  const OutboundBulkTransfersModel = require('./OutboundBulkTransfersModel');
@@ -43,11 +43,12 @@ const TransfersModel = require('./TransfersModel');
43
43
  module.exports = {
44
44
  AccountsModel,
45
45
  BackendError,
46
+ InboundTransfersModel,
47
+ InboundPingModel,
46
48
  OutboundBulkQuotesModel,
47
49
  OutboundBulkTransfersModel,
48
50
  OutboundRequestToPayTransferModel,
49
51
  OutboundRequestToPayModel,
50
- InboundTransfersModel,
51
52
  OutboundTransfersModel,
52
53
  ProxyModel,
53
54
  PersistentStateMachine,
@@ -33,6 +33,7 @@ const {
33
33
  axios,
34
34
  MojaloopRequests, Errors, WSO2Auth, Jws, Logger, common,
35
35
  httpRequester,
36
+ requests: { PingRequests },
36
37
  Ilp: { ILP_VERSIONS }
37
38
  } = jest.requireActual('@mojaloop/sdk-standard-components');
38
39
 
@@ -200,6 +201,7 @@ module.exports = {
200
201
  axios,
201
202
  Ilp,
202
203
  httpRequester,
204
+ requests: { PingRequests },
203
205
  MojaloopRequests: MockMojaloopRequests,
204
206
  Jws: {
205
207
  validator: MockJwsValidator,
@@ -0,0 +1,124 @@
1
+ /*****
2
+ License
3
+ --------------
4
+ Copyright © 2020-2025 Mojaloop Foundation
5
+ The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
10
+
11
+ Contributors
12
+ --------------
13
+ This is the official list of the Mojaloop project contributors for this file.
14
+ Names of the original copyright holders (individuals or organizations)
15
+ should be listed with a '*' in the first column. People who have
16
+ contributed from an organization can be listed under the organization
17
+ that actually holds the copyright for their contributions (see the
18
+ Mojaloop Foundation for an example). Those individuals should have
19
+ their names indented and be marked with a '-'. Email address can be added
20
+ optionally within square brackets <email>.
21
+
22
+ * Mojaloop Foundation
23
+ * Eugen Klymniuk <eugen.klymniuk@infitx.com>
24
+
25
+ --------------
26
+ ******/
27
+
28
+ const { mockAxios, jsonContentTypeHeader} = require('../../../helpers');
29
+ const { Headers } = require('@mojaloop/central-services-shared').Enum.Http;
30
+
31
+ const InboundPingModel = require('../../../../src/lib/model/InboundPingModel');
32
+ const { logger } = require('../../../../src/lib/logger');
33
+ const defaultConfig = require('./data/defaultConfig');
34
+
35
+ const createInboundPingModel = (conf = configDto()) => new InboundPingModel(conf);
36
+
37
+ const configDto = ({
38
+ dfspId = 'mojaloop-sdk',
39
+ jwsSign = false,
40
+ wso2 = { auth: null }
41
+ } = {}) => ({
42
+ ...defaultConfig,
43
+ logger,
44
+ dfspId,
45
+ jwsSign,
46
+ wso2
47
+ });
48
+
49
+ const postPingParamsDto = ({
50
+ jwsPingValidationResult,
51
+ sourceFspId = 'sourceDfsp',
52
+ requestId = String(Date.now()),
53
+ headers = pingHeadersDto({ source: sourceFspId })
54
+ } = {}) => ({
55
+ jwsPingValidationResult,
56
+ sourceFspId,
57
+ body: { requestId },
58
+ headers
59
+ });
60
+
61
+ const pingHeadersDto = ({
62
+ source = 'sourceDfsp',
63
+ destination = configDto().dfspId,
64
+ signature
65
+ } = {}) => ({
66
+ ...jsonContentTypeHeader,
67
+ [Headers.FSPIOP.SOURCE]: source,
68
+ [Headers.FSPIOP.DESTINATION]: destination,
69
+ ...(signature && { [Headers.FSPIOP.SIGNATURE]: signature })
70
+ });
71
+
72
+ describe.skip('InboundPingModel Tests -->', () => {
73
+ beforeEach(() => {
74
+ mockAxios.reset();
75
+ });
76
+
77
+ test('should create InboundPingModel instance with default config', () => {
78
+ const model = createInboundPingModel();
79
+ expect(model).toBeDefined();
80
+ });
81
+
82
+ test('should send successful ping callback', async () => {
83
+ expect.hasAssertions();
84
+ const jwsPingValidationResult = true;
85
+ const requestId = String(Date.now());
86
+ const sourceFspId = 'fromDfsp';
87
+ const dfspId = 'theSDK';
88
+ mockAxios.onPut().reply((reqConfig) => {
89
+ expect(reqConfig.url).toBe(`/ping/${requestId}/`);
90
+ expect(reqConfig.headers[Headers.FSPIOP.SOURCE]).toBe(dfspId);
91
+ expect(reqConfig.headers[Headers.FSPIOP.DESTINATION]).toBe(sourceFspId);
92
+ return [200];
93
+ });
94
+ const model = createInboundPingModel(configDto({ dfspId }));
95
+
96
+ await model.postPing(postPingParamsDto({
97
+ jwsPingValidationResult, requestId, sourceFspId
98
+ }));
99
+ });
100
+
101
+ test('should send error callback, if jwsPingValidationResult is undefined (no validationKeys)', async () => {
102
+ expect.hasAssertions();
103
+ const requestId = String(Date.now());
104
+ mockAxios.onPut().reply((reqConfig) => {
105
+ expect(reqConfig.url).toBe(`/ping/${requestId}/error`);
106
+ return [200];
107
+ });
108
+ const model = createInboundPingModel();
109
+ await model.postPing(postPingParamsDto({ requestId }));
110
+ });
111
+
112
+ test('should send error callback, if jwsPingValidationResult is validation error', async () => {
113
+ expect.hasAssertions();
114
+ const jwsPingValidationResult = new Error('Validation Error');
115
+ const requestId = String(Date.now());
116
+ mockAxios.onPut().reply((reqConfig) => {
117
+ expect(reqConfig.url).toBe(`/ping/${requestId}/error`);
118
+ return [200];
119
+ });
120
+ const model = createInboundPingModel();
121
+ await model.postPing(postPingParamsDto({ jwsPingValidationResult, requestId }));
122
+ });
123
+ });
124
+
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mojaloop/sdk-scheme-adapter-outbound-command-event-handler",
3
- "version": "0.3.0-snapshot.30",
3
+ "version": "0.3.0-snapshot.31",
4
4
  "description": "Mojaloop sdk scheme adapter command event handler",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/mojaloop/sdk-scheme-adapter/",
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "@mojaloop/api-snippets": "17.10.2",
45
- "@mojaloop/central-services-shared": "18.23.2",
45
+ "@mojaloop/central-services-shared": "18.24.0",
46
46
  "@mojaloop/logging-bc-client-lib": "0.5.8",
47
47
  "@mojaloop/logging-bc-public-types-lib": "0.5.5",
48
48
  "@mojaloop/sdk-scheme-adapter-private-shared-lib": "workspace:^",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mojaloop/sdk-scheme-adapter-outbound-domain-event-handler",
3
- "version": "0.3.0-snapshot.30",
3
+ "version": "0.3.0-snapshot.31",
4
4
  "description": "mojaloop sdk scheme adapter outbound domain event handler",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/mojaloop/sdk-scheme-adapter/",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mojaloop/sdk-scheme-adapter-private-shared-lib",
3
- "version": "0.4.0-snapshot.30",
3
+ "version": "0.4.0-snapshot.31",
4
4
  "description": "SDK Scheme Adapter private shared library.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/mojaloop/accounts-and-balances-bc/tree/main/modules/private-types",
@@ -30,7 +30,7 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@mojaloop/api-snippets": "17.10.2",
33
- "@mojaloop/central-services-shared": "18.23.2",
33
+ "@mojaloop/central-services-shared": "18.24.0",
34
34
  "@mojaloop/logging-bc-public-types-lib": "0.5.5",
35
35
  "@mojaloop/platform-shared-lib-messaging-types-lib": "0.7.2",
36
36
  "@mojaloop/platform-shared-lib-nodejs-kafka-client-lib": "0.5.18",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mojaloop/sdk-scheme-adapter",
3
- "version": "24.8.0",
3
+ "version": "24.9.0",
4
4
  "description": "mojaloop sdk-scheme-adapter",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/mojaloop/sdk-scheme-adapter",