UNPKG

nestjs-appwrite

Version:

Easier Appwrite integration for your NestJS application.

134 lines (133 loc) 7.24 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createAppwriteAsyncProviders = createAppwriteAsyncProviders; const common_1 = require("@nestjs/common"); const type_metadata_storage_1 = require("./storage/type-metadata.storage"); const appwrite_constants_1 = require("./appwrite.constants"); const node_appwrite_1 = require("node-appwrite"); const appwrite_database_repository_1 = require("appwrite-database-repository"); const attribute_status_enum_1 = require("./domain/attribute-status.enum"); const MAX_PAGE_SIZE = 5000; const HALF_SECOND_IN_MS = 500; const logger = new common_1.Logger('appwrite.providers'); const setupAttributes = async (option, repository, config) => { const classProperties = type_metadata_storage_1.default.getClassProperties(option.class); const currentAttributes = (await repository.getCollection()).attributes; await Promise.all(classProperties.map(async (prop) => { const { options } = prop; try { switch (prop.options.type) { case String: { if (options.enum) { return await repository.createOrUpdateEnumAttribute(prop.propertyKey, Object.values(prop.options.enum), options.required, options.default, options.isArray); } if (options.isEmail) { return await repository.createOrUpdateEmailAttribute(prop.propertyKey, options.required, options.default, options.isArray); } return await repository.createOrUpdateStringAttribute(prop.propertyKey, options.size, options.required, options.default, options.isArray, options.encrypt); } case Number: { if (options.isInt) { return await repository.createOrUpdateIntegerAttribute(prop.propertyKey, options.required, options.min, options.max, options.default); } return await repository.createOrUpdateFloatAttribute(prop.propertyKey, options.required, options.min, options.max, options.default); } case Boolean: { return await repository.createOrUpdateBooleanAttribute(prop.propertyKey, options.required, options.default, options.isArray); } case Date: { return await repository.createOrUpdateDateTimeAttribute(prop.propertyKey, options.required, options.default, options.isArray); } default: { logger.warn(`Property type ${prop.options.type} is not a valid Appwrite property type`); } } } catch (err) { logger.log(`Caught err for attribute ${prop.propertyKey} of type ${prop.options.type}}`); logger.error(err); } return; })); if (!config.DELETE_REMOVED_ATTRIBUTES) { return; } const collection = await repository.getCollection(); const collectionAttributes = collection.attributes; const attributeKeys = classProperties.map(property => property.propertyKey); const deletedAttributes = currentAttributes.filter(attribute => !attributeKeys.includes(attribute.key)); await Promise.all(deletedAttributes.map(async (attribute) => { const attributeFound = collectionAttributes.find(collectionAttribute => attribute.key === collectionAttribute.key); if (attributeFound?.status === attribute_status_enum_1.DatabaseResourceStatus.Processing) { return; } try { return await repository.deleteAttribute(attribute.key); } catch (err) { logger.log(`Caught err when deleting attribute with key ${attribute.key}`); logger.error(err); } })); }; const waitForAttributesToBeCreated = async (client, config, collectionId) => { const databases = new node_appwrite_1.Databases(client); let attributesCreating = true; while (attributesCreating) { const attributes = (await databases.listAttributes(config.APPWRITE_DATABASE_ID, collectionId, [node_appwrite_1.Query.limit(MAX_PAGE_SIZE)])).attributes; attributesCreating = attributes.some((attribute) => attribute.status === attribute_status_enum_1.DatabaseResourceStatus.Processing); if (attributesCreating) { logger.log('Waiting for attribute to be created'); await new Promise(resolve => setTimeout(resolve, HALF_SECOND_IN_MS)); } } }; const setupIndexes = async (option, repository) => { const classIndexes = type_metadata_storage_1.default.getClassIndexes(option.class); await Promise.all(classIndexes.map(async (index) => { const { options } = index; const indexName = `${index.propertyKey}-${options.type}`; try { let existingIndex = await repository.createOrUpdateIndex(indexName, options.type, options.attributes, options.orders); let indexCreating = existingIndex.status === attribute_status_enum_1.DatabaseResourceStatus.Processing; while (indexCreating) { await new Promise(resolve => setTimeout(resolve, HALF_SECOND_IN_MS)); existingIndex = await repository.getIndex(indexName); indexCreating = existingIndex.status === attribute_status_enum_1.DatabaseResourceStatus.Processing; } } catch (err) { logger.log(`Caught err for index ${indexName} of type ${options.type}}`); logger.error(err); } })); }; function createAppwriteAsyncProviders(modelFactories = []) { return modelFactories.reduce((providers, option) => { return [ ...providers, { provide: option.class.name, useFactory: async (client, config) => { const { name } = option.class; logger.debug(`Initializing schema: ${name}`); const schemaMetaData = type_metadata_storage_1.default.getSchemaMetadata(option.class); const repository = new appwrite_database_repository_1.AppwriteRepository(client, config.APPWRITE_DATABASE_ID, schemaMetaData.collectionId); try { await repository.createOrUpdateCollection(schemaMetaData.collectionName, schemaMetaData.permissions, schemaMetaData.documentSecurity, schemaMetaData.enabled); } catch (err) { logger.log(`Caught err for update collection ${schemaMetaData.collectionName}`); logger.error(err); } await setupAttributes(option, repository, config); await waitForAttributesToBeCreated(client, config, schemaMetaData.collectionId); await setupIndexes(option, repository); logger.debug(`Done initializing schema: ${name}`); return repository; }, inject: [appwrite_constants_1.CLIENT_PROVIDER_NAME, appwrite_constants_1.CONFIG_PROVIDER_NAME, ...(option.inject || [])], }, ]; }, []); }