Преглед на файлове

feat(core): Add two new queries to get public payment and shipping methods

Miguel Vieira преди 1 година
родител
ревизия
5c7fe42af1

+ 1 - 0
.gitignore

@@ -4,6 +4,7 @@
 # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
 
 # User-specific stuff:
+.history
 ../.idea/workspace.xml
 .idea/**/tasks.xml
 .idea/dictionaries

+ 84 - 21
packages/core/e2e/payment-method.e2e-spec.ts

@@ -209,9 +209,8 @@ describe('PaymentMethod resolver', () => {
     });
 
     it('paymentMethodHandlers', async () => {
-        const { paymentMethodHandlers } = await adminClient.query<Codegen.GetPaymentMethodHandlersQuery>(
-            GET_PAYMENT_METHOD_HANDLERS,
-        );
+        const { paymentMethodHandlers } =
+            await adminClient.query<Codegen.GetPaymentMethodHandlersQuery>(GET_PAYMENT_METHOD_HANDLERS);
         expect(paymentMethodHandlers).toEqual([
             {
                 code: dummyPaymentHandler.code,
@@ -383,9 +382,8 @@ describe('PaymentMethod resolver', () => {
 
         it('method is listed in channel2', async () => {
             adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
-            const { paymentMethods } = await adminClient.query<Codegen.GetPaymentMethodListQuery>(
-                GET_PAYMENT_METHOD_LIST,
-            );
+            const { paymentMethods } =
+                await adminClient.query<Codegen.GetPaymentMethodListQuery>(GET_PAYMENT_METHOD_LIST);
 
             expect(paymentMethods.totalItems).toBe(1);
             expect(paymentMethods.items[0].code).toBe('channel-2-method');
@@ -393,18 +391,16 @@ describe('PaymentMethod resolver', () => {
 
         it('method is not listed in channel3', async () => {
             adminClient.setChannelToken(THIRD_CHANNEL_TOKEN);
-            const { paymentMethods } = await adminClient.query<Codegen.GetPaymentMethodListQuery>(
-                GET_PAYMENT_METHOD_LIST,
-            );
+            const { paymentMethods } =
+                await adminClient.query<Codegen.GetPaymentMethodListQuery>(GET_PAYMENT_METHOD_LIST);
 
             expect(paymentMethods.totalItems).toBe(0);
         });
 
         it('method is listed in default channel', async () => {
             adminClient.setChannelToken(E2E_DEFAULT_CHANNEL_TOKEN);
-            const { paymentMethods } = await adminClient.query<Codegen.GetPaymentMethodListQuery>(
-                GET_PAYMENT_METHOD_LIST,
-            );
+            const { paymentMethods } =
+                await adminClient.query<Codegen.GetPaymentMethodListQuery>(GET_PAYMENT_METHOD_LIST);
 
             expect(paymentMethods.totalItems).toBe(4);
             expect(paymentMethods.items.map(i => i.code).sort()).toEqual([
@@ -417,9 +413,8 @@ describe('PaymentMethod resolver', () => {
 
         it('delete from channel', async () => {
             adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
-            const { paymentMethods } = await adminClient.query<Codegen.GetPaymentMethodListQuery>(
-                GET_PAYMENT_METHOD_LIST,
-            );
+            const { paymentMethods } =
+                await adminClient.query<Codegen.GetPaymentMethodListQuery>(GET_PAYMENT_METHOD_LIST);
 
             expect(paymentMethods.totalItems).toBe(1);
 
@@ -478,9 +473,8 @@ describe('PaymentMethod resolver', () => {
                 'The selected PaymentMethod is assigned to the following Channels: second-channel. Set "force: true" to delete from all Channels.',
             );
 
-            const { paymentMethods: check1 } = await adminClient.query<Codegen.GetPaymentMethodListQuery>(
-                GET_PAYMENT_METHOD_LIST,
-            );
+            const { paymentMethods: check1 } =
+                await adminClient.query<Codegen.GetPaymentMethodListQuery>(GET_PAYMENT_METHOD_LIST);
             expect(check1.totalItems).toBe(5);
 
             const { deletePaymentMethod: delete2 } = await adminClient.query<
@@ -493,9 +487,8 @@ describe('PaymentMethod resolver', () => {
 
             expect(delete2.result).toBe(DeletionResult.DELETED);
 
-            const { paymentMethods: check2 } = await adminClient.query<Codegen.GetPaymentMethodListQuery>(
-                GET_PAYMENT_METHOD_LIST,
-            );
+            const { paymentMethods: check2 } =
+                await adminClient.query<Codegen.GetPaymentMethodListQuery>(GET_PAYMENT_METHOD_LIST);
             expect(check2.totalItems).toBe(4);
         });
     });
@@ -547,6 +540,60 @@ describe('PaymentMethod resolver', () => {
             ],
         });
     });
+
+    it('returns only active payment methods', async () => {
+        // Cleanup: Remove all existing payment methods
+        const { paymentMethods } = await adminClient.query(GET_PAYMENT_METHOD_LIST);
+        for (const method of paymentMethods.items) {
+            await adminClient.query(DELETE_PAYMENT_METHOD, { id: method.id, force: true });
+        }
+
+        // Arrange: Create both enabled and disabled payment methods
+        await adminClient.query(CREATE_PAYMENT_METHOD, {
+            input: {
+                code: 'active-method',
+                enabled: true,
+                handler: {
+                    code: 'dummy-payment-handler',
+                    arguments: [{ name: 'automaticSettle', value: 'true' }],
+                },
+                translations: [
+                    {
+                        languageCode: LanguageCode.en,
+                        name: 'Active Method',
+                        description: 'This is an active method',
+                    },
+                ],
+            },
+        });
+
+        await adminClient.query(CREATE_PAYMENT_METHOD, {
+            input: {
+                code: 'inactive-method',
+                enabled: false,
+                handler: {
+                    code: 'dummy-payment-handler',
+                    arguments: [{ name: 'automaticSettle', value: 'true' }],
+                },
+                translations: [
+                    {
+                        languageCode: LanguageCode.en,
+                        name: 'Inactive Method',
+                        description: 'This is an inactive method',
+                    },
+                ],
+            },
+        });
+
+        // Act: Query active payment methods
+        const { activePaymentMethods } = await shopClient.query(ACTIVE_PAYMENT_METHODS_QUERY);
+
+        // Assert: Ensure only the active payment method is returned
+        expect(activePaymentMethods).toHaveLength(1);
+        expect(activePaymentMethods[0].code).toBe('active-method');
+        expect(activePaymentMethods[0].name).toBe('Active Method');
+        expect(activePaymentMethods[0].description).toBe('This is an active method');
+    });
 });
 
 export const PAYMENT_METHOD_FRAGMENT = gql`
@@ -650,3 +697,19 @@ export const DELETE_PAYMENT_METHOD = gql`
         }
     }
 `;
+
+const ACTIVE_PAYMENT_METHODS_QUERY = gql`
+    query ActivePaymentMethods {
+        activePaymentMethods {
+            id
+            code
+            name
+            description
+            translations {
+                languageCode
+                name
+                description
+            }
+        }
+    }
+`;

+ 74 - 12
packages/core/e2e/shipping-method.e2e-spec.ts

@@ -65,9 +65,8 @@ describe('ShippingMethod resolver', () => {
     });
 
     it('shippingEligibilityCheckers', async () => {
-        const { shippingEligibilityCheckers } = await adminClient.query<Codegen.GetEligibilityCheckersQuery>(
-            GET_ELIGIBILITY_CHECKERS,
-        );
+        const { shippingEligibilityCheckers } =
+            await adminClient.query<Codegen.GetEligibilityCheckersQuery>(GET_ELIGIBILITY_CHECKERS);
 
         expect(shippingEligibilityCheckers).toEqual([
             {
@@ -151,9 +150,8 @@ describe('ShippingMethod resolver', () => {
     });
 
     it('shippingMethods', async () => {
-        const { shippingMethods } = await adminClient.query<Codegen.GetShippingMethodListQuery>(
-            GET_SHIPPING_METHOD_LIST,
-        );
+        const { shippingMethods } =
+            await adminClient.query<Codegen.GetShippingMethodListQuery>(GET_SHIPPING_METHOD_LIST);
         expect(shippingMethods.totalItems).toEqual(2);
         expect(shippingMethods.items[0].code).toBe('standard-shipping');
         expect(shippingMethods.items[1].code).toBe('express-shipping');
@@ -310,9 +308,8 @@ describe('ShippingMethod resolver', () => {
     });
 
     it('deleteShippingMethod', async () => {
-        const listResult1 = await adminClient.query<Codegen.GetShippingMethodListQuery>(
-            GET_SHIPPING_METHOD_LIST,
-        );
+        const listResult1 =
+            await adminClient.query<Codegen.GetShippingMethodListQuery>(GET_SHIPPING_METHOD_LIST);
         expect(listResult1.shippingMethods.items.map(i => i.id)).toEqual(['T_1', 'T_2', 'T_3']);
 
         const { deleteShippingMethod } = await adminClient.query<
@@ -327,9 +324,8 @@ describe('ShippingMethod resolver', () => {
             message: null,
         });
 
-        const listResult2 = await adminClient.query<Codegen.GetShippingMethodListQuery>(
-            GET_SHIPPING_METHOD_LIST,
-        );
+        const listResult2 =
+            await adminClient.query<Codegen.GetShippingMethodListQuery>(GET_SHIPPING_METHOD_LIST);
         expect(listResult2.shippingMethods.items.map(i => i.id)).toEqual(['T_1', 'T_2']);
     });
 
@@ -458,6 +454,56 @@ describe('ShippingMethod resolver', () => {
             });
         });
     });
+
+    it('returns only active shipping methods', async () => {
+        // Arrange: Delete all existing shipping methods using deleteShippingMethod
+        const { shippingMethods } =
+            await adminClient.query<Codegen.GetShippingMethodListQuery>(GET_SHIPPING_METHOD_LIST);
+
+        for (const method of shippingMethods.items) {
+            await adminClient.query<
+                Codegen.DeleteShippingMethodMutation,
+                Codegen.DeleteShippingMethodMutationVariables
+            >(DELETE_SHIPPING_METHOD, {
+                id: method.id,
+            });
+        }
+
+        // Create a new active shipping method
+        const { createShippingMethod } = await adminClient.query<
+            Codegen.CreateShippingMethodMutation,
+            Codegen.CreateShippingMethodMutationVariables
+        >(CREATE_SHIPPING_METHOD, {
+            input: {
+                code: 'active-method',
+                fulfillmentHandler: manualFulfillmentHandler.code,
+                checker: {
+                    code: defaultShippingEligibilityChecker.code,
+                    arguments: [{ name: 'orderMinimum', value: '0' }],
+                },
+                calculator: {
+                    code: defaultShippingCalculator.code,
+                    arguments: [],
+                },
+                translations: [
+                    {
+                        languageCode: LanguageCode.en,
+                        name: 'Active Method',
+                        description: 'This is an active shipping method',
+                    },
+                ],
+            },
+        });
+
+        // Act: Query active shipping methods
+        const { activeShippingMethods } = await shopClient.query(GET_ACTIVE_SHIPPING_METHODS);
+
+        // Assert: Ensure only the new active method is returned
+        expect(activeShippingMethods).toHaveLength(1);
+        expect(activeShippingMethods[0].code).toBe('active-method');
+        expect(activeShippingMethods[0].name).toBe('Active Method');
+        expect(activeShippingMethods[0].description).toBe('This is an active shipping method');
+    });
 });
 
 const GET_SHIPPING_METHOD = gql`
@@ -526,3 +572,19 @@ export const TEST_ELIGIBLE_SHIPPING_METHODS = gql`
         }
     }
 `;
+
+const GET_ACTIVE_SHIPPING_METHODS = gql`
+    query GetActiveShippingMethods {
+        activeShippingMethods {
+            id
+            code
+            name
+            description
+            translations {
+                languageCode
+                name
+                description
+            }
+        }
+    }
+`;

+ 4 - 0
packages/core/src/api/api-internal-modules.ts

@@ -86,7 +86,9 @@ import { ShopAuthResolver } from './resolvers/shop/shop-auth.resolver';
 import { ShopCustomerResolver } from './resolvers/shop/shop-customer.resolver';
 import { ShopEnvironmentResolver } from './resolvers/shop/shop-environment.resolver';
 import { ShopOrderResolver } from './resolvers/shop/shop-order.resolver';
+import { ShopPaymentMethodsResolver } from './resolvers/shop/shop-payment-methods.resolver';
 import { ShopProductsResolver } from './resolvers/shop/shop-products.resolver';
+import { ShopShippingMethodsResolver } from './resolvers/shop/shop-shipping-methods.resolver';
 
 const adminResolvers = [
     AdministratorResolver,
@@ -125,6 +127,8 @@ const shopResolvers = [
     ShopOrderResolver,
     ShopProductsResolver,
     ShopEnvironmentResolver,
+    ShopPaymentMethodsResolver,
+    ShopShippingMethodsResolver,
 ];
 
 export const entityResolvers = [

+ 16 - 0
packages/core/src/api/resolvers/shop/shop-payment-methods.resolver.ts

@@ -0,0 +1,16 @@
+import { Query, Resolver } from '@nestjs/graphql';
+
+import { PaymentMethod } from '../../../entity/payment-method/payment-method.entity';
+import { PaymentMethodService } from '../../../service/services/payment-method.service';
+import { RequestContext } from '../../common/request-context';
+import { Ctx } from '../../decorators/request-context.decorator';
+
+@Resolver()
+export class ShopPaymentMethodsResolver {
+    constructor(private paymentMethodService: PaymentMethodService) {}
+
+    @Query()
+    async activePaymentMethods(@Ctx() ctx: RequestContext): Promise<PaymentMethod[]> {
+        return this.paymentMethodService.getActivePaymentMethods(ctx);
+    }
+}

+ 16 - 0
packages/core/src/api/resolvers/shop/shop-shipping-methods.resolver.ts

@@ -0,0 +1,16 @@
+import { Query, Resolver } from '@nestjs/graphql';
+
+import { ShippingMethod } from '../../../entity/shipping-method/shipping-method.entity';
+import { ShippingMethodService } from '../../../service/services/shipping-method.service';
+import { RequestContext } from '../../common/request-context';
+import { Ctx } from '../../decorators/request-context.decorator';
+
+@Resolver()
+export class ShopShippingMethodsResolver {
+    constructor(private shippingMethodService: ShippingMethodService) {}
+
+    @Query()
+    async activeShippingMethods(@Ctx() ctx: RequestContext): Promise<ShippingMethod[]> {
+        return this.shippingMethodService.getActiveShippingMethods(ctx);
+    }
+}

+ 20 - 0
packages/core/src/api/schema/shop-api/shop.api.graphql

@@ -45,6 +45,26 @@ type Query {
     products(options: ProductListOptions): ProductList!
     "Search Products based on the criteria set by the `SearchInput`"
     search(input: SearchInput!): SearchResponse!
+    "Get active payment methods"
+    activePaymentMethods: [PublicPaymentMethod]!
+    "Get active shipping methods"
+    activeShippingMethods: [PublicShippingMethod]!
+}
+
+type PublicPaymentMethod {
+    id: ID!
+    code: String!
+    name: String!
+    description: String
+    translations: [PaymentMethodTranslation!]!
+}
+
+type PublicShippingMethod {
+    id: ID!
+    code: String!
+    name: String!
+    description: String
+    translations: [ShippingMethodTranslation!]!
 }
 
 type Mutation {

+ 7 - 0
packages/core/src/service/services/payment-method.service.ts

@@ -317,4 +317,11 @@ export class PaymentMethodService {
             this.configArgService.getByCode('PaymentMethodEligibilityChecker', paymentMethod.checker.code);
         return { paymentMethod, handler, checker };
     }
+
+    async getActivePaymentMethods(ctx: RequestContext): Promise<PaymentMethod[]> {
+        const paymentMethods = await this.connection
+            .getRepository(ctx, PaymentMethod)
+            .find({ where: { enabled: true, channels: { id: ctx.channelId } }, relations: ['channels'] });
+        return paymentMethods.map(p => this.translator.translate(p, ctx));
+    }
 }