@fiado/type-kit 3.499.0 → 3.501.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/_test_/unit/cognitoBackofficeConnector/dtos/AuthStartLogin.test.ts +76 -0
- package/_test_/unit/platformRbac/auth/dtos.test.ts +33 -0
- package/_test_/unit/platformRbac/enums/ChallengeNameEnum.test.ts +17 -0
- package/_test_/unit/retailPromotions/constants.test.ts +1 -1
- package/_test_/unit/retailPromotions/evaluatedSalePromotion.test.ts +2 -0
- package/_test_/unit/retailPromotions/resolveSalePromotionKind.test.ts +2 -0
- package/_test_/unit/retailPromotions/salePromotionConditions.test.ts +4 -0
- package/_test_/unit/retailPromotions/stackablePromotions.test.ts +238 -0
- package/bin/cognitoBackofficeConnector/dtos/AuthStartLoginRequest.d.ts +13 -0
- package/bin/cognitoBackofficeConnector/dtos/AuthStartLoginRequest.js +53 -0
- package/bin/cognitoBackofficeConnector/dtos/AuthStartLoginResponse.d.ts +22 -0
- package/bin/cognitoBackofficeConnector/dtos/AuthStartLoginResponse.js +79 -0
- package/bin/cognitoBackofficeConnector/index.d.ts +2 -0
- package/bin/cognitoBackofficeConnector/index.js +2 -0
- package/bin/platformRbac/auth/DefineNextChallengeRequest.d.ts +1 -0
- package/bin/platformRbac/auth/DefineNextChallengeRequest.js +5 -0
- package/bin/platformRbac/enums/ChallengeNameEnum.d.ts +3 -1
- package/bin/platformRbac/enums/ChallengeNameEnum.js +4 -0
- package/bin/retailPromotions/constants/retailPromotionsErrorHttpStatus.js +1 -0
- package/bin/retailPromotions/dtos/LinePromotionAmount.d.ts +6 -0
- package/bin/retailPromotions/dtos/LinePromotionAmount.js +36 -0
- package/bin/retailPromotions/dtos/LinePromotionDiscount.d.ts +4 -0
- package/bin/retailPromotions/dtos/SalePromotion.d.ts +2 -0
- package/bin/retailPromotions/dtos/SalePromotionRef.d.ts +5 -0
- package/bin/retailPromotions/dtos/SalePromotionRef.js +30 -0
- package/bin/retailPromotions/dtos/requests/CreateSalePromotionRequest.d.ts +2 -0
- package/bin/retailPromotions/dtos/requests/CreateSalePromotionRequest.js +6 -0
- package/bin/retailPromotions/dtos/requests/HoldPromotionInput.d.ts +8 -0
- package/bin/retailPromotions/dtos/requests/HoldPromotionInput.js +46 -0
- package/bin/retailPromotions/dtos/requests/HoldSalePromotionRequest.d.ts +9 -3
- package/bin/retailPromotions/dtos/requests/HoldSalePromotionRequest.js +11 -0
- package/bin/retailPromotions/dtos/requests/UpdateSalePromotionRequest.d.ts +2 -0
- package/bin/retailPromotions/dtos/requests/UpdateSalePromotionRequest.js +6 -0
- package/bin/retailPromotions/dtos/responses/WizardSalePromotionResponses.d.ts +20 -0
- package/bin/retailPromotions/enums/RetailPromotionsErrorCodeEnum.d.ts +3 -1
- package/bin/retailPromotions/enums/RetailPromotionsErrorCodeEnum.js +2 -0
- package/bin/retailPromotions/index.d.ts +4 -0
- package/bin/retailPromotions/index.js +5 -0
- package/bin/retailPromotions/validators/IsHoldModeExclusiveConstraint.d.ts +6 -0
- package/bin/retailPromotions/validators/IsHoldModeExclusiveConstraint.js +24 -0
- package/bin/retailWizard/dtos/Sale.d.ts +3 -0
- package/bin/retailWizard/dtos/WizardLinePromotionFields.d.ts +4 -0
- package/bin/retailWizard/dtos/WizardSession.d.ts +3 -0
- package/bin/retailWizard/dtos/requests/ApplyLinePromotionRequest.d.ts +5 -0
- package/bin/retailWizard/dtos/requests/ApplyLinePromotionRequest.js +16 -0
- package/bin/retailWizard/dtos/requests/ApplySalePromotionRequest.d.ts +3 -0
- package/bin/retailWizard/dtos/requests/ApplySalePromotionRequest.js +9 -0
- package/bin/retailWizard/dtos/responses/PrivateWizardSessionResponse.d.ts +3 -0
- package/bin/retailWizard/events/RetailPromotionInboundMessage.d.ts +8 -0
- package/bin/retailWizard/events/RetailPromotionInboundMessage.js +22 -0
- package/bin/retailWizard/index.d.ts +2 -0
- package/bin/retailWizard/index.js +5 -0
- package/package.json +1 -1
- package/src/cognitoBackofficeConnector/dtos/AuthStartLoginRequest.ts +16 -0
- package/src/cognitoBackofficeConnector/dtos/AuthStartLoginResponse.ts +29 -0
- package/src/cognitoBackofficeConnector/index.ts +2 -0
- package/src/platformRbac/auth/DefineNextChallengeRequest.ts +7 -1
- package/src/platformRbac/enums/ChallengeNameEnum.ts +4 -0
- package/src/retailPromotions/constants/retailPromotionsErrorHttpStatus.ts +1 -0
- package/src/retailPromotions/dtos/LinePromotionAmount.ts +20 -0
- package/src/retailPromotions/dtos/LinePromotionDiscount.ts +5 -0
- package/src/retailPromotions/dtos/SalePromotion.ts +2 -0
- package/src/retailPromotions/dtos/SalePromotionRef.ts +15 -0
- package/src/retailPromotions/dtos/requests/CreateSalePromotionRequest.ts +6 -0
- package/src/retailPromotions/dtos/requests/HoldPromotionInput.ts +30 -0
- package/src/retailPromotions/dtos/requests/HoldSalePromotionRequest.ts +28 -4
- package/src/retailPromotions/dtos/requests/UpdateSalePromotionRequest.ts +6 -0
- package/src/retailPromotions/dtos/responses/WizardSalePromotionResponses.ts +21 -0
- package/src/retailPromotions/enums/RetailPromotionsErrorCodeEnum.ts +2 -0
- package/src/retailPromotions/index.ts +6 -0
- package/src/retailPromotions/validators/IsHoldModeExclusiveConstraint.ts +18 -0
- package/src/retailWizard/dtos/Sale.ts +3 -0
- package/src/retailWizard/dtos/WizardLinePromotionFields.ts +4 -0
- package/src/retailWizard/dtos/WizardSession.ts +3 -0
- package/src/retailWizard/dtos/requests/ApplyLinePromotionRequest.ts +18 -2
- package/src/retailWizard/dtos/requests/ApplySalePromotionRequest.ts +11 -2
- package/src/retailWizard/dtos/responses/PrivateWizardSessionResponse.ts +3 -0
- package/src/retailWizard/events/RetailPromotionInboundMessage.ts +22 -0
- package/src/retailWizard/index.ts +2 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { plainToInstance } from 'class-transformer';
|
|
3
|
+
import { validate } from 'class-validator';
|
|
4
|
+
import { AuthStartLoginRequest } from '../../../../src/cognitoBackofficeConnector/dtos/AuthStartLoginRequest';
|
|
5
|
+
import {
|
|
6
|
+
AuthStartLoginResponse,
|
|
7
|
+
AuthStartLoginTokens,
|
|
8
|
+
} from '../../../../src/cognitoBackofficeConnector/dtos/AuthStartLoginResponse';
|
|
9
|
+
import { CognitoChallengeType } from '../../../../src/cognitoBackofficeConnector/enums/CognitoChallengeType';
|
|
10
|
+
|
|
11
|
+
describe('AuthStartLoginRequest', () => {
|
|
12
|
+
const base = {
|
|
13
|
+
userPoolId: 'us-east-1_abc',
|
|
14
|
+
region: 'us-east-1',
|
|
15
|
+
clientId: 'client-123',
|
|
16
|
+
username: 'sub-123',
|
|
17
|
+
password: 'Secret123!',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
it('valida happy path', async () => {
|
|
21
|
+
const dto = plainToInstance(AuthStartLoginRequest, base);
|
|
22
|
+
expect(await validate(dto)).toEqual([]);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('falla si falta password', async () => {
|
|
26
|
+
const dto = plainToInstance(AuthStartLoginRequest, { ...base, password: '' });
|
|
27
|
+
expect((await validate(dto)).some((e) => e.property === 'password')).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('falla si clientId no es string', async () => {
|
|
31
|
+
const dto = plainToInstance(AuthStartLoginRequest, { ...base, clientId: 42 });
|
|
32
|
+
expect((await validate(dto)).some((e) => e.property === 'clientId')).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('AuthStartLoginResponse', () => {
|
|
37
|
+
it('tokens directos (2FA apagado)', async () => {
|
|
38
|
+
const dto = plainToInstance(AuthStartLoginResponse, {
|
|
39
|
+
tokens: {
|
|
40
|
+
accessToken: 'a',
|
|
41
|
+
idToken: 'i',
|
|
42
|
+
refreshToken: 'r',
|
|
43
|
+
expiresIn: 3600,
|
|
44
|
+
tokenType: 'Bearer',
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
expect(await validate(dto)).toEqual([]);
|
|
48
|
+
expect(dto.tokens).toBeInstanceOf(AuthStartLoginTokens);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('challenge CUSTOM_CHALLENGE + session + parameters', async () => {
|
|
52
|
+
const dto = plainToInstance(AuthStartLoginResponse, {
|
|
53
|
+
challengeType: CognitoChallengeType.CUSTOM_CHALLENGE,
|
|
54
|
+
session: 'sess-123',
|
|
55
|
+
challengeParameters: { challengeName: 'EMAIL_OTP' },
|
|
56
|
+
});
|
|
57
|
+
expect(await validate(dto)).toEqual([]);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('vacio es valido (todos opcionales)', async () => {
|
|
61
|
+
const dto = plainToInstance(AuthStartLoginResponse, {});
|
|
62
|
+
expect(await validate(dto)).toEqual([]);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('falla si challengeType no es del enum', async () => {
|
|
66
|
+
const dto = plainToInstance(AuthStartLoginResponse, { challengeType: 'BOGUS' });
|
|
67
|
+
expect((await validate(dto)).some((e) => e.property === 'challengeType')).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('falla si expiresIn de tokens no es number', async () => {
|
|
71
|
+
const dto = plainToInstance(AuthStartLoginResponse, {
|
|
72
|
+
tokens: { accessToken: 'a', idToken: 'i', refreshToken: 'r', expiresIn: 'x', tokenType: 'Bearer' },
|
|
73
|
+
});
|
|
74
|
+
expect((await validate(dto)).length).toBeGreaterThan(0);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -39,4 +39,37 @@ describe('DefineNextChallengeRequest', () => {
|
|
|
39
39
|
expect(errors.length).toBeGreaterThan(0);
|
|
40
40
|
expect(errors[0].property).toBe('cognitoSub');
|
|
41
41
|
});
|
|
42
|
+
|
|
43
|
+
it('acepta userStatus opcional (cognito:user_status, C-06)', async () => {
|
|
44
|
+
const dto = plainToInstance(DefineNextChallengeRequest, {
|
|
45
|
+
cognitoSub: 'abc-123',
|
|
46
|
+
userNotFound: false,
|
|
47
|
+
session: [],
|
|
48
|
+
userStatus: 'FORCE_CHANGE_PASSWORD',
|
|
49
|
+
});
|
|
50
|
+
const errors = await validate(dto);
|
|
51
|
+
expect(errors).toEqual([]);
|
|
52
|
+
expect(dto.userStatus).toBe('FORCE_CHANGE_PASSWORD');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('userStatus ausente sigue siendo valido (aditivo)', async () => {
|
|
56
|
+
const dto = plainToInstance(DefineNextChallengeRequest, {
|
|
57
|
+
cognitoSub: 'abc-123',
|
|
58
|
+
userNotFound: false,
|
|
59
|
+
session: [],
|
|
60
|
+
});
|
|
61
|
+
expect(await validate(dto)).toEqual([]);
|
|
62
|
+
expect(dto.userStatus).toBeUndefined();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('falla si userStatus no es string', async () => {
|
|
66
|
+
const dto = plainToInstance(DefineNextChallengeRequest, {
|
|
67
|
+
cognitoSub: 'abc-123',
|
|
68
|
+
userNotFound: false,
|
|
69
|
+
session: [],
|
|
70
|
+
userStatus: 99,
|
|
71
|
+
});
|
|
72
|
+
const errors = await validate(dto);
|
|
73
|
+
expect(errors.some((e) => e.property === 'userStatus')).toBe(true);
|
|
74
|
+
});
|
|
42
75
|
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ChallengeNameEnum } from '../../../../src/platformRbac/enums/ChallengeNameEnum';
|
|
2
|
+
|
|
3
|
+
describe('ChallengeNameEnum', () => {
|
|
4
|
+
it('incluye SRP_A y PASSWORD_VERIFIER (SRP dentro de CUSTOM_AUTH)', () => {
|
|
5
|
+
expect(ChallengeNameEnum.SRP_A).toBe('SRP_A');
|
|
6
|
+
expect(ChallengeNameEnum.PASSWORD_VERIFIER).toBe('PASSWORD_VERIFIER');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('conserva los valores previos', () => {
|
|
10
|
+
expect(ChallengeNameEnum.PASSWORD).toBe('PASSWORD');
|
|
11
|
+
expect(ChallengeNameEnum.EMAIL_OTP).toBe('EMAIL_OTP');
|
|
12
|
+
expect(ChallengeNameEnum.TOTP).toBe('TOTP');
|
|
13
|
+
expect(ChallengeNameEnum.NEW_PASSWORD_REQUIRED).toBe('NEW_PASSWORD_REQUIRED');
|
|
14
|
+
expect(ChallengeNameEnum.RESET_PASSWORD).toBe('RESET_PASSWORD');
|
|
15
|
+
expect(ChallengeNameEnum.SMS_OTP).toBe('SMS_OTP');
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -43,7 +43,7 @@ describe('catálogo de errores', () => {
|
|
|
43
43
|
expect(RETAIL_PROMOTIONS_ERROR_HTTP_STATUS.CODE_NOT_FOUND).toBe(404);
|
|
44
44
|
expect(RETAIL_PROMOTIONS_ERROR_HTTP_STATUS.DISCOUNT_AFTER_QUOTE).toBe(409);
|
|
45
45
|
expect(RETAIL_PROMOTIONS_ERROR_HTTP_STATUS.CODE_NOT_OWNED).toBe(422);
|
|
46
|
-
expect(Object.keys(RetailPromotionsErrorCodeEnum)).toHaveLength(
|
|
46
|
+
expect(Object.keys(RetailPromotionsErrorCodeEnum)).toHaveLength(31);
|
|
47
47
|
expect(RETAIL_PROMOTIONS_ERROR_HTTP_STATUS.SPLIT_FINANCING_DISABLED).toBe(409);
|
|
48
48
|
});
|
|
49
49
|
|
|
@@ -16,6 +16,8 @@ const storeItem: StoreSalePromotionItem = {
|
|
|
16
16
|
levers: { gift: null, deviceDiscount: null, accessoryDiscount: null, coupon: null, walletBonus: null, extendedWarranty: null, airtime: null },
|
|
17
17
|
scope: { storeIds: null, productCategories: null, tiers: null, skus: null },
|
|
18
18
|
appliesTo: PromotionAppliesToEnum.ANY_FINANCIER,
|
|
19
|
+
stackable: false,
|
|
20
|
+
anchorBound: false,
|
|
19
21
|
};
|
|
20
22
|
|
|
21
23
|
const evaluated: EvaluatedSalePromotion = {
|
|
@@ -31,6 +33,8 @@ const evaluated: EvaluatedSalePromotion = {
|
|
|
31
33
|
giftChooseCount: null,
|
|
32
34
|
afterSale: [],
|
|
33
35
|
appliesTo: PromotionAppliesToEnum.SUREKEEP_ONLY,
|
|
36
|
+
stackable: false,
|
|
37
|
+
anchorBound: false,
|
|
34
38
|
};
|
|
35
39
|
|
|
36
40
|
describe('SalePromotionConditions', () => {
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { plainToInstance, type ClassConstructor } from 'class-transformer';
|
|
3
|
+
import { validateSync, type ValidationError } from 'class-validator';
|
|
4
|
+
import {
|
|
5
|
+
CreateSalePromotionRequest,
|
|
6
|
+
HoldPromotionInput,
|
|
7
|
+
HoldSalePromotionRequest,
|
|
8
|
+
LinePromotionAmount,
|
|
9
|
+
RETAIL_PROMOTIONS_ERROR_HTTP_STATUS,
|
|
10
|
+
RetailPromotionsErrorCodeEnum,
|
|
11
|
+
SalePromotionKindEnum,
|
|
12
|
+
SalePromotionRef,
|
|
13
|
+
UpdateSalePromotionRequest,
|
|
14
|
+
type HeldPromotion,
|
|
15
|
+
type HoldSalePromotionResponse,
|
|
16
|
+
type LinePromotionDiscount,
|
|
17
|
+
} from '../../../src/retailPromotions/index';
|
|
18
|
+
import {
|
|
19
|
+
ApplyLinePromotionRequest,
|
|
20
|
+
ApplySalePromotionRequest,
|
|
21
|
+
RetailPromotionInboundMessage,
|
|
22
|
+
RetailPromotionInboundMessageTypeEnum,
|
|
23
|
+
LinePromotionAmount as WizardLinePromotionAmount,
|
|
24
|
+
SalePromotionRef as WizardSalePromotionRef,
|
|
25
|
+
type WizardLinePromotionFields,
|
|
26
|
+
} from '../../../src/retailWizard';
|
|
27
|
+
|
|
28
|
+
function paths(errors: ValidationError[], prefix = ''): string[] {
|
|
29
|
+
return errors.flatMap((e) => {
|
|
30
|
+
const p = prefix ? `${prefix}.${e.property}` : e.property;
|
|
31
|
+
return e.children && e.children.length > 0 ? paths(e.children, p) : [p];
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function errorsOf<T extends object>(cls: ClassConstructor<T>, payload: object): string[] {
|
|
36
|
+
return paths(validateSync(plainToInstance(cls, payload) as object));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const amount = { promotionId: 'P1', promotionVersion: 1, discountCents: 5000 };
|
|
40
|
+
|
|
41
|
+
describe('stackable en el CRUD de promos', () => {
|
|
42
|
+
const create = { name: 'Promo', customerText: 'Texto', levers: {} };
|
|
43
|
+
|
|
44
|
+
it('acepta stackable booleano o ausente', () => {
|
|
45
|
+
expect(errorsOf(CreateSalePromotionRequest, { ...create, stackable: true })).not.toContain('stackable');
|
|
46
|
+
expect(errorsOf(CreateSalePromotionRequest, create)).not.toContain('stackable');
|
|
47
|
+
expect(errorsOf(UpdateSalePromotionRequest, { stackable: false })).toEqual([]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('rechaza stackable que no es booleano', () => {
|
|
51
|
+
expect(errorsOf(CreateSalePromotionRequest, { ...create, stackable: 'si' })).toContain('stackable');
|
|
52
|
+
expect(errorsOf(UpdateSalePromotionRequest, { stackable: 1 })).toContain('stackable');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('HoldSalePromotionRequest.promotions', () => {
|
|
57
|
+
const promotions = [
|
|
58
|
+
{ promotionId: 'P1', promotionVersion: 2, lineIds: ['L1'], giftSkus: ['SKU-1'] },
|
|
59
|
+
{ promotionId: 'P2', promotionVersion: 1 },
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
it('acepta la lista nueva sola, con o sin código', () => {
|
|
63
|
+
expect(errorsOf(HoldSalePromotionRequest, { promotions })).toEqual([]);
|
|
64
|
+
expect(errorsOf(HoldSalePromotionRequest, { promotions, discountCode: 'BUENFIN' })).toEqual([]);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('sigue aceptando la forma vieja sola', () => {
|
|
68
|
+
expect(
|
|
69
|
+
errorsOf(HoldSalePromotionRequest, {
|
|
70
|
+
gift: { promotionId: 'G1', promotionVersion: 1, skus: ['SKU-1'] },
|
|
71
|
+
promotionId: 'P1',
|
|
72
|
+
promotionVersion: 1,
|
|
73
|
+
saleGiftSkus: ['SKU-2'],
|
|
74
|
+
}),
|
|
75
|
+
).toEqual([]);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it.each([
|
|
79
|
+
['gift', { gift: { promotionId: 'G1', promotionVersion: 1, skus: ['SKU-1'] } }],
|
|
80
|
+
['promotionId', { promotionId: 'P1', promotionVersion: 1 }],
|
|
81
|
+
['saleGiftSkus', { saleGiftSkus: ['SKU-2'] }],
|
|
82
|
+
])('rechaza mezclar promotions con %s', (_field, legacy) => {
|
|
83
|
+
expect(errorsOf(HoldSalePromotionRequest, { promotions, ...legacy })).toContain('promotions');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('valida cada elemento anidado', () => {
|
|
87
|
+
const found = errorsOf(HoldSalePromotionRequest, {
|
|
88
|
+
promotions: [{ promotionId: '', promotionVersion: 0, lineIds: [''], giftSkus: 'SKU' }],
|
|
89
|
+
});
|
|
90
|
+
expect(found).toEqual(
|
|
91
|
+
expect.arrayContaining([
|
|
92
|
+
'promotions.0.promotionId',
|
|
93
|
+
'promotions.0.promotionVersion',
|
|
94
|
+
'promotions.0.lineIds',
|
|
95
|
+
'promotions.0.giftSkus',
|
|
96
|
+
]),
|
|
97
|
+
);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('HoldPromotionInput se valida solo', () => {
|
|
101
|
+
expect(errorsOf(HoldPromotionInput, { promotionId: 'P1', promotionVersion: 1 })).toEqual([]);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('promotionDiscounts por línea', () => {
|
|
106
|
+
it('ApplyLinePromotionRequest acepta la lista y el descuento del código', () => {
|
|
107
|
+
expect(
|
|
108
|
+
errorsOf(ApplyLinePromotionRequest, {
|
|
109
|
+
promotionId: 'P1',
|
|
110
|
+
promotionVersion: 1,
|
|
111
|
+
discountCents: 7000,
|
|
112
|
+
codeDiscountCents: 2000,
|
|
113
|
+
promotionDiscounts: [amount],
|
|
114
|
+
}),
|
|
115
|
+
).toEqual([]);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('ApplyLinePromotionRequest valida la lista anidada y el código negativo', () => {
|
|
119
|
+
const found = errorsOf(ApplyLinePromotionRequest, {
|
|
120
|
+
codeDiscountCents: -1,
|
|
121
|
+
promotionDiscounts: [{ promotionId: '', promotionVersion: 0, discountCents: -5 }],
|
|
122
|
+
});
|
|
123
|
+
expect(found).toEqual(
|
|
124
|
+
expect.arrayContaining([
|
|
125
|
+
'codeDiscountCents',
|
|
126
|
+
'promotionDiscounts.0.promotionId',
|
|
127
|
+
'promotionDiscounts.0.promotionVersion',
|
|
128
|
+
'promotionDiscounts.0.discountCents',
|
|
129
|
+
]),
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('LinePromotionAmount es la misma clase en ambos módulos', () => {
|
|
134
|
+
expect(WizardLinePromotionAmount).toBe(LinePromotionAmount);
|
|
135
|
+
expect(errorsOf(LinePromotionAmount, amount)).toEqual([]);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('las interfaces aceptan la lista por promo', () => {
|
|
139
|
+
const fields: WizardLinePromotionFields = { promotionDiscounts: [amount], codeDiscountCents: 0 };
|
|
140
|
+
const discount: LinePromotionDiscount = {
|
|
141
|
+
lineId: 'L1',
|
|
142
|
+
promotionId: 'P1',
|
|
143
|
+
promotionVersion: 1,
|
|
144
|
+
discountCode: null,
|
|
145
|
+
originalPriceCents: 10000,
|
|
146
|
+
discountCents: 5000,
|
|
147
|
+
finalPriceCents: 5000,
|
|
148
|
+
promotionDiscounts: [amount],
|
|
149
|
+
codeDiscountCents: 0,
|
|
150
|
+
};
|
|
151
|
+
expect(fields.promotionDiscounts).toEqual(discount.promotionDiscounts);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe('salePromotions por venta', () => {
|
|
156
|
+
const ref = { promotionId: 'P1', promotionVersion: 1 };
|
|
157
|
+
|
|
158
|
+
it('ApplySalePromotionRequest acepta la lista y la valida anidada', () => {
|
|
159
|
+
const base = { salePromotionId: null, salePromotionVersion: null };
|
|
160
|
+
expect(errorsOf(ApplySalePromotionRequest, { ...base, salePromotions: [ref] })).toEqual([]);
|
|
161
|
+
expect(errorsOf(ApplySalePromotionRequest, { ...base, salePromotions: null })).toEqual([]);
|
|
162
|
+
expect(
|
|
163
|
+
errorsOf(ApplySalePromotionRequest, { ...base, salePromotions: [{ promotionId: '', promotionVersion: 0 }] }),
|
|
164
|
+
).toEqual(expect.arrayContaining(['salePromotions.0.promotionId', 'salePromotions.0.promotionVersion']));
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('SalePromotionRef es la misma clase en ambos módulos', () => {
|
|
168
|
+
expect(WizardSalePromotionRef).toBe(SalePromotionRef);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('RetailPromotionInboundMessage valida salePromotions y promotionDiscounts de la línea', () => {
|
|
172
|
+
const message = {
|
|
173
|
+
type: RetailPromotionInboundMessageTypeEnum.SALE_CONFIRMED,
|
|
174
|
+
tenantId: 'T',
|
|
175
|
+
retailerId: 'R',
|
|
176
|
+
storeId: 'S',
|
|
177
|
+
saleId: 'V',
|
|
178
|
+
sessionId: 'W',
|
|
179
|
+
customerId: 'C',
|
|
180
|
+
soldAt: '2026-10-06T00:00:00.000Z',
|
|
181
|
+
occurredAt: '2026-10-06T00:00:00.000Z',
|
|
182
|
+
salePromotions: [ref],
|
|
183
|
+
lines: [
|
|
184
|
+
{
|
|
185
|
+
lineId: 'L1',
|
|
186
|
+
sku: 'SKU',
|
|
187
|
+
qty: 1,
|
|
188
|
+
originalPriceCents: 10000,
|
|
189
|
+
identifiers: [],
|
|
190
|
+
promotionDiscounts: [amount],
|
|
191
|
+
codeDiscountCents: 0,
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
};
|
|
195
|
+
expect(errorsOf(RetailPromotionInboundMessage, message)).toEqual([]);
|
|
196
|
+
const bad = {
|
|
197
|
+
...message,
|
|
198
|
+
salePromotions: [{ promotionId: 'P1' }],
|
|
199
|
+
lines: [{ ...message.lines[0], promotionDiscounts: [{ ...amount, discountCents: 'x' }], codeDiscountCents: -1 }],
|
|
200
|
+
};
|
|
201
|
+
expect(errorsOf(RetailPromotionInboundMessage, bad)).toEqual(
|
|
202
|
+
expect.arrayContaining([
|
|
203
|
+
'salePromotions.0.promotionVersion',
|
|
204
|
+
'lines.0.promotionDiscounts.0.discountCents',
|
|
205
|
+
'lines.0.codeDiscountCents',
|
|
206
|
+
]),
|
|
207
|
+
);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
describe('respuesta del apartado y error nuevo', () => {
|
|
212
|
+
it('HoldSalePromotionResponse trae promotions sin quitar los campos viejos', () => {
|
|
213
|
+
const held: HeldPromotion = {
|
|
214
|
+
promotionId: 'P1',
|
|
215
|
+
promotionVersion: 1,
|
|
216
|
+
kind: SalePromotionKindEnum.SALE,
|
|
217
|
+
stackable: true,
|
|
218
|
+
lineIds: ['L1'],
|
|
219
|
+
giftSkus: [],
|
|
220
|
+
};
|
|
221
|
+
const response: HoldSalePromotionResponse = {
|
|
222
|
+
sessionId: 'W',
|
|
223
|
+
gift: null,
|
|
224
|
+
promotionId: null,
|
|
225
|
+
promotionVersion: null,
|
|
226
|
+
discountCode: null,
|
|
227
|
+
saleGiftSkus: [],
|
|
228
|
+
lines: [],
|
|
229
|
+
totalDiscountCents: 0,
|
|
230
|
+
promotions: [held],
|
|
231
|
+
};
|
|
232
|
+
expect(response.promotions).toHaveLength(1);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('PROMOTION_NOT_STACKABLE responde 422', () => {
|
|
236
|
+
expect(RETAIL_PROMOTIONS_ERROR_HTTP_STATUS[RetailPromotionsErrorCodeEnum.PROMOTION_NOT_STACKABLE]).toBe(422);
|
|
237
|
+
});
|
|
238
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request del login de un solo flujo (SRP dentro de CUSTOM_AUTH). El connector
|
|
3
|
+
* hace InitiateAuth(CUSTOM_AUTH, CHALLENGE_NAME=SRP_A) y responde PASSWORD_VERIFIER
|
|
4
|
+
* firmado en la misma operacion. La contraseña nunca viaja en claro a Cognito: SRP
|
|
5
|
+
* la convierte en prueba. El caller (rbac) no conoce el mecanismo.
|
|
6
|
+
*/
|
|
7
|
+
export declare class AuthStartLoginRequest {
|
|
8
|
+
userPoolId: string;
|
|
9
|
+
region: string;
|
|
10
|
+
clientId: string;
|
|
11
|
+
username: string;
|
|
12
|
+
password: string;
|
|
13
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
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 __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.AuthStartLoginRequest = void 0;
|
|
13
|
+
const class_transformer_1 = require("class-transformer");
|
|
14
|
+
const class_validator_1 = require("class-validator");
|
|
15
|
+
/**
|
|
16
|
+
* Request del login de un solo flujo (SRP dentro de CUSTOM_AUTH). El connector
|
|
17
|
+
* hace InitiateAuth(CUSTOM_AUTH, CHALLENGE_NAME=SRP_A) y responde PASSWORD_VERIFIER
|
|
18
|
+
* firmado en la misma operacion. La contraseña nunca viaja en claro a Cognito: SRP
|
|
19
|
+
* la convierte en prueba. El caller (rbac) no conoce el mecanismo.
|
|
20
|
+
*/
|
|
21
|
+
class AuthStartLoginRequest {
|
|
22
|
+
}
|
|
23
|
+
exports.AuthStartLoginRequest = AuthStartLoginRequest;
|
|
24
|
+
__decorate([
|
|
25
|
+
(0, class_transformer_1.Expose)(),
|
|
26
|
+
(0, class_validator_1.IsString)(),
|
|
27
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
28
|
+
__metadata("design:type", String)
|
|
29
|
+
], AuthStartLoginRequest.prototype, "userPoolId", void 0);
|
|
30
|
+
__decorate([
|
|
31
|
+
(0, class_transformer_1.Expose)(),
|
|
32
|
+
(0, class_validator_1.IsString)(),
|
|
33
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
34
|
+
__metadata("design:type", String)
|
|
35
|
+
], AuthStartLoginRequest.prototype, "region", void 0);
|
|
36
|
+
__decorate([
|
|
37
|
+
(0, class_transformer_1.Expose)(),
|
|
38
|
+
(0, class_validator_1.IsString)(),
|
|
39
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
40
|
+
__metadata("design:type", String)
|
|
41
|
+
], AuthStartLoginRequest.prototype, "clientId", void 0);
|
|
42
|
+
__decorate([
|
|
43
|
+
(0, class_transformer_1.Expose)(),
|
|
44
|
+
(0, class_validator_1.IsString)(),
|
|
45
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
46
|
+
__metadata("design:type", String)
|
|
47
|
+
], AuthStartLoginRequest.prototype, "username", void 0);
|
|
48
|
+
__decorate([
|
|
49
|
+
(0, class_transformer_1.Expose)(),
|
|
50
|
+
(0, class_validator_1.IsString)(),
|
|
51
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
52
|
+
__metadata("design:type", String)
|
|
53
|
+
], AuthStartLoginRequest.prototype, "password", void 0);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { CognitoChallengeType } from '../enums/CognitoChallengeType';
|
|
2
|
+
export declare class AuthStartLoginTokens {
|
|
3
|
+
accessToken: string;
|
|
4
|
+
idToken: string;
|
|
5
|
+
refreshToken: string;
|
|
6
|
+
expiresIn: number;
|
|
7
|
+
tokenType: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Respuesta de `authStartLogin`. Tras verificar el password via SRP
|
|
11
|
+
* (PASSWORD_VERIFIER), Cognito devuelve una de dos cosas: `tokens` si el flujo
|
|
12
|
+
* quedo completo (2FA apagado), o `challengeType` + `session` + `challengeParameters`
|
|
13
|
+
* si falta un paso (NEW_PASSWORD_REQUIRED, o el CUSTOM_CHALLENGE del 2do factor con
|
|
14
|
+
* el sub-challenge real en `challengeParameters.challengeName`). Todos los campos son
|
|
15
|
+
* opcionales: el caller (rbac) decide por que rama sigue.
|
|
16
|
+
*/
|
|
17
|
+
export declare class AuthStartLoginResponse {
|
|
18
|
+
tokens?: AuthStartLoginTokens;
|
|
19
|
+
challengeType?: CognitoChallengeType;
|
|
20
|
+
session?: string;
|
|
21
|
+
challengeParameters?: Record<string, string>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
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 __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.AuthStartLoginResponse = exports.AuthStartLoginTokens = void 0;
|
|
13
|
+
const class_transformer_1 = require("class-transformer");
|
|
14
|
+
const class_validator_1 = require("class-validator");
|
|
15
|
+
const CognitoChallengeType_1 = require("../enums/CognitoChallengeType");
|
|
16
|
+
class AuthStartLoginTokens {
|
|
17
|
+
}
|
|
18
|
+
exports.AuthStartLoginTokens = AuthStartLoginTokens;
|
|
19
|
+
__decorate([
|
|
20
|
+
(0, class_transformer_1.Expose)(),
|
|
21
|
+
(0, class_validator_1.IsString)(),
|
|
22
|
+
__metadata("design:type", String)
|
|
23
|
+
], AuthStartLoginTokens.prototype, "accessToken", void 0);
|
|
24
|
+
__decorate([
|
|
25
|
+
(0, class_transformer_1.Expose)(),
|
|
26
|
+
(0, class_validator_1.IsString)(),
|
|
27
|
+
__metadata("design:type", String)
|
|
28
|
+
], AuthStartLoginTokens.prototype, "idToken", void 0);
|
|
29
|
+
__decorate([
|
|
30
|
+
(0, class_transformer_1.Expose)(),
|
|
31
|
+
(0, class_validator_1.IsString)(),
|
|
32
|
+
__metadata("design:type", String)
|
|
33
|
+
], AuthStartLoginTokens.prototype, "refreshToken", void 0);
|
|
34
|
+
__decorate([
|
|
35
|
+
(0, class_transformer_1.Expose)(),
|
|
36
|
+
(0, class_validator_1.IsNumber)(),
|
|
37
|
+
__metadata("design:type", Number)
|
|
38
|
+
], AuthStartLoginTokens.prototype, "expiresIn", void 0);
|
|
39
|
+
__decorate([
|
|
40
|
+
(0, class_transformer_1.Expose)(),
|
|
41
|
+
(0, class_validator_1.IsString)(),
|
|
42
|
+
__metadata("design:type", String)
|
|
43
|
+
], AuthStartLoginTokens.prototype, "tokenType", void 0);
|
|
44
|
+
/**
|
|
45
|
+
* Respuesta de `authStartLogin`. Tras verificar el password via SRP
|
|
46
|
+
* (PASSWORD_VERIFIER), Cognito devuelve una de dos cosas: `tokens` si el flujo
|
|
47
|
+
* quedo completo (2FA apagado), o `challengeType` + `session` + `challengeParameters`
|
|
48
|
+
* si falta un paso (NEW_PASSWORD_REQUIRED, o el CUSTOM_CHALLENGE del 2do factor con
|
|
49
|
+
* el sub-challenge real en `challengeParameters.challengeName`). Todos los campos son
|
|
50
|
+
* opcionales: el caller (rbac) decide por que rama sigue.
|
|
51
|
+
*/
|
|
52
|
+
class AuthStartLoginResponse {
|
|
53
|
+
}
|
|
54
|
+
exports.AuthStartLoginResponse = AuthStartLoginResponse;
|
|
55
|
+
__decorate([
|
|
56
|
+
(0, class_transformer_1.Expose)(),
|
|
57
|
+
(0, class_validator_1.IsOptional)(),
|
|
58
|
+
(0, class_validator_1.ValidateNested)(),
|
|
59
|
+
(0, class_transformer_1.Type)(() => AuthStartLoginTokens),
|
|
60
|
+
__metadata("design:type", AuthStartLoginTokens)
|
|
61
|
+
], AuthStartLoginResponse.prototype, "tokens", void 0);
|
|
62
|
+
__decorate([
|
|
63
|
+
(0, class_transformer_1.Expose)(),
|
|
64
|
+
(0, class_validator_1.IsOptional)(),
|
|
65
|
+
(0, class_validator_1.IsEnum)(CognitoChallengeType_1.CognitoChallengeType),
|
|
66
|
+
__metadata("design:type", String)
|
|
67
|
+
], AuthStartLoginResponse.prototype, "challengeType", void 0);
|
|
68
|
+
__decorate([
|
|
69
|
+
(0, class_transformer_1.Expose)(),
|
|
70
|
+
(0, class_validator_1.IsOptional)(),
|
|
71
|
+
(0, class_validator_1.IsString)(),
|
|
72
|
+
__metadata("design:type", String)
|
|
73
|
+
], AuthStartLoginResponse.prototype, "session", void 0);
|
|
74
|
+
__decorate([
|
|
75
|
+
(0, class_transformer_1.Expose)(),
|
|
76
|
+
(0, class_validator_1.IsOptional)(),
|
|
77
|
+
(0, class_validator_1.IsObject)(),
|
|
78
|
+
__metadata("design:type", Object)
|
|
79
|
+
], AuthStartLoginResponse.prototype, "challengeParameters", void 0);
|
|
@@ -21,6 +21,8 @@ export * from './dtos/UserDetailResponse';
|
|
|
21
21
|
export * from './dtos/AuthEventsRequest';
|
|
22
22
|
export * from './dtos/AuthEventResponse';
|
|
23
23
|
export * from './dtos/AuthTokensResponse';
|
|
24
|
+
export * from './dtos/AuthStartLoginRequest';
|
|
25
|
+
export * from './dtos/AuthStartLoginResponse';
|
|
24
26
|
export * from './dtos/InitiateAuthRequest';
|
|
25
27
|
export * from './dtos/InitiateAuthResponse';
|
|
26
28
|
export * from './dtos/VerifyPasswordRequest';
|
|
@@ -37,6 +37,8 @@ __exportStar(require("./dtos/UserDetailResponse"), exports);
|
|
|
37
37
|
__exportStar(require("./dtos/AuthEventsRequest"), exports);
|
|
38
38
|
__exportStar(require("./dtos/AuthEventResponse"), exports);
|
|
39
39
|
__exportStar(require("./dtos/AuthTokensResponse"), exports);
|
|
40
|
+
__exportStar(require("./dtos/AuthStartLoginRequest"), exports);
|
|
41
|
+
__exportStar(require("./dtos/AuthStartLoginResponse"), exports);
|
|
40
42
|
__exportStar(require("./dtos/InitiateAuthRequest"), exports);
|
|
41
43
|
__exportStar(require("./dtos/InitiateAuthResponse"), exports);
|
|
42
44
|
__exportStar(require("./dtos/VerifyPasswordRequest"), exports);
|
|
@@ -42,3 +42,8 @@ __decorate([
|
|
|
42
42
|
(0, class_transformer_1.Type)(() => ChallengeResultEntry),
|
|
43
43
|
__metadata("design:type", Array)
|
|
44
44
|
], DefineNextChallengeRequest.prototype, "session", void 0);
|
|
45
|
+
__decorate([
|
|
46
|
+
(0, class_validator_1.IsOptional)(),
|
|
47
|
+
(0, class_validator_1.IsString)(),
|
|
48
|
+
__metadata("design:type", String)
|
|
49
|
+
], DefineNextChallengeRequest.prototype, "userStatus", void 0);
|
|
@@ -9,4 +9,8 @@ var ChallengeNameEnum;
|
|
|
9
9
|
ChallengeNameEnum["NEW_PASSWORD_REQUIRED"] = "NEW_PASSWORD_REQUIRED";
|
|
10
10
|
ChallengeNameEnum["RESET_PASSWORD"] = "RESET_PASSWORD";
|
|
11
11
|
ChallengeNameEnum["SMS_OTP"] = "SMS_OTP";
|
|
12
|
+
// SRP dentro de CUSTOM_AUTH: entries nativos que Cognito deja en session[] y que
|
|
13
|
+
// el orquestador del rbac lee para decidir el siguiente paso del login de un solo flujo.
|
|
14
|
+
ChallengeNameEnum["SRP_A"] = "SRP_A";
|
|
15
|
+
ChallengeNameEnum["PASSWORD_VERIFIER"] = "PASSWORD_VERIFIER";
|
|
12
16
|
})(ChallengeNameEnum || (exports.ChallengeNameEnum = ChallengeNameEnum = {}));
|
|
@@ -34,4 +34,5 @@ exports.RETAIL_PROMOTIONS_ERROR_HTTP_STATUS = {
|
|
|
34
34
|
[RetailPromotionsErrorCodeEnum_1.RetailPromotionsErrorCodeEnum.INVALID_NEXT_TOKEN]: 400,
|
|
35
35
|
[RetailPromotionsErrorCodeEnum_1.RetailPromotionsErrorCodeEnum.CATALOG_SKU_NOT_FOUND]: 404,
|
|
36
36
|
[RetailPromotionsErrorCodeEnum_1.RetailPromotionsErrorCodeEnum.SPLIT_FINANCING_DISABLED]: 409,
|
|
37
|
+
[RetailPromotionsErrorCodeEnum_1.RetailPromotionsErrorCodeEnum.PROMOTION_NOT_STACKABLE]: 422,
|
|
37
38
|
};
|