@almatar/branding 0.2.0 → 1.0.0-beta.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.
- package/lib/index.d.ts +6 -0
- package/lib/index.js +14 -5
- package/lib/lib/AlmatarBranding.js +6 -9
- package/lib/lib/BrandIdentifier.d.ts +2 -2
- package/lib/lib/BrandIdentifier.js +72 -88
- package/lib/lib/BrandManager.d.ts +5 -0
- package/lib/lib/BrandManager.js +178 -127
- package/lib/lib/Models/BrandModel.js +3 -4
- package/lib/lib/Models/EmployeeBrandModel.js +3 -4
- package/lib/lib/Storage.js +78 -151
- package/lib/lib/TenantModel/Mongoose/MultiTenant.js +13 -17
- package/lib/lib/TenantModel/MongooseModel.js +56 -39
- package/lib/lib/TenantModel/NestMongoose/TenantMongooseModule.js +12 -33
- package/lib/lib/TenantModel/NestMongoose/mongoose.providers.d.ts +0 -24
- package/lib/lib/TenantModel/NestMongoose/mongoose.providers.js +8 -9
- package/lib/lib/TenantModel/TypeORM/TenantEntitySubscriber.d.ts +27 -0
- package/lib/lib/TenantModel/TypeORM/TenantEntitySubscriber.js +68 -0
- package/lib/lib/TenantModel/TypeORM/TenantHttpInterceptor.d.ts +26 -0
- package/lib/lib/TenantModel/TypeORM/TenantHttpInterceptor.js +92 -0
- package/lib/lib/TenantModel/TypeORM/TenantRepository.d.ts +58 -0
- package/lib/lib/TenantModel/TypeORM/TenantRepository.js +281 -0
- package/lib/lib/request/PromiseRequest.js +37 -56
- package/lib/lib/request/TenantRequest.d.ts +4 -1
- package/lib/lib/request/TenantRequest.js +38 -68
- package/package.json +31 -25
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { OnModuleInit } from '@nestjs/common';
|
|
2
|
+
/**
|
|
3
|
+
* HTTP Interceptor that automatically adds x-brand and x-employee-brands headers
|
|
4
|
+
* to ALL outgoing HTTP requests, similar to holiday-consumer-service
|
|
5
|
+
*
|
|
6
|
+
* Usage in app.module.ts:
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { TenantHttpInterceptor } from '@almatar/branding';
|
|
9
|
+
* import { HttpModule } from '@nestjs/axios';
|
|
10
|
+
*
|
|
11
|
+
* @Module({
|
|
12
|
+
* imports: [HttpModule],
|
|
13
|
+
* providers: [
|
|
14
|
+
* {
|
|
15
|
+
* provide: APP_INTERCEPTOR,
|
|
16
|
+
* useClass: TenantHttpInterceptor,
|
|
17
|
+
* },
|
|
18
|
+
* ],
|
|
19
|
+
* })
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare class TenantHttpInterceptor implements OnModuleInit {
|
|
23
|
+
private readonly httpService?;
|
|
24
|
+
constructor(httpService?: any);
|
|
25
|
+
onModuleInit(): void;
|
|
26
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.TenantHttpInterceptor = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const Storage_1 = __importDefault(require("../../Storage"));
|
|
15
|
+
/**
|
|
16
|
+
* HTTP Interceptor that automatically adds x-brand and x-employee-brands headers
|
|
17
|
+
* to ALL outgoing HTTP requests, similar to holiday-consumer-service
|
|
18
|
+
*
|
|
19
|
+
* Usage in app.module.ts:
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { TenantHttpInterceptor } from '@almatar/branding';
|
|
22
|
+
* import { HttpModule } from '@nestjs/axios';
|
|
23
|
+
*
|
|
24
|
+
* @Module({
|
|
25
|
+
* imports: [HttpModule],
|
|
26
|
+
* providers: [
|
|
27
|
+
* {
|
|
28
|
+
* provide: APP_INTERCEPTOR,
|
|
29
|
+
* useClass: TenantHttpInterceptor,
|
|
30
|
+
* },
|
|
31
|
+
* ],
|
|
32
|
+
* })
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
let TenantHttpInterceptor = class TenantHttpInterceptor {
|
|
36
|
+
// Use any type to avoid requiring @nestjs/axios at compile time (it's a peer dependency)
|
|
37
|
+
// HttpService will be injected by NestJS when HttpModule is imported
|
|
38
|
+
// We use a factory function in app.module.ts to inject HttpService
|
|
39
|
+
constructor(httpService) {
|
|
40
|
+
this.httpService = httpService;
|
|
41
|
+
}
|
|
42
|
+
onModuleInit() {
|
|
43
|
+
// Intercept ALL axios requests and add brand headers automatically
|
|
44
|
+
// Checks both x-brand and x-employee-brands from context
|
|
45
|
+
// Try to get HttpService if not injected
|
|
46
|
+
let httpService = this.httpService;
|
|
47
|
+
if (!httpService) {
|
|
48
|
+
try {
|
|
49
|
+
// Dynamic require to avoid compile-time dependency
|
|
50
|
+
const axios = require('axios');
|
|
51
|
+
// Use axios directly if HttpService not available
|
|
52
|
+
httpService = { axiosRef: axios };
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
// HttpService not available - skip interceptor setup
|
|
56
|
+
// tslint:disable-next-line no-console
|
|
57
|
+
console.warn('[TenantHttpInterceptor] HttpService not available, skipping axios interceptor setup');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (!httpService.axiosRef) {
|
|
62
|
+
// tslint:disable-next-line no-console
|
|
63
|
+
console.warn('[TenantHttpInterceptor] HttpService.axiosRef not available, skipping axios interceptor setup');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
httpService.axiosRef.interceptors.request.use((config) => {
|
|
67
|
+
const employeeBrands = Storage_1.default.getEmployeeBrands();
|
|
68
|
+
const brand = Storage_1.default.getBrand();
|
|
69
|
+
// Add x-employee-brands header (priority: employeeBrands > brand)
|
|
70
|
+
if (employeeBrands && employeeBrands.length > 0) {
|
|
71
|
+
config.headers = config.headers || {};
|
|
72
|
+
config.headers['x-employee-brands'] = employeeBrands.join(',');
|
|
73
|
+
}
|
|
74
|
+
else if (brand) {
|
|
75
|
+
config.headers = config.headers || {};
|
|
76
|
+
config.headers['x-employee-brands'] = brand;
|
|
77
|
+
}
|
|
78
|
+
// Add x-brand header (for backward compatibility)
|
|
79
|
+
if (brand) {
|
|
80
|
+
config.headers = config.headers || {};
|
|
81
|
+
config.headers['x-brand'] = brand;
|
|
82
|
+
}
|
|
83
|
+
return config;
|
|
84
|
+
}, (error) => {
|
|
85
|
+
return Promise.reject(error);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
TenantHttpInterceptor = __decorate([
|
|
90
|
+
common_1.Injectable()
|
|
91
|
+
], TenantHttpInterceptor);
|
|
92
|
+
exports.TenantHttpInterceptor = TenantHttpInterceptor;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Repository, SelectQueryBuilder, FindOptionsWhere, FindManyOptions, FindOneOptions, ObjectLiteral } from 'typeorm';
|
|
2
|
+
/**
|
|
3
|
+
* Base Repository class for TypeORM that AUTOMATICALLY applies brand filtering to ALL queries
|
|
4
|
+
* Similar to TenantModel in PHP/MongoDB - just extend this class and everything works!
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { TenantRepository } from '@almatar/branding';
|
|
9
|
+
*
|
|
10
|
+
* export class EmployeeRepository extends TenantRepository<EmployeeEntity> {
|
|
11
|
+
* constructor(@InjectRepository(EmployeeEntity) repository: Repository<EmployeeEntity>) {
|
|
12
|
+
* super(repository.target, repository.manager, repository.queryRunner);
|
|
13
|
+
* }
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* Brand filtering is automatically applied to:
|
|
18
|
+
* - createQueryBuilder() - automatically adds brand filter
|
|
19
|
+
* - find() - automatically filters by brand
|
|
20
|
+
* - findOne() - automatically filters by brand
|
|
21
|
+
* - findOneBy() - automatically filters by brand
|
|
22
|
+
* - findAndCount() - automatically filters by brand
|
|
23
|
+
*/
|
|
24
|
+
export declare class TenantRepository<T extends ObjectLiteral> extends Repository<T> {
|
|
25
|
+
protected readonly brandColumn: string;
|
|
26
|
+
protected readonly tableName: string;
|
|
27
|
+
constructor(target: any, manager: any, queryRunner?: any);
|
|
28
|
+
/**
|
|
29
|
+
* Override createQueryBuilder to automatically apply brand filtering
|
|
30
|
+
*/
|
|
31
|
+
createQueryBuilder(alias?: string): SelectQueryBuilder<T>;
|
|
32
|
+
/**
|
|
33
|
+
* Override find to automatically apply brand filtering
|
|
34
|
+
*/
|
|
35
|
+
find(options?: FindManyOptions<T>): Promise<T[]>;
|
|
36
|
+
/**
|
|
37
|
+
* Override findOne to automatically apply brand filtering
|
|
38
|
+
*/
|
|
39
|
+
findOne(options?: FindOneOptions<T>): Promise<T | null>;
|
|
40
|
+
/**
|
|
41
|
+
* Override findOneBy to automatically apply brand filtering
|
|
42
|
+
*/
|
|
43
|
+
findOneBy(where: FindOptionsWhere<T> | FindOptionsWhere<T>[]): Promise<T | null>;
|
|
44
|
+
/**
|
|
45
|
+
* Override findAndCount to automatically apply brand filtering
|
|
46
|
+
*/
|
|
47
|
+
findAndCount(options?: FindManyOptions<T>): Promise<[T[], number]>;
|
|
48
|
+
/**
|
|
49
|
+
* Get brands from context (from token/headers)
|
|
50
|
+
*/
|
|
51
|
+
private getBrandsFromContext;
|
|
52
|
+
/**
|
|
53
|
+
* Apply brand filter to query builder
|
|
54
|
+
* Handles both cases: brand as JSON array and brand as string
|
|
55
|
+
* Uses $in equivalent for multiple brands from x-employee-brands
|
|
56
|
+
*/
|
|
57
|
+
private applyBrandFilter;
|
|
58
|
+
}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.TenantRepository = void 0;
|
|
7
|
+
const typeorm_1 = require("typeorm");
|
|
8
|
+
const Storage_1 = __importDefault(require("../../Storage"));
|
|
9
|
+
/**
|
|
10
|
+
* Base Repository class for TypeORM that AUTOMATICALLY applies brand filtering to ALL queries
|
|
11
|
+
* Similar to TenantModel in PHP/MongoDB - just extend this class and everything works!
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { TenantRepository } from '@almatar/branding';
|
|
16
|
+
*
|
|
17
|
+
* export class EmployeeRepository extends TenantRepository<EmployeeEntity> {
|
|
18
|
+
* constructor(@InjectRepository(EmployeeEntity) repository: Repository<EmployeeEntity>) {
|
|
19
|
+
* super(repository.target, repository.manager, repository.queryRunner);
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* Brand filtering is automatically applied to:
|
|
25
|
+
* - createQueryBuilder() - automatically adds brand filter
|
|
26
|
+
* - find() - automatically filters by brand
|
|
27
|
+
* - findOne() - automatically filters by brand
|
|
28
|
+
* - findOneBy() - automatically filters by brand
|
|
29
|
+
* - findAndCount() - automatically filters by brand
|
|
30
|
+
*/
|
|
31
|
+
class TenantRepository extends typeorm_1.Repository {
|
|
32
|
+
constructor(target, manager, queryRunner) {
|
|
33
|
+
super(target, manager, queryRunner);
|
|
34
|
+
this.brandColumn = 'brand';
|
|
35
|
+
// Get table name from metadata
|
|
36
|
+
this.tableName = this.metadata.tableName;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Override createQueryBuilder to automatically apply brand filtering
|
|
40
|
+
*/
|
|
41
|
+
createQueryBuilder(alias) {
|
|
42
|
+
const query = super.createQueryBuilder(alias);
|
|
43
|
+
const tableAlias = alias || this.tableName;
|
|
44
|
+
// Automatically apply brand filter
|
|
45
|
+
return this.applyBrandFilter(query, tableAlias);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Override find to automatically apply brand filtering
|
|
49
|
+
*/
|
|
50
|
+
async find(options) {
|
|
51
|
+
const brands = this.getBrandsFromContext();
|
|
52
|
+
if (brands && brands.length > 0) {
|
|
53
|
+
// Use query builder to support JSON_CONTAINS
|
|
54
|
+
const alias = this.tableName;
|
|
55
|
+
let query = this.createQueryBuilder(alias);
|
|
56
|
+
// Apply existing where conditions
|
|
57
|
+
if (options === null || options === void 0 ? void 0 : options.where) {
|
|
58
|
+
if (Array.isArray(options.where)) {
|
|
59
|
+
options.where.forEach((where, index) => {
|
|
60
|
+
Object.keys(where).forEach(key => {
|
|
61
|
+
query = query.andWhere(`${alias}.${key} = :${key}_${index}`, { [`${key}_${index}`]: where[key] });
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
Object.keys(options.where).forEach(key => {
|
|
67
|
+
query = query.andWhere(`${alias}.${key} = :${key}`, { [key]: options.where[key] });
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Apply relations
|
|
72
|
+
if (options === null || options === void 0 ? void 0 : options.relations) {
|
|
73
|
+
const relations = Array.isArray(options.relations)
|
|
74
|
+
? options.relations
|
|
75
|
+
: Object.keys(options.relations).filter(key => options.relations[key]);
|
|
76
|
+
relations.forEach((relation) => {
|
|
77
|
+
query = query.leftJoinAndSelect(`${alias}.${relation}`, relation);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
// Apply select
|
|
81
|
+
if (options === null || options === void 0 ? void 0 : options.select) {
|
|
82
|
+
const selectCols = Array.isArray(options.select)
|
|
83
|
+
? options.select
|
|
84
|
+
: Object.keys(options.select).filter(key => options.select[key]);
|
|
85
|
+
const selectPaths = selectCols.map((col) => `${alias}.${String(col)}`);
|
|
86
|
+
query = query.select(selectPaths);
|
|
87
|
+
}
|
|
88
|
+
// Apply order
|
|
89
|
+
if (options === null || options === void 0 ? void 0 : options.order) {
|
|
90
|
+
if (typeof options.order === 'object' && !Array.isArray(options.order)) {
|
|
91
|
+
Object.keys(options.order).forEach(key => {
|
|
92
|
+
const orderValue = options.order[key];
|
|
93
|
+
if (orderValue) {
|
|
94
|
+
query = query.orderBy(`${alias}.${key}`, orderValue);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Apply skip/take
|
|
100
|
+
if (options === null || options === void 0 ? void 0 : options.skip) {
|
|
101
|
+
query = query.skip(options.skip);
|
|
102
|
+
}
|
|
103
|
+
if (options === null || options === void 0 ? void 0 : options.take) {
|
|
104
|
+
query = query.take(options.take);
|
|
105
|
+
}
|
|
106
|
+
return query.getMany();
|
|
107
|
+
}
|
|
108
|
+
return super.find(options);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Override findOne to automatically apply brand filtering
|
|
112
|
+
*/
|
|
113
|
+
async findOne(options) {
|
|
114
|
+
const brands = this.getBrandsFromContext();
|
|
115
|
+
if (brands && brands.length > 0) {
|
|
116
|
+
const alias = this.tableName;
|
|
117
|
+
let query = this.createQueryBuilder(alias);
|
|
118
|
+
if (options === null || options === void 0 ? void 0 : options.where) {
|
|
119
|
+
if (Array.isArray(options.where)) {
|
|
120
|
+
options.where.forEach((where, index) => {
|
|
121
|
+
Object.keys(where).forEach(key => {
|
|
122
|
+
query = query.andWhere(`${alias}.${key} = :${key}_${index}`, { [`${key}_${index}`]: where[key] });
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
Object.keys(options.where).forEach(key => {
|
|
128
|
+
query = query.andWhere(`${alias}.${key} = :${key}`, { [key]: options.where[key] });
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (options === null || options === void 0 ? void 0 : options.relations) {
|
|
133
|
+
const relations = Array.isArray(options.relations)
|
|
134
|
+
? options.relations
|
|
135
|
+
: Object.keys(options.relations).filter(key => options.relations[key]);
|
|
136
|
+
relations.forEach((relation) => {
|
|
137
|
+
query = query.leftJoinAndSelect(`${alias}.${relation}`, relation);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
if (options === null || options === void 0 ? void 0 : options.select) {
|
|
141
|
+
const selectCols = Array.isArray(options.select)
|
|
142
|
+
? options.select
|
|
143
|
+
: Object.keys(options.select).filter(key => options.select[key]);
|
|
144
|
+
const selectPaths = selectCols.map((col) => `${alias}.${String(col)}`);
|
|
145
|
+
query = query.select(selectPaths);
|
|
146
|
+
}
|
|
147
|
+
if (options === null || options === void 0 ? void 0 : options.order) {
|
|
148
|
+
if (typeof options.order === 'object' && !Array.isArray(options.order)) {
|
|
149
|
+
Object.keys(options.order).forEach(key => {
|
|
150
|
+
const orderValue = options.order[key];
|
|
151
|
+
if (orderValue) {
|
|
152
|
+
query = query.orderBy(`${alias}.${key}`, orderValue);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return query.getOne();
|
|
158
|
+
}
|
|
159
|
+
return super.findOne(options || {});
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Override findOneBy to automatically apply brand filtering
|
|
163
|
+
*/
|
|
164
|
+
async findOneBy(where) {
|
|
165
|
+
const brands = this.getBrandsFromContext();
|
|
166
|
+
if (brands && brands.length > 0) {
|
|
167
|
+
const alias = this.tableName;
|
|
168
|
+
let query = this.createQueryBuilder(alias);
|
|
169
|
+
if (Array.isArray(where)) {
|
|
170
|
+
where.forEach((w, index) => {
|
|
171
|
+
Object.keys(w).forEach(key => {
|
|
172
|
+
query = query.andWhere(`${alias}.${key} = :${key}_${index}`, { [`${key}_${index}`]: w[key] });
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
Object.keys(where).forEach(key => {
|
|
178
|
+
query = query.andWhere(`${alias}.${key} = :${key}`, { [key]: where[key] });
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
return query.getOne();
|
|
182
|
+
}
|
|
183
|
+
return super.findOneBy(where);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Override findAndCount to automatically apply brand filtering
|
|
187
|
+
*/
|
|
188
|
+
async findAndCount(options) {
|
|
189
|
+
const brands = this.getBrandsFromContext();
|
|
190
|
+
if (brands && brands.length > 0) {
|
|
191
|
+
const alias = this.tableName;
|
|
192
|
+
let query = this.createQueryBuilder(alias);
|
|
193
|
+
if (options === null || options === void 0 ? void 0 : options.where) {
|
|
194
|
+
if (Array.isArray(options.where)) {
|
|
195
|
+
options.where.forEach((where, index) => {
|
|
196
|
+
Object.keys(where).forEach(key => {
|
|
197
|
+
query = query.andWhere(`${alias}.${key} = :${key}_${index}`, { [`${key}_${index}`]: where[key] });
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
Object.keys(options.where).forEach(key => {
|
|
203
|
+
query = query.andWhere(`${alias}.${key} = :${key}`, { [key]: options.where[key] });
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (options === null || options === void 0 ? void 0 : options.relations) {
|
|
208
|
+
const relations = Array.isArray(options.relations)
|
|
209
|
+
? options.relations
|
|
210
|
+
: Object.keys(options.relations).filter(key => options.relations[key]);
|
|
211
|
+
relations.forEach((relation) => {
|
|
212
|
+
query = query.leftJoinAndSelect(`${alias}.${relation}`, relation);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
if (options === null || options === void 0 ? void 0 : options.select) {
|
|
216
|
+
const selectCols = Array.isArray(options.select)
|
|
217
|
+
? options.select
|
|
218
|
+
: Object.keys(options.select).filter(key => options.select[key]);
|
|
219
|
+
const selectPaths = selectCols.map((col) => `${alias}.${String(col)}`);
|
|
220
|
+
query = query.select(selectPaths);
|
|
221
|
+
}
|
|
222
|
+
if (options === null || options === void 0 ? void 0 : options.order) {
|
|
223
|
+
if (typeof options.order === 'object' && !Array.isArray(options.order)) {
|
|
224
|
+
Object.keys(options.order).forEach(key => {
|
|
225
|
+
const orderValue = options.order[key];
|
|
226
|
+
if (orderValue) {
|
|
227
|
+
query = query.orderBy(`${alias}.${key}`, orderValue);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
if (options === null || options === void 0 ? void 0 : options.skip) {
|
|
233
|
+
query = query.skip(options.skip);
|
|
234
|
+
}
|
|
235
|
+
if (options === null || options === void 0 ? void 0 : options.take) {
|
|
236
|
+
query = query.take(options.take);
|
|
237
|
+
}
|
|
238
|
+
return query.getManyAndCount();
|
|
239
|
+
}
|
|
240
|
+
return super.findAndCount(options);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Get brands from context (from token/headers)
|
|
244
|
+
*/
|
|
245
|
+
getBrandsFromContext() {
|
|
246
|
+
const contextEmployeeBrands = Storage_1.default.getEmployeeBrands();
|
|
247
|
+
const contextBrand = Storage_1.default.getBrand();
|
|
248
|
+
return contextEmployeeBrands || (contextBrand ? [contextBrand] : null);
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Apply brand filter to query builder
|
|
252
|
+
* Handles both cases: brand as JSON array and brand as string
|
|
253
|
+
* Uses $in equivalent for multiple brands from x-employee-brands
|
|
254
|
+
*/
|
|
255
|
+
applyBrandFilter(query, tableAlias) {
|
|
256
|
+
const brands = this.getBrandsFromContext();
|
|
257
|
+
if (brands && brands.length > 0) {
|
|
258
|
+
// Handle both cases for multiple brands (x-employee-brands can have many):
|
|
259
|
+
// 1. Brand column is JSON array: Check if ANY of the user's brands exist in the entity's brand array
|
|
260
|
+
// Using JSON_CONTAINS for each brand (equivalent to $in for arrays)
|
|
261
|
+
// 2. Brand column is string: Check if entity's brand is IN the user's brands array
|
|
262
|
+
// Using IN clause (equivalent to $in for strings)
|
|
263
|
+
// For JSON array: Check if any user brand is contained in entity's brand array
|
|
264
|
+
const jsonArrayConditions = brands.map((brandItem, index) => {
|
|
265
|
+
return `JSON_CONTAINS(${tableAlias}.${this.brandColumn}, :brand${index})`;
|
|
266
|
+
}).join(' OR ');
|
|
267
|
+
// For string: Check if entity's brand is in user's brands (IN clause)
|
|
268
|
+
const stringInPlaceholders = brands.map((brandItem, index) => `:brandStr${index}`).join(', ');
|
|
269
|
+
// Combine both conditions: (JSON array match) OR (string IN match)
|
|
270
|
+
const brandConditions = `(${jsonArrayConditions}) OR (${tableAlias}.${this.brandColumn} IN (${stringInPlaceholders}))`;
|
|
271
|
+
const brandParams = {};
|
|
272
|
+
brands.forEach((brand, index) => {
|
|
273
|
+
brandParams[`brand${index}`] = JSON.stringify(brand);
|
|
274
|
+
brandParams[`brandStr${index}`] = brand;
|
|
275
|
+
});
|
|
276
|
+
query.andWhere(`(${brandConditions})`, brandParams);
|
|
277
|
+
}
|
|
278
|
+
return query;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
exports.TenantRepository = TenantRepository;
|
|
@@ -1,61 +1,42 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
-
function step(op) {
|
|
16
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
-
switch (op[0]) {
|
|
21
|
-
case 0: case 1: t = op; break;
|
|
22
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
-
default:
|
|
26
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
-
if (t[2]) _.ops.pop();
|
|
31
|
-
_.trys.pop(); continue;
|
|
32
|
-
}
|
|
33
|
-
op = body.call(thisArg, _);
|
|
34
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
3
|
exports.PromiseRequest = void 0;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
resolve(body);
|
|
54
|
-
});
|
|
55
|
-
})];
|
|
4
|
+
class PromiseRequest {
|
|
5
|
+
static async request(options) {
|
|
6
|
+
try {
|
|
7
|
+
const url = options.url || options.uri;
|
|
8
|
+
const method = options.method || 'GET';
|
|
9
|
+
const headers = options.headers || {};
|
|
10
|
+
const body = options.body ? JSON.stringify(options.body) : undefined;
|
|
11
|
+
// tslint:disable-next-line no-console
|
|
12
|
+
console.log('[PromiseRequest] Making request:', { url, method, headers: Object.assign(Object.assign({}, headers), { authorization: headers.authorization ? 'Bearer ***' : undefined }) });
|
|
13
|
+
const response = await fetch(url, {
|
|
14
|
+
method,
|
|
15
|
+
headers: Object.assign({ 'Content-Type': 'application/json' }, headers),
|
|
16
|
+
body,
|
|
56
17
|
});
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
18
|
+
const responseData = await response.text();
|
|
19
|
+
let parsedBody;
|
|
20
|
+
try {
|
|
21
|
+
parsedBody = responseData ? JSON.parse(responseData) : null;
|
|
22
|
+
}
|
|
23
|
+
catch (parseError) {
|
|
24
|
+
// tslint:disable-next-line no-console
|
|
25
|
+
console.log('[PromiseRequest] Failed to parse response as JSON:', parseError);
|
|
26
|
+
parsedBody = responseData;
|
|
27
|
+
}
|
|
28
|
+
// Return in the same format as the old request library
|
|
29
|
+
return {
|
|
30
|
+
status: response.status,
|
|
31
|
+
data: parsedBody,
|
|
32
|
+
body: parsedBody,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
// tslint:disable-next-line no-console
|
|
37
|
+
console.log('[PromiseRequest] Request error:', err);
|
|
38
|
+
throw err;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
61
42
|
exports.PromiseRequest = PromiseRequest;
|