@almatar/branding 1.0.0-beta.3.1 → 1.0.0-beta.3.3

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.
@@ -4,5 +4,6 @@ export declare class BrandIdentifier {
4
4
  getBrand(req: any): Promise<string | string[] | null>;
5
5
  getBrands(): Promise<string[] | null>;
6
6
  getDefaultBrand(): string;
7
+ private getQueryBrand;
7
8
  private error;
8
9
  }
@@ -22,12 +22,31 @@ Object.defineProperty(exports, "__esModule", { value: true });
22
22
  exports.BrandIdentifier = void 0;
23
23
  const Boom = __importStar(require("@hapi/boom"));
24
24
  const BrandManager_1 = require("./BrandManager");
25
+ const url = __importStar(require("url"));
25
26
  class BrandIdentifier {
26
27
  constructor(type) {
27
28
  this.type = type;
28
29
  }
29
30
  async getBrand(req) {
31
+ var _a;
30
32
  const brandManager = new BrandManager_1.BrandManager(this.type);
33
+ // Priority 1: Query parameter brand (super admin override)
34
+ const queryBrand = this.getQueryBrand(req);
35
+ // Troubleshoot logs (keep while integrating across microservices)
36
+ // tslint:disable-next-line no-console
37
+ console.log('[BrandIdentifier] Query brand detection:', {
38
+ 'req.query': req === null || req === void 0 ? void 0 : req.query,
39
+ 'req.query.brand': (_a = req === null || req === void 0 ? void 0 : req.query) === null || _a === void 0 ? void 0 : _a.brand,
40
+ 'req.url': req === null || req === void 0 ? void 0 : req.url,
41
+ 'req.originalUrl': req === null || req === void 0 ? void 0 : req.originalUrl,
42
+ 'queryBrand (final)': queryBrand,
43
+ });
44
+ if (queryBrand) {
45
+ // tslint:disable-next-line no-console
46
+ console.log('[BrandIdentifier] Using query parameter brand (overriding token brands):', queryBrand);
47
+ // Return as array so ContextNamespace stores it as employeeBrands (used by filters)
48
+ return [queryBrand];
49
+ }
31
50
  if (req.headers['x-brand']) {
32
51
  // B2C scenario - return the brand from x-brand header
33
52
  return brandManager.getB2CBrand(req);
@@ -36,12 +55,16 @@ class BrandIdentifier {
36
55
  // Employee/Console scenario - extract from token or x-employee-brands header
37
56
  const brand = await brandManager.getConsoleBrands(req);
38
57
  if (brand && brand.length > 0) {
58
+ // tslint:disable-next-line no-console
59
+ console.log('[BrandIdentifier] Using console/token brands:', brand);
39
60
  return brand;
40
61
  }
41
62
  // If no brand found but we have authorization, try extracting from token directly
42
63
  if (req.headers.authorization) {
43
64
  const tokenBrands = brandManager.extractBrandFromToken(req.headers.authorization);
44
65
  if (tokenBrands && tokenBrands.length > 0) {
66
+ // tslint:disable-next-line no-console
67
+ console.log('[BrandIdentifier] Using brands extracted directly from token:', tokenBrands);
45
68
  return tokenBrands;
46
69
  }
47
70
  }
@@ -78,6 +101,29 @@ class BrandIdentifier {
78
101
  const brandManager = new BrandManager_1.BrandManager(this.type);
79
102
  return brandManager.getDefaultBrand();
80
103
  }
104
+ getQueryBrand(req) {
105
+ var _a, _b;
106
+ try {
107
+ // Express/NestJS typically populates req.query
108
+ const fromQuery = (_a = req === null || req === void 0 ? void 0 : req.query) === null || _a === void 0 ? void 0 : _a.brand;
109
+ if (typeof fromQuery === 'string' && fromQuery.trim()) {
110
+ return fromQuery.trim();
111
+ }
112
+ // Fallback to parsing URLs if req.query isn't populated yet
113
+ const urlToParse = (req === null || req === void 0 ? void 0 : req.originalUrl) || (req === null || req === void 0 ? void 0 : req.url);
114
+ if (typeof urlToParse === 'string' && urlToParse.includes('?')) {
115
+ const parsedUrl = url.parse(urlToParse, true);
116
+ const parsedBrand = (_b = parsedUrl.query) === null || _b === void 0 ? void 0 : _b.brand;
117
+ if (typeof parsedBrand === 'string' && parsedBrand.trim()) {
118
+ return parsedBrand.trim();
119
+ }
120
+ }
121
+ }
122
+ catch (err) {
123
+ // ignore - fall back to other brand sources
124
+ }
125
+ return null;
126
+ }
81
127
  error(message) {
82
128
  switch (this.type) {
83
129
  case 'hapi':
@@ -14,4 +14,5 @@ export default class ContextNamespace {
14
14
  static getDefaultBrand(): string | null;
15
15
  static isValidBrand(brand: string): boolean;
16
16
  static isBrandIncludesProduct(brandKey: string, product: string): boolean;
17
+ private static getQueryBrand;
17
18
  }
@@ -1,6 +1,26 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
20
+ };
2
21
  Object.defineProperty(exports, "__esModule", { value: true });
3
22
  const BrandIdentifier_1 = require("./BrandIdentifier");
23
+ const url = __importStar(require("url"));
4
24
  const createNamespace = require('cls-hooked').createNamespace;
5
25
  const namespaceName = 'request';
6
26
  const ns = createNamespace(namespaceName);
@@ -16,19 +36,52 @@ class ContextNamespace {
16
36
  });
17
37
  }
18
38
  static async setEBrand(req, res, next) {
39
+ var _a, _b, _c;
19
40
  const brandIdentifier = new BrandIdentifier_1.BrandIdentifier('express');
20
41
  const brand = await brandIdentifier.getBrand(req);
42
+ // Check if query parameter brand was used (allows super admin to query specific brand)
43
+ const queryBrand = ContextNamespace.getQueryBrand(req);
44
+ const isQueryParameterBrand = !!queryBrand;
45
+ // Troubleshoot logs (keep while integrating across microservices)
46
+ // tslint:disable-next-line no-console
47
+ console.log('[ContextNamespace] setEBrand request snapshot:', {
48
+ 'req.url': req === null || req === void 0 ? void 0 : req.url,
49
+ 'req.originalUrl': req === null || req === void 0 ? void 0 : req.originalUrl,
50
+ 'req.query': req === null || req === void 0 ? void 0 : req.query,
51
+ 'queryBrand (final)': queryBrand,
52
+ 'hasAuthorization': !!((_a = req === null || req === void 0 ? void 0 : req.headers) === null || _a === void 0 ? void 0 : _a.authorization),
53
+ 'incoming x-brand': (_b = req === null || req === void 0 ? void 0 : req.headers) === null || _b === void 0 ? void 0 : _b['x-brand'],
54
+ 'incoming x-employee-brands': (_c = req === null || req === void 0 ? void 0 : req.headers) === null || _c === void 0 ? void 0 : _c['x-employee-brands'],
55
+ });
21
56
  // Check if this is a B2C request (x-brand header) - don't set x-employee-brands for B2C
22
- const isB2CRequest = !!req.headers['x-brand'];
23
- // Check if this is an employee/console request (authorization token)
24
- const isEmployeeRequest = !!req.headers.authorization || !!req.headers['x-employee-brands'];
57
+ const isB2CRequest = !!req.headers['x-brand'] && !isQueryParameterBrand;
58
+ // Check if this is an employee/console request (authorization token or query parameter brand)
59
+ const isEmployeeRequest = !!req.headers.authorization || !!req.headers['x-employee-brands'] || isQueryParameterBrand;
25
60
  if (Array.isArray(brand)) {
26
- // Employee/Console scenario with multiple brands
61
+ // Employee/Console scenario with multiple brands (or query parameter brand)
27
62
  // tslint:disable-next-line no-console
28
- console.log('[ContextNamespace] Setting employeeBrands in context:', brand);
63
+ console.log('[ContextNamespace] Setting employeeBrands in context:', brand, isQueryParameterBrand ? '(from query parameter)' : '');
64
+ // IMPORTANT: If query parameter brand is used, clear any existing brand context first
65
+ // This ensures query parameter brand takes absolute priority
66
+ if (isQueryParameterBrand) {
67
+ // Clear any existing brand context to ensure query parameter brand is used
68
+ ns.set('brand', null);
69
+ // tslint:disable-next-line no-console
70
+ console.log('[ContextNamespace] Cleared existing brand context to prioritize query parameter brand');
71
+ }
72
+ // IMPORTANT: Set employeeBrands in context - this is what MongooseModel pre-hooks will read
29
73
  ns.set('employeeBrands', brand);
30
- // Only set x-employee-brands header for employee requests (not B2C)
31
- if (isEmployeeRequest && brand.length > 0) {
74
+ // Troubleshoot: verify it was set
75
+ // tslint:disable-next-line no-console
76
+ console.log('[ContextNamespace] employeeBrands in context now:', ns.get('employeeBrands'));
77
+ // If query parameter brand is used with authorization token, always set x-employee-brands header
78
+ if (isQueryParameterBrand && req.headers.authorization && brand.length > 0) {
79
+ req.headers['x-employee-brands'] = brand.join(',');
80
+ // tslint:disable-next-line no-console
81
+ console.log('[ContextNamespace] Set x-employee-brands from query brand (with token):', req.headers['x-employee-brands']);
82
+ }
83
+ else if (isEmployeeRequest && brand.length > 0) {
84
+ // Set x-employee-brands header for employee requests (normal flow)
32
85
  req.headers['x-employee-brands'] = brand.join(',');
33
86
  // tslint:disable-next-line no-console
34
87
  console.log('[ContextNamespace] Setting x-employee-brands header on request:', req.headers['x-employee-brands']);
@@ -49,10 +102,18 @@ class ContextNamespace {
49
102
  // tslint:disable-next-line no-console
50
103
  console.log('[ContextNamespace] Setting brand in context (Employee):', brand);
51
104
  ns.set('brand', brand);
52
- // Set x-employee-brands header for employee requests
53
- req.headers['x-employee-brands'] = brand;
54
- // tslint:disable-next-line no-console
55
- console.log('[ContextNamespace] Setting x-employee-brands header on request:', req.headers['x-employee-brands']);
105
+ // If query parameter brand is used with authorization token, always set x-employee-brands header
106
+ if (isQueryParameterBrand && req.headers.authorization) {
107
+ req.headers['x-employee-brands'] = brand;
108
+ // tslint:disable-next-line no-console
109
+ console.log('[ContextNamespace] Set x-employee-brands from query brand (with token):', req.headers['x-employee-brands']);
110
+ }
111
+ else {
112
+ // Set x-employee-brands header for employee requests (normal flow)
113
+ req.headers['x-employee-brands'] = brand;
114
+ // tslint:disable-next-line no-console
115
+ console.log('[ContextNamespace] Setting x-employee-brands header on request:', req.headers['x-employee-brands']);
116
+ }
56
117
  next();
57
118
  }
58
119
  else {
@@ -83,9 +144,16 @@ class ContextNamespace {
83
144
  });
84
145
  }
85
146
  static async setHBrand(req, reply) {
147
+ var _a;
86
148
  const brandIdentifier = new BrandIdentifier_1.BrandIdentifier('hapi');
87
149
  const brand = await brandIdentifier.getBrand(req);
150
+ // Check if query parameter brand was used
151
+ const queryBrand = (_a = req.query) === null || _a === void 0 ? void 0 : _a.brand;
152
+ const isQueryParameterBrand = !!queryBrand;
88
153
  if (Array.isArray(brand)) {
154
+ // Employee/Console scenario with multiple brands (or query parameter brand)
155
+ // tslint:disable-next-line no-console
156
+ console.log('[ContextNamespace] Setting employeeBrands in context (Hapi):', brand, isQueryParameterBrand ? '(from query parameter)' : '');
89
157
  ns.set('employeeBrands', brand);
90
158
  reply();
91
159
  }
@@ -137,5 +205,26 @@ class ContextNamespace {
137
205
  const brand = (_a = ns.get('brandsList')) === null || _a === void 0 ? void 0 : _a.filter((b) => b.key === brandKey)[0];
138
206
  return (_b = brand.products) === null || _b === void 0 ? void 0 : _b.includes(product);
139
207
  }
208
+ static getQueryBrand(req) {
209
+ var _a, _b;
210
+ try {
211
+ const fromQuery = (_a = req === null || req === void 0 ? void 0 : req.query) === null || _a === void 0 ? void 0 : _a.brand;
212
+ if (typeof fromQuery === 'string' && fromQuery.trim()) {
213
+ return fromQuery.trim();
214
+ }
215
+ const urlToParse = (req === null || req === void 0 ? void 0 : req.originalUrl) || (req === null || req === void 0 ? void 0 : req.url);
216
+ if (typeof urlToParse === 'string' && urlToParse.includes('?')) {
217
+ const parsedUrl = url.parse(urlToParse, true);
218
+ const parsedBrand = (_b = parsedUrl.query) === null || _b === void 0 ? void 0 : _b.brand;
219
+ if (typeof parsedBrand === 'string' && parsedBrand.trim()) {
220
+ return parsedBrand.trim();
221
+ }
222
+ }
223
+ }
224
+ catch (err) {
225
+ // ignore
226
+ }
227
+ return null;
228
+ }
140
229
  }
141
230
  exports.default = ContextNamespace;
@@ -38,6 +38,13 @@ class MongooseModel {
38
38
  const employeeBrands = Storage_1.default.getEmployeeBrands();
39
39
  const brand = Storage_1.default.getBrand();
40
40
  const contextBrands = employeeBrands || (brand ? [brand] : null);
41
+ // Troubleshoot logs (keep while integrating across microservices)
42
+ // tslint:disable-next-line no-console
43
+ console.log('[MongooseModel] addPreCondition - Context:', {
44
+ employeeBrands,
45
+ brand,
46
+ contextBrands,
47
+ });
41
48
  // Only apply filter if brands are explicitly set (not null/undefined)
42
49
  if (contextBrands && contextBrands.length > 0) {
43
50
  // Use $in to check if entity's brand is in the user's brands array
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almatar/branding",
3
- "version": "1.0.0-beta.3.1",
3
+ "version": "1.0.0-beta.3.3",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "peerDependencies": {
41
41
  "typeorm": "^0.3.0",
42
- "@nestjs/axios": "^4.0.0"
42
+ "@nestjs/axios": "^3.0.0 || ^4.0.0"
43
43
  },
44
44
  "author": "",
45
45
  "license": "ISC",