Browse Source

fix(core): Fix inconsistencies in behaviour between DB drivers

E.g. Postgres & MySQL differ in how they order arrays by default.
Michael Bromley 6 years ago
parent
commit
71b8f4cb94

+ 6 - 2
packages/core/src/api/resolvers/entity/order-entity.resolver.ts

@@ -1,5 +1,5 @@
 import { Args, Parent, ResolveProperty, Resolver } from '@nestjs/graphql';
-import { OrderHistoryArgs } from '@vendure/common/lib/generated-types';
+import { HistoryEntryListOptions, OrderHistoryArgs, SortOrder } from '@vendure/common/lib/generated-types';
 
 import { Order } from '../../../entity/order/order.entity';
 import { HistoryService } from '../../../service/services/history.service';
@@ -46,7 +46,11 @@ export class OrderEntityResolver {
     @ResolveProperty()
     async history(@Api() apiType: ApiType, @Parent() order: Order, @Args() args: OrderHistoryArgs) {
         const publicOnly = apiType === 'shop';
-        return this.historyService.getHistoryForOrder(order.id, publicOnly, args.options || undefined);
+        const options: HistoryEntryListOptions = { ...args.options };
+        if (!options.sort) {
+            options.sort = { createdAt: SortOrder.ASC };
+        }
+        return this.historyService.getHistoryForOrder(order.id, publicOnly, options);
     }
 
     @ResolveProperty()

+ 5 - 4
packages/core/src/config/collection/default-collection-filters.ts

@@ -82,15 +82,16 @@ export const variantNameCollectionFilter = new CollectionFilter({
     description: [{ languageCode: LanguageCode.en, value: 'Filter by ProductVariant name' }],
     apply: (qb, args) => {
         qb.leftJoin('productVariant.translations', 'translation');
+        const LIKE = qb.connection.options.type === 'postgres' ? 'ILIKE' : 'LIKE';
         switch (args.operator) {
             case 'contains':
-                return qb.andWhere('translation.name LIKE :term', { term: `%${args.term}%` });
+                return qb.andWhere(`translation.name ${LIKE} :term`, { term: `%${args.term}%` });
             case 'doesNotContain':
-                return qb.andWhere('translation.name NOT LIKE :term', { term: `%${args.term}%` });
+                return qb.andWhere(`translation.name NOT ${LIKE} :term`, { term: `%${args.term}%` });
             case 'startsWith':
-                return qb.andWhere('translation.name LIKE :term', { term: `${args.term}%` });
+                return qb.andWhere(`translation.name ${LIKE} :term`, { term: `${args.term}%` });
             case 'endsWith':
-                return qb.andWhere('translation.name LIKE :term', { term: `%${args.term}` });
+                return qb.andWhere(`translation.name ${LIKE} :term`, { term: `%${args.term}` });
             default:
                 throw new UserInputError(`${args.operator} is not a valid operator`);
         }

+ 8 - 2
packages/core/src/entity/register-custom-entity-fields.ts

@@ -62,11 +62,16 @@ function registerCustomFieldsForEntity(
                     const length = customField.length || 255;
                     if (MAX_STRING_LENGTH < length) {
                         throw new Error(
-                            `ERROR: The "length" property of the custom field "${customField.name}" is greater than the maximum allowed value of ${MAX_STRING_LENGTH}`,
+                            `ERROR: The "length" property of the custom field "${
+                                customField.name
+                            }" is greater than the maximum allowed value of ${MAX_STRING_LENGTH}`,
                         );
                     }
                     options.length = length;
                 }
+                if (customField.type === 'datetime' && options.precision == null) {
+                    options.precision = 6;
+                }
                 Column(options)(new ctor(), name);
             };
 
@@ -94,7 +99,8 @@ function formatDefaultDatetime(dbEngine: ConnectionOptions['type'], datetime: an
         case 'mysql':
         case 'postgres':
         default:
-            return DateUtils.mixedDateToDate(datetime, true, true);
+            return DateUtils.mixedDateToUtcDatetimeString(datetime);
+        // return DateUtils.mixedDateToDate(datetime, true, true);
     }
 }
 

+ 1 - 1
packages/core/src/service/helpers/utils/get-user-channels-permissions.ts

@@ -34,5 +34,5 @@ export function getUserChannelsPermissions(user: User): UserChannelPermissions[]
         }
     }
 
-    return Object.values(channelsMap);
+    return Object.values(channelsMap).sort((a, b) => (a.id < b.id ? -1 : 1));
 }

+ 1 - 2
packages/core/src/service/services/collection.service.ts

@@ -361,12 +361,11 @@ export class CollectionService implements OnModuleInit {
      */
     private async applyCollectionFilters(ctx: RequestContext, collections: Collection[]): Promise<void> {
         const collectionIds = collections.map(c => c.id);
-        // Logger.info('applyCollectionFilters');
 
         const job = this.jobService.createJob({
             name: 'apply-collection-filters',
             metadata: { collectionIds },
-            singleInstance: true,
+            singleInstance: false,
             work: async reporter => {
                 Logger.verbose(`sending ApplyCollectionFiltersMessage message`);
                 this.workerService.send(new ApplyCollectionFiltersMessage({ collectionIds })).subscribe({

+ 3 - 3
packages/core/src/service/services/customer.service.ts

@@ -344,9 +344,9 @@ export class CustomerService {
         if (result) {
             const customerAddresses = result.customer.addresses;
             if (1 < customerAddresses.length) {
-                const otherAddresses = customerAddresses.filter(
-                    address => !idsAreEqual(address.id, addressToDelete.id),
-                );
+                const otherAddresses = customerAddresses
+                    .filter(address => !idsAreEqual(address.id, addressToDelete.id))
+                    .sort((a, b) => (a.id < b.id ? -1 : 1));
                 if (addressToDelete.defaultShippingAddress) {
                     otherAddresses[0].defaultShippingAddress = true;
                 }

+ 4 - 1
packages/core/src/service/services/order.service.ts

@@ -110,6 +110,8 @@ export class OrderService {
             .leftJoinAndSelect('items.fulfillment', 'fulfillment')
             .leftJoinAndSelect('lines.taxCategory', 'lineTaxCategory')
             .where('order.id = :orderId', { orderId })
+            .addOrderBy('lines.createdAt', 'ASC')
+            .addOrderBy('items.createdAt', 'ASC')
             .getOne();
         if (order) {
             order.lines.forEach(line => {
@@ -839,6 +841,7 @@ export class OrderService {
             .getRepository(OrderLine)
             .findByIds(orderLinesInput.map(l => l.orderLineId), {
                 relations: ['order', 'items', 'items.fulfillment'],
+                order: { id: 'ASC' },
             });
         for (const line of lines) {
             const inputLine = orderLinesInput.find(l => idsAreEqual(l.orderLineId, line.id));
@@ -849,7 +852,7 @@ export class OrderService {
             if (!orders.has(order.id)) {
                 orders.set(order.id, order);
             }
-            const matchingItems = line.items.filter(itemMatcher);
+            const matchingItems = line.items.sort((a, b) => (a.id < b.id ? -1 : 1)).filter(itemMatcher);
             if (matchingItems.length < inputLine.quantity) {
                 throw new IllegalOperationError(noMatchesError);
             }

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

@@ -109,6 +109,9 @@ export class ProductVariantService {
                     'assets',
                     'featuredAsset',
                 ],
+                order: {
+                    id: 'ASC',
+                },
             })
             .then(variants =>
                 variants.map(variant => {