Skip to content

Commit f0f9d99

Browse files
authored
feat(core,content,category,product)!: allow validators to narrow type (#4548)
BREAKING CHANGE: `GraphQlApolloValidator` now requires 2 type params
1 parent 5cdc2b0 commit f0f9d99

8 files changed

Lines changed: 109 additions & 31 deletions

File tree

libs/category/driver/magento/src/queries/resolve-url/validator.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,19 @@ import { GraphQlApolloValidator } from '@daffodil/core/graphql';
22

33
import { MagentoCategoryUrlResolverResponse } from './response.type';
44

5-
export const magentoCategoryGetByUrlValidator: GraphQlApolloValidator<MagentoCategoryUrlResolverResponse>
6-
= (response) => {
7-
if (!response.data?.route?.uid) {
8-
throw new Error('The platform did not respond with a category.');
9-
}
10-
11-
return response;
12-
};
5+
interface Shape {
6+
data: { route: { uid: true } };
7+
}
8+
type ValidatorFn = GraphQlApolloValidator<MagentoCategoryUrlResolverResponse, Shape>;
9+
10+
const isValid = (
11+
response: Parameters<ValidatorFn>[0],
12+
): response is ReturnType<ValidatorFn> => !!response.data?.route?.uid;
13+
14+
export const magentoCategoryGetByUrlValidator: ValidatorFn = (response) => {
15+
if (isValid(response)) {
16+
return response;
17+
}
18+
19+
throw new Error('The platform did not respond with a category.');
20+
};
Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,34 @@
1-
import { Apollo } from 'apollo-angular';
21

32
import { DaffContentInvalidAPIResponseError } from '@daffodil/content/driver';
43
import { validateFieldPresence } from '@daffodil/core';
54
import { GraphQlApolloValidator } from '@daffodil/core/graphql';
65

76
import { MagentoContentGetPageResponse } from '../queries/public_api';
87

9-
export const validateMagentoContentGetPageResponse: GraphQlApolloValidator<MagentoContentGetPageResponse> = (response: Apollo.QueryResult<MagentoContentGetPageResponse>) => {
8+
interface Shape {
9+
data: {
10+
route: {
11+
type: true;
12+
content: true;
13+
title: true;
14+
identifier: true;
15+
};
16+
};
17+
}
18+
type ValidatorFn = GraphQlApolloValidator<MagentoContentGetPageResponse, Shape>;
19+
20+
const isValid = (
21+
response: Parameters<ValidatorFn>[0],
22+
): response is ReturnType<ValidatorFn> => validateFieldPresence<any>(response.data?.route, 'content', 'title', 'identifier');
23+
24+
export const validateMagentoContentGetPageResponse: ValidatorFn = (response) => {
1025
if (response.data?.route?.type === 'CMS_PAGE') {
11-
if (validateFieldPresence<any>(response.data.route, 'content', 'title', 'identifier')) {
26+
if (isValid(response)) {
1227
return response;
13-
} else {
14-
throw new DaffContentInvalidAPIResponseError('The page response does not contain required fields.');
1528
}
16-
} else {
17-
throw new DaffContentInvalidAPIResponseError('Get page response does not contain a page.');
29+
30+
throw new DaffContentInvalidAPIResponseError('The page response does not contain required fields.');
1831
}
32+
33+
throw new DaffContentInvalidAPIResponseError('Get page response does not contain a page.');
1934
};
Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,34 @@
1-
import { Apollo } from 'apollo-angular';
21

32
import { DaffContentInvalidAPIResponseError } from '@daffodil/content/driver';
43
import { validateFieldPresence } from '@daffodil/core';
54
import { GraphQlApolloValidator } from '@daffodil/core/graphql';
65

76
import { MagentoContentGetSchemaPageResponse } from '../queries/public_api';
87

9-
export const validateMagentoContentGetSchemaPageResponse: GraphQlApolloValidator<MagentoContentGetSchemaPageResponse> = (response: Apollo.QueryResult<MagentoContentGetSchemaPageResponse>) => {
8+
interface Shape {
9+
data: {
10+
route: {
11+
type: true;
12+
content_schema_json: true;
13+
title: true;
14+
identifier: true;
15+
};
16+
};
17+
}
18+
type ValidatorFn = GraphQlApolloValidator<MagentoContentGetSchemaPageResponse, Shape>;
19+
20+
const isValid = (
21+
response: Parameters<ValidatorFn>[0],
22+
): response is ReturnType<ValidatorFn> => validateFieldPresence<any>(response.data?.route, 'content_schema_json', 'title', 'identifier');
23+
24+
export const validateMagentoContentGetSchemaPageResponse: ValidatorFn = (response) => {
1025
if (response.data?.route?.type === 'CMS_PAGE') {
11-
if (validateFieldPresence<any>(response.data.route, 'content_schema_json', 'title', 'identifier')) {
26+
if (isValid(response)) {
1227
return response;
13-
} else {
14-
throw new DaffContentInvalidAPIResponseError('The page response does not contain required fields.');
1528
}
16-
} else {
17-
throw new DaffContentInvalidAPIResponseError('Get page response does not contain a page.');
29+
30+
throw new DaffContentInvalidAPIResponseError('The page response does not contain required fields.');
1831
}
32+
33+
throw new DaffContentInvalidAPIResponseError('Get page response does not contain a page.');
1934
};
Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
import { Apollo } from 'apollo-angular';
22

3+
export type ValidatedResponse<T, Shape> = {
4+
[K in keyof T]: K extends keyof Shape
5+
? Shape[K] extends true
6+
? NonNullable<T[K]>
7+
: ValidatedResponse<NonNullable<T[K]>, Shape[K]>
8+
: T[K];
9+
};
10+
311
/**
412
* A validator for a GraphQL Apollo response.
513
* Throws errors to indicate that the response is not valid.
614
* Returns the response and throws no errors to indicate that the response
715
* is valid as far as this particular validator is concerned.
816
*/
9-
export type GraphQlApolloValidator<T> = (response: Apollo.QueryResult<T>) => Apollo.QueryResult<T>;
17+
export type GraphQlApolloValidator<T, Shape> = (response: Apollo.QueryResult<T>) => ValidatedResponse<Apollo.QueryResult<T>, Shape>;

libs/product/driver/magento/src/product.service.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { getAllProducts } from './queries/get-all-products';
3030
import { getProduct } from './queries/get-product';
3131
import {
3232
getProductByUrl,
33+
magentoProductGetAllValidator,
3334
magentoProductGetByUrlValidator,
3435
} from './queries/public_api';
3536
import { DaffMagentoProductsTransformer } from './transforms/product-transformers';
@@ -92,6 +93,7 @@ export class DaffMagentoProductService implements DaffProductServiceInterface {
9293
...this.extraPageFragments,
9394
]),
9495
}).pipe(
96+
map(magentoProductGetAllValidator),
9597
map(result => this.magentoProductsTransformer.transformManyMagentoProducts(result.data.products.items, this.config.baseMediaUrl)),
9698
);
9799
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { GraphQlApolloValidator } from '@daffodil/core/graphql';
2+
import { DaffProductInvalidAPIResponseError } from '@daffodil/product/driver';
3+
4+
import { MagentoGetProductResponse } from '../../models/public_api';
5+
6+
interface Shape {
7+
data: { products: { items: true } };
8+
}
9+
type ValidatorFn = GraphQlApolloValidator<MagentoGetProductResponse, Shape>;
10+
11+
const isValid = (
12+
response: Parameters<ValidatorFn>[0],
13+
): response is ReturnType<ValidatorFn> => !!response.data?.products?.items;
14+
15+
export const magentoProductGetAllValidator: ValidatorFn = (response) => {
16+
if (isValid(response)) {
17+
return response;
18+
}
19+
20+
throw new DaffProductInvalidAPIResponseError('The platform did not respond with products.');
21+
};

libs/product/driver/magento/src/queries/get-product-by-url/validator.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,19 @@ import { DaffProductInvalidAPIResponseError } from '@daffodil/product/driver';
33

44
import { MagentoProductGetByUrlReponse } from './response.type';
55

6-
export const magentoProductGetByUrlValidator: GraphQlApolloValidator<MagentoProductGetByUrlReponse>
7-
= (response) => {
8-
if (!response.data?.route?.sku) {
9-
throw new DaffProductInvalidAPIResponseError('The platform did not respond with a product.');
10-
}
11-
12-
return response;
13-
};
6+
interface Shape {
7+
data: { route: { sku: true } };
8+
}
9+
type ValidatorFn = GraphQlApolloValidator<MagentoProductGetByUrlReponse, Shape>;
10+
11+
const isValid = (
12+
response: Parameters<ValidatorFn>[0],
13+
): response is ReturnType<ValidatorFn> => !!response.data?.route?.sku;
14+
15+
export const magentoProductGetByUrlValidator: ValidatorFn = (response) => {
16+
if (isValid(response)) {
17+
return response;
18+
}
19+
20+
throw new DaffProductInvalidAPIResponseError('The platform did not respond with a product.');
21+
};

libs/product/driver/magento/src/queries/public_api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export * from './get-product-by-url/public_api';
33
export * from './get-all-products';
44
export * from './get-filter-types';
55
export * from './fragments/public_api';
6+
export { magentoProductGetAllValidator } from './get-all-products/validator';

0 commit comments

Comments
 (0)