From b26e4fd256eccbd11cb5b64ae26e8f09b2065c72 Mon Sep 17 00:00:00 2001
From: Marin Karamihalev <marin.karamihalev@iopsys.eu>
Date: Mon, 23 Jan 2023 09:34:18 +0100
Subject: [PATCH] comply to notify resp rules

---
 src/commands/common/notify.ts     | 12 +++++++-----
 src/commands/recipes/subscribe.ts | 14 +++++++-------
 src/configurations/build.ts       |  7 ++++---
 src/types.ts                      | 22 ++++++++++++++++++++--
 src/util.ts                       | 15 +++++++++++----
 5 files changed, 49 insertions(+), 21 deletions(-)

diff --git a/src/commands/common/notify.ts b/src/commands/common/notify.ts
index ceadbbb..cc9b133 100644
--- a/src/commands/common/notify.ts
+++ b/src/commands/common/notify.ts
@@ -10,8 +10,8 @@ const decode: DecodeFn = (msg) => {
   const parent = util.searchParent(msg, "subscriptionId");
   if (parent) {
     const id = parent.subscriptionId;
-    const relField = Object.keys(parent).find((k) =>
-      k !== "subscriptionId" && k !== "sendResp"
+    const relField = Object.keys(parent).find(
+      (k) => k !== "subscriptionId" && k !== "sendResp"
     );
     return id && relField
       ? [parseInfo(relField, msg), id, null]
@@ -20,7 +20,7 @@ const decode: DecodeFn = (msg) => {
   return [null];
 };
 
-const encode: EncodeFn = ({ paths }) => ({
+const encode: EncodeFn = ({ sendResp, subscriptionId, ...rest }) => ({
   lookup: "Msg",
   header: {
     msgId: util.uniq("NOTIFY@"),
@@ -31,8 +31,10 @@ const encode: EncodeFn = ({ paths }) => ({
     lookup: "Body",
     request: {
       lookup: "Request",
-      get: {
-        paramPaths: Array.isArray(paths) ? paths : [paths],
+      notify: {
+        subscriptionId,
+        sendResp,
+        ...rest,
       },
     },
   },
diff --git a/src/commands/recipes/subscribe.ts b/src/commands/recipes/subscribe.ts
index 412cb98..7d4c67c 100644
--- a/src/commands/recipes/subscribe.ts
+++ b/src/commands/recipes/subscribe.ts
@@ -1,4 +1,4 @@
-import { MakeFn, SubscribeRecipe } from "../../types";
+import { CallbackOptions, MakeFn, SubscribeRecipe } from "../../types";
 import { uniq } from "../util";
 
 const subscriptionPath = "Device.LocalAgent.Subscription.";
@@ -23,12 +23,12 @@ const make: MakeFn =
       },
     });
 
-    const respond = opts?.forceNoResponse
-      ? () => {}
-      : () => call("NOTIFY_RESP", { subscriptionId: id });
-    const clear = on(id, (...args) => {
-      respond();
-      callback(...args);
+    // from https://usp.technology/specification/07-index-messages.html#sec:responses-and-retry
+
+    const respond = () => call("NOTIFY_RESP", { subscriptionId: id });
+    const clear = on(id, (msg, fullMsg, opts) => {
+      opts?.sendResp && respond();
+      callback(msg, fullMsg);
     });
     return () => {
       clear();
diff --git a/src/configurations/build.ts b/src/configurations/build.ts
index 0b4ba79..5605265 100644
--- a/src/configurations/build.ts
+++ b/src/configurations/build.ts
@@ -15,7 +15,7 @@ import {
   BuildConnectionFn,
   USPVersion,
 } from "../types";
-import { knownUSPVersions, makeCallbackRouter, makeRouter } from "../util";
+import { knownUSPVersions, makeCallbackRouter, makeRouter, parseCallbackOptions } from "../util";
 
 const defaultPublishEndpoint = "/usp/endpoint";
 const defaultSubscribeEndpoint = "/usp/controller";
@@ -187,9 +187,10 @@ const buildConnect: BuildConnectionFn =
         }
 
         const cbs = callbackRouter.get(id);
+        const callbackOptions = parseCallbackOptions(parsedMsg)
         cbs.forEach((cb) => {
-          if (message) cb(message, parsedMsg);
-          else if (err) cb(err, parsedMsg);
+          if (callbackOptions) cb(message || err || "", parsedMsg, callbackOptions)
+          else cb(message || err || "", parsedMsg)
         });
       }
     });
diff --git a/src/types.ts b/src/types.ts
index 612c5b6..cdad800 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -240,6 +240,7 @@ export type PbRequestCommand =
   | PbRequestCommandSupport
   | PbRequestCommandInstance
   | PbRequestCommandSupportProto
+  | PbRequestCommandNotify
   | PbRequestCommandNotifyResp;
 
 export interface SuportedCommandOpts {
@@ -266,6 +267,14 @@ export type PbRequestCommandNotifyResp = {
   };
 };
 
+export type PbRequestCommandNotify = {
+  notify: {
+    subscriptionId: string;
+    sendResp: boolean;
+    [key: string]: unknown;
+  };
+};
+
 export type PbRequestCommandSupportProto = {
   getSupportedProtocol: {
     controllerSupportedProtocolVersions: string;
@@ -615,7 +624,6 @@ export interface SubscriptionOptions {
   id?: string;
   notif: NotifType;
   retry?: boolean;
-  forceNoResponse?: boolean;
   reference: string | string[];
 }
 
@@ -726,10 +734,20 @@ export type EncodeArgs = {
 
 export type OnIdent = string | RegExp;
 
+export type CallbackOptions = {
+  sendResp: boolean
+}
+
+export type OnCallback = (
+  msg: Response,
+  fullMsg?: Record<string, any>,
+  opts?: CallbackOptions
+) => void;
+
 export type EncodeFn = (args: Record<string, any>) => PbRequestMessage;
 export type CallArgs = Record<string, any>;
 export type ClearFn = () => void;
-export type OnFn = (ident: OnIdent, callback: SubscriptionCallback) => ClearFn;
+export type OnFn = (ident: OnIdent, callback: OnCallback) => ClearFn;
 export type MakeFn = (call: CallFn, on: OnFn) => Command;
 export type MakeRecipeFn = (call: CallFn) => Recipe;
 export type CommandTrigger = {
diff --git a/src/util.ts b/src/util.ts
index c1fbfc5..318d87e 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -1,4 +1,5 @@
-import { CommandType, OnIdent, SubscriptionCallback } from "./types";
+import { search } from "./commands/util";
+import { CallbackOptions, CommandType, OnCallback, OnIdent, SubscriptionCallback } from "./types";
 
 /**
  * Makes a router for storing resolve/reject for a message
@@ -24,19 +25,25 @@ const satisfies = (id: OnIdent, matches: string): boolean =>
 export const makeCallbackRouter = () => {
   const routes = new Map<
     string,
-    { callback: SubscriptionCallback; ident: OnIdent }
+    { callback: OnCallback; ident: OnIdent }
   >();
   return {
-    get: (id: string): SubscriptionCallback[] => {
+    get: (id: string): OnCallback[] => {
       return Array.from(routes.values())
         .filter(({ ident }) => satisfies(ident, id))
         .map((v) => v.callback);
     },
-    add: (ident: OnIdent, callback: SubscriptionCallback) => {
+    add: (ident: OnIdent, callback: OnCallback) => {
       routes.set(toId(ident), { callback, ident });
     },
     del: (id: OnIdent) => routes.delete(toId(id)),
   };
 };
 
+export const parseCallbackOptions = (msg: Record<string, any>): CallbackOptions | null => {
+  const sendResp = search(msg, "sendResp")
+  if (typeof sendResp === "boolean") return { sendResp }
+  return null
+}
+
 export const knownUSPVersions = ["1.0", "1.1", "1.2"] as const;
-- 
GitLab