@routr/location 2.6.2 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.d.ts CHANGED
@@ -23,6 +23,7 @@ export default class Location implements ILocationService {
23
23
  * @param {AddRouteRequest} request - Add route request
24
24
  * @param {string} request.aor - AOR of the route
25
25
  * @param {Route} request.route - Route to add
26
+ * @param {number} request.maxContacts - Max number of contacts to accept
26
27
  * @return {Promise<void>}
27
28
  */
28
29
  addRoute(request: AddRouteRequest): Promise<void>;
@@ -33,7 +34,7 @@ export default class Location implements ILocationService {
33
34
  * @param {string} request.aor - AOR of the route
34
35
  * @param {Map<string, string>} request.labels - Optional Route labels (reserved for future use)
35
36
  * @param {object} request.backend - Optional Route backend (reserved for future use)
36
- * @return {Promise<void>}
37
+ * @return {Promise<Route[]>}
37
38
  */
38
39
  findRoutes(request: FindRoutesRequest): Promise<Route[]>;
39
40
  /**
package/dist/client.js CHANGED
@@ -33,7 +33,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
33
33
  };
34
34
  Object.defineProperty(exports, "__esModule", { value: true });
35
35
  /*
36
- * Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
36
+ * Copyright (C) 2024 by Fonoster Inc (https://fonoster.com)
37
37
  * http://github.com/fonoster/routr
38
38
  *
39
39
  * This file is part of Routr
@@ -58,6 +58,9 @@ const container = (self, request, name) => {
58
58
  if ((err === null || err === void 0 ? void 0 : err.code) === grpc.status.UNAVAILABLE) {
59
59
  return reject(new common_1.CommonErrors.ServiceUnavailableError(self.config.addr));
60
60
  }
61
+ else if (err === null || err === void 0 ? void 0 : err.code) {
62
+ return reject(err);
63
+ }
61
64
  resolve(response);
62
65
  });
63
66
  });
@@ -83,10 +86,13 @@ class Location {
83
86
  * @param {AddRouteRequest} request - Add route request
84
87
  * @param {string} request.aor - AOR of the route
85
88
  * @param {Route} request.route - Route to add
89
+ * @param {number} request.maxContacts - Max number of contacts to accept
86
90
  * @return {Promise<void>}
87
91
  */
88
92
  addRoute(request) {
89
- return container(this, request, this.addRoute.name);
93
+ return __awaiter(this, void 0, void 0, function* () {
94
+ return container(this, request, this.addRoute.name);
95
+ });
90
96
  }
91
97
  /**
92
98
  * Add a route to the location service.
@@ -95,7 +101,7 @@ class Location {
95
101
  * @param {string} request.aor - AOR of the route
96
102
  * @param {Map<string, string>} request.labels - Optional Route labels (reserved for future use)
97
103
  * @param {object} request.backend - Optional Route backend (reserved for future use)
98
- * @return {Promise<void>}
104
+ * @return {Promise<Route[]>}
99
105
  */
100
106
  findRoutes(request) {
101
107
  var _a, _b;
@@ -113,7 +119,9 @@ class Location {
113
119
  * @return {Promise<void>}
114
120
  */
115
121
  removeRoutes(request) {
116
- return container(this, request, this.removeRoutes.name);
122
+ return __awaiter(this, void 0, void 0, function* () {
123
+ return container(this, request, this.removeRoutes.name);
124
+ });
117
125
  }
118
126
  }
119
127
  exports.default = Location;
@@ -28,7 +28,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports.getConfig = void 0;
30
30
  /*
31
- * Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
31
+ * Copyright (C) 2024 by Fonoster Inc (https://fonoster.com)
32
32
  * http://github.com/fonoster/routr
33
33
  *
34
34
  * This file is part of Routr
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.schema = void 0;
4
4
  /*
5
- * Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
5
+ * Copyright (C) 2024 by Fonoster Inc (https://fonoster.com)
6
6
  * http://github.com/fonoster/routr
7
7
  *
8
8
  * This file is part of Routr
package/dist/envs.js CHANGED
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CONFIG_PATH = void 0;
4
4
  /*
5
- * Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
5
+ * Copyright (C) 2024 by Fonoster Inc (https://fonoster.com)
6
6
  * http://github.com/fonoster/routr
7
7
  *
8
8
  * This file is part of Routr.
package/dist/errors.js CHANGED
@@ -25,7 +25,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.InvalidSchemaConfiguration = exports.NotRoutesFoundForAOR = exports.UnsupportedSchema = void 0;
27
27
  /*
28
- * Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
28
+ * Copyright (C) 2024 by Fonoster Inc (https://fonoster.com)
29
29
  * http://github.com/fonoster/routr
30
30
  *
31
31
  * This file is part of Routr
package/dist/helper.js CHANGED
@@ -1,9 +1,27 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createRouteFromLastMessage = exports.createRoute = void 0;
4
+ /*
5
+ * Copyright (C) 2024 by Fonoster Inc (https://fonoster.com)
6
+ * http://github.com/fonoster/routr
7
+ *
8
+ * This file is part of Routr
9
+ *
10
+ * Licensed under the MIT License (the "License");
11
+ * you may not use this file except in compliance with
12
+ * the License. You may obtain a copy of the License at
13
+ *
14
+ * https://opensource.org/licenses/MIT
15
+ *
16
+ * Unless required by applicable law or agreed to in writing, software
17
+ * distributed under the License is distributed on an "AS IS" BASIS,
18
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
+ * See the License for the specific language governing permissions and
20
+ * limitations under the License.
21
+ */
22
+ /* eslint-disable require-jsdoc */
4
23
  const common_1 = require("@routr/common");
5
24
  const processor_1 = require("@routr/processor");
6
- /* eslint-disable require-jsdoc */
7
25
  // TODO: Before finalizing this, consider using the old approach of saving the rport
8
26
  // and received values (like here:
9
27
  // https://github.com/fonoster/routr/blob/59bc98af86078088aede7e658c0d82a19fa18fa4/mod/registrar/utils.js#L87)
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
31
31
  Object.defineProperty(exports, "__esModule", { value: true });
32
32
  exports.locationService = exports.CacheProvider = exports.getConfig = exports.LocationClient = exports.Utils = exports.Helper = void 0;
33
33
  /*
34
- * Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
34
+ * Copyright (C) 2024 by Fonoster Inc (https://fonoster.com)
35
35
  * http://github.com/fonoster/routr
36
36
  *
37
37
  * This file is part of Routr
@@ -1,5 +1,5 @@
1
- import { AddRouteRequest, FindRoutesRequest, ILocationService, ILocatorStore, RemoveRoutesRequest } from "./types";
2
1
  import { Route } from "@routr/common";
2
+ import { AddRouteRequest, FindRoutesRequest, ILocationService, ILocatorStore, RemoveRoutesRequest } from "./types";
3
3
  /**
4
4
  * A locator store that uses the location service to find routes for AORs.
5
5
  */
package/dist/location.js CHANGED
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  /*
13
- * Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
13
+ * Copyright (C) 2024 by Fonoster Inc (https://fonoster.com)
14
14
  * http://github.com/fonoster/routr
15
15
  *
16
16
  * This file is part of Routr
@@ -27,6 +27,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
27
27
  * See the License for the specific language governing permissions and
28
28
  * limitations under the License.
29
29
  */
30
+ /* eslint-disable require-jsdoc */
30
31
  const errors_1 = require("./errors");
31
32
  const common_1 = require("@routr/common");
32
33
  const utils_1 = require("./utils");
@@ -50,75 +51,97 @@ class Location {
50
51
  }
51
52
  /** @inheritdoc */
52
53
  addRoute(request) {
53
- if (!request.aor.startsWith(AOR_SCHEME.SIP) &&
54
- !request.aor.startsWith(AOR_SCHEME.BACKEND)) {
55
- throw new errors_1.UnsupportedSchema(request.aor);
56
- }
57
- return this.store.put(request.aor, request.route);
54
+ return __awaiter(this, void 0, void 0, function* () {
55
+ if (!request.aor.startsWith(AOR_SCHEME.SIP) &&
56
+ !request.aor.startsWith(AOR_SCHEME.BACKEND)) {
57
+ throw new errors_1.UnsupportedSchema(request.aor);
58
+ }
59
+ const existingRoutes = yield this.store.get(request.aor);
60
+ const routeAlreadyExists = existingRoutes.some((route) => route.user === request.route.user &&
61
+ route.host === request.route.host &&
62
+ route.port === request.route.port &&
63
+ route.transport === request.route.transport);
64
+ if (!routeAlreadyExists &&
65
+ request.maxContacts !== -1 &&
66
+ existingRoutes.length >= request.maxContacts) {
67
+ throw new common_1.CommonErrors.BadRequestError(`exceeds maximum of ${request.maxContacts} allowed contacts`);
68
+ }
69
+ return this.store.put(request.aor, request.route);
70
+ });
58
71
  }
59
72
  /** @inheritdoc */
60
73
  findRoutes(request) {
61
74
  var _a;
62
75
  return __awaiter(this, void 0, void 0, function* () {
63
- let routes = yield this.store.get(`${request.aor}:${request.callId}`);
64
- if (routes.length > 0) {
76
+ const formatLabels = (labelsMap) => {
77
+ return Array.from(labelsMap)
78
+ .sort(([keyA], [keyB]) => keyA.localeCompare(keyB))
79
+ .map(([key, value]) => `${key}=${value}`)
80
+ .join(";");
81
+ };
82
+ const labelString = request.labels ? formatLabels(request.labels) : null;
83
+ const storeKeyWithLabels = labelString
84
+ ? `${request.aor}:${request.callId}:${labelString}`
85
+ : `${request.aor}:${request.callId}`;
86
+ let routes = yield this.store.get(storeKeyWithLabels);
87
+ if (routes && routes.length > 0) {
65
88
  return routes;
66
89
  }
67
- routes = request.labels
68
- ? (yield this.store.get(request.aor)).filter((0, utils_1.filterOnlyMatchingLabels)(request.labels))
69
- : (_a = (yield this.store.get(request.aor))) !== null && _a !== void 0 ? _a : [];
70
- if (request.aor.startsWith(AOR_SCHEME.SIP)) {
71
- // Set call to the last route
90
+ if (labelString) {
91
+ const storeKey = `${request.aor}:${request.callId}:${labelString}`;
92
+ routes = yield this.store.get(storeKey);
93
+ if (!routes || routes.length === 0) {
94
+ routes = (yield this.store.get(request.aor)).filter((0, utils_1.filterOnlyMatchingLabels)(request.labels));
95
+ }
96
+ }
97
+ else {
98
+ routes = (_a = (yield this.store.get(request.aor))) !== null && _a !== void 0 ? _a : [];
99
+ }
100
+ const { backend } = request;
101
+ if (!backend) {
72
102
  this.store.put(`${request.aor}:${request.callId}`, routes[0]);
73
103
  return routes;
74
104
  }
75
- else if (request.aor.startsWith(AOR_SCHEME.BACKEND)) {
76
- const { backend } = request;
77
- // If it has not affinity session then get next
78
- const r =
79
- // Falls back to round-robin if no session affinity ref is provided
80
- backend.withSessionAffinity && request.sessionAffinityRef
81
- ? [yield this.nextWithAffinity(routes, request.sessionAffinityRef)]
82
- : [this.next(routes, backend)];
83
- // Next time we will get the route with callId
84
- this.store.put(`${request.aor}:${request.callId}`, r[0]);
85
- return r;
86
- }
87
- throw new errors_1.UnsupportedSchema(request.aor);
105
+ // If it has no affinity session then get next
106
+ const r = (backend === null || backend === void 0 ? void 0 : backend.withSessionAffinity) && request.sessionAffinityRef
107
+ ? [yield this.nextWithAffinity(routes, request.sessionAffinityRef)]
108
+ : [this.next(routes, request)];
109
+ this.store.put(storeKeyWithLabels, r[0]);
110
+ return r;
88
111
  });
89
112
  }
90
113
  /** @inheritdoc */
91
114
  removeRoutes(request) {
92
115
  return this.store.delete(request.aor);
93
116
  }
94
- // eslint-disable-next-line require-jsdoc
95
- next(routes, backend) {
117
+ next(routes, request) {
96
118
  var _a;
97
- if (backend.balancingAlgorithm === common_1.CommonTypes.LoadBalancingAlgorithm.LEAST_SESSIONS) {
119
+ const { backend } = request;
120
+ const ref = (backend === null || backend === void 0 ? void 0 : backend.ref) || request.aor;
121
+ if ((backend === null || backend === void 0 ? void 0 : backend.balancingAlgorithm) === common_1.CommonTypes.LoadBalancingAlgorithm.LEAST_SESSIONS) {
98
122
  return routes.sort((r1, r2) => r1.sessionCount - r2.sessionCount)[0];
99
123
  }
100
124
  // Continues using round-robin
101
- const nextPosition = (_a = this.rrCount.get(`${AOR_SCHEME.BACKEND}${backend.ref}`)) !== null && _a !== void 0 ? _a : 0;
125
+ const nextPosition = (_a = this.rrCount.get(ref)) !== null && _a !== void 0 ? _a : 0;
102
126
  const result = routes[nextPosition];
103
127
  if (nextPosition >= routes.length - 1) {
104
128
  // Restarting round-robin counter
105
- this.rrCount.set(`${AOR_SCHEME.BACKEND}${backend.ref}`, 0);
129
+ this.rrCount.set(ref, 0);
106
130
  }
107
131
  else {
108
- this.rrCount.set(`${AOR_SCHEME.BACKEND}${backend.ref}`, nextPosition + 1);
132
+ this.rrCount.set(ref, nextPosition + 1);
109
133
  }
110
134
  return result;
111
135
  }
112
136
  // Backend with session affinity does not support round-robin
113
- // eslint-disable-next-line require-jsdoc
114
- nextWithAffinity(routes, sessionAffinityHeader) {
137
+ nextWithAffinity(routes, sessionAffinityRef) {
115
138
  return __awaiter(this, void 0, void 0, function* () {
116
- const route = yield this.store.get(sessionAffinityHeader);
139
+ const route = yield this.store.get(sessionAffinityRef);
117
140
  if (route.length > 0) {
118
141
  return route[0];
119
142
  }
120
143
  const r = routes.sort((r1, r2) => r1.sessionCount - r2.sessionCount)[0];
121
- this.store.put(sessionAffinityHeader, r);
144
+ this.store.put(sessionAffinityRef, r);
122
145
  return r;
123
146
  });
124
147
  }
package/dist/runner.js CHANGED
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  /*
8
- * Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
8
+ * Copyright (C) 2024 by Fonoster Inc (https://fonoster.com)
9
9
  * http://github.com/fonoster/routr
10
10
  *
11
11
  * This file is part of Routr
package/dist/service.js CHANGED
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  /*
7
- * Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
7
+ * Copyright (C) 2024 by Fonoster Inc (https://fonoster.com)
8
8
  * http://github.com/fonoster/routr
9
9
  *
10
10
  * This file is part of Routr
package/dist/tracer.js CHANGED
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.init = void 0;
7
7
  /*
8
- * Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
8
+ * Copyright (C) 2024 by Fonoster Inc (https://fonoster.com)
9
9
  * http://github.com/fonoster/routr
10
10
  *
11
11
  * This file is part of Routr
package/dist/types.d.ts CHANGED
@@ -12,6 +12,7 @@ export interface ILocatorStore {
12
12
  export interface AddRouteRequest {
13
13
  aor: string;
14
14
  route: Route;
15
+ maxContacts: number;
15
16
  }
16
17
  export interface FindRoutesRequest {
17
18
  aor: string;
@@ -27,7 +28,7 @@ export interface RemoveRoutesRequest {
27
28
  aor: string;
28
29
  }
29
30
  export interface Backend {
30
- ref: string;
31
+ ref?: string;
31
32
  balancingAlgorithm: CT.LoadBalancingAlgorithm;
32
33
  withSessionAffinity: boolean;
33
34
  }
package/dist/utils.js CHANGED
@@ -11,7 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.configFromString = exports.getServiceInfo = exports.filterOnlyMatchingLabels = exports.compareArrays = exports.mergeKeyValue = exports.duplicateFilter = exports.expiredFilter = void 0;
13
13
  /*
14
- * Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
14
+ * Copyright (C) 2024 by Fonoster Inc (https://fonoster.com)
15
15
  * http://github.com/fonoster/routr
16
16
  *
17
17
  * This file is part of Routr
@@ -32,7 +32,7 @@ const common_1 = require("@routr/common");
32
32
  const errors_1 = require("./errors");
33
33
  const expiredFilter = (r) => r.expires - (Date.now() - r.registeredOn) / 1000 > 0;
34
34
  exports.expiredFilter = expiredFilter;
35
- const duplicateFilter = (r1, r2) => !(r1.host === r2.host && r1.port === r2.port);
35
+ const duplicateFilter = (r1, r2) => !(r1.host === r2.host && r1.port === r2.port && r1.transport === r2.transport);
36
36
  exports.duplicateFilter = duplicateFilter;
37
37
  const mergeKeyValue = (map) => Array.from(map).map((l) => l[0] + l[1]);
38
38
  exports.mergeKeyValue = mergeKeyValue;
@@ -49,23 +49,22 @@ const getServiceInfo = (bindAddr, locator) => {
49
49
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
50
  service: common_1.LOCATION_OBJECT_PROTO.Location.service,
51
51
  handlers: {
52
- addRoute: (call, callback) => {
52
+ addRoute: (call, callback) => __awaiter(void 0, void 0, void 0, function* () {
53
53
  try {
54
- locator.addRoute(call.request);
54
+ yield locator.addRoute(call.request);
55
55
  callback(null, {});
56
56
  }
57
57
  catch (e) {
58
58
  callback(e, null);
59
59
  }
60
- },
60
+ }),
61
61
  findRoutes: (call, callback) => __awaiter(void 0, void 0, void 0, function* () {
62
62
  try {
63
63
  const routes = yield locator.findRoutes(call.request);
64
- if (routes.length === 0)
64
+ if (routes.length === 0) {
65
65
  throw new errors_1.NotRoutesFoundForAOR(call.request.aor);
66
- callback(null, {
67
- routes: routes
68
- });
66
+ }
67
+ callback(null, { routes });
69
68
  }
70
69
  catch (e) {
71
70
  callback(e, null);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@routr/location",
3
- "version": "2.6.2",
3
+ "version": "2.7.0",
4
4
  "description": "Creates and maintains a list of routes to all registered endpoints",
5
5
  "author": "Pedro Sanders <psanders@fonoster.com>",
6
6
  "homepage": "https://github.com/fonoster/routr#readme",
@@ -35,8 +35,8 @@
35
35
  "@opentelemetry/sdk-trace-base": "^1.0.4",
36
36
  "@opentelemetry/sdk-trace-node": "^1.0.4",
37
37
  "@opentelemetry/semantic-conventions": "^1.0.4",
38
- "@routr/common": "^2.6.1",
39
- "@routr/processor": "^2.6.2",
38
+ "@routr/common": "^2.7.0",
39
+ "@routr/processor": "^2.7.0",
40
40
  "ajv": "^6.12.6",
41
41
  "fp-ts": "^2.11.8",
42
42
  "redis": "^4.0.4"
@@ -48,5 +48,5 @@
48
48
  "bugs": {
49
49
  "url": "https://github.com/fonoster/routr/issues"
50
50
  },
51
- "gitHead": "87f2724c378af3b3b3a4ae22b91dfa63201161d3"
51
+ "gitHead": "46d067f61a0797fbab905073f70c7ab319979d1d"
52
52
  }