@medusajs/index
Version:
Medusa Index module
516 lines • 23.5 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _PostgresProvider_isReady_;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PostgresProvider = void 0;
const types_1 = require("@medusajs/framework/types");
const utils_1 = require("@medusajs/framework/utils");
const _models_1 = require("../models");
const utils_2 = require("../utils");
const flatten_object_keys_1 = require("../utils/flatten-object-keys");
const normalize_fields_selection_1 = require("../utils/normalize-fields-selection");
class PostgresProvider {
constructor(container, options, moduleOptions) {
_PostgresProvider_isReady_.set(this, void 0);
this.eventActionToMethodMap_ = {
created: "onCreate",
updated: "onUpdate",
deleted: "onDelete",
attached: "onAttach",
detached: "onDetach",
};
this.manager_ = container.manager;
this.query_ = container.query;
this.moduleOptions_ = moduleOptions;
this.baseRepository_ = container.baseRepository;
this.schemaObjectRepresentation_ = options.schemaObjectRepresentation;
this.schemaEntitiesMap_ = options.entityMap;
}
async onApplicationStart() {
let initalizedOk = () => { };
let initalizedNok = () => { };
__classPrivateFieldSet(this, _PostgresProvider_isReady_, new Promise((resolve, reject) => {
initalizedOk = resolve;
initalizedNok = reject;
}), "f");
await (0, utils_2.createPartitions)(this.schemaObjectRepresentation_, this.manager_.fork())
.then(initalizedOk)
.catch(initalizedNok);
}
static parseData(data, schemaEntityObjectRepresentation) {
const data_ = Array.isArray(data) ? data : [data];
// Always keep the id in the entity properties
const entityProperties = ["id"];
const parentsProperties = {};
/**
* Split fields into entity properties and parents properties
*/
schemaEntityObjectRepresentation.fields.forEach((field) => {
if (field.includes(".")) {
const parentAlias = field.split(".")[0];
const parentSchemaObjectRepresentation = schemaEntityObjectRepresentation.parents.find((parent) => parent.inverseSideProp === parentAlias);
if (!parentSchemaObjectRepresentation) {
throw new Error(`IndexModule error, unable to parse data for ${schemaEntityObjectRepresentation.entity}. The parent schema object representation could not be found for the alias ${parentAlias} for the entity ${schemaEntityObjectRepresentation.entity}.`);
}
parentsProperties[parentSchemaObjectRepresentation.ref.entity] ??= [];
parentsProperties[parentSchemaObjectRepresentation.ref.entity].push(field);
}
else {
entityProperties.push(field);
}
});
return {
data: data_,
entityProperties,
parentsProperties,
};
}
static parseMessageData(message) {
const isExpectedFormat = (0, utils_1.isDefined)(message?.data) && (0, utils_1.isDefined)(message?.metadata?.action);
if (!isExpectedFormat) {
return;
}
const result = {
action: "",
data: [],
ids: [],
};
result.action = message.metadata.action;
result.data = message.data;
result.data = Array.isArray(result.data) ? result.data : [result.data];
result.ids = result.data.flatMap((d) => Array.isArray(d.id) ? d.id : [d.id]);
return result;
}
consumeEvent(schemaEntityObjectRepresentation) {
return async (event) => {
await __classPrivateFieldGet(this, _PostgresProvider_isReady_, "f");
const data_ = Array.isArray(event.data)
? event.data
: [event.data];
let ids = data_.flatMap((d) => Array.isArray(d.id) ? d.id : [d.id]);
let action = event.name.split(".").pop() || "";
const parsedMessage = PostgresProvider.parseMessageData(event);
if (parsedMessage) {
action = parsedMessage.action;
ids = parsedMessage.ids;
}
const targetMethod = this.eventActionToMethodMap_[action];
if (!targetMethod) {
return;
}
const { fields, alias } = schemaEntityObjectRepresentation;
let withDeleted;
if (action === utils_1.CommonEvents.DELETED || action === utils_1.CommonEvents.DETACHED) {
withDeleted = true;
}
// Process ids in batches of 100
const batchSize = 100;
const idsBatches = [];
for (let i = 0; i < ids.length; i += batchSize) {
idsBatches.push(ids.slice(i, i + batchSize));
}
for (const idsBatch of idsBatches) {
const graphConfig = {
entity: alias,
filters: {
id: idsBatch,
},
fields: [...new Set(["id", ...fields])],
withDeleted,
};
const { data: entityData } = await this.query_.graph(graphConfig);
const argument = {
entity: schemaEntityObjectRepresentation.entity,
data: entityData,
schemaEntityObjectRepresentation,
};
await this[targetMethod](argument);
}
};
}
async query(config, sharedContext = {}) {
await __classPrivateFieldGet(this, _PostgresProvider_isReady_, "f");
const { fields = [], filters = {}, joinFilters = {}, idsOnly } = config;
const { take, skip, order: inputOrderBy = {} } = config.pagination ?? {};
const select = (0, normalize_fields_selection_1.normalizeFieldsSelection)(fields);
const where = (0, flatten_object_keys_1.flattenObjectKeys)((0, utils_1.unflattenObjectKeys)(filters));
const inputOrderByObj = (0, utils_1.unflattenObjectKeys)(inputOrderBy);
const joinWhere = (0, flatten_object_keys_1.flattenObjectKeys)((0, utils_1.unflattenObjectKeys)(joinFilters));
const orderBy = (0, flatten_object_keys_1.flattenObjectKeys)(inputOrderByObj);
const { manager } = sharedContext;
let hasPagination = false;
let hasCount = false;
if ((0, utils_1.isDefined)(skip) || (0, utils_1.isDefined)(take)) {
hasPagination = true;
if ((0, utils_1.isDefined)(skip)) {
hasCount = true;
}
}
const requestedFields = (0, utils_1.deepMerge)((0, utils_1.deepMerge)(select, filters), inputOrderByObj);
const connection = manager.getConnection();
const qb = new utils_2.QueryBuilder({
schema: this.schemaObjectRepresentation_,
entityMap: this.schemaEntitiesMap_,
knex: connection.getKnex(),
selector: {
select,
where,
joinWhere,
},
options: {
skip,
take,
orderBy,
},
rawConfig: config,
requestedFields,
idsOnly,
});
const { sql, sqlCount } = qb.buildQuery({
hasPagination,
hasCount,
});
const [resultSet, countResult] = await Promise.all([
manager.execute(sql),
hasCount ? manager.execute(sqlCount) : null,
]);
const resultMetadata = hasPagination
? {
estimate_count: hasCount
? parseInt(countResult[0]?.estimate_count ?? 0)
: undefined,
skip,
take,
}
: undefined;
return {
data: qb.buildObjectFromResultset(resultSet),
metadata: resultMetadata,
};
}
/**
* Create the index entry and the index relation entry when this event is emitted.
* @param entity
* @param data
* @param schemaEntityObjectRepresentation
* @param sharedContext
* @protected
*/
async onCreate({ entity, data, schemaEntityObjectRepresentation, }, sharedContext = {}) {
const { transactionManager: em } = sharedContext;
const indexRepository = em.getRepository((0, utils_1.toMikroORMEntity)(_models_1.IndexData));
const indexRelationRepository = em.getRepository((0, utils_1.toMikroORMEntity)(_models_1.IndexRelation));
const { data: data_, entityProperties, parentsProperties, } = PostgresProvider.parseData(data, schemaEntityObjectRepresentation);
/**
* Clean the entity data to only keep the properties that are defined in the schema
*/
const cleanedData = data_.map((entityData) => {
return entityProperties.reduce((acc, property) => {
acc[property] = entityData[property];
return acc;
}, {});
});
/**
* Loop through the data and create index entries for each entity as well as the
* index relation entries if the entity has parents
*/
const entitiesToUpsert = new Set();
const relationsToUpsert = new Set();
cleanedData.forEach((entityData, index) => {
entitiesToUpsert.add(JSON.stringify({
id: entityData.id,
name: entity,
data: entityData,
staled_at: null,
}));
/**
* Retrieve the parents to attach it to the index entry.
*/
for (const [parentEntity, parentProperties] of Object.entries(parentsProperties)) {
const parentAlias = parentProperties[0].split(".")[0];
const parentData = data_[index][parentAlias];
if (!parentData) {
continue;
}
const parentDataCollection = Array.isArray(parentData)
? parentData
: [parentData];
for (const parentData_ of parentDataCollection) {
relationsToUpsert.add(JSON.stringify({
parent_id: parentData_.id,
parent_name: parentEntity,
child_id: entityData.id,
child_name: entity,
pivot: `${parentEntity}-${entity}`,
staled_at: null,
}));
}
}
});
if (entitiesToUpsert.size) {
await indexRepository.upsertMany(Array.from(entitiesToUpsert).map((entity) => JSON.parse(entity)), {
onConflictAction: "merge",
onConflictFields: ["id", "name"],
onConflictMergeFields: ["data", "staled_at"],
});
}
if (relationsToUpsert.size) {
await indexRelationRepository.upsertMany(Array.from(relationsToUpsert).map((relation) => JSON.parse(relation)), {
onConflictAction: "merge",
onConflictFields: [
"pivot",
"parent_id",
"child_id",
"parent_name",
"child_name",
],
onConflictMergeFields: ["staled_at"],
});
}
}
/**
* Update the index entry when this event is emitted.
* @param entity
* @param data
* @param schemaEntityObjectRepresentation
* @param sharedContext
* @protected
*/
async onUpdate({ entity, data, schemaEntityObjectRepresentation, }, sharedContext = {}) {
const { transactionManager: em } = sharedContext;
const indexRepository = em.getRepository((0, utils_1.toMikroORMEntity)(_models_1.IndexData));
const { data: data_, entityProperties } = PostgresProvider.parseData(data, schemaEntityObjectRepresentation);
await indexRepository.upsertMany(data_.map((entityData) => {
return {
id: entityData.id,
name: entity,
data: entityProperties.reduce((acc, property) => {
acc[property] = entityData[property];
return acc;
}, {}),
staled_at: null,
};
}), {
onConflictAction: "merge",
onConflictFields: ["id", "name"],
onConflictMergeFields: ["data", "staled_at"],
});
}
/**
* Delete the index entry when this event is emitted.
* @param entity
* @param data
* @param schemaEntityObjectRepresentation
* @param sharedContext
* @protected
*/
async onDelete({ entity, data, schemaEntityObjectRepresentation, }, sharedContext = {}) {
const { transactionManager: em } = sharedContext;
const indexRepository = em.getRepository((0, utils_1.toMikroORMEntity)(_models_1.IndexData));
const indexRelationRepository = em.getRepository((0, utils_1.toMikroORMEntity)(_models_1.IndexRelation));
const { data: data_ } = PostgresProvider.parseData(data, schemaEntityObjectRepresentation);
const ids = data_.map((entityData) => entityData.id);
await indexRepository.nativeDelete({
id: { $in: ids },
name: entity,
});
await indexRelationRepository.nativeDelete({
$or: [
{
parent_id: { $in: ids },
parent_name: entity,
},
{
child_id: { $in: ids },
child_name: entity,
},
],
});
}
/**
* event emitted from the link modules to attach a link entity to its parent and child entities from the linked modules.
* @param entity
* @param data
* @param schemaEntityObjectRepresentation
* @protected
*/
async onAttach({ entity, data, schemaEntityObjectRepresentation, }, sharedContext = {}) {
const { transactionManager: em } = sharedContext;
const indexRepository = em.getRepository((0, utils_1.toMikroORMEntity)(_models_1.IndexData));
const indexRelationRepository = em.getRepository((0, utils_1.toMikroORMEntity)(_models_1.IndexRelation));
const { data: data_, entityProperties } = PostgresProvider.parseData(data, schemaEntityObjectRepresentation);
/**
* Retrieve the property that represent the foreign key related to the parent entity of the link entity.
* Then from the service name of the parent entity, retrieve the entity name using the linkable keys.
*/
const parentPropertyId = schemaEntityObjectRepresentation.moduleConfig.relationships[0].foreignKey;
const parentServiceName = schemaEntityObjectRepresentation.moduleConfig.relationships[0]
.serviceName;
const parentEntityName = this.schemaObjectRepresentation_._serviceNameModuleConfigMap[parentServiceName].linkableKeys?.[parentPropertyId];
if (!parentEntityName) {
throw new Error(`IndexModule error, unable to handle attach event for ${entity}. The parent entity name could not be found using the linkable keys from the module ${parentServiceName}.`);
}
/**
* Retrieve the property that represent the foreign key related to the child entity of the link entity.
* Then from the service name of the child entity, retrieve the entity name using the linkable keys.
*/
const childPropertyId = schemaEntityObjectRepresentation.moduleConfig.relationships[1].foreignKey;
const childServiceName = schemaEntityObjectRepresentation.moduleConfig.relationships[1]
.serviceName;
const childEntityName = this.schemaObjectRepresentation_._serviceNameModuleConfigMap[childServiceName].linkableKeys?.[childPropertyId];
if (!childEntityName) {
throw new Error(`IndexModule error, unable to handle attach event for ${entity}. The child entity name could not be found using the linkable keys from the module ${childServiceName}.`);
}
/**
* Clean the link entity data to only keep the properties that are defined in the schema
*/
const cleanedData = data_.map((entityData) => {
return entityProperties.reduce((acc, property) => {
acc[property] = entityData[property];
return acc;
}, {});
});
let relationsToUpsert = [];
const entitiesToUpsert = cleanedData.map((entityData) => {
relationsToUpsert.push({
parent_id: entityData[parentPropertyId],
parent_name: parentEntityName,
child_id: entityData.id,
child_name: entity,
pivot: `${parentEntityName}-${entity}`,
staled_at: null,
}, {
parent_id: entityData.id,
parent_name: entity,
child_id: entityData[childPropertyId],
child_name: childEntityName,
pivot: `${entity}-${childEntityName}`,
staled_at: null,
});
return {
id: entityData.id,
name: entity,
data: entityData,
staled_at: null,
};
});
if (entitiesToUpsert.length) {
await indexRepository.upsertMany(entitiesToUpsert, {
onConflictAction: "merge",
onConflictFields: ["id", "name"],
onConflictMergeFields: ["data", "staled_at"],
});
}
if (relationsToUpsert.length) {
await indexRelationRepository.upsertMany(relationsToUpsert, {
onConflictAction: "merge",
onConflictFields: [
"pivot",
"parent_id",
"child_id",
"parent_name",
"child_name",
],
onConflictMergeFields: ["staled_at"],
});
}
}
/**
* Event emitted from the link modules to detach a link entity from its parent and child entities from the linked modules.
* @param entity
* @param data
* @param schemaEntityObjectRepresentation
* @param sharedContext
* @protected
*/
async onDetach({ entity, data, schemaEntityObjectRepresentation, }, sharedContext = {}) {
const { transactionManager: em } = sharedContext;
const indexRepository = em.getRepository((0, utils_1.toMikroORMEntity)(_models_1.IndexData));
const indexRelationRepository = em.getRepository((0, utils_1.toMikroORMEntity)(_models_1.IndexRelation));
const { data: data_ } = PostgresProvider.parseData(data, schemaEntityObjectRepresentation);
const ids = data_.map((entityData) => entityData.id);
await indexRepository.nativeDelete({
id: { $in: ids },
name: entity,
});
await indexRelationRepository.nativeDelete({
$or: [
{
parent_id: { $in: ids },
parent_name: entity,
},
{
child_id: { $in: ids },
child_name: entity,
},
],
});
}
}
exports.PostgresProvider = PostgresProvider;
_PostgresProvider_isReady_ = new WeakMap();
__decorate([
(0, utils_1.InjectManager)(),
__param(1, (0, utils_1.MedusaContext)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], PostgresProvider.prototype, "query", null);
__decorate([
(0, utils_1.InjectTransactionManager)(),
__param(1, (0, utils_1.MedusaContext)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], PostgresProvider.prototype, "onCreate", null);
__decorate([
(0, utils_1.InjectTransactionManager)(),
__param(1, (0, utils_1.MedusaContext)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], PostgresProvider.prototype, "onUpdate", null);
__decorate([
(0, utils_1.InjectTransactionManager)(),
__param(1, (0, utils_1.MedusaContext)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], PostgresProvider.prototype, "onDelete", null);
__decorate([
(0, utils_1.InjectTransactionManager)(),
__param(1, (0, utils_1.MedusaContext)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], PostgresProvider.prototype, "onAttach", null);
__decorate([
(0, utils_1.InjectTransactionManager)(),
__param(1, (0, utils_1.MedusaContext)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], PostgresProvider.prototype, "onDetach", null);
//# sourceMappingURL=postgres-provider.js.map
;