Jelajahi Sumber

Merge branch 'master' into minor

Michael Bromley 2 tahun lalu
induk
melakukan
350d25fb01

+ 1 - 0
docs/docs/guides/deployment/deploy-to-northflank/index.md

@@ -455,6 +455,7 @@ This setup is suitable for testing purposes, but is not recommended for producti
         },
         {
           "kind": "DeploymentService",
+          "ref": "server",
           "spec": {
             "name": "server",
             "billing": {

+ 8 - 1
docs/docs/guides/developer-guide/importing-data/index.md

@@ -311,7 +311,8 @@ import {
     LanguageCode,
     ParsedProductWithVariants,
     RequestContext, RequestContextService,
-    TransactionalConnection, User
+    TransactionalConnection, User,
+    SearchService,
 } from '@vendure/core';
 import { createClient, OldCommerceProduct } from '@old-commerce/client';
 
@@ -344,6 +345,9 @@ async function importData() {
     // Most service methods require a RequestContext, so we'll create one here.
     const ctx = await getSuperadminContext(app);
 
+    // To reindex after importing products
+    const searchService = app.get(SearchService);
+
     // Fetch all the products to import from the OldCommerce API
     const productsToImport: OldCommerceProduct[] = await client.getAllProducts();
 
@@ -387,6 +391,9 @@ async function importData() {
         console.log(`Imported ${progress.imported} of ${importRows.length} products`);
     });
 
+    // Rebuild search index 
+    await searchService.reindex(ctx);
+
     // Close the app
     await app.close();
 }

+ 1 - 0
docs/docs/guides/developer-guide/stand-alone-scripts/index.md

@@ -50,6 +50,7 @@ async function getProductCount() {
             `There are ${totalItems} products`,
             '------------------------------',
         ].join('\n'),
+    )
 }
 ``` 
 

+ 1 - 1
docs/docs/reference/core-plugins/stellate-plugin/index.md

@@ -52,7 +52,7 @@ export const config: VendureConfig = {
            serviceName: 'my-service',
            // The API token for the Stellate Purging API. See the "pre-requisites" section above.
            apiToken: process.env.STELLATE_PURGE_API_TOKEN,
-           debugMode: !isProd || process.env.STELLATE_DEBUG_MODE ? true : false,
+           devMode: !isProd || process.env.STELLATE_DEBUG_MODE ? true : false,
            debugLogging: process.env.STELLATE_DEBUG_MODE ? true : false,
            purgeRules: [
                ...defaultPurgeRules,

+ 30 - 0
packages/core/e2e/custom-fields.e2e-spec.ts

@@ -1013,6 +1013,36 @@ describe('Custom fields', () => {
         );
     });
 
+    describe('product on productVariant entity', () => {
+        it('is translated', async () => {
+            const { productVariants } = await adminClient.query(gql`
+                query {
+                    productVariants(productId: "T_1") {
+                        items {
+                            product {
+                                name
+                                id
+                                customFields {
+                                    localeStringWithDefault
+                                    stringWithDefault
+                                }
+                            }
+                        }
+                    }
+                }
+            `);
+
+            expect(productVariants.items[0].product).toEqual({
+                id: 'T_1',
+                name: 'Laptop',
+                customFields: {
+                    localeStringWithDefault: 'hola',
+                    stringWithDefault: 'hello',
+                },
+            });
+        });
+    });
+
     describe('unique constraint', () => {
         it('setting unique value works', async () => {
             const result = await adminClient.query(

+ 2 - 1
packages/core/src/api/resolvers/entity/product-variant-entity.resolver.ts

@@ -79,9 +79,10 @@ export class ProductVariantEntityResolver {
         @Ctx() ctx: RequestContext,
         @Parent() productVariant: ProductVariant,
     ): Promise<Product | undefined> {
-        if (productVariant.product) {
+        if (productVariant.product.name) {
             return productVariant.product;
         }
+
         return this.requestContextCache.get(
             ctx,
             `ProductVariantEntityResolver.product(${productVariant.productId})`,

+ 48 - 15
packages/core/src/job-queue/polling-job-queue-strategy.ts

@@ -118,9 +118,9 @@ class ActiveQueue<Data extends JobData<Data> = object> {
                                 },
                             )
                             .finally(() => {
-                                if (!this.running && nextJob.state !== JobState.PENDING) {
-                                    return;
-                                }
+                                // if (!this.running && nextJob.state !== JobState.PENDING) {
+                                //     return;
+                                // }
                                 nextJob.off('progress', onProgress);
                                 return this.onFailOrComplete(nextJob);
                             })
@@ -145,24 +145,55 @@ class ActiveQueue<Data extends JobData<Data> = object> {
         void runNextJobs();
     }
 
-    stop(): Promise<void> {
+    async stop(): Promise<void> {
         this.running = false;
-        this.queueStopped$.next(STOP_SIGNAL);
         clearTimeout(this.timer);
+        await this.awaitRunningJobsOrTimeout();
+        Logger.info(`Stopped queue: ${this.queueName}`);
+        // Allow any job status changes to be persisted
+        // before we permit the application shutdown to continue.
+        // Otherwise, the DB connection will close before our
+        // changes are persisted.
+        await new Promise(resolve => setTimeout(resolve, 1000));
+    }
 
+    private awaitRunningJobsOrTimeout(): Promise<void> {
         const start = +new Date();
-        // Wait for 2 seconds to allow running jobs to complete
-        const maxTimeout = 2000;
-        let pollTimer: any;
+        const stopActiveQueueTimeout = 20_000;
+        let timeout: ReturnType<typeof setTimeout>;
         return new Promise(resolve => {
-            const pollActiveJobs = async () => {
-                const timedOut = +new Date() - start > maxTimeout;
-                if (this.activeJobs.length === 0 || timedOut) {
-                    clearTimeout(pollTimer);
+            let lastStatusUpdate = +new Date();
+            const pollActiveJobs = () => {
+                const now = +new Date();
+                const timedOut =
+                    stopActiveQueueTimeout === undefined ? false : now - start > stopActiveQueueTimeout;
+
+                if (this.activeJobs.length === 0) {
+                    clearTimeout(timeout);
+                    resolve();
+                    return;
+                }
+
+                if (timedOut) {
+                    Logger.warn(
+                        `Timed out (${stopActiveQueueTimeout}ms) waiting for ${this.activeJobs.length} active jobs in queue "${this.queueName}" to complete. Forcing stop...`,
+                    );
+                    this.queueStopped$.next(STOP_SIGNAL);
+                    clearTimeout(timeout);
                     resolve();
-                } else {
-                    pollTimer = setTimeout(pollActiveJobs, 50);
+                    return;
                 }
+
+                if (this.activeJobs.length > 0) {
+                    if (now - lastStatusUpdate > 2000) {
+                        Logger.info(
+                            `Stopping queue: ${this.queueName} - waiting for ${this.activeJobs.length} active jobs to complete...`,
+                        );
+                        lastStatusUpdate = now;
+                    }
+                }
+
+                timeout = setTimeout(pollActiveJobs, 200);
             };
             void pollActiveJobs();
         });
@@ -175,7 +206,9 @@ class ActiveQueue<Data extends JobData<Data> = object> {
 
     private removeJobFromActive(job: Job<Data>) {
         const index = this.activeJobs.indexOf(job);
-        this.activeJobs.splice(index, 1);
+        if (index !== -1) {
+            this.activeJobs.splice(index, 1);
+        }
     }
 }
 

+ 10 - 3
packages/core/src/service/services/product-variant.service.ts

@@ -296,9 +296,16 @@ export class ProductVariantService {
      * page, this method returns only the Product itself.
      */
     async getProductForVariant(ctx: RequestContext, variant: ProductVariant): Promise<Translated<Product>> {
-        const product = await this.connection.getEntityOrThrow(ctx, Product, variant.productId, {
-            includeSoftDeleted: true,
-        });
+        let product;
+
+        if (!variant.product) {
+            product = await this.connection.getEntityOrThrow(ctx, Product, variant.productId, {
+                includeSoftDeleted: true,
+            });
+        } else {
+            product = variant.product;
+        }
+
         return this.translator.translate(product, ctx);
     }
 

+ 2 - 2
packages/payments-plugin/src/mollie/mollie.service.ts

@@ -231,9 +231,9 @@ export class MollieService {
                 `Unable to find order ${mollieOrder.orderNumber}, unable to process Mollie order ${mollieOrder.id}`,
             );
         }
-        if (order.state === 'PaymentSettled') {
+        if (order.state === 'PaymentSettled' || order.state === 'Shipped' || order.state === 'Delivered') {
             Logger.info(
-                `Order ${order.code} is already 'PaymentSettled', no need for handling Mollie status '${mollieOrder.status}'`,
+                `Order ${order.code} is already '${order.state}', no need for handling Mollie status '${mollieOrder.status}'`,
                 loggerCtx,
             );
             return;

+ 1 - 1
packages/stellate-plugin/src/stellate-plugin.ts

@@ -54,7 +54,7 @@ const StellateOptionsProvider = {
  *            serviceName: 'my-service',
  *            // The API token for the Stellate Purging API. See the "pre-requisites" section above.
  *            apiToken: process.env.STELLATE_PURGE_API_TOKEN,
- *            debugMode: !isProd || process.env.STELLATE_DEBUG_MODE ? true : false,
+ *            devMode: !isProd || process.env.STELLATE_DEBUG_MODE ? true : false,
  *            debugLogging: process.env.STELLATE_DEBUG_MODE ? true : false,
  *            purgeRules: [
  *                ...defaultPurgeRules,