From cee549ca8b6912ef6db12237b341a7c0024fdd60 Mon Sep 17 00:00:00 2001 From: kmohr-soprasteria Date: Fri, 16 Feb 2024 17:14:24 +0100 Subject: [PATCH 01/21] Add description of how to set operationKey in configFile Fixes #956 --- .../CreatingConfigFile/CreatingConfigFile.md | 2 ++ .../CreatingConfigFile/OperationKeys.md | 32 ++++++++++++++++++ .../pictures/example_configFile_basicAuth.png | Bin 0 -> 33041 bytes .../pictures/example_configFile_noAuth.png | Bin 0 -> 31182 bytes .../example_configfile_apiKeyAuth.png | Bin 0 -> 33809 bytes .../pictures/example_oas_apiKeyAuth.png | Bin 0 -> 14427 bytes .../pictures/example_oas_basicAuth.png | Bin 0 -> 13218 bytes .../pictures/example_oas_noAuth.png | Bin 0 -> 7094 bytes 8 files changed, 34 insertions(+) create mode 100644 doc/SpecifyingApplications/CreatingConfigFile/OperationKeys.md create mode 100644 doc/SpecifyingApplications/CreatingConfigFile/pictures/example_configFile_basicAuth.png create mode 100644 doc/SpecifyingApplications/CreatingConfigFile/pictures/example_configFile_noAuth.png create mode 100644 doc/SpecifyingApplications/CreatingConfigFile/pictures/example_configfile_apiKeyAuth.png create mode 100644 doc/SpecifyingApplications/CreatingConfigFile/pictures/example_oas_apiKeyAuth.png create mode 100644 doc/SpecifyingApplications/CreatingConfigFile/pictures/example_oas_basicAuth.png create mode 100644 doc/SpecifyingApplications/CreatingConfigFile/pictures/example_oas_noAuth.png diff --git a/doc/SpecifyingApplications/CreatingConfigFile/CreatingConfigFile.md b/doc/SpecifyingApplications/CreatingConfigFile/CreatingConfigFile.md index 508aeca3..96f6ed9c 100644 --- a/doc/SpecifyingApplications/CreatingConfigFile/CreatingConfigFile.md +++ b/doc/SpecifyingApplications/CreatingConfigFile/CreatingConfigFile.md @@ -65,6 +65,8 @@ Several need to be added, and it is recommended to copy and paste the existing o - After you updated the OperationClients at all the HttpClients, double check whether the ordering of the OperationClients is consistent with the ServiceList. - After assuring that, double check, whether the abbreviation of the application's name inside the UUIDs of all the OperationClients, HttpClients and TcpClients is correct. - Finally, double check the sequence number inside the UUIDs of all the OperationClients, HttpClients and TcpClients. + +- Also ensure that the [operationKey](./OperationKeys.md) fields of OperationClients and OperationServers are set correctly. #### ForwardingConstructs diff --git a/doc/SpecifyingApplications/CreatingConfigFile/OperationKeys.md b/doc/SpecifyingApplications/CreatingConfigFile/OperationKeys.md new file mode 100644 index 00000000..cc8b68c4 --- /dev/null +++ b/doc/SpecifyingApplications/CreatingConfigFile/OperationKeys.md @@ -0,0 +1,32 @@ +# Setting the correct operationKeys in the CONFIGfile + +This document describes how to assign the correct operationKeys to OperationClients and OperationServers in the configFile. + + +### Concept + +- For some operations an operationKey needs to be provided, whereas other operations do not require an operationKey to be provided. +- Whether providing an operationKey is required is defined in the `security` section of a given operations specification. +- Currently the following three options are available: + 1. `apiAuthKey`: an operationKey needs to be provided + - operationKey = default operationKey = "Operation key not yet provided." + 2. `basicAuth`: instead of an operationKey an authorization code needs to be provided upon request execution + - operationKey = "n.a." + 3. no security tag at all: no authentication is required. + - operationKey = "n.a." + +#### Examples +The following code snippets show OAS examples for all three options: + +| OAS Example | Info | +|------------------------------------------------------|--------------------------------------------------------------------------| +| ![apiKeyAuth](./pictures/example_oas_apiKeyAuth.png) | operationKey required,
operationKey = default operationKey | +| ![basicAuth](./pictures/example_oas_basicAuth.png) | requires authorizationCode instead of operationKey,
operationKey = n.a. | +| ![noAuth](./pictures/example_oas_noAuth.png) | operationKey = n.a. | + +Examples from ConfigFile: +| ConfigFile Example | ConfigFile | +|---------------|-----------------------------------------------------------------| +| 1. apiKeyAuth | ![apiKeyAuthConf](./pictures/example_configFile_apiKeyAuth.png) | +| 2. basicAuth | ![basicAuthConf](./pictures/example_configFile_basicAuth.png) | +| 3. noAuth | ![noAuthConf](./pictures/example_configFile_noAuth.png) | \ No newline at end of file diff --git a/doc/SpecifyingApplications/CreatingConfigFile/pictures/example_configFile_basicAuth.png b/doc/SpecifyingApplications/CreatingConfigFile/pictures/example_configFile_basicAuth.png new file mode 100644 index 0000000000000000000000000000000000000000..546a699713c5c58423df6716214b4c142b782912 GIT binary patch literal 33041 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 literal 0 HcmV?d00001 diff --git a/doc/SpecifyingApplications/CreatingConfigFile/pictures/example_configFile_noAuth.png b/doc/SpecifyingApplications/CreatingConfigFile/pictures/example_configFile_noAuth.png new file mode 100644 index 0000000000000000000000000000000000000000..187b1455bb16742727406e70f489b4e14a0c779e GIT binary patch literal 31182 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

