|
|
@@ -3,31 +3,23 @@ import { InjectConnection } from '@nestjs/typeorm';
|
|
|
import {
|
|
|
AdjustmentOperation,
|
|
|
AdjustmentOperationInput,
|
|
|
- AdjustmentSource as ResolvedAdjustmentSource,
|
|
|
AdjustmentType,
|
|
|
CreateAdjustmentSourceInput,
|
|
|
UpdateAdjustmentSourceInput,
|
|
|
} from 'shared/generated-types';
|
|
|
import { omit } from 'shared/omit';
|
|
|
-import { pick } from 'shared/pick';
|
|
|
import { ID, PaginatedList } from 'shared/shared-types';
|
|
|
-import { assertNever } from 'shared/shared-utils';
|
|
|
import { Connection } from 'typeorm';
|
|
|
|
|
|
import { RequestContext } from '../../api/common/request-context';
|
|
|
import { ListQueryOptions } from '../../common/types/common-types';
|
|
|
import { assertFound } from '../../common/utils';
|
|
|
import {
|
|
|
- AdjustmentActionArgType,
|
|
|
- AdjustmentActionConfig,
|
|
|
- AdjustmentConditionArgType,
|
|
|
- AdjustmentConditionConfig,
|
|
|
+ AdjustmentActionDefinition,
|
|
|
+ AdjustmentConditionDefinition,
|
|
|
} from '../../config/adjustment/adjustment-types';
|
|
|
import { ConfigService } from '../../config/config.service';
|
|
|
-import {
|
|
|
- AdjustmentOperationValues,
|
|
|
- AdjustmentSource,
|
|
|
-} from '../../entity/adjustment-source/adjustment-source.entity';
|
|
|
+import { AdjustmentSource } from '../../entity/adjustment-source/adjustment-source.entity';
|
|
|
import { I18nError } from '../../i18n/i18n-error';
|
|
|
import { buildListQuery } from '../helpers/build-list-query';
|
|
|
import { patchEntity } from '../helpers/patch-entity';
|
|
|
@@ -36,8 +28,14 @@ import { ChannelService } from './channel.service';
|
|
|
|
|
|
@Injectable()
|
|
|
export class AdjustmentSourceService {
|
|
|
- availableConditions: Array<AdjustmentConditionConfig<any>> = [];
|
|
|
- availableActions: Array<AdjustmentActionConfig<any>> = [];
|
|
|
+ availableConditions: AdjustmentConditionDefinition[] = [];
|
|
|
+ availableActions: AdjustmentActionDefinition[] = [];
|
|
|
+ /**
|
|
|
+ * All active AdjustmentSources are cached in memory becuase they are needed
|
|
|
+ * every time an order is changed, which will happen often. Caching them means
|
|
|
+ * a DB call is not required newly each time.
|
|
|
+ */
|
|
|
+ private activeSources: AdjustmentSource[] = [];
|
|
|
|
|
|
constructor(
|
|
|
@InjectConnection() private connection: Connection,
|
|
|
@@ -48,25 +46,19 @@ export class AdjustmentSourceService {
|
|
|
this.availableActions = this.configService.adjustmentActions;
|
|
|
}
|
|
|
|
|
|
- findAll(
|
|
|
- type: AdjustmentType,
|
|
|
- options?: ListQueryOptions<AdjustmentSource>,
|
|
|
- ): Promise<PaginatedList<ResolvedAdjustmentSource>> {
|
|
|
+ findAll(options?: ListQueryOptions<AdjustmentSource>): Promise<PaginatedList<AdjustmentSource>> {
|
|
|
return buildListQuery(this.connection, AdjustmentSource, options)
|
|
|
.getManyAndCount()
|
|
|
.then(([items, totalItems]) => ({
|
|
|
- items: items.map(i => this.asResolvedAdjustmentSource(i)),
|
|
|
+ items,
|
|
|
totalItems,
|
|
|
}));
|
|
|
}
|
|
|
|
|
|
- async findOne(adjustmentSourceId: ID): Promise<ResolvedAdjustmentSource | undefined> {
|
|
|
- const adjustmentSource = await this.connection.manager.findOne(AdjustmentSource, adjustmentSourceId, {
|
|
|
+ async findOne(adjustmentSourceId: ID): Promise<AdjustmentSource | undefined> {
|
|
|
+ return this.connection.manager.findOne(AdjustmentSource, adjustmentSourceId, {
|
|
|
relations: [],
|
|
|
});
|
|
|
- if (adjustmentSource) {
|
|
|
- return this.asResolvedAdjustmentSource(adjustmentSource);
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -75,8 +67,8 @@ export class AdjustmentSourceService {
|
|
|
getAdjustmentOperations(
|
|
|
type: AdjustmentType,
|
|
|
): {
|
|
|
- conditions: Array<AdjustmentConditionConfig<any>>;
|
|
|
- actions: Array<AdjustmentActionConfig<any>>;
|
|
|
+ conditions: AdjustmentConditionDefinition[];
|
|
|
+ actions: AdjustmentActionDefinition[];
|
|
|
} {
|
|
|
return {
|
|
|
conditions: this.availableConditions.filter(o => o.type === type),
|
|
|
@@ -84,10 +76,20 @@ export class AdjustmentSourceService {
|
|
|
};
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Returns all active AdjustmentSources.
|
|
|
+ */
|
|
|
+ async getActiveAdjustmentSources(): Promise<AdjustmentSource[]> {
|
|
|
+ if (!this.activeSources.length) {
|
|
|
+ await this.updateActiveSources();
|
|
|
+ }
|
|
|
+ return this.activeSources;
|
|
|
+ }
|
|
|
+
|
|
|
async createAdjustmentSource(
|
|
|
ctx: RequestContext,
|
|
|
input: CreateAdjustmentSourceInput,
|
|
|
- ): Promise<ResolvedAdjustmentSource> {
|
|
|
+ ): Promise<AdjustmentSource> {
|
|
|
const adjustmentSource = new AdjustmentSource({
|
|
|
name: input.name,
|
|
|
type: input.type,
|
|
|
@@ -96,13 +98,14 @@ export class AdjustmentSourceService {
|
|
|
});
|
|
|
this.channelService.assignToChannels(adjustmentSource, ctx);
|
|
|
const newAdjustmentSource = await this.connection.manager.save(adjustmentSource);
|
|
|
+ await this.updateActiveSources();
|
|
|
return assertFound(this.findOne(newAdjustmentSource.id));
|
|
|
}
|
|
|
|
|
|
async updateAdjustmentSource(
|
|
|
ctx: RequestContext,
|
|
|
input: UpdateAdjustmentSourceInput,
|
|
|
- ): Promise<ResolvedAdjustmentSource> {
|
|
|
+ ): Promise<AdjustmentSource> {
|
|
|
const adjustmentSource = await this.connection.getRepository(AdjustmentSource).findOne(input.id);
|
|
|
if (!adjustmentSource) {
|
|
|
throw new I18nError(`error.entity-with-id-not-found`, {
|
|
|
@@ -120,56 +123,28 @@ export class AdjustmentSourceService {
|
|
|
updatedAdjustmentSource.actions = input.actions.map(a => this.parseOperationArgs('action', a));
|
|
|
}
|
|
|
await this.connection.manager.save(updatedAdjustmentSource);
|
|
|
+ await this.updateActiveSources();
|
|
|
return assertFound(this.findOne(updatedAdjustmentSource.id));
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * The internal entity (AdjustmentSource) differs from the object returned by the GraphQL API (ResolvedAdjustmentSource) in that
|
|
|
- * the external object combines the AdjustmentOperation definition with the argument values. This method augments
|
|
|
- * an AdjustmentSource entity so that it fits the ResolvedAdjustmentSource interface.
|
|
|
- */
|
|
|
- private asResolvedAdjustmentSource(adjustmentSource: AdjustmentSource): ResolvedAdjustmentSource {
|
|
|
- const output = {
|
|
|
- ...pick(adjustmentSource, ['id', 'createdAt', 'updatedAt', 'name', 'type']),
|
|
|
- ...{
|
|
|
- conditions: this.mapOperationValuesToOperation('condition', adjustmentSource.conditions),
|
|
|
- actions: this.mapOperationValuesToOperation('action', adjustmentSource.actions),
|
|
|
- },
|
|
|
- };
|
|
|
- return output;
|
|
|
- }
|
|
|
-
|
|
|
- private mapOperationValuesToOperation(
|
|
|
- type: 'condition' | 'action',
|
|
|
- values: AdjustmentOperationValues[],
|
|
|
- ): AdjustmentOperation[] {
|
|
|
- return values.map(v => {
|
|
|
- const match = this.getAdjustmentOperationByCode(type, v.code);
|
|
|
- return {
|
|
|
- type: match.type,
|
|
|
- target: match.target,
|
|
|
- code: v.code,
|
|
|
- args: match.args.map((args, i) => ({
|
|
|
- ...args,
|
|
|
- value: !!v.args[i] ? v.args[i].toString() : '',
|
|
|
- })),
|
|
|
- description: match.description,
|
|
|
- };
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* Converts the input values of the "create" and "update" mutations into the format expected by the AdjustmentSource entity.
|
|
|
*/
|
|
|
private parseOperationArgs(
|
|
|
type: 'condition' | 'action',
|
|
|
input: AdjustmentOperationInput,
|
|
|
- ): AdjustmentOperationValues {
|
|
|
+ ): AdjustmentOperation {
|
|
|
const match = this.getAdjustmentOperationByCode(type, input.code);
|
|
|
- const output: AdjustmentOperationValues = {
|
|
|
+ const output: AdjustmentOperation = {
|
|
|
code: input.code,
|
|
|
+ type: match.type,
|
|
|
+ description: match.description,
|
|
|
args: input.arguments.map((inputArg, i) => {
|
|
|
- return this.castArgument(inputArg, match.args[i].type as any);
|
|
|
+ return {
|
|
|
+ name: match.args[i].name,
|
|
|
+ type: match.args[i].type,
|
|
|
+ value: inputArg,
|
|
|
+ };
|
|
|
}),
|
|
|
};
|
|
|
return output;
|
|
|
@@ -186,24 +161,11 @@ export class AdjustmentSourceService {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Input arguments are always received as strings, but for certain parameter types they
|
|
|
- * should be cast to a different type.
|
|
|
+ * Update the activeSources cache.
|
|
|
*/
|
|
|
- private castArgument(
|
|
|
- inputArg: string,
|
|
|
- type: AdjustmentConditionArgType | AdjustmentActionArgType,
|
|
|
- ): string | number {
|
|
|
- switch (type) {
|
|
|
- case 'string':
|
|
|
- case 'datetime':
|
|
|
- return inputArg;
|
|
|
- case 'money':
|
|
|
- case 'int':
|
|
|
- case 'percentage':
|
|
|
- return Number.parseInt(inputArg, 10);
|
|
|
- default:
|
|
|
- assertNever(type);
|
|
|
- return inputArg;
|
|
|
- }
|
|
|
+ private async updateActiveSources() {
|
|
|
+ this.activeSources = await this.connection.getRepository(AdjustmentSource).find({
|
|
|
+ where: { enabled: true },
|
|
|
+ });
|
|
|
}
|
|
|
}
|