Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
cee549c
Add description of how to set operationKey in configFile
kmohr-soprasteria Feb 16, 2024
31532f9
corrected filename step1
kmohr-soprasteria Feb 16, 2024
ac8c073
correct filename
kmohr-soprasteria Feb 16, 2024
053611c
Merge pull request #957 from openBackhaul/kmohr-soprasteria/issue956
kmohr-soprasteria Mar 12, 2024
22c7973
Create HowToDefineErrorMessages.HowToDefineErrorMessages.md
openBackhaul Mar 15, 2024
2c7aba3
Delete doc/SpecifyingApplications/HowToDefineErrorMessages.HowToDefin…
openBackhaul Mar 15, 2024
eee889d
Create HowToDefineErrorMessages.md
openBackhaul Mar 15, 2024
8f20f34
Update SpecifyingApplications.md
openBackhaul Mar 15, 2024
9e95caa
Update HowToDefineErrorMessages.md
openBackhaul Mar 15, 2024
c3e5b69
Improve readability of Defining ErrorMessages documentation
kmohr-soprasteria Mar 18, 2024
2719b91
format
kmohr-soprasteria Mar 18, 2024
635d165
fix error
kmohr-soprasteria Mar 18, 2024
a6b203c
Merge pull request #963 from openBackhaul/kmohr-soprasteria/issue962
IswaryaaS Mar 18, 2024
7b8045c
To support new APIs in version 2.1.1
Mar 23, 2024
c587db7
To implement /v1/register-yourself
Mar 23, 2024
d666c29
To support new API /v1/inquire-basic-auth-approvals
Mar 24, 2024
9505d01
To implement /v1/embed-yourself
Mar 24, 2024
98ea362
To support new attributes to the callback ServiceRequestCausesLogging…
Mar 24, 2024
98d3150
RequestHeader constructor to initialize trace-indicator as a string i…
Mar 24, 2024
ed37012
eventDispatcher function shall be updated for returning response-body…
Mar 24, 2024
2bd76f1
Change the file -path to file-name in the onfAttribute
Mar 24, 2024
29d600f
To limit the number of instance in tcp-server-list to 1
Mar 24, 2024
1e8577e
To include a generic non-blocking logic that waits for operation-key …
Mar 25, 2024
cf5a5c5
Merge branch 'v2.1.1_impl' into PrathibaJee/issue955
PrathibaJee Mar 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
32 changes: 32 additions & 0 deletions doc/SpecifyingApplications/CreatingConfigFile/OperationKeys.md
Original file line number Diff line number Diff line change
@@ -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, <br> operationKey = default operationKey |
| ![basicAuth](./pictures/example_oas_basicAuth.png) | requires authorizationCode instead of operationKey, <br> 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) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# 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 maximum 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'
```
3 changes: 2 additions & 1 deletion doc/SpecifyingApplications/SpecifyingApplications.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}
**/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<boolean>} true|false
* @returns {Promise<boolean>} 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;
}

/**
Expand Down Expand Up @@ -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);
});
}
}

/**
Expand All @@ -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;
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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<String|undefined>} 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
Expand Down
Loading