oqsgLCcsAo=*!mL%Q* zEZRv7o@~y$S}wYI^~-+J%iYHG5A~ui8zhkt1l{jSc zkiXC^6hnRD7C@^yDi__DHzIs1SXX0I&0OR*9FZEbv3q8Y*N!<$o02XlH9^Kn4(ROj zNwZi#n{CA*zb0oHMKjQ3zYGu}@%*y7puY1&1d

ox!IoMq99W`@`!_s9cbkg@6nd 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 literal 0 HcmV?d00001 diff --git a/doc/SpecifyingApplications/CreatingConfigFile/pictures/example_oas_basicAuth.png b/doc/SpecifyingApplications/CreatingConfigFile/pictures/example_oas_basicAuth.png new file mode 100644 index 0000000000000000000000000000000000000000..f53d2a343fbaa6299be6745b26f478e09dec8bc3 GIT binary patch literal 13218 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 literal 0 HcmV?d00001 From 31532f98f4d467f478af09272a8b4a9d65e8cb43 Mon Sep 17 00:00:00 2001 From: kmohr-soprasteria Date: Fri, 16 Feb 2024 17:18:10 +0100 Subject: [PATCH 02/21] corrected filename step1 --- ...yAuth.png => example_configFile_apiKeyAuth1.png} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename doc/SpecifyingApplications/CreatingConfigFile/pictures/{example_configfile_apiKeyAuth.png => example_configFile_apiKeyAuth1.png} (100%) diff --git a/doc/SpecifyingApplications/CreatingConfigFile/pictures/example_configfile_apiKeyAuth.png b/doc/SpecifyingApplications/CreatingConfigFile/pictures/example_configFile_apiKeyAuth1.png similarity index 100% rename from doc/SpecifyingApplications/CreatingConfigFile/pictures/example_configfile_apiKeyAuth.png rename to doc/SpecifyingApplications/CreatingConfigFile/pictures/example_configFile_apiKeyAuth1.png From ac8c07316e391686387b3eb33674108173fd638d Mon Sep 17 00:00:00 2001 From: kmohr-soprasteria Date: Fri, 16 Feb 2024 17:18:29 +0100 Subject: [PATCH 03/21] correct filename --- ...yAuth1.png => example_configFile_apiKeyAuth.png} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename doc/SpecifyingApplications/CreatingConfigFile/pictures/{example_configFile_apiKeyAuth1.png => example_configFile_apiKeyAuth.png} (100%) diff --git a/doc/SpecifyingApplications/CreatingConfigFile/pictures/example_configFile_apiKeyAuth1.png b/doc/SpecifyingApplications/CreatingConfigFile/pictures/example_configFile_apiKeyAuth.png similarity index 100% rename from doc/SpecifyingApplications/CreatingConfigFile/pictures/example_configFile_apiKeyAuth1.png rename to doc/SpecifyingApplications/CreatingConfigFile/pictures/example_configFile_apiKeyAuth.png From 22c79732278dc6b76e6c97dbc2819352555cbb67 Mon Sep 17 00:00:00 2001 From: Thorsten Heinze <46722892+openBackhaul@users.noreply.github.com> Date: Fri, 15 Mar 2024 17:43:52 +0100 Subject: [PATCH 04/21] Create HowToDefineErrorMessages.HowToDefineErrorMessages.md --- .../HowToDefineErrorMessages.HowToDefineErrorMessages.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/SpecifyingApplications/HowToDefineErrorMessages.HowToDefineErrorMessages.md diff --git a/doc/SpecifyingApplications/HowToDefineErrorMessages.HowToDefineErrorMessages.md b/doc/SpecifyingApplications/HowToDefineErrorMessages.HowToDefineErrorMessages.md new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/doc/SpecifyingApplications/HowToDefineErrorMessages.HowToDefineErrorMessages.md @@ -0,0 +1 @@ + From 2c7aba35049a086579bd06e8741cff033bd6d4b1 Mon Sep 17 00:00:00 2001 From: Thorsten Heinze <46722892+openBackhaul@users.noreply.github.com> Date: Fri, 15 Mar 2024 17:45:31 +0100 Subject: [PATCH 05/21] Delete doc/SpecifyingApplications/HowToDefineErrorMessages.HowToDefineErrorMessages.md --- .../HowToDefineErrorMessages.HowToDefineErrorMessages.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 doc/SpecifyingApplications/HowToDefineErrorMessages.HowToDefineErrorMessages.md diff --git a/doc/SpecifyingApplications/HowToDefineErrorMessages.HowToDefineErrorMessages.md b/doc/SpecifyingApplications/HowToDefineErrorMessages.HowToDefineErrorMessages.md deleted file mode 100644 index 8b137891..00000000 --- a/doc/SpecifyingApplications/HowToDefineErrorMessages.HowToDefineErrorMessages.md +++ /dev/null @@ -1 +0,0 @@ - From eee889da96082dae3d82cf3ba3b39acb21c1a9aa Mon Sep 17 00:00:00 2001 From: Thorsten Heinze <46722892+openBackhaul@users.noreply.github.com> Date: Fri, 15 Mar 2024 17:50:37 +0100 Subject: [PATCH 06/21] Create HowToDefineErrorMessages.md --- .../HowToDefineErrorMessages.md | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 doc/SpecifyingApplications/HowToDefineErrorMessages/HowToDefineErrorMessages.md diff --git a/doc/SpecifyingApplications/HowToDefineErrorMessages/HowToDefineErrorMessages.md b/doc/SpecifyingApplications/HowToDefineErrorMessages/HowToDefineErrorMessages.md new file mode 100644 index 00000000..814d3109 --- /dev/null +++ b/doc/SpecifyingApplications/HowToDefineErrorMessages/HowToDefineErrorMessages.md @@ -0,0 +1,50 @@ +# How to define Error Messages + +Error responses should be meaningful and helpful for handling the errors. +It should not only inform about a problem, but also assist in dealing with it. +Ideally, an error message provides a machine- and human-readable recommendation for the next step to be taken (e.g. as part of an automation). + +How to define error responses in the OAS: +1. List possible reasons why the requested data cannot be provided or the requested service cannot be performed. +2. Reasons for failure should be grouped into different expected behaviors of the client (e.g., try again later, make sure the device is mounted first). +3. Groups of failures with the same expected client behavior should be associated with a response code according to https://developer.mozilla.org/en-US/docs/Web/HTTP/Status. + If an appropriate response code could not be found, define a new one. +4. If not already covered by the ApplicationPattern, add the new responseCode to the responses of the affected service. +5. The response definition for a specific responseCode must be homogeneous at least within the scope of the application (harmonization across all applications will be done at a later stage, by consolidating individual definitions into an updated applicationPattern). + To ensure this, the response definition at an individual service should only reference a concrete definition in the common components section ($ref: '#/components/responses/{responseCode}). +6. The concrete definition in the common components section is identified by the responseCode. +7. It must begin with a description statement. The description statement is addressed to the implementor of the server (application under specification). It defines the conditions for sending a response with this code. Example: "Response to be sent whenever the number of currently executed requests reaches the maximum defined in the instance of IntegerProfile whose name contains limitOfParallelRequestsAt and the service name. +6. Some "standard" response codes from the list above define additional headers or parameters. + Such additional headers and parameters must also be included in the response definition in the common components, even if they are not used within the scope of this application. +7. If additional attributes are required to manage the client's behavior, they shall be included in the body of the definitions in the common components. +8. code and message attributes shall always be supported. They shall be available for the implementers to detail the reason of failure. +9. If an additional attribute would be required to pass a predefined message, that message shall be defined as an enumeration with only a single value. +10. If sending a response implies a specific expectation to the client's behavior, this expectation should be explicitly expressed by an expectation-to-the-client attribute. This attribute should be an enumeration with a single value. This value should contain a statement that is understood by both an automation implementer and a human user. + +Example: +``` +components: + responses: + 429: + description: 'Response to be send whenever the number of currently executed requests reached the maximum defined in the instance of IntegerProfile with its name containing limitOfParallelRequestsAt and the service name.' + content: + application/json: + schema: + type: object + required: + - code + - message + - expectation-to-the-client + properties: + code: + type: integer + format: int32 + message: + type: string + enum: + - 'The number of requests that can be handled by this service in parallel has been reached' + expectation-to-the-client: + type: string + enum: + - 'Please try again later' +``` From 8f20f34cc5ebad43a02c28855e4079c15a1e6c7d Mon Sep 17 00:00:00 2001 From: Thorsten Heinze <46722892+openBackhaul@users.noreply.github.com> Date: Fri, 15 Mar 2024 17:52:51 +0100 Subject: [PATCH 07/21] Update SpecifyingApplications.md --- doc/SpecifyingApplications/SpecifyingApplications.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/SpecifyingApplications/SpecifyingApplications.md b/doc/SpecifyingApplications/SpecifyingApplications.md index 320b3d01..bdf2ffef 100644 --- a/doc/SpecifyingApplications/SpecifyingApplications.md +++ b/doc/SpecifyingApplications/SpecifyingApplications.md @@ -89,7 +89,8 @@ The ForwardingList must describe all relationships between events and reactions The OpenApiSpecification (OAS) represents the detailed specification of the REST API of the application. **Concepts** -- [Structure of the OAS](./StructureOfOas/StructureOfOas.md) +- [Structure of the OAS](./StructureOfOas/StructureOfOas.md) +- [How to define error messages](./HowToDefineErrorMessages/HowToDefineErrorMessages.md) **Step-by-Step Guidelines** - [Creating the OAS](./CreatingOas/CreatingOas.md) From 9e95caab21e3250f9654cedb6542fb1af65c5f65 Mon Sep 17 00:00:00 2001 From: Thorsten Heinze <46722892+openBackhaul@users.noreply.github.com> Date: Fri, 15 Mar 2024 18:20:25 +0100 Subject: [PATCH 08/21] Update HowToDefineErrorMessages.md --- .../HowToDefineErrorMessages/HowToDefineErrorMessages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/SpecifyingApplications/HowToDefineErrorMessages/HowToDefineErrorMessages.md b/doc/SpecifyingApplications/HowToDefineErrorMessages/HowToDefineErrorMessages.md index 814d3109..a00c012d 100644 --- a/doc/SpecifyingApplications/HowToDefineErrorMessages/HowToDefineErrorMessages.md +++ b/doc/SpecifyingApplications/HowToDefineErrorMessages/HowToDefineErrorMessages.md @@ -42,7 +42,7 @@ components: message: type: string enum: - - 'The number of requests that can be handled by this service in parallel has been reached' + - 'The maximum number of requests that can be handled by this service in parallel has been reached' expectation-to-the-client: type: string enum: From c3e5b697b3217f4a7ce1233c7427d470b81107c4 Mon Sep 17 00:00:00 2001 From: kmohr-soprasteria Date: Mon, 18 Mar 2024 11:37:24 +0100 Subject: [PATCH 09/21] Improve readability of Defining ErrorMessages documentation Fixes #962 --- .../HowToDefineErrorMessages.md | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/doc/SpecifyingApplications/HowToDefineErrorMessages/HowToDefineErrorMessages.md b/doc/SpecifyingApplications/HowToDefineErrorMessages/HowToDefineErrorMessages.md index a00c012d..dab2f0a6 100644 --- a/doc/SpecifyingApplications/HowToDefineErrorMessages/HowToDefineErrorMessages.md +++ b/doc/SpecifyingApplications/HowToDefineErrorMessages/HowToDefineErrorMessages.md @@ -5,21 +5,30 @@ It should not only inform about a problem, but also assist in dealing with it. Ideally, an error message provides a machine- and human-readable recommendation for the next step to be taken (e.g. as part of an automation). How to define error responses in the OAS: -1. List possible reasons why the requested data cannot be provided or the requested service cannot be performed. -2. Reasons for failure should be grouped into different expected behaviors of the client (e.g., try again later, make sure the device is mounted first). -3. Groups of failures with the same expected client behavior should be associated with a response code according to https://developer.mozilla.org/en-US/docs/Web/HTTP/Status. - If an appropriate response code could not be found, define a new one. -4. If not already covered by the ApplicationPattern, add the new responseCode to the responses of the affected service. -5. The response definition for a specific responseCode must be homogeneous at least within the scope of the application (harmonization across all applications will be done at a later stage, by consolidating individual definitions into an updated applicationPattern). - To ensure this, the response definition at an individual service should only reference a concrete definition in the common components section ($ref: '#/components/responses/{responseCode}). -6. The concrete definition in the common components section is identified by the responseCode. -7. It must begin with a description statement. The description statement is addressed to the implementor of the server (application under specification). It defines the conditions for sending a response with this code. Example: "Response to be sent whenever the number of currently executed requests reaches the maximum defined in the instance of IntegerProfile whose name contains limitOfParallelRequestsAt and the service name. -6. Some "standard" response codes from the list above define additional headers or parameters. - Such additional headers and parameters must also be included in the response definition in the common components, even if they are not used within the scope of this application. -7. If additional attributes are required to manage the client's behavior, they shall be included in the body of the definitions in the common components. -8. code and message attributes shall always be supported. They shall be available for the implementers to detail the reason of failure. -9. If an additional attribute would be required to pass a predefined message, that message shall be defined as an enumeration with only a single value. -10. If sending a response implies a specific expectation to the client's behavior, this expectation should be explicitly expressed by an expectation-to-the-client attribute. This attribute should be an enumeration with a single value. This value should contain a statement that is understood by both an automation implementer and a human user. +1. **List possible reasons** why the requested data cannot be provided or the requested service cannot be performed. +2. **Reasons for failure** should be **grouped into** different **expected behaviors of the client** + - e.g., *"try again later"*, *"make sure the device is mounted first"*. +3. **Groups of failures** with the **same expected client behavior** should be **associated with a response code** + - according to https://developer.mozilla.org/en-US/docs/Web/HTTP/Status. + - If an appropriate response code could not be found, define a new one. +4. If not already covered by the ApplicationPattern, **add** the **new responseCode to** the **responses of the affected service**. +5. The **response definition for a specific responseCode must be homogeneous** + - at least within the scope of the application (harmonization across all applications will be done at a later stage, by consolidating individual definitions into an updated applicationPattern). + - To ensure this, the response definition at an individual service should only reference a concrete definition in the common components section ($ref: '#/components/responses/{responseCode}). +6. The **concrete definition in** the **common components section** is **identified by the responseCode**. +7. It must begin with a **description statement**. + - The description statement is addressed to the implementor of the server (application under specification). + - It defines the conditions for sending a response with this code. + - Example: *"Response to be sent whenever the number of currently executed requests reaches the maximum defined in the instance of IntegerProfile whose name contains limitOfParallelRequestsAt*\*."* +6. Some "standard" response codes from the list above define **additional headers or parameters**. + - Such additional headers and parameters must also be included in the response definition in the common components, even if they are not used within the scope of this application. +7. If **additional attributes** are **required to manage the client's behavior**, they shall be **included in** the **body of the definitions** in the common components. +8. **code and message attributes shall always be supported**. + - They shall be available for the implementers to detail the reason of failure. +9. If an **additional attribute** would be required to pass a **predefined message**, that message shall be **defined as an enumeration** with only a single value. +10. If sending a **response implies** a **specific expectation to the client's behavior**, this expectation should be explicitly expressed by an **expectation-to-the-client attribute**. + - This attribute should be an **enumeration** with a single value. + - This value should contain a statement that is understood by both an automation implementer and a human user. Example: ``` From 2719b91ddc4fa2036bf08d3eb30397dcd1bf67f5 Mon Sep 17 00:00:00 2001 From: kmohr-soprasteria Date: Mon, 18 Mar 2024 11:38:35 +0100 Subject: [PATCH 10/21] format --- .../HowToDefineErrorMessages/HowToDefineErrorMessages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/SpecifyingApplications/HowToDefineErrorMessages/HowToDefineErrorMessages.md b/doc/SpecifyingApplications/HowToDefineErrorMessages/HowToDefineErrorMessages.md index dab2f0a6..d80253c1 100644 --- a/doc/SpecifyingApplications/HowToDefineErrorMessages/HowToDefineErrorMessages.md +++ b/doc/SpecifyingApplications/HowToDefineErrorMessages/HowToDefineErrorMessages.md @@ -14,7 +14,7 @@ How to define error responses in the OAS: 4. If not already covered by the ApplicationPattern, **add** the **new responseCode to** the **responses of the affected service**. 5. The **response definition for a specific responseCode must be homogeneous** - at least within the scope of the application (harmonization across all applications will be done at a later stage, by consolidating individual definitions into an updated applicationPattern). - - To ensure this, the response definition at an individual service should only reference a concrete definition in the common components section ($ref: '#/components/responses/{responseCode}). + - To ensure this, the response definition at an individual service should only reference a concrete definition in the common components section (`$ref: '#/components/responses/{responseCode}`). 6. The **concrete definition in** the **common components section** is **identified by the responseCode**. 7. It must begin with a **description statement**. - The description statement is addressed to the implementor of the server (application under specification). From 635d165adc8228d8b9117eaf36cf71987ad0616a Mon Sep 17 00:00:00 2001 From: kmohr-soprasteria Date: Mon, 18 Mar 2024 11:40:36 +0100 Subject: [PATCH 11/21] fix error --- .../HowToDefineErrorMessages/HowToDefineErrorMessages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/SpecifyingApplications/HowToDefineErrorMessages/HowToDefineErrorMessages.md b/doc/SpecifyingApplications/HowToDefineErrorMessages/HowToDefineErrorMessages.md index d80253c1..4af6fe92 100644 --- a/doc/SpecifyingApplications/HowToDefineErrorMessages/HowToDefineErrorMessages.md +++ b/doc/SpecifyingApplications/HowToDefineErrorMessages/HowToDefineErrorMessages.md @@ -19,7 +19,7 @@ How to define error responses in the OAS: 7. It must begin with a **description statement**. - The description statement is addressed to the implementor of the server (application under specification). - It defines the conditions for sending a response with this code. - - Example: *"Response to be sent whenever the number of currently executed requests reaches the maximum defined in the instance of IntegerProfile whose name contains limitOfParallelRequestsAt*\*."* + - Example: *"Response to be sent whenever the number of currently executed requests reaches the maximum defined in the instance of IntegerProfile whose name contains limitOfParallelRequestsAt and the service name."* 6. Some "standard" response codes from the list above define **additional headers or parameters**. - Such additional headers and parameters must also be included in the response definition in the common components, even if they are not used within the scope of this application. 7. If **additional attributes** are **required to manage the client's behavior**, they shall be **included in** the **body of the definitions** in the common components. From 7b8045c472bca7c6321e8365eae8ef876774d891 Mon Sep 17 00:00:00 2001 From: Prathiba Date: Sat, 23 Mar 2024 21:05:31 +0100 Subject: [PATCH 12/21] To support new APIs in version 2.1.1 Fixes #954 --- .../onfModel/constants/OnfAttributes.js | 4 +- .../OperationServerInterface.js | 22 ++ .../onfModel/models/profile/FileProfile.js | 7 +- .../onfModel/models/profile/IntegerProfile.js | 24 +- .../LogicalTerminationPointServices.js | 12 +- .../BasicServicesOperationsMapping.js | 7 + .../basicServices/BasicServicesService.js | 272 ++++++++++++++++++ .../services/PrepareForwardingAutomation.js | 81 ++++++ .../PrepareForwardingConfiguration.js | 62 ++++ .../utility/LogicalTerminationPoint.js | 24 ++ 10 files changed, 506 insertions(+), 9 deletions(-) diff --git a/server/applicationPattern/applicationPattern/onfModel/constants/OnfAttributes.js b/server/applicationPattern/applicationPattern/onfModel/constants/OnfAttributes.js index c263dbe1..b730154e 100644 --- a/server/applicationPattern/applicationPattern/onfModel/constants/OnfAttributes.js +++ b/server/applicationPattern/applicationPattern/onfModel/constants/OnfAttributes.js @@ -80,9 +80,7 @@ const FILE_PROFILE = { CONFIGURATION: "file-profile-configuration", FILE_IDENTIFIER: "file-identifier", FILE_DESCRIPTION: "file-description", - FILE_PATH: "file-path", - USER_NAME: "user-name", - PASSWORD: "password", + FILE_NAME: "file-name", OPERATION: "operation" }; diff --git a/server/applicationPattern/applicationPattern/onfModel/models/layerProtocols/OperationServerInterface.js b/server/applicationPattern/applicationPattern/onfModel/models/layerProtocols/OperationServerInterface.js index a4dc5411..5933e88e 100644 --- a/server/applicationPattern/applicationPattern/onfModel/models/layerProtocols/OperationServerInterface.js +++ b/server/applicationPattern/applicationPattern/onfModel/models/layerProtocols/OperationServerInterface.js @@ -12,6 +12,8 @@ const LayerProtocol = require('../LayerProtocol'); const onfPaths = require('../../constants/OnfPaths'); const onfAttributes = require('../../constants/OnfAttributes'); const fileOperation = require('../../../databaseDriver/JSONDriver'); +const ForwardingDomain = require('../../models/ForwardingDomain'); +const FcPort = require('../FcPort'); /** * @extends LayerProtocol */ @@ -209,6 +211,26 @@ class OperationServerInterface extends LayerProtocol { } } + /** + * @description This function returns the input operationServer operationName of the fowarding. + * @param {String} forwardingName: the value should be a valid forwardingName + * @returns {Promise} operationServerName + **/ + + static async getInputOperationServerNameFromForwarding(forwardingName) { + let forwardConstruct = await ForwardingDomain.getForwardingConstructForTheForwardingNameAsync(forwardingName) + if (forwardConstruct === undefined) { + return undefined; + } + let fcPorts = forwardConstruct[onfAttributes.FORWARDING_CONSTRUCT.FC_PORT]; + let fcPortOutput = fcPorts.filter( + fcPort => fcPort[onfAttributes.FC_PORT.PORT_DIRECTION] === FcPort.portDirectionEnum.INPUT + )[0]; + let operationServerUuid = fcPortOutput[onfAttributes.FC_PORT.LOGICAL_TERMINATION_POINT]; + let operationServerName = await this.getOperationNameAsync(operationServerUuid); + return operationServerName; + } + /** * @description Determines if given UUID belongs to a server operation. * @param {String} operationUuid UUID to be checked diff --git a/server/applicationPattern/applicationPattern/onfModel/models/profile/FileProfile.js b/server/applicationPattern/applicationPattern/onfModel/models/profile/FileProfile.js index b298a4ae..3782ff5e 100644 --- a/server/applicationPattern/applicationPattern/onfModel/models/profile/FileProfile.js +++ b/server/applicationPattern/applicationPattern/onfModel/models/profile/FileProfile.js @@ -179,8 +179,9 @@ class FileProfile extends profile { for (let profileUuidIndex = 0; profileUuidIndex < profileUuid.length; profileUuidIndex++) { let uuid = profileUuid[profileUuidIndex]; let value = await FileProfile.getFilePath(uuid) - if (fileSystem.existsSync(value)) { - applicationDataFile = value; + let completeFilePath = "./application-data/" + value; + if (fileSystem.existsSync(completeFilePath)) { + applicationDataFile = completeFilePath; } } if (applicationDataFile !== undefined) { @@ -210,7 +211,7 @@ class FileProfile extends profile { if (uuidOfProfile === profileUuid) { let fileProfilePac = profile[onfAttributes.FILE_PROFILE.PAC]; let fileProfileConfiguration = fileProfilePac[onfAttributes.FILE_PROFILE.CONFIGURATION]; - filePath = fileProfileConfiguration[onfAttributes.FILE_PROFILE.FILE_PATH]; + filePath = fileProfileConfiguration[onfAttributes.FILE_PROFILE.FILE_NAME]; } } resolve(filePath); diff --git a/server/applicationPattern/applicationPattern/onfModel/models/profile/IntegerProfile.js b/server/applicationPattern/applicationPattern/onfModel/models/profile/IntegerProfile.js index 951966e4..4d5f46ac 100644 --- a/server/applicationPattern/applicationPattern/onfModel/models/profile/IntegerProfile.js +++ b/server/applicationPattern/applicationPattern/onfModel/models/profile/IntegerProfile.js @@ -122,7 +122,7 @@ class IntegerProfile extends Profile { * @deprecated should be removed * @description This function returns the maxmimum value for the integer profile. * @param {String} integerProfileUuid : the value should be a valid string in the pattern '^([a-z]{2,6})-([0-9]{1,2})-([0-9]{1,2})-([0-9]{1,2})-integer-p-([0-9]{3})$' - * @returns {promise} string {approvalStatus} + * @returns {promise} string {integerValue} **/ static async getIntegerValueAsync(integerProfileUuid) { return new Promise(async function (resolve, reject) { @@ -145,6 +145,28 @@ class IntegerProfile extends Profile { } }); } + + /** + * @description This function returns the configured value for the integer profile. + * @param {String} integerProfileKey : name of the integer profile + * @returns {promise} string {integerValue} + **/ + static async getIntegerValueForTheIntegerProfileNameAsync(integerProfileName) { + let integerValue + let profileList = await ProfileCollection.getProfileListAsync(); + for (let i = 0; i < profileList.length; i++) { + let profileInstance = profileList[i]; + let profileName = profileInstance[onfAttributes.PROFILE.PROFILE_NAME]; + if (profileName == "integer-profile-1-0:PROFILE_NAME_TYPE_INTEGER_PROFILE") { + let Integerval = profileInstance[onfAttributes.INTEGER_PROFILE.PAC][onfAttributes.INTEGER_PROFILE.CAPABILITY][onfAttributes.INTEGER_PROFILE.INTEGER_NAME] + if (Integerval == integerProfileName) { + integerValue = profileInstance[onfAttributes.INTEGER_PROFILE.PAC][onfAttributes.INTEGER_PROFILE.CONFIGURATION][onfAttributes.INTEGER_PROFILE.INTEGER_VALUE] + break; + } + } + } + return integerValue; + } } module.exports = IntegerProfile; diff --git a/server/applicationPattern/applicationPattern/onfModel/services/LogicalTerminationPointServices.js b/server/applicationPattern/applicationPattern/onfModel/services/LogicalTerminationPointServices.js index 66349f26..1c2a290d 100644 --- a/server/applicationPattern/applicationPattern/onfModel/services/LogicalTerminationPointServices.js +++ b/server/applicationPattern/applicationPattern/onfModel/services/LogicalTerminationPointServices.js @@ -202,6 +202,7 @@ async function createLtpInstanceGroupAsync(logicalTerminationPointConfigurationI async function updateLtpInstanceGroupAsync(ltpConfigurationInput, isApplicationRO) { const httpClientUuid = ltpConfigurationInput.httpClientUuid; const releaseNumber = ltpConfigurationInput.releaseNumber; + const applicationName = ltpConfigurationInput.applicationName ? ltpConfigurationInput.applicationName : undefined; const tcpList = ltpConfigurationInput.tcpList; const operationServerName = ltpConfigurationInput.operationServerName; const operationNamesByAttributes = ltpConfigurationInput.operationNamesByAttributes; @@ -235,7 +236,8 @@ async function updateLtpInstanceGroupAsync(ltpConfigurationInput, isApplicationR const httpClientConfigurationStatus = await updateHttpClientLtpAsync( httpClientUuid, releaseNumber, - operationClientListChanged + operationClientListChanged, + applicationName ) return new LogicalTerminationPointConfigurationStatus( operationClientConfigurationStatusList, @@ -268,7 +270,7 @@ async function createHttpClientLtpAsync(applicationName, releaseNumber) { * @param {Boolean} isOperationClientChanged if there was a change with any of this http-client operation-client * @return {Promise} **/ -async function updateHttpClientLtpAsync(httpClientUuid, releaseNumber, isOperationClientChanged) { +async function updateHttpClientLtpAsync(httpClientUuid, releaseNumber, isOperationClientChanged, applicationName) { let isUpdated = false; if (isOperationClientChanged) { isUpdated = true; @@ -279,6 +281,12 @@ async function updateHttpClientLtpAsync(httpClientUuid, releaseNumber, isOperati httpClientUuid, releaseNumber ); } + const existingApplicationName = await HttpClientInterface.getApplicationNameAsync(httpClientUuid); + if (applicationName && existingApplicationName !== applicationName) { + isUpdated = await HttpClientInterface.setApplicationNameAsync( + httpClientUuid, applicationName + ); + } return new ConfigurationStatus(httpClientUuid, '', isUpdated); } diff --git a/server/basicServices/basicServices/BasicServicesOperationsMapping.js b/server/basicServices/basicServices/BasicServicesOperationsMapping.js index fe42d9de..990159b1 100644 --- a/server/basicServices/basicServices/BasicServicesOperationsMapping.js +++ b/server/basicServices/basicServices/BasicServicesOperationsMapping.js @@ -41,6 +41,13 @@ module.exports.basicServicesOperationsMapping = "sequence": "000" } }, + "/v1/inquire-basic-auth-approvals": + { + "basic-auth-approval-operation": { + "api-segment": "bs", + "sequence": "000" + } + }, "/v1/redirect-topology-change-information": { "topology-operation-application-update": { "api-segment": "bm", diff --git a/server/basicServices/basicServices/BasicServicesService.js b/server/basicServices/basicServices/BasicServicesService.js index f7c2c686..02946f20 100644 --- a/server/basicServices/basicServices/BasicServicesService.js +++ b/server/basicServices/basicServices/BasicServicesService.js @@ -34,6 +34,68 @@ const controlConstruct = require('onf-core-model-ap/applicationPattern/onfModel/ const basicServicesOperationsMapping = require('./BasicServicesOperationsMapping'); const genericRepresentation = require('./GenericRepresentation'); const createHttpError = require('http-errors'); +const HttpServerInterface = require('onf-core-model-ap/applicationPattern/onfModel/models/layerProtocols/HttpServerInterface'); +const OperationClientInterface = require('onf-core-model-ap/applicationPattern/onfModel/models/layerProtocols/OperationClientInterface'); + +/** + * Removes application from configuration and application data + * + * body V1_disposeremaindersofderegisteredapplication_body + * user String User identifier from the system starting the service call + * originator String 'Identification for the system consuming the API, as defined in [/core-model-1-4:control-construct/logical-termination-point={uuid}/layer-protocol=0/http-client-interface-1-0:http-client-interface-pac/http-client-interface-configuration/application-name]' + * xCorrelator String UUID for the service execution flow that allows to correlate requests and responses + * traceIndicator String Sequence of request numbers along the flow + * customerJourney String Holds information supporting customer’s journey to which the execution applies + * no response value expected for this operation + **/ +exports.disposeRemaindersOfDeregisteredApplication = async function (body, user, originator, xCorrelator, traceIndicator, customerJourney, operationServerName, newReleaseFWName) { + let applicationName = body["application-name"]; + let applicationReleaseNumber = body["release-number"]; + + let httpClientUuid = await httpClientInterface.getHttpClientUuidExcludingOldReleaseAndNewRelease( + applicationName, + applicationReleaseNumber, + newReleaseFWName + ); + let ltpConfigurationStatus = await LogicalTerminationPointService.deleteApplicationLtpsAsync( + httpClientUuid + ); + + /**************************************************************************************** + * Prepare attributes to remove fc-ports from forwarding-construct + ****************************************************************************************/ + + let forwardingConfigurationInputList = []; + let forwardingConstructConfigurationStatus; + let operationClientConfigurationStatusList = ltpConfigurationStatus.operationClientConfigurationStatusList; + + if (operationClientConfigurationStatusList) { + forwardingConfigurationInputList = await prepareForwardingConfiguration.disposeRemaindersOfDeregisteredApplication( + operationClientConfigurationStatusList + ); + forwardingConstructConfigurationStatus = await ForwardingConfigurationService. + unConfigureForwardingConstructAsync( + operationServerName, + forwardingConfigurationInputList + ); + } + + /**************************************************************************************** + * Prepare attributes to automate forwarding-construct + ****************************************************************************************/ + let forwardingAutomationInputList = await prepareForwardingAutomation.disposeRemaindersOfDeregisteredApplication( + ltpConfigurationStatus, + forwardingConstructConfigurationStatus + ); + ForwardingAutomationService.automateForwardingConstructAsync( + operationServerName, + forwardingAutomationInputList, + user, + xCorrelator, + traceIndicator, + customerJourney + ); +} /** * Embed yourself into the MBH SDN application layer @@ -247,6 +309,33 @@ exports.informAboutApplicationInGenericRepresentation = async function (operatio }); } + +/** + * Provides name and number of the preceding release + * If there is no OldRelease for this application , then a hardcoded applicationName "OldRelease" will be sent to smooth the ApprovingApplicationCausesPreparingTheEmbedding.RequestForOldRelease + * @oldReleaseForwardingName {String|undefined} forwardingName to identify a oldRelease , if there is no oldRelease then "undefined" shall be sent as a value. + * returns inline_response_200_3 + **/ +exports.informAboutPrecedingRelease = async function (oldReleaseForwardingName) { + try { + let precedingApplicationInformation = {}; + + let oldApplicationNameAndReleaseNumber = await ServiceUtils.resolveApplicationNameAndReleaseNumberFromForwardingAsync( + oldReleaseForwardingName); + + if (oldApplicationNameAndReleaseNumber) { + precedingApplicationInformation.applicationName = oldApplicationNameAndReleaseNumber.applicationName; + precedingApplicationInformation.releaseNumber = oldApplicationNameAndReleaseNumber.releaseNumber; + } else { + precedingApplicationInformation.applicationName = "OldRelease"; + precedingApplicationInformation.releaseNumber = await HttpServerInterface.getReleaseNumberAsync(); + } + return onfAttributeFormatter.modifyJsonObjectKeysToKebabCase(precedingApplicationInformation); + } catch (error) { + console.log(error); + } +} + /** * Returns release history * @@ -270,6 +359,98 @@ exports.informAboutReleaseHistoryInGenericRepresentation = async function (opera }); } + +/** + * Receives information about where to ask for approval of BasicAuth requests + * + * body V1_inquirebasicauthapprovals_body + * user String User identifier from the system starting the service call + * xCorrelator String UUID for the service execution flow that allows to correlate requests and responses + * traceIndicator String Sequence of request numbers along the flow + * customerJourney String Holds information supporting customer’s journey to which the execution applies + * operationServerName String Holds information about the url + * newReleaseFwName String Holds information about the new release forwarding name if exist + * no response value expected for this operation + **/ +exports.inquireBasicAuthRequestApprovals = async function (body, user, xCorrelator, traceIndicator, customerJourney, operationServerName, newReleaseFwName) { + let applicationName = body["application-name"]; + let releaseNumber = body["release-number"]; + let applicationProtocol = body["protocol"]; + let applicationAddress = body["address"]; + let applicationPort = body["port"]; + let basicAuthApprovalOperation = body["operation-name"]; + + + let httpClientUuid = await httpClientInterface.getHttpClientUuidFromForwarding("BasicAuthRequestCausesInquiryForAuthentication"); + + let operationNamesByAttributes = new Map(); + operationNamesByAttributes.set("basic-auth-approval-operation", basicAuthApprovalOperation); + + let tcpObjectList = [new TcpObject(applicationProtocol, applicationAddress, applicationPort)]; + + if (!httpClientUuid) { + httpClientUuid = await httpClientInterface.getHttpClientUuidExcludingOldReleaseAndNewRelease( + applicationName, + undefined, + newReleaseFwName + ); + } + let ltpConfigurationInput = new LogicalTerminationPointConfigurationInput( + httpClientUuid, + applicationName, + releaseNumber, + tcpObjectList, + operationServerName, + operationNamesByAttributes, + basicServicesOperationsMapping.basicServicesOperationsMapping + ); + let ltpConfigurationStatus; + if (httpClientUuid) { + ltpConfigurationStatus = await LogicalTerminationPointService.createOrUpdateApplicationLtpsAsync( + ltpConfigurationInput, false + ); + } + + /**************************************************************************************** + * Prepare attributes to configure forwarding-construct + ****************************************************************************************/ + + let forwardingConfigurationInputList = []; + let forwardingConstructConfigurationStatus; + let operationClientConfigurationStatusList; + if (ltpConfigurationStatus) { + operationClientConfigurationStatusList = ltpConfigurationStatus.operationClientConfigurationStatusList; + } + + if (operationClientConfigurationStatusList) { + forwardingConfigurationInputList = await prepareForwardingConfiguration.inquireBasicAuthRequestApprovals( + operationClientConfigurationStatusList, + basicAuthApprovalOperation + ); + forwardingConstructConfigurationStatus = await ForwardingConfigurationService. + configureForwardingConstructAsync( + operationServerName, + forwardingConfigurationInputList + ); + } + + /**************************************************************************************** + * Prepare attributes to automate forwarding-construct + ****************************************************************************************/ + let forwardingAutomationInputList = await prepareForwardingAutomation.inquireBasicAuthRequestApprovals( + ltpConfigurationStatus, + forwardingConstructConfigurationStatus + ); + ForwardingAutomationService.automateForwardingConstructAsync( + operationServerName, + forwardingAutomationInputList, + user, + xCorrelator, + traceIndicator, + customerJourney + ); +} + /** * Receives information about where to ask for approval of OaM requests * @@ -1080,6 +1261,97 @@ exports.updateClient = async function (body, user, xCorrelator, traceIndicator, ); } + +/** + * Configures Http and TcpClient of the NewRelease + * + * body V1_updateclientofsubsequentrelease_body + * user String User identifier from the system starting the service call + * originator String 'Identification for the system consuming the API, as defined in [/core-model-1-4:control-construct/logical-termination-point={uuid}/layer-protocol=0/http-client-interface-1-0:http-client-interface-pac/http-client-interface-configuration/application-name]' + * xCorrelator String UUID for the service execution flow that allows to correlate requests and responses + * traceIndicator String Sequence of request numbers along the flow + * customerJourney String Holds information supporting customer’s journey to which the execution applies + * returns inline_response_200_4 + **/ +exports.updateClientOfSubsequentRelease = async function (body, user, xCorrelator, traceIndicator, customerJourney, operationServerName, newReleaseForwardingName) { + let futureApplicationName = body["application-name"]; + let futureReleaseNumber = body["release-number"]; + let futureProtocol = body["protocol"]; + let futureAddress = body["address"]; + let futurePort = body["port"]; + + let bequeathYourDataAndDieOperation; + let dataTransferOperationsList = []; + /**************************************************************************************** + * Prepare logicalTerminatinPointConfigurationInput object to + * configure logical-termination-point + ****************************************************************************************/ + + let ltpConfigurationStatus = {}; + let newReleaseHttpClientLtpUuid = await httpClientInterface.getHttpClientUuidFromForwarding(newReleaseForwardingName); + if (newReleaseHttpClientLtpUuid != undefined) { + let isReleaseUpdated = await httpClientInterface.setReleaseNumberAsync(newReleaseHttpClientLtpUuid, futureReleaseNumber); + let isApplicationNameUpdated = await httpClientInterface.setApplicationNameAsync(newReleaseHttpClientLtpUuid, futureApplicationName); + + if (isReleaseUpdated || isApplicationNameUpdated) { + let configurationStatus = new ConfigurationStatus( + newReleaseHttpClientLtpUuid, + '', + true); + ltpConfigurationStatus.httpClientConfigurationStatus = configurationStatus; + } + + let newReleaseTcpClientUuidList = await LogicalTerminationPoint.getServerLtpListAsync(newReleaseHttpClientLtpUuid); + let newReleaseTcpClientUuid = newReleaseTcpClientUuidList[0]; + + let isProtocolUpdated = await tcpClientInterface.setRemoteProtocolAsync(newReleaseTcpClientUuid, futureProtocol); + let isAddressUpdated = await tcpClientInterface.setRemoteAddressAsync(newReleaseTcpClientUuid, futureAddress); + let isPortUpdated = await tcpClientInterface.setRemotePortAsync(newReleaseTcpClientUuid, futurePort); + + if (isProtocolUpdated || isAddressUpdated || isPortUpdated) { + let configurationStatus = new ConfigurationStatus( + newReleaseTcpClientUuid, + '', + true); + ltpConfigurationStatus.tcpClientConfigurationStatusList = [configurationStatus]; + } + let forwardingAutomationInputList; + if (ltpConfigurationStatus != undefined) { + + /**************************************************************************************** + * Prepare attributes to automate forwarding-construct + ****************************************************************************************/ + forwardingAutomationInputList = await prepareForwardingAutomation.updateClientOfSubsequentRelease( + ltpConfigurationStatus + ); + ForwardingAutomationService.automateForwardingConstructAsync( + operationServerName, + forwardingAutomationInputList, + user, + xCorrelator, + traceIndicator, + customerJourney + ); + } + } + + bequeathYourDataAndDieOperation = await operationServerInterface.getInputOperationServerNameFromForwarding(newReleaseForwardingName); + let dataTransferOperationClientUuidList = await LogicalTerminationPoint.getClientLtpListAsync(newReleaseHttpClientLtpUuid); + for (let i = 0; i < dataTransferOperationClientUuidList.length; i++) { + let operationClientUuid = dataTransferOperationClientUuidList[i]; + let operationClientName = await OperationClientInterface.getOperationNameAsync(operationClientUuid); + dataTransferOperationsList.push(operationClientName); + } + + /**************************************************************************************** + * Prepare attributes for the response body + ****************************************************************************************/ + var handOverAndDatatransferInformation = {}; + handOverAndDatatransferInformation.bequeathYourDataAndDieOperation = bequeathYourDataAndDieOperation; + handOverAndDatatransferInformation.dataTransferOperationsList = dataTransferOperationsList; + return onfAttributeFormatter.modifyJsonObjectKeysToKebabCase(handOverAndDatatransferInformation); +} + /** * Allows updating operation clients to redirect to backward compatible services * diff --git a/server/basicServices/basicServices/services/PrepareForwardingAutomation.js b/server/basicServices/basicServices/services/PrepareForwardingAutomation.js index 9327f558..cc22a453 100644 --- a/server/basicServices/basicServices/services/PrepareForwardingAutomation.js +++ b/server/basicServices/basicServices/services/PrepareForwardingAutomation.js @@ -14,6 +14,33 @@ const forwardingConstructAutomationInput = require('onf-core-model-ap/applicatio const prepareALTForwardingAutomation = require('./PrepareALTForwardingAutomation'); const TcpClientInterface = require('onf-core-model-ap/applicationPattern/onfModel/models/layerProtocols/TcpClientInterface'); +exports.disposeRemaindersOfDeregisteredApplication = function (logicalTerminationPointconfigurationStatus, forwardingConstructConfigurationStatus) { + return new Promise(async function (resolve, reject) { + let forwardingConstructAutomationList = []; + try { + + /*********************************************************************************** + * forwardings for application layer topology + ************************************************************************************/ + let applicationLayerTopologyForwardingInputList = await prepareALTForwardingAutomation.getALTUnConfigureForwardingAutomationInputAsync( + logicalTerminationPointconfigurationStatus, + forwardingConstructConfigurationStatus + ); + + if (applicationLayerTopologyForwardingInputList) { + for (let i = 0; i < applicationLayerTopologyForwardingInputList.length; i++) { + let applicationLayerTopologyForwardingInput = applicationLayerTopologyForwardingInputList[i]; + forwardingConstructAutomationList.push(applicationLayerTopologyForwardingInput); + } + } + + resolve(forwardingConstructAutomationList); + } catch (error) { + reject(error); + } + }); +} + exports.embedYourself = function (logicalTerminationPointconfigurationStatus, forwardingConstructConfigurationStatus, oldApplicationName = '') { return new Promise(async function (resolve, reject) { let forwardingConstructAutomationList = []; @@ -165,6 +192,33 @@ exports.endSubscription = function (forwardingConstructConfigurationStatus) { ); } +exports.inquireBasicAuthRequestApprovals = function (logicalTerminationPointconfigurationStatus, forwardingConstructConfigurationStatus) { + return new Promise(async function (resolve, reject) { + let forwardingConstructAutomationList = []; + try { + + /*********************************************************************************** + * forwardings for application layer topology + ************************************************************************************/ + let applicationLayerTopologyForwardingInputList = await prepareALTForwardingAutomation.getALTForwardingAutomationInputAsync( + logicalTerminationPointconfigurationStatus, + forwardingConstructConfigurationStatus + ); + + if (applicationLayerTopologyForwardingInputList) { + for (let i = 0; i < applicationLayerTopologyForwardingInputList.length; i++) { + let applicationLayerTopologyForwardingInput = applicationLayerTopologyForwardingInputList[i]; + forwardingConstructAutomationList.push(applicationLayerTopologyForwardingInput); + } + } + + resolve(forwardingConstructAutomationList); + } catch (error) { + reject(error); + } + }); +} + exports.inquireOamRequestApprovals = function (logicalTerminationPointconfigurationStatus, forwardingConstructConfigurationStatus) { return new Promise(async function (resolve, reject) { let forwardingConstructAutomationList = []; @@ -361,6 +415,33 @@ exports.updateClient = function (logicalTerminationPointconfigurationStatus, for }); } +exports.updateClientOfSubsequentRelease = function (logicalTerminationPointconfigurationStatus, forwardingConstructConfigurationStatus) { + return new Promise(async function (resolve, reject) { + let forwardingConstructAutomationList = []; + try { + + /*********************************************************************************** + * forwardings for application layer topology + ************************************************************************************/ + let applicationLayerTopologyForwardingInputList = await prepareALTForwardingAutomation.getALTForwardingAutomationInputAsync( + logicalTerminationPointconfigurationStatus, + forwardingConstructConfigurationStatus + ); + + if (applicationLayerTopologyForwardingInputList) { + for (let i = 0; i < applicationLayerTopologyForwardingInputList.length; i++) { + let applicationLayerTopologyForwardingInput = applicationLayerTopologyForwardingInputList[i]; + forwardingConstructAutomationList.push(applicationLayerTopologyForwardingInput); + } + } + + resolve(forwardingConstructAutomationList); + } catch (error) { + reject(error); + } + }); +} + function isLifeCycleStateDeprecated(lifeCycleState) { let deprecatedLifeCycleState = operationServerInterface.OperationServerInterfacePac.OperationServerInterfaceConfiguration.lifeCycleStateEnum.DEPRECATED let lifeCycleStateEnum = operationServerInterface.OperationServerInterfacePac.OperationServerInterfaceConfiguration.lifeCycleStateEnum; diff --git a/server/basicServices/basicServices/services/PrepareForwardingConfiguration.js b/server/basicServices/basicServices/services/PrepareForwardingConfiguration.js index 9a0f7a6a..253a0b0e 100644 --- a/server/basicServices/basicServices/services/PrepareForwardingConfiguration.js +++ b/server/basicServices/basicServices/services/PrepareForwardingConfiguration.js @@ -8,6 +8,38 @@ const logicalTerminationPoint = require('onf-core-model-ap/applicationPattern/on +exports.disposeRemaindersOfDeregisteredApplication = function (operationClientConfigurationStatusList) { + return new Promise(async function (resolve, reject) { + let forwardingConfigurationInputList = []; + try { + for (let i = 0; i < operationClientConfigurationStatusList.length; i++) { + + let configurationStatus = operationClientConfigurationStatusList[i]; + let operationClientUuid = configurationStatus.uuid; + + let forwardingConstructList = await forwardingDomain.getForwardingConstructListForTheFcPortAsync( + operationClientUuid, + FcPort.portDirectionEnum.OUTPUT); + + for (let j = 0; j < forwardingConstructList.length; j++) { + let fcNameList = forwardingConstructList[j]["name"]; + let forwardingName = getValueFromKey(fcNameList, "ForwardingName"); + let forwardingConfigurationInput = new forwardingConstructConfigurationInput( + forwardingName, + operationClientUuid + ); + forwardingConfigurationInputList.push( + forwardingConfigurationInput + ); + } + } + resolve(forwardingConfigurationInputList); + } catch (error) { + reject(error); + } + }); +} + exports.embedYourself = function (operationClientConfigurationStatusList, deregisterOperation, relayServerReplacementOperation, relayOperationUpdateOperation) { return new Promise(async function (resolve, reject) { let forwardingConfigurationInputList = []; @@ -83,6 +115,36 @@ exports.registerYourself = function (operationClientConfigurationStatusList, reg } +exports.inquireBasicAuthRequestApprovals = function (operationClientConfigurationStatusList, basicAuthApprovalOperation) { + return new Promise(async function (resolve, reject) { + let forwardingConfigurationInputList = []; + try { + for (let i = 0; i < operationClientConfigurationStatusList.length; i++) { + let configurationStatus = operationClientConfigurationStatusList[i]; + let operationClientUuid = configurationStatus.uuid; + let operationClientName = await operationClientInterface. + getOperationNameAsync(operationClientUuid); + let forwardingConfigurationInput; + let forwardingName; + if (operationClientName == basicAuthApprovalOperation) { + forwardingName = + "BasicAuthRequestCausesInquiryForAuthentication"; + forwardingConfigurationInput = new forwardingConstructConfigurationInput( + forwardingName, + operationClientUuid + ); + } + forwardingConfigurationInputList.push( + forwardingConfigurationInput + ); + } + resolve(forwardingConfigurationInputList); + } catch (error) { + reject(error); + } + }); +} + exports.inquireOamRequestApprovals = function (operationClientConfigurationStatusList, oamApprovalOperation) { return new Promise(async function (resolve, reject) { let forwardingConfigurationInputList = []; diff --git a/server/basicServices/basicServices/utility/LogicalTerminationPoint.js b/server/basicServices/basicServices/utility/LogicalTerminationPoint.js index 57406613..ba24dec3 100644 --- a/server/basicServices/basicServices/utility/LogicalTerminationPoint.js +++ b/server/basicServices/basicServices/utility/LogicalTerminationPoint.js @@ -96,6 +96,30 @@ exports.resolveApplicationNameFromForwardingAsync = async function (forwardingNa return await httpClientInterface.getApplicationNameAsync(httpLtpUuidList[0]); } +/** + * Resolves application name and release number from the forwarding name. + * @param {String} forwardingName + * @returns {Promise} application name and release number + */ +exports.resolveApplicationNameAndReleaseNumberFromForwardingAsync = async function (forwardingName) { + const forwardingConstruct = await ForwardingDomain.getForwardingConstructForTheForwardingNameAsync(forwardingName); + if (forwardingConstruct === undefined) { + return undefined; + } + const fcPortList = forwardingConstruct[onfAttributes.FORWARDING_CONSTRUCT.FC_PORT]; + const roFcPort = fcPortList.find(fcPort => + FcPort.portDirectionEnum.OUTPUT === fcPort[onfAttributes.FC_PORT.PORT_DIRECTION] + ); + const httpLtpUuidList = await LogicalTerminationPoint.getServerLtpListAsync(roFcPort[onfAttributes.FC_PORT.LOGICAL_TERMINATION_POINT]); + let applicationName = await httpClientInterface.getApplicationNameAsync(httpLtpUuidList[0]); + let releaseNumber = await httpClientInterface.getReleaseNumberAsync(httpLtpUuidList[0]); + let result = { + "applicationName" : applicationName, + "releaseNumber" : releaseNumber + }; + return result; +} + /** * Resolves registry office application name from forwarding name : "PromptForRegisteringCausesRegistrationRequest". * @returns {Promise} application name From c587db772b8fea06f55847fa611e9e8de10fc55f Mon Sep 17 00:00:00 2001 From: Prathiba Date: Sat, 23 Mar 2024 22:08:40 +0100 Subject: [PATCH 13/21] To implement /v1/register-yourself Fixes #949 --- .../basicServices/BasicServicesService.js | 14 +----- .../services/PrepareForwardingAutomation.js | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/server/basicServices/basicServices/BasicServicesService.js b/server/basicServices/basicServices/BasicServicesService.js index 02946f20..ad8ac432 100644 --- a/server/basicServices/basicServices/BasicServicesService.js +++ b/server/basicServices/basicServices/BasicServicesService.js @@ -999,9 +999,6 @@ exports.registerYourself = async function (body, user, xCorrelator, traceIndicat let httpAddress = body["http-address"]; let httpPort = body["http-port"]; - let httpsAddress = body["https-address"]; - let httpsPort = body["https-port"]; - oldApplicationName = body["preceding-application-name"]; oldReleaseNumber = body["preceding-release-number"]; @@ -1052,16 +1049,7 @@ exports.registerYourself = async function (body, user, xCorrelator, traceIndicat let tcpClientConfigurationStatusList = ltpConfigurationStatus.tcpClientConfigurationStatusList; tcpClientConfigurationStatusList.push(configurationStatus); } - } - - let tcpServerWithHttpsUpdated = await updateTcpServerDetails("HTTPS", httpsAddress, httpsPort); - if (tcpServerWithHttpsUpdated.istcpServerUpdated) { - let configurationStatus = new ConfigurationStatus(tcpServerWithHttpsUpdated.tcpServerUuid, '', tcpServerWithHttpsUpdated.istcpServerUpdated); - if (ltpConfigurationStatus) { - let tcpClientConfigurationStatusList = ltpConfigurationStatus.tcpClientConfigurationStatusList; - tcpClientConfigurationStatusList.push(configurationStatus); - } - } + } // update old release configuration let isOldApplicationIsUpdated = false; diff --git a/server/basicServices/basicServices/services/PrepareForwardingAutomation.js b/server/basicServices/basicServices/services/PrepareForwardingAutomation.js index cc22a453..b46afa1a 100644 --- a/server/basicServices/basicServices/services/PrepareForwardingAutomation.js +++ b/server/basicServices/basicServices/services/PrepareForwardingAutomation.js @@ -164,6 +164,54 @@ exports.registerYourself = function (logicalTerminationPointconfigurationStatus, ); forwardingConstructAutomationList.push(forwardingAutomation); + /*********************************************************************************** + * PromptForRegisteringCausesRegistrationRequest2 /v2/register-application + ***********************************************************************************/ + let registrationRequest2ForwardingName = "PromptForRegisteringCausesRegistrationRequest2"; + let registrationRequest2Context; + let registrationRequest2RequestBody = {}; + let registrationRequest2TcpServerList = []; + registrationRequest2RequestBody.applicationName = await httpServerInterface.getApplicationNameAsync(); + registrationRequest2RequestBody.releaseNumber = await httpServerInterface.getReleaseNumberAsync(); + registrationRequest2RequestBody.embeddingOperation = await operationServerInterface.getOperationNameAsync(controlConstructUuid + "-op-s-bm-001"); + registrationRequest2RequestBody.clientUpdateOperation = await operationServerInterface.getOperationNameAsync(controlConstructUuid + "-op-s-bm-007"); + registrationRequest2RequestBody.operationClientUpdateOperation = await operationServerInterface.getOperationNameAsync(controlConstructUuid + "-op-s-bm-011"); + registrationRequest2RequestBody.disposeRemaindersOperation = await operationServerInterface.getOperationNameAsync(controlConstructUuid + "-op-s-bm-013"); + registrationRequest2RequestBody.precedingReleaseOperation = await operationServerInterface.getOperationNameAsync(controlConstructUuid + "-op-s-bm-014"); + registrationRequest2RequestBody.subsequentReleaseOperation = await operationServerInterface.getOperationNameAsync(controlConstructUuid + "-op-s-bm-015"); + + // formulate the tcp-server-list + let registrationRequest2TcpHttpAddress = await tcpServerInterface.getLocalAddressOfTheProtocol("HTTP"); + let registrationRequest2TcpHttpPort = await tcpServerInterface.getLocalPortOfTheProtocol("HTTP"); + if (registrationRequest2TcpHttpAddress != undefined && registrationRequest2TcpHttpPort != undefined) { + if ("ipv-4-address" in registrationRequest2TcpHttpAddress) { + registrationRequest2TcpHttpAddress = { + "ip-address": registrationRequest2TcpHttpAddress + } + } + let registrationRequest2tcpServer = { + protocol: "HTTP", + port: registrationRequest2TcpHttpPort, + address: registrationRequest2TcpHttpAddress + } + registrationRequest2TcpServerList.push(registrationRequest2tcpServer); + } + + registrationRequest2RequestBody.tcpServerList = registrationRequest2TcpServerList; + if (oldApplicationName) { + registrationRequest2RequestBody.precedingApplicationName = oldApplicationName; + } + if (oldReleaseNumber) { + registrationRequest2RequestBody.precedingReleaseNumber = oldReleaseNumber; + } + registrationRequest2RequestBody = onfFormatter.modifyJsonObjectKeysToKebabCase(registrationRequest2RequestBody); + let registrationRequest2forwardingAutomation = new forwardingConstructAutomationInput( + registrationRequest2ForwardingName, + registrationRequest2RequestBody, + registrationRequest2Context + ); + forwardingConstructAutomationList.push(registrationRequest2forwardingAutomation); + /*********************************************************************************** * forwardings for application layer topology ************************************************************************************/ From d666c2941b91228d894707cf4368718e183ed556 Mon Sep 17 00:00:00 2001 From: Prathiba Date: Sun, 24 Mar 2024 12:47:46 +0100 Subject: [PATCH 14/21] To support new API /v1/inquire-basic-auth-approvals Fixes #953 --- .../applicationPattern/commons/AppCommons.js | 3 ++- .../services/AuthorizingService.js | 13 +++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/server/applicationPattern/applicationPattern/commons/AppCommons.js b/server/applicationPattern/applicationPattern/commons/AppCommons.js index 3da21a8c..14652caa 100644 --- a/server/applicationPattern/applicationPattern/commons/AppCommons.js +++ b/server/applicationPattern/applicationPattern/commons/AppCommons.js @@ -69,7 +69,8 @@ async function validateOperationKey(request, scopes, schema) { */ // eslint-disable-next-line no-unused-vars async function validateBasicAuth(request, scopes, schema) { - const authStatus = await authorizingService.isAuthorized(request.headers.authorization, request.method); + let pathDefinedInOpenApi = request.openapi.openApiRoute; + const authStatus = await authorizingService.isAuthorized(request.headers.authorization, request.method, pathDefinedInOpenApi); if (authStatus.isAuthorized == true) { return true; } else { diff --git a/server/applicationPattern/applicationPattern/services/AuthorizingService.js b/server/applicationPattern/applicationPattern/services/AuthorizingService.js index 47d69d54..02e88a4d 100644 --- a/server/applicationPattern/applicationPattern/services/AuthorizingService.js +++ b/server/applicationPattern/applicationPattern/services/AuthorizingService.js @@ -16,7 +16,7 @@ const FcPort = require('../onfModel/models/FcPort'); * @param {String} method is the https method name * @returns {Promise} authStatus */ -exports.isAuthorized = async function (authorizationCode, method) { +exports.isAuthorized = async function (authorizationCode, method, operationName) { let authStatus = { "isAuthorized": false } @@ -26,8 +26,8 @@ exports.isAuthorized = async function (authorizationCode, method) { let applicationName = await HttpServerInterface.getApplicationNameAsync(); let applicationReleaseNumber = await HttpServerInterface.getReleaseNumberAsync(); let httpRequestHeader = onfAttributeFormatter.modifyJsonObjectKeysToKebabCase(new RequestHeader(userName, applicationName, "", "", "unknown", operationKey)); - let httpRequestBody = formulateRequestBody(applicationName, applicationReleaseNumber, authorizationCode, method); - let response = await RequestBuilder.BuildAndTriggerRestRequest(operationClientUuid, "POST", httpRequestHeader, httpRequestBody); + let httpRequestBody = formulateRequestBody(applicationName, applicationReleaseNumber, authorizationCode, method, operationName); + let response = await RequestBuilder.BuildAndTriggerRestRequest(operationClientUuid, "POST", httpRequestHeader, httpRequestBody, undefined); if (response && response.status === 200) { if (response.data["oam-request-is-approved"] == true) { authStatus.isAuthorized = true; @@ -50,18 +50,19 @@ exports.isAuthorized = async function (authorizationCode, method) { * @param {String} method HTTP method of the OAM layer call. It can be PUT,GET * @returns {Object} formulated requestBody */ - function formulateRequestBody(applicationName, releaseNumber, authorizationCode, method) { + function formulateRequestBody(applicationName, releaseNumber, authorizationCode, method, operationName) { return { "application-name": applicationName, "release-number": releaseNumber, "Authorization": authorizationCode, - "method": method + "method": method, + "operation-name": operationName }; } async function getOperationClientToAuthenticateTheRequest() { let forwardingConstruct = await ForwardingDomain.getForwardingConstructForTheForwardingNameAsync( - "OamRequestCausesInquiryForAuthentication"); + "BasicAuthRequestCausesInquiryForAuthentication"); if (forwardingConstruct) { let fcPortList = forwardingConstruct["fc-port"]; for (let fcPort of fcPortList) { From 9505d01221108b48c7883bd7a4c37418ba9d575b Mon Sep 17 00:00:00 2001 From: Prathiba Date: Sun, 24 Mar 2024 13:01:18 +0100 Subject: [PATCH 15/21] To implement /v1/embed-yourself Fixes #950 --- .../basicServices/BasicServicesService.js | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/server/basicServices/basicServices/BasicServicesService.js b/server/basicServices/basicServices/BasicServicesService.js index ad8ac432..b90a53bb 100644 --- a/server/basicServices/basicServices/BasicServicesService.js +++ b/server/basicServices/basicServices/BasicServicesService.js @@ -116,12 +116,7 @@ exports.embedYourself = async function (body, user, xCorrelator, traceIndicator, let deregisterOperation = body["deregistration-operation"]; let relayOperationUpdateOperation = body["relay-operation-update-operation"]; let relayServerReplacementOperation = body["relay-server-replacement-operation"]; - - let oldReleaseProtocol = body["old-release-protocol"]; - let oldReleaseAddress = body["old-release-address"]; - let oldReleasePort = body["old-release-port"]; - - + const registryOfficeApplicationName = await ServiceUtils.resolveRegistryOfficeApplicationNameFromForwardingAsync(); if (registryOfficeApplicationName !== applicationName) { throw new createHttpError.BadRequest(`The registry-office-application ${applicationName} was not found.`); @@ -162,29 +157,48 @@ exports.embedYourself = async function (body, user, xCorrelator, traceIndicator, ltpConfigurationInput, isApplicationRo ); } + + /*********************************************************************** + * oldRelease information to be updated if provided in the requestBody + ***********************************************************************/ let isOldApplicationTcpClientUpdated = false; let oldApplicationTcpClientUuid; let oldApplicationForwardingTag = "PromptForEmbeddingCausesRequestForBequeathingData"; let isOldReleaseExist = await isForwardingNameExist(oldApplicationForwardingTag); let oldApplicationNameInConfiguration; + if (isOldReleaseExist) { let httpUuidOfOldApplication = await httpClientInterface.getHttpClientUuidFromForwarding(oldApplicationForwardingTag); oldApplicationNameInConfiguration = await ServiceUtils.resolveApplicationNameFromForwardingAsync(oldApplicationForwardingTag); + if (httpUuidOfOldApplication != undefined) { let tcpClientUuidList = await LogicalTerminationPoint.getServerLtpListAsync(httpUuidOfOldApplication); + if (tcpClientUuidList != undefined) { oldApplicationTcpClientUuid = tcpClientUuidList[0]; let tcpClientProtocolOfOldApplication = await tcpClientInterface.getRemoteProtocolAsync(oldApplicationTcpClientUuid); - if (oldReleaseProtocol != tcpClientProtocolOfOldApplication) { + if ("old-release-protocol" in body) + { + let oldReleaseProtocol = body["old-release-protocol"]; + if(oldReleaseProtocol != tcpClientProtocolOfOldApplication) { isOldApplicationTcpClientUpdated = await tcpClientInterface.setRemoteProtocolAsync(oldApplicationTcpClientUuid, oldReleaseProtocol); } - let tcpClientAddressOfOldApplication = await tcpClientInterface.getRemoteAddressAsync(oldApplicationTcpClientUuid); - if (oldReleaseAddress != tcpClientAddressOfOldApplication) { + } + if ("old-release-address" in body) + { + let oldReleaseAddress = body["old-release-address"]; + let tcpClientAddressOfOldApplication = await tcpClientInterface.getRemoteAddressAsync(oldApplicationTcpClientUuid); + if (oldReleaseAddress != tcpClientAddressOfOldApplication) { isOldApplicationTcpClientUpdated = await tcpClientInterface.setRemoteAddressAsync(oldApplicationTcpClientUuid, oldReleaseAddress); } - let tcpClientPortOfOldApplication = await tcpClientInterface.getRemotePortAsync(oldApplicationTcpClientUuid); - if (oldReleasePort != tcpClientPortOfOldApplication) { - isOldApplicationTcpClientUpdated = await tcpClientInterface.setRemotePortAsync(oldApplicationTcpClientUuid, oldReleasePort); + } + if ("old-release-port" in body) + { + let oldReleasePort = body["old-release-port"]; + let tcpClientPortOfOldApplication = await tcpClientInterface.getRemotePortAsync(oldApplicationTcpClientUuid); + if (oldReleasePort != tcpClientPortOfOldApplication) { + isOldApplicationTcpClientUpdated = await tcpClientInterface.setRemotePortAsync(oldApplicationTcpClientUuid, oldReleasePort); + } } } } From 98ea362b0f52f1af76e2e532a693f206f0dbdb79 Mon Sep 17 00:00:00 2001 From: Prathiba Date: Sun, 24 Mar 2024 15:06:49 +0100 Subject: [PATCH 16/21] To support new attributes to the callback ServiceRequestCausesLoggingRequest Fixes #952 To exclude OldRelease and NewRelease while updating the LTP Fixes #951 --- .../rest/client/RequestBuilder.js | 4 +++- .../rest/client/eventDispatcher.js | 13 ++++++++++++- .../services/ExecutionAndTraceService.js | 18 +++++++++++------- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/server/applicationPattern/applicationPattern/rest/client/RequestBuilder.js b/server/applicationPattern/applicationPattern/rest/client/RequestBuilder.js index 843c86ec..f12a25ea 100644 --- a/server/applicationPattern/applicationPattern/rest/client/RequestBuilder.js +++ b/server/applicationPattern/applicationPattern/rest/client/RequestBuilder.js @@ -56,7 +56,9 @@ exports.BuildAndTriggerRestRequest = async function (operationClientUuid, method return error.response; } else if (error.request) { console.log(`Request errored with ${error}`); - return new createHttpError.RequestTimeout(); + let requestTimeoutError = new createHttpError.RequestTimeout(); + requestTimeoutError.url = error.config ? error.config.url ? error.config.url : undefined : undefined; + return requestTimeoutError; } console.log(`Unknown request error: ${error}`); return new createHttpError.InternalServerError(); diff --git a/server/applicationPattern/applicationPattern/rest/client/eventDispatcher.js b/server/applicationPattern/applicationPattern/rest/client/eventDispatcher.js index 3345579c..a29e502d 100644 --- a/server/applicationPattern/applicationPattern/rest/client/eventDispatcher.js +++ b/server/applicationPattern/applicationPattern/rest/client/eventDispatcher.js @@ -61,7 +61,18 @@ exports.dispatchEvent = function (operationClientUuid, httpRequestBody, user, xC if (responseCode.toString().startsWith("2")) { result = true; } else if (responseCode == 408) { - recordServiceRequestFromClient(serverApplicationName, serverApplicationReleaseNumber, xCorrelator, traceIndicator, user, originator, operationName, responseCode, httpRequestBody, response.data) + recordServiceRequestFromClient( + serverApplicationName, + serverApplicationReleaseNumber, + xCorrelator, + traceIndicator, + user, + originator, + operationName, + responseCode, + httpRequestBody, + response.data, + response.url) .catch((error) => console.log(`record service request ${JSON.stringify({ xCorrelator, traceIndicator, diff --git a/server/applicationPattern/applicationPattern/services/ExecutionAndTraceService.js b/server/applicationPattern/applicationPattern/services/ExecutionAndTraceService.js index a829a5c7..029d0480 100644 --- a/server/applicationPattern/applicationPattern/services/ExecutionAndTraceService.js +++ b/server/applicationPattern/applicationPattern/services/ExecutionAndTraceService.js @@ -22,10 +22,11 @@ const moment = require('moment'); * @param {number} responseCode response code of the rest call execution
* @param {object} requestBody request body
* @param {object} responseBody response body
+ * @param {String} url complete url of the request * @returns {Promise} returns true if the operation is successful. Promise is never rejected.
*/ exports.recordServiceRequestFromClient = async function (serverApplicationName, serverApplicationReleaseNumber, xCorrelator, traceIndicator, userName, originator, - operationName, responseCode, requestBody, responseBody) { + operationName, responseCode, requestBody, responseBody, url) { let httpRequestBody = {}; try { let operationClientUuid = await getOperationClientToLogServiceRequest(); @@ -37,7 +38,7 @@ exports.recordServiceRequestFromClient = async function (serverApplicationName, let stringifiedRequestBody = JSON.stringify(requestBody); let stringifiedResponseBody = JSON.stringify(responseBody); let httpRequestBody = formulateRequestBody(xCorrelator, traceIndicator, userName, originator, serverApplicationName, serverApplicationReleaseNumber, - operationName, responseCode, timestamp, stringifiedRequestBody, stringifiedResponseBody, detailedLoggingIsOn); + operationName, responseCode, timestamp, stringifiedRequestBody, stringifiedResponseBody, detailedLoggingIsOn, url); let response = await RequestBuilder.BuildAndTriggerRestRequest(operationClientUuid, "POST", httpRequestHeader, httpRequestBody); let responseCodeValue = response.status.toString(); if (responseCodeValue.startsWith("2")) { @@ -61,10 +62,11 @@ exports.recordServiceRequestFromClient = async function (serverApplicationName, * @param {number} responseCode response code of the rest call execution
* @param {object} requestBody request body
* @param {object} responseBody response body
+ * @param {String} execTime time taken to execute this request * @returns {Promise} returns true if the operation is successful. Promise is never rejected.
*/ exports.recordServiceRequest = async function (xCorrelator, traceIndicator, userName, originator, - operationName, responseCode, requestBody, responseBody) { + operationName, responseCode, requestBody, responseBody, execTime) { let httpRequestBody = {}; try { let operationClientUuid = await getOperationClientToLogServiceRequest(); @@ -77,7 +79,7 @@ exports.recordServiceRequest = async function (xCorrelator, traceIndicator, user let stringifiedRequestBody = JSON.stringify(requestBody); let stringifiedResponseBody = JSON.stringify(responseBody); let httpRequestBody = formulateRequestBody(xCorrelator, traceIndicator, userName, originator, applicationName, applicationReleaseNumber, - operationName, responseCode, timestamp, stringifiedRequestBody, stringifiedResponseBody, detailedLoggingIsOn); + operationName, responseCode, timestamp, stringifiedRequestBody, stringifiedResponseBody, detailedLoggingIsOn, undefined, execTime); let response = await RequestBuilder.BuildAndTriggerRestRequest(operationClientUuid, "POST", httpRequestHeader, httpRequestBody); let responseCodeValue = response.status.toString(); if (responseCodeValue.startsWith("2")) { @@ -108,7 +110,7 @@ exports.recordServiceRequest = async function (xCorrelator, traceIndicator, user * @returns {Object} return the formulated responseBody
*/ function formulateRequestBody(xCorrelator, traceIndicator, userName, originator, applicationName, applicationReleaseNumber, - operationName, responseCode, timestamp, stringifiedBody, stringifiedResponse, detailedLoggingIsOn) { + operationName, responseCode, timestamp, stringifiedBody, stringifiedResponse, detailedLoggingIsOn, Url, execTime) { let httpRequestBody = { "x-correlator": xCorrelator, "trace-indicator": traceIndicator, @@ -117,10 +119,12 @@ function formulateRequestBody(xCorrelator, traceIndicator, userName, originator, "application-name": applicationName, "release-number": applicationReleaseNumber, "operation-name": operationName, - "response-code": responseCode + "response-code": responseCode, + "timestamp" : timestamp, + "url": Url, + "exec-time": execTime }; if (detailedLoggingIsOn) { - httpRequestBody["timestamp"] = timestamp; httpRequestBody["stringified-body"] = stringifiedBody; httpRequestBody["stringified-response"] = stringifiedResponse; } From 98d3150b4887f8f48330cc5fd204d1a1e81b6ed7 Mon Sep 17 00:00:00 2001 From: Prathiba Date: Sun, 24 Mar 2024 15:13:37 +0100 Subject: [PATCH 17/21] RequestHeader constructor to initialize trace-indicator as a string instead of integer Fixes #964 --- .../applicationPattern/rest/client/RequestHeader.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/applicationPattern/applicationPattern/rest/client/RequestHeader.js b/server/applicationPattern/applicationPattern/rest/client/RequestHeader.js index ae59229a..b48190c5 100644 --- a/server/applicationPattern/applicationPattern/rest/client/RequestHeader.js +++ b/server/applicationPattern/applicationPattern/rest/client/RequestHeader.js @@ -38,11 +38,11 @@ class RequestHeader { } this.traceIndicator = traceIndicator; if (traceIndicator == undefined || traceIndicator.length == 0) { - this.traceIndicator = 1; + this.traceIndicator = "1"; } this.customerJourney = customerJourney; if (customerJourney == undefined || customerJourney.length == 0) { - this.customerJourney = 1; + this.customerJourney = "unknown"; } if (operationKey != undefined && operationKey.length > 0) { this.operationKey = operationKey; From ed370124d6aa01c9d360460f500089b3c71cf191 Mon Sep 17 00:00:00 2001 From: Prathiba Date: Sun, 24 Mar 2024 15:31:08 +0100 Subject: [PATCH 18/21] eventDispatcher function shall be updated for returning response-body on receiving 200 response-code Fixes #960 --- .../applicationPattern/rest/client/eventDispatcher.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/applicationPattern/applicationPattern/rest/client/eventDispatcher.js b/server/applicationPattern/applicationPattern/rest/client/eventDispatcher.js index a29e502d..220945a9 100644 --- a/server/applicationPattern/applicationPattern/rest/client/eventDispatcher.js +++ b/server/applicationPattern/applicationPattern/rest/client/eventDispatcher.js @@ -26,8 +26,10 @@ const LogicalTerminationPoint = require('../../onfModel/models/LogicalTerminatio * @param {String} customerJourney Holds information supporting customer’s journey to which the execution applies. * @param {String} httpMethod method of the request if undefined defaults to POST * @param {Object} params path and query params + * @param {Boolean} isresponseRequired should be true to receive responseBody along with the status + * @returns {Boolean | Object} result If isresponseRequired is true , then the complete response will be provided if the request is success */ -exports.dispatchEvent = function (operationClientUuid, httpRequestBody, user, xCorrelator, traceIndicator, customerJourney, httpMethod="POST", params) { +exports.dispatchEvent = function (operationClientUuid, httpRequestBody, user, xCorrelator, traceIndicator, customerJourney, httpMethod="POST", params, isresponseRequired) { return new Promise(async function (resolve, reject) { let result = false; try { @@ -59,6 +61,9 @@ exports.dispatchEvent = function (operationClientUuid, httpRequestBody, user, xC ); let responseCode = response.status; if (responseCode.toString().startsWith("2")) { + if(isresponseRequired){ + result = response; + } result = true; } else if (responseCode == 408) { recordServiceRequestFromClient( From 2bd76f14c5c82e98900e4e83259ae2a108888a61 Mon Sep 17 00:00:00 2001 From: Prathiba Date: Sun, 24 Mar 2024 15:38:56 +0100 Subject: [PATCH 19/21] Change the file -path to file-name in the onfAttribute Fixes #958 --- .../onfModel/constants/OnfPaths.js | 8 +- .../onfModel/models/profile/FileProfile.js | 171 +++--------------- 2 files changed, 27 insertions(+), 152 deletions(-) diff --git a/server/applicationPattern/applicationPattern/onfModel/constants/OnfPaths.js b/server/applicationPattern/applicationPattern/onfModel/constants/OnfPaths.js index d5d84d3a..76a51ae3 100644 --- a/server/applicationPattern/applicationPattern/onfModel/constants/OnfPaths.js +++ b/server/applicationPattern/applicationPattern/onfModel/constants/OnfPaths.js @@ -29,9 +29,7 @@ const ACTION_PROFILE_CONSEQUENT_OPERATION_REFERENCE = ACTION_PROFILE_CONFIGURATI const FILE_PROFILE_PAC = PROFILE + "={profileUuid}/file-profile-1-0:file-profile-pac"; const FILE_PROFILE_CONFIGURATION = FILE_PROFILE_PAC + "/file-profile-configuration"; -const FILE_PROFILE_FILE_PATH = FILE_PROFILE_CONFIGURATION + "/file-path"; -const FILE_PROFILE_USER_NAME = FILE_PROFILE_CONFIGURATION + "/user-name"; -const FILE_PROFILE_PASSWORD = FILE_PROFILE_CONFIGURATION + "/password"; +const FILE_PROFILE_FILE_NAME = FILE_PROFILE_CONFIGURATION + "/file-name"; const FILE_PROFILE_OPERATION = FILE_PROFILE_CONFIGURATION + "/operation"; @@ -96,9 +94,7 @@ module.exports = { ACTION_PROFILE_CONSEQUENT_OPERATION_REFERENCE, FILE_PROFILE_PAC, FILE_PROFILE_CONFIGURATION, - FILE_PROFILE_FILE_PATH, - FILE_PROFILE_USER_NAME, - FILE_PROFILE_PASSWORD, + FILE_PROFILE_FILE_NAME, FILE_PROFILE_OPERATION, LOGICAL_TERMINATION_POINT, LOGICAL_TERMINATION_POINT_UUID, diff --git a/server/applicationPattern/applicationPattern/onfModel/models/profile/FileProfile.js b/server/applicationPattern/applicationPattern/onfModel/models/profile/FileProfile.js index 3782ff5e..21ea7745 100644 --- a/server/applicationPattern/applicationPattern/onfModel/models/profile/FileProfile.js +++ b/server/applicationPattern/applicationPattern/onfModel/models/profile/FileProfile.js @@ -19,7 +19,7 @@ class FileProfile extends profile { /** * FileProfilePac class holds the following properties, * 1.fileProfileCapability - class that holds the fileIdentifier, fileProfileConfiguration. - * 2.fileProfileConfiguration - class that holds the filePath, username, password, operation. + * 2.fileProfileConfiguration - class that holds the fileName, operation. */ static FileProfilePac = class FileProfilePac { static profileName = profile.profileNameEnum.FILE_PROFILE; @@ -43,9 +43,7 @@ class FileProfile extends profile { }; static FileProfileConfiguration = class FileProfileConfiguration { - filePath; - userName; - password; + fileName; operation; static operationEnum = { @@ -57,16 +55,12 @@ class FileProfile extends profile { /** * constructor - * @param {string} filePath path for the file. - * @param {string} userName user name to access the file. - * @param {string} password password name to access the file. + * @param {string} fileName name of the file. * @param {string} operation operation. * This constructor will instantiate the fileProfileConfiguration class */ - constructor(filePath, userName, password, operation) { - this.filePath = filePath; - this.userName = userName; - this.password = password; + constructor(fileName, operation) { + this.fileName = fileName; this.operation = operation; } }; @@ -75,19 +69,17 @@ class FileProfile extends profile { * constructor * @param {string} fileIdentifier Identifier for the file. * @param {string} fileDescription Description for the file. - * @param {string} filePath path for the file. - * @param {string} userName user name to access the file. - * @param {string} password password to access the file.. + * @param {string} fileName Name of the file. * @param {string} operation operation. * This constructor will instantiate the FileProfilePac class */ - constructor(fileIdentifier, fileDescription, filePath, userName, password, operation) { + constructor(fileIdentifier, fileDescription, fileName, operation) { this.fileProfileCapability = new FileProfilePac. FileProfileCapability( fileIdentifier, fileDescription); this.FileProfileConfiguration = new FileProfilePac. FileProfileConfiguration( - filePath, userName, password, operation); + fileName, operation); } } @@ -96,13 +88,11 @@ class FileProfile extends profile { * @param {string} uuid : the value should be a valid string in the pattern '-\d+-\d+-\d+-file-p-\d+$' * @param {string} fileIdentifier Identifier for the file. * @param {string} fileDescription Description for the file. - * @param {string} filePath path for the file. - * @param {string} userName user name to access the file. - * @param {string} password password to access the file.. + * @param {string} fileName name of the file. * @param {string} operation operation. * This constructor will instantiate the FileProfile class */ - constructor(uuid, fileIdentifier, fileDescription, filePath, userName, password, operation) { + constructor(uuid, fileIdentifier, fileDescription, fileName, operation) { super( uuid, FileProfile.FileProfilePac.profileName @@ -110,9 +100,7 @@ class FileProfile extends profile { this[onfAttributes.FILE_PROFILE.PAC] = new FileProfile.FileProfilePac( fileIdentifier, fileDescription, - filePath, - userName, - password, + fileName, operation ); } @@ -168,7 +156,7 @@ class FileProfile extends profile { } /** - * @description Fetch application data file path + * @description Fetch application data complete file path */ static async getApplicationDataFileContent() { return new Promise(async function (resolve, reject) { @@ -178,7 +166,7 @@ class FileProfile extends profile { let profileUuid = profiles.flatMap(profile => profile[onfAttributes.GLOBAL_CLASS.UUID]); for (let profileUuidIndex = 0; profileUuidIndex < profileUuid.length; profileUuidIndex++) { let uuid = profileUuid[profileUuidIndex]; - let value = await FileProfile.getFilePath(uuid) + let value = await FileProfile.getFileName(uuid) let completeFilePath = "./application-data/" + value; if (fileSystem.existsSync(completeFilePath)) { applicationDataFile = completeFilePath; @@ -197,77 +185,24 @@ class FileProfile extends profile { } /** - * @description This function returns the file path for the provided file profile uuid. + * @description This function returns the file name for the provided file profile uuid. * @param {String} profileUuid : the value should be a valid string in the pattern '-\d+-\d+-\d+-file-p-\d+$' - * @returns {promise} string {filePath} + * @returns {promise} string {fileName} **/ - static async getFilePath(profileUuid) { + static async getFileName(profileUuid) { return new Promise(async function (resolve, reject) { try { - let filePath; + let fileName; let profileList = await profileCollection.getProfileListForProfileNameAsync(FileProfile.FileProfilePac.profileName); for (let profile of profileList) { let uuidOfProfile = profile[onfAttributes.GLOBAL_CLASS.UUID]; if (uuidOfProfile === profileUuid) { let fileProfilePac = profile[onfAttributes.FILE_PROFILE.PAC]; let fileProfileConfiguration = fileProfilePac[onfAttributes.FILE_PROFILE.CONFIGURATION]; - filePath = fileProfileConfiguration[onfAttributes.FILE_PROFILE.FILE_NAME]; + fileName = fileProfileConfiguration[onfAttributes.FILE_PROFILE.FILE_NAME]; } } - resolve(filePath); - } catch (error) { - reject(error); - } - }); - } - - - - /** - * @description This function returns the user name for the provided file profile uuid. - * @param {String} profileUuid : the value should be a valid string in the pattern '-\d+-\d+-\d+-file-p-\d+$' - * @returns {promise} string {userName} - **/ - static async getUserName(profileUuid) { - return new Promise(async function (resolve, reject) { - try { - let userName; - let profileList = await profileCollection.getProfileListForProfileNameAsync(FileProfile.FileProfilePac.profileName); - for (let profile of profileList) { - let uuidOfProfile = profile[onfAttributes.GLOBAL_CLASS.UUID]; - if (uuidOfProfile === profileUuid) { - let fileProfilePac = profile[onfAttributes.FILE_PROFILE.PAC]; - let fileProfileConfiguration = fileProfilePac[onfAttributes.FILE_PROFILE.CONFIGURATION]; - userName = fileProfileConfiguration[onfAttributes.FILE_PROFILE.USER_NAME]; - } - } - resolve(userName); - } catch (error) { - reject(error); - } - }); - } - - - /** - * @description This function returns the password for the provided file profile uuid. - * @param {String} profileUuid : the value should be a valid string in the pattern '-\d+-\d+-\d+-file-p-\d+$' - * @returns {promise} string {password} - **/ - static async getPassword(profileUuid) { - return new Promise(async function (resolve, reject) { - try { - let password; - let profileList = await profileCollection.getProfileListForProfileNameAsync(FileProfile.FileProfilePac.profileName); - for (let profile of profileList) { - let uuidOfProfile = profile[onfAttributes.GLOBAL_CLASS.UUID]; - if (uuidOfProfile === profileUuid) { - let fileProfilePac = profile[onfAttributes.FILE_PROFILE.PAC]; - let fileProfileConfiguration = fileProfilePac[onfAttributes.FILE_PROFILE.CONFIGURATION]; - password = fileProfileConfiguration[onfAttributes.FILE_PROFILE.PASSWORD]; - } - } - resolve(password); + resolve(fileName); } catch (error) { reject(error); } @@ -300,78 +235,22 @@ class FileProfile extends profile { } /** - * @description This function sets the file path for the provided file profile uuid. - * @param {String} profileUuid : the value should be a valid string in the pattern '-\d+-\d+-\d+-file-p-\d+$' - * @param {String} filePathValueToBeUpdated : Value of file path that needs to be updated. - * @returns {promise} boolean {true|false} - **/ - static async setFilePath(profileUuid, filePathValueToBeUpdated) { - return new Promise(async function (resolve, reject) { - try { - let isUpdated = false; - try { - let filePath = onfPaths.FILE_PROFILE_FILE_PATH - .replace( - "{profileUuid}", profileUuid); - isUpdated = await fileOperation.writeToDatabaseAsync( - filePath, - filePathValueToBeUpdated, - false); - resolve(isUpdated); - } catch (error) { - reject(error); - } - } catch (error) { - reject(error); - } - }); - } - - /** - * @description This function sets the user name for the provided file profile uuid. - * @param {String} profileUuid : the value should be a valid string in the pattern '-\d+-\d+-\d+-file-p-\d+$' - * @param {String} userName : the value of user name - * @returns {promise} boolean {true|false} - **/ - static async setUserName(profileUuid, userName) { - return new Promise(async function (resolve, reject) { - try { - let isUpdated = false; - try { - let userNamePath = onfPaths.FILE_PROFILE_USER_NAME - .replace( - "{profileUuid}", profileUuid); - isUpdated = await fileOperation.writeToDatabaseAsync( - userNamePath, - userName, - false); - resolve(isUpdated); - } catch (error) { - reject(error); - } - } catch (error) { - reject(error); - } - }); - } - - /** - * @description This function sets the password for the provided file profile uuid. + * @description This function sets the file name for the provided file profile uuid. * @param {String} profileUuid : the value should be a valid string in the pattern '-\d+-\d+-\d+-file-p-\d+$' - * @param {String} password : the value of password + * @param {String} fileNameToBeUpdated : Value of file name that needs to be updated. * @returns {promise} boolean {true|false} **/ - static async setPassword(profileUuid, password) { + static async setFileName(profileUuid, fileNameToBeUpdated) { return new Promise(async function (resolve, reject) { try { let isUpdated = false; try { - let passwordPath = onfPaths.FILE_PROFILE_PASSWORD + let fileName = onfPaths.FILE_PROFILE_FILE_NAME .replace( "{profileUuid}", profileUuid); isUpdated = await fileOperation.writeToDatabaseAsync( - passwordPath, - password, + fileName, + fileNameToBeUpdated, false); resolve(isUpdated); } catch (error) { From 29d600f618e3ffd672a780e75d70baa50735d6cf Mon Sep 17 00:00:00 2001 From: Prathiba Date: Sun, 24 Mar 2024 16:59:16 +0100 Subject: [PATCH 20/21] To limit the number of instance in tcp-server-list to 1 Fixes #904 --- .../services/PrepareForwardingAutomation.js | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/server/basicServices/basicServices/services/PrepareForwardingAutomation.js b/server/basicServices/basicServices/services/PrepareForwardingAutomation.js index b46afa1a..51562934 100644 --- a/server/basicServices/basicServices/services/PrepareForwardingAutomation.js +++ b/server/basicServices/basicServices/services/PrepareForwardingAutomation.js @@ -133,21 +133,6 @@ exports.registerYourself = function (logicalTerminationPointconfigurationStatus, } tcpServerList.push(tcpServer); } - let tcpHttpsAddress = await tcpServerInterface.getLocalAddressOfTheProtocol("HTTPS"); - let tcpHttpsPort = await tcpServerInterface.getLocalPortOfTheProtocol("HTTPS"); - if (tcpHttpsAddress != undefined && tcpHttpsPort != undefined) { - if ("ipv-4-address" in tcpHttpsAddress) { - tcpHttpsAddress = { - "ip-address": tcpHttpsAddress - } - } - let tcpServer = { - protocol: "HTTPS", - port: tcpHttpsPort, - address: tcpHttpsAddress - } - tcpServerList.push(tcpServer); - } registrationApplicationRequestBody.tcpServerList = tcpServerList; if (oldApplicationName) { From 1e8577ee725162f5cefdf46e0f9166605d61cd1f Mon Sep 17 00:00:00 2001 From: Prathiba Date: Tue, 26 Mar 2024 00:17:03 +0100 Subject: [PATCH 21/21] To include a generic non-blocking logic that waits for operation-key updates Fixes #966 --- .../onfModel/constants/OnfPaths.js | 8 +- .../onfModel/models/ProfileCollection.js | 1 - .../OperationClientInterface.js | 154 +++++++++++++++- .../onfModel/models/profile/FileProfile.js | 171 +++++++++++++++--- .../onfModel/models/profile/IntegerProfile.js | 2 +- .../rest/client/RequestHeader.js | 4 +- .../rest/client/eventDispatcher.js | 7 +- .../basicServices/BasicServicesService.js | 28 +-- .../services/PrepareForwardingAutomation.js | 15 ++ 9 files changed, 324 insertions(+), 66 deletions(-) diff --git a/server/applicationPattern/applicationPattern/onfModel/constants/OnfPaths.js b/server/applicationPattern/applicationPattern/onfModel/constants/OnfPaths.js index 76a51ae3..d5d84d3a 100644 --- a/server/applicationPattern/applicationPattern/onfModel/constants/OnfPaths.js +++ b/server/applicationPattern/applicationPattern/onfModel/constants/OnfPaths.js @@ -29,7 +29,9 @@ const ACTION_PROFILE_CONSEQUENT_OPERATION_REFERENCE = ACTION_PROFILE_CONFIGURATI const FILE_PROFILE_PAC = PROFILE + "={profileUuid}/file-profile-1-0:file-profile-pac"; const FILE_PROFILE_CONFIGURATION = FILE_PROFILE_PAC + "/file-profile-configuration"; -const FILE_PROFILE_FILE_NAME = FILE_PROFILE_CONFIGURATION + "/file-name"; +const FILE_PROFILE_FILE_PATH = FILE_PROFILE_CONFIGURATION + "/file-path"; +const FILE_PROFILE_USER_NAME = FILE_PROFILE_CONFIGURATION + "/user-name"; +const FILE_PROFILE_PASSWORD = FILE_PROFILE_CONFIGURATION + "/password"; const FILE_PROFILE_OPERATION = FILE_PROFILE_CONFIGURATION + "/operation"; @@ -94,7 +96,9 @@ module.exports = { ACTION_PROFILE_CONSEQUENT_OPERATION_REFERENCE, FILE_PROFILE_PAC, FILE_PROFILE_CONFIGURATION, - FILE_PROFILE_FILE_NAME, + FILE_PROFILE_FILE_PATH, + FILE_PROFILE_USER_NAME, + FILE_PROFILE_PASSWORD, FILE_PROFILE_OPERATION, LOGICAL_TERMINATION_POINT, LOGICAL_TERMINATION_POINT_UUID, diff --git a/server/applicationPattern/applicationPattern/onfModel/models/ProfileCollection.js b/server/applicationPattern/applicationPattern/onfModel/models/ProfileCollection.js index 12da20b0..95ef2611 100644 --- a/server/applicationPattern/applicationPattern/onfModel/models/ProfileCollection.js +++ b/server/applicationPattern/applicationPattern/onfModel/models/ProfileCollection.js @@ -17,7 +17,6 @@ const Profile = require('./Profile'); class ProfileCollection { /** - * @deprecated use getProfileListForProfileNameAsync * @description This function returns the profile list from /core-model-1-4:control-construct/profile-collection/profile. * @returns {promise} list {profile list} **/ diff --git a/server/applicationPattern/applicationPattern/onfModel/models/layerProtocols/OperationClientInterface.js b/server/applicationPattern/applicationPattern/onfModel/models/layerProtocols/OperationClientInterface.js index 187dedef..f3a1b18c 100644 --- a/server/applicationPattern/applicationPattern/onfModel/models/layerProtocols/OperationClientInterface.js +++ b/server/applicationPattern/applicationPattern/onfModel/models/layerProtocols/OperationClientInterface.js @@ -16,7 +16,9 @@ const tcpClientInterface = require('./TcpClientInterface'); const onfPaths = require('../../constants/OnfPaths'); const onfAttributes = require('../../constants/OnfAttributes'); const fileOperation = require('../../../databaseDriver/JSONDriver'); - +global.operationKeyNotificationChannel = []; +global.notificationChannelSubscriber = []; +global.isNotificationChannelON = false; /** * @extends LayerProtocol */ @@ -121,7 +123,7 @@ class OperationClientInterface extends LayerProtocol { } return undefined; } - + /** * @description This function returns the detailedLoggingIsOn attribute of the operation client. * @param {String} operationClientUuid : uuid of the operation client ,the value should be a valid string @@ -201,15 +203,23 @@ class OperationClientInterface extends LayerProtocol { * @param {String} operationClientUuid : uuid of the http client ,the value should be a valid string * in the pattern '-\d+-\d+-\d+-op-client-\d+$' * @param {String} operationKey : key that needs to be updated. - * @returns {Promise} true|false + * @returns {Promise} isOperationKeySet **/ static async setOperationKeyAsync(operationClientUuid, operationKey) { - let operationKeyPath = onfPaths.OPERATION_CLIENT_OPERATION_KEY.replace( - "{uuid}", operationClientUuid); - return await fileOperation.writeToDatabaseAsync( - operationKeyPath, - operationKey, - false); + let isOperationKeySet = false + let oldoperationKey = await this.getOperationKeyAsync(operationClientUuid); + if (oldoperationKey != operationKey) { + let operationKeyPath = onfPaths.OPERATION_CLIENT_OPERATION_KEY.replace( + "{uuid}", operationClientUuid); + isOperationKeySet = await fileOperation.writeToDatabaseAsync( + operationKeyPath, + operationKey, + false); + } + if (isOperationKeySet == true || oldoperationKey == operationKey) { + this.addOperationKeyUpdateToNotificationChannel(operationClientUuid); + } + return isOperationKeySet; } /** @@ -289,6 +299,92 @@ class OperationClientInterface extends LayerProtocol { const layerProtocolName = layerProtocol[onfAttributes.LAYER_PROTOCOL.LAYER_PROTOCOL_NAME]; return LayerProtocol.layerProtocolNameEnum.OPERATION_CLIENT === layerProtocolName; } + + /** + * @param {HRTime} timeStampIdentifier an identifier the process that turns ON the notification channel + * @return {boolean} result whether the notificationChannel is turned ON or not + */ + static turnONNotificationChannel(timeStampIdentifier) { + try { + if (!global.notificationChannelSubscriber.includes(timeStampIdentifier)) { + global.notificationChannelSubscriber.push(timeStampIdentifier); + } + global.isNotificationChannelON = true; + console.log("******* Notification channel for Operation key is turned ON ***************"); + return global.isNotificationChannelON; + } catch (error) { + return false; + } + } + + /** + * @param {HRTime} timeStampAsIdentifier an identifier the process that turns OFF the notification channel + * @return {boolean} result whether the notificationChannel is turned OFF or not + */ + static turnOFFNotificationChannel(timeStampAsIdentifier) { + try { + if (global.notificationChannelSubscriber.includes(timeStampAsIdentifier)) { + global.notificationChannelSubscriber.forEach((element, index) => { + if (element == timeStampAsIdentifier) { + global.notificationChannelSubscriber = global.notificationChannelSubscriber.splice(index, 1); + } + }); + } + if (global.notificationChannelSubscriber.length > 0) { + global.isNotificationChannelON = false; + console.log("******* Notification channel for Operation key is turned OFF ***************"); + } + return !global.isNotificationChannelON; + } catch (error) { + return false; + } + } + + /** + * function to add notifications to the global operationKeyNotificationChannel + * @param {String} operationClientUuid of the operationKey updated instance + */ + static async addOperationKeyUpdateToNotificationChannel(operationClientUuid) { + try { + if (global.isNotificationChannelON) { + let operationKeyNotification = { + "eventTime": new Date(), + "operationClientUuid": operationClientUuid + }; + console.log("Notification pushed :" + operationKeyNotification.eventTime + "," + operationKeyNotification.operationClientUuid); + global.operationKeyNotificationChannel.push(operationKeyNotification); + } + } catch (error) { + console.log(error); + } + } + + /** + * This function waits for a desired time until an operation-key updation is received for a client + * @param {String} operationClientUuid for which the an operation-key update is monitored + * @param {Date} timestampOfCurrentRequest shall be used to monitor whether operation-key is updated after this event's occurance + * @param {Integer} waitTime will be the maximum time to wait for an operation-key update in Seconds + * @returns {promise} boolean that represents an operation-key update + */ + static async waitUntilOperationKeyIsUpdated(operationClientUuid, timestampOfCurrentRequest, waitTime) { + let startTime = process.hrtime(); + console.log("Waiting to receive operation key update for the operationClientUuid " + operationClientUuid); + return await new Promise(resolve => { + const interval = setInterval(() => { + let operationKeyUpdated = isOperationKeyUpdated(operationClientUuid, timestampOfCurrentRequest); + let waitTimeExceeded = isWaitTimeExceeded(startTime, waitTime); + if (operationKeyUpdated == true) { + console.log("Operation key update received for the operationClientUuid " + operationClientUuid); + resolve(true); + clearInterval(interval); + } else if (waitTimeExceeded == true) { + console.log("Waiting time exceeded for receiving operation key for the operationClientUuid " + operationClientUuid); + resolve(false); + clearInterval(interval); + } + }, 100); + }); + } } /** @@ -309,4 +405,42 @@ function getConfiguredRemoteAddress(remoteAddress) { return remoteAddress; } -module.exports = OperationClientInterface; +/** + * + * @param {HRTime} startTime of the process + * @param {Integer} waitingTime of the process in Seconds + * @returns + */ +function isWaitTimeExceeded(startTime, waitingTime) { + let NanoSecondPerSecond = 1e9; + let executionTime = process.hrtime(startTime); + let executionTimeInseconds = (executionTime[0] * NanoSecondPerSecond + executionTime[1]) / NanoSecondPerSecond + if (executionTimeInseconds >= waitingTime) { + return true + } else { + return false; + } +} + +/** + * + * @param {String} operationClientUuid uuid of the operation client interface + * @param {HRTime} eventTime of the process + * @returns + */ +function isOperationKeyUpdated(operationClientUuid, eventTime) { + let result = false; + console.log(operationClientUuid + "," + eventTime); + let oKNotificationChannel = global.operationKeyNotificationChannel; + oKNotificationChannel.filter((notification) => { + console.log("*****************************************"); + console.log(notification.operationClientUuid); + console.log(operationClientUuid); + if (notification.operationClientUuid == operationClientUuid && notification.eventTime > eventTime) { + result = true; + } + }); + return result; +} + +module.exports = OperationClientInterface; \ No newline at end of file diff --git a/server/applicationPattern/applicationPattern/onfModel/models/profile/FileProfile.js b/server/applicationPattern/applicationPattern/onfModel/models/profile/FileProfile.js index 21ea7745..3782ff5e 100644 --- a/server/applicationPattern/applicationPattern/onfModel/models/profile/FileProfile.js +++ b/server/applicationPattern/applicationPattern/onfModel/models/profile/FileProfile.js @@ -19,7 +19,7 @@ class FileProfile extends profile { /** * FileProfilePac class holds the following properties, * 1.fileProfileCapability - class that holds the fileIdentifier, fileProfileConfiguration. - * 2.fileProfileConfiguration - class that holds the fileName, operation. + * 2.fileProfileConfiguration - class that holds the filePath, username, password, operation. */ static FileProfilePac = class FileProfilePac { static profileName = profile.profileNameEnum.FILE_PROFILE; @@ -43,7 +43,9 @@ class FileProfile extends profile { }; static FileProfileConfiguration = class FileProfileConfiguration { - fileName; + filePath; + userName; + password; operation; static operationEnum = { @@ -55,12 +57,16 @@ class FileProfile extends profile { /** * constructor - * @param {string} fileName name of the file. + * @param {string} filePath path for the file. + * @param {string} userName user name to access the file. + * @param {string} password password name to access the file. * @param {string} operation operation. * This constructor will instantiate the fileProfileConfiguration class */ - constructor(fileName, operation) { - this.fileName = fileName; + constructor(filePath, userName, password, operation) { + this.filePath = filePath; + this.userName = userName; + this.password = password; this.operation = operation; } }; @@ -69,17 +75,19 @@ class FileProfile extends profile { * constructor * @param {string} fileIdentifier Identifier for the file. * @param {string} fileDescription Description for the file. - * @param {string} fileName Name of the file. + * @param {string} filePath path for the file. + * @param {string} userName user name to access the file. + * @param {string} password password to access the file.. * @param {string} operation operation. * This constructor will instantiate the FileProfilePac class */ - constructor(fileIdentifier, fileDescription, fileName, operation) { + constructor(fileIdentifier, fileDescription, filePath, userName, password, operation) { this.fileProfileCapability = new FileProfilePac. FileProfileCapability( fileIdentifier, fileDescription); this.FileProfileConfiguration = new FileProfilePac. FileProfileConfiguration( - fileName, operation); + filePath, userName, password, operation); } } @@ -88,11 +96,13 @@ class FileProfile extends profile { * @param {string} uuid : the value should be a valid string in the pattern '-\d+-\d+-\d+-file-p-\d+$' * @param {string} fileIdentifier Identifier for the file. * @param {string} fileDescription Description for the file. - * @param {string} fileName name of the file. + * @param {string} filePath path for the file. + * @param {string} userName user name to access the file. + * @param {string} password password to access the file.. * @param {string} operation operation. * This constructor will instantiate the FileProfile class */ - constructor(uuid, fileIdentifier, fileDescription, fileName, operation) { + constructor(uuid, fileIdentifier, fileDescription, filePath, userName, password, operation) { super( uuid, FileProfile.FileProfilePac.profileName @@ -100,7 +110,9 @@ class FileProfile extends profile { this[onfAttributes.FILE_PROFILE.PAC] = new FileProfile.FileProfilePac( fileIdentifier, fileDescription, - fileName, + filePath, + userName, + password, operation ); } @@ -156,7 +168,7 @@ class FileProfile extends profile { } /** - * @description Fetch application data complete file path + * @description Fetch application data file path */ static async getApplicationDataFileContent() { return new Promise(async function (resolve, reject) { @@ -166,7 +178,7 @@ class FileProfile extends profile { let profileUuid = profiles.flatMap(profile => profile[onfAttributes.GLOBAL_CLASS.UUID]); for (let profileUuidIndex = 0; profileUuidIndex < profileUuid.length; profileUuidIndex++) { let uuid = profileUuid[profileUuidIndex]; - let value = await FileProfile.getFileName(uuid) + let value = await FileProfile.getFilePath(uuid) let completeFilePath = "./application-data/" + value; if (fileSystem.existsSync(completeFilePath)) { applicationDataFile = completeFilePath; @@ -185,24 +197,77 @@ class FileProfile extends profile { } /** - * @description This function returns the file name for the provided file profile uuid. + * @description This function returns the file path for the provided file profile uuid. * @param {String} profileUuid : the value should be a valid string in the pattern '-\d+-\d+-\d+-file-p-\d+$' - * @returns {promise} string {fileName} + * @returns {promise} string {filePath} **/ - static async getFileName(profileUuid) { + static async getFilePath(profileUuid) { return new Promise(async function (resolve, reject) { try { - let fileName; + let filePath; let profileList = await profileCollection.getProfileListForProfileNameAsync(FileProfile.FileProfilePac.profileName); for (let profile of profileList) { let uuidOfProfile = profile[onfAttributes.GLOBAL_CLASS.UUID]; if (uuidOfProfile === profileUuid) { let fileProfilePac = profile[onfAttributes.FILE_PROFILE.PAC]; let fileProfileConfiguration = fileProfilePac[onfAttributes.FILE_PROFILE.CONFIGURATION]; - fileName = fileProfileConfiguration[onfAttributes.FILE_PROFILE.FILE_NAME]; + filePath = fileProfileConfiguration[onfAttributes.FILE_PROFILE.FILE_NAME]; } } - resolve(fileName); + resolve(filePath); + } catch (error) { + reject(error); + } + }); + } + + + + /** + * @description This function returns the user name for the provided file profile uuid. + * @param {String} profileUuid : the value should be a valid string in the pattern '-\d+-\d+-\d+-file-p-\d+$' + * @returns {promise} string {userName} + **/ + static async getUserName(profileUuid) { + return new Promise(async function (resolve, reject) { + try { + let userName; + let profileList = await profileCollection.getProfileListForProfileNameAsync(FileProfile.FileProfilePac.profileName); + for (let profile of profileList) { + let uuidOfProfile = profile[onfAttributes.GLOBAL_CLASS.UUID]; + if (uuidOfProfile === profileUuid) { + let fileProfilePac = profile[onfAttributes.FILE_PROFILE.PAC]; + let fileProfileConfiguration = fileProfilePac[onfAttributes.FILE_PROFILE.CONFIGURATION]; + userName = fileProfileConfiguration[onfAttributes.FILE_PROFILE.USER_NAME]; + } + } + resolve(userName); + } catch (error) { + reject(error); + } + }); + } + + + /** + * @description This function returns the password for the provided file profile uuid. + * @param {String} profileUuid : the value should be a valid string in the pattern '-\d+-\d+-\d+-file-p-\d+$' + * @returns {promise} string {password} + **/ + static async getPassword(profileUuid) { + return new Promise(async function (resolve, reject) { + try { + let password; + let profileList = await profileCollection.getProfileListForProfileNameAsync(FileProfile.FileProfilePac.profileName); + for (let profile of profileList) { + let uuidOfProfile = profile[onfAttributes.GLOBAL_CLASS.UUID]; + if (uuidOfProfile === profileUuid) { + let fileProfilePac = profile[onfAttributes.FILE_PROFILE.PAC]; + let fileProfileConfiguration = fileProfilePac[onfAttributes.FILE_PROFILE.CONFIGURATION]; + password = fileProfileConfiguration[onfAttributes.FILE_PROFILE.PASSWORD]; + } + } + resolve(password); } catch (error) { reject(error); } @@ -235,22 +300,78 @@ class FileProfile extends profile { } /** - * @description This function sets the file name for the provided file profile uuid. + * @description This function sets the file path for the provided file profile uuid. + * @param {String} profileUuid : the value should be a valid string in the pattern '-\d+-\d+-\d+-file-p-\d+$' + * @param {String} filePathValueToBeUpdated : Value of file path that needs to be updated. + * @returns {promise} boolean {true|false} + **/ + static async setFilePath(profileUuid, filePathValueToBeUpdated) { + return new Promise(async function (resolve, reject) { + try { + let isUpdated = false; + try { + let filePath = onfPaths.FILE_PROFILE_FILE_PATH + .replace( + "{profileUuid}", profileUuid); + isUpdated = await fileOperation.writeToDatabaseAsync( + filePath, + filePathValueToBeUpdated, + false); + resolve(isUpdated); + } catch (error) { + reject(error); + } + } catch (error) { + reject(error); + } + }); + } + + /** + * @description This function sets the user name for the provided file profile uuid. + * @param {String} profileUuid : the value should be a valid string in the pattern '-\d+-\d+-\d+-file-p-\d+$' + * @param {String} userName : the value of user name + * @returns {promise} boolean {true|false} + **/ + static async setUserName(profileUuid, userName) { + return new Promise(async function (resolve, reject) { + try { + let isUpdated = false; + try { + let userNamePath = onfPaths.FILE_PROFILE_USER_NAME + .replace( + "{profileUuid}", profileUuid); + isUpdated = await fileOperation.writeToDatabaseAsync( + userNamePath, + userName, + false); + resolve(isUpdated); + } catch (error) { + reject(error); + } + } catch (error) { + reject(error); + } + }); + } + + /** + * @description This function sets the password for the provided file profile uuid. * @param {String} profileUuid : the value should be a valid string in the pattern '-\d+-\d+-\d+-file-p-\d+$' - * @param {String} fileNameToBeUpdated : Value of file name that needs to be updated. + * @param {String} password : the value of password * @returns {promise} boolean {true|false} **/ - static async setFileName(profileUuid, fileNameToBeUpdated) { + static async setPassword(profileUuid, password) { return new Promise(async function (resolve, reject) { try { let isUpdated = false; try { - let fileName = onfPaths.FILE_PROFILE_FILE_NAME + let passwordPath = onfPaths.FILE_PROFILE_PASSWORD .replace( "{profileUuid}", profileUuid); isUpdated = await fileOperation.writeToDatabaseAsync( - fileName, - fileNameToBeUpdated, + passwordPath, + password, false); resolve(isUpdated); } catch (error) { diff --git a/server/applicationPattern/applicationPattern/onfModel/models/profile/IntegerProfile.js b/server/applicationPattern/applicationPattern/onfModel/models/profile/IntegerProfile.js index 4d5f46ac..08179b40 100644 --- a/server/applicationPattern/applicationPattern/onfModel/models/profile/IntegerProfile.js +++ b/server/applicationPattern/applicationPattern/onfModel/models/profile/IntegerProfile.js @@ -148,7 +148,7 @@ class IntegerProfile extends Profile { /** * @description This function returns the configured value for the integer profile. - * @param {String} integerProfileKey : name of the integer profile + * @param {String} integerProfileName : name of the integer profile * @returns {promise} string {integerValue} **/ static async getIntegerValueForTheIntegerProfileNameAsync(integerProfileName) { diff --git a/server/applicationPattern/applicationPattern/rest/client/RequestHeader.js b/server/applicationPattern/applicationPattern/rest/client/RequestHeader.js index b48190c5..ae59229a 100644 --- a/server/applicationPattern/applicationPattern/rest/client/RequestHeader.js +++ b/server/applicationPattern/applicationPattern/rest/client/RequestHeader.js @@ -38,11 +38,11 @@ class RequestHeader { } this.traceIndicator = traceIndicator; if (traceIndicator == undefined || traceIndicator.length == 0) { - this.traceIndicator = "1"; + this.traceIndicator = 1; } this.customerJourney = customerJourney; if (customerJourney == undefined || customerJourney.length == 0) { - this.customerJourney = "unknown"; + this.customerJourney = 1; } if (operationKey != undefined && operationKey.length > 0) { this.operationKey = operationKey; diff --git a/server/applicationPattern/applicationPattern/rest/client/eventDispatcher.js b/server/applicationPattern/applicationPattern/rest/client/eventDispatcher.js index 220945a9..a29e502d 100644 --- a/server/applicationPattern/applicationPattern/rest/client/eventDispatcher.js +++ b/server/applicationPattern/applicationPattern/rest/client/eventDispatcher.js @@ -26,10 +26,8 @@ const LogicalTerminationPoint = require('../../onfModel/models/LogicalTerminatio * @param {String} customerJourney Holds information supporting customer’s journey to which the execution applies. * @param {String} httpMethod method of the request if undefined defaults to POST * @param {Object} params path and query params - * @param {Boolean} isresponseRequired should be true to receive responseBody along with the status - * @returns {Boolean | Object} result If isresponseRequired is true , then the complete response will be provided if the request is success */ -exports.dispatchEvent = function (operationClientUuid, httpRequestBody, user, xCorrelator, traceIndicator, customerJourney, httpMethod="POST", params, isresponseRequired) { +exports.dispatchEvent = function (operationClientUuid, httpRequestBody, user, xCorrelator, traceIndicator, customerJourney, httpMethod="POST", params) { return new Promise(async function (resolve, reject) { let result = false; try { @@ -61,9 +59,6 @@ exports.dispatchEvent = function (operationClientUuid, httpRequestBody, user, xC ); let responseCode = response.status; if (responseCode.toString().startsWith("2")) { - if(isresponseRequired){ - result = response; - } result = true; } else if (responseCode == 408) { recordServiceRequestFromClient( diff --git a/server/basicServices/basicServices/BasicServicesService.js b/server/basicServices/basicServices/BasicServicesService.js index b90a53bb..422e8197 100644 --- a/server/basicServices/basicServices/BasicServicesService.js +++ b/server/basicServices/basicServices/BasicServicesService.js @@ -37,6 +37,7 @@ const createHttpError = require('http-errors'); const HttpServerInterface = require('onf-core-model-ap/applicationPattern/onfModel/models/layerProtocols/HttpServerInterface'); const OperationClientInterface = require('onf-core-model-ap/applicationPattern/onfModel/models/layerProtocols/OperationClientInterface'); +const integerProfile = require('onf-core-model-ap/applicationPattern/onfModel/models/profile/IntegerProfile'); /** * Removes application from configuration and application data * @@ -1416,25 +1417,14 @@ exports.updateOperationClient = async function (body, user, xCorrelator, traceIn exports.updateOperationKey = async function (body) { let operationUuid = body["operation-uuid"]; let newOperationKey = body["new-operation-key"]; - - if (await operationServerInterface.isOperationServerAsync(operationUuid)) { - let OldoperationKey = await operationServerInterface.getOperationKeyAsync(operationUuid) - if (OldoperationKey != undefined) { - if (newOperationKey != OldoperationKey) { - await operationServerInterface.setOperationKeyAsync(operationUuid, newOperationKey); - } - } else { - throw new createHttpError.BadRequest("OperationServerUuid is not present"); - } - } else if (await operationClientInterface.isOperationClientAsync(operationUuid)) { - let OldoperationKey = await operationClientInterface.getOperationKeyAsync(operationUuid) - if (OldoperationKey != undefined) { - if (newOperationKey != OldoperationKey) { - await operationClientInterface.setOperationKeyAsync(operationUuid, newOperationKey); - } - } else { - throw new createHttpError.BadRequest("OperationClientUuid is not present"); - } + let isOperationServerUuid = await operationServerInterface.isOperationServerAsync(operationUuid); + let isOperationClientUuid = await operationClientInterface.isOperationClientAsync(operationUuid); + if (isOperationServerUuid) { + operationServerInterface.setOperationKeyAsync(operationUuid, newOperationKey); + } else if (isOperationClientUuid) { + operationClientInterface.setOperationKeyAsync(operationUuid, newOperationKey); + }else { + throw new createHttpError.BadRequest("OperationClientUuid/OperationServerUuid is not present"); } } diff --git a/server/basicServices/basicServices/services/PrepareForwardingAutomation.js b/server/basicServices/basicServices/services/PrepareForwardingAutomation.js index 51562934..b46afa1a 100644 --- a/server/basicServices/basicServices/services/PrepareForwardingAutomation.js +++ b/server/basicServices/basicServices/services/PrepareForwardingAutomation.js @@ -133,6 +133,21 @@ exports.registerYourself = function (logicalTerminationPointconfigurationStatus, } tcpServerList.push(tcpServer); } + let tcpHttpsAddress = await tcpServerInterface.getLocalAddressOfTheProtocol("HTTPS"); + let tcpHttpsPort = await tcpServerInterface.getLocalPortOfTheProtocol("HTTPS"); + if (tcpHttpsAddress != undefined && tcpHttpsPort != undefined) { + if ("ipv-4-address" in tcpHttpsAddress) { + tcpHttpsAddress = { + "ip-address": tcpHttpsAddress + } + } + let tcpServer = { + protocol: "HTTPS", + port: tcpHttpsPort, + address: tcpHttpsAddress + } + tcpServerList.push(tcpServer); + } registrationApplicationRequestBody.tcpServerList = tcpServerList; if (oldApplicationName) {