'use strict';
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createSchema = createSchema;
exports.createSchemaStructure = createSchemaStructure;
exports.createTable = createTable;
exports.createTableStructure = createTableStructure;
exports.dropSchema = dropSchema;
exports.dropTable = dropTable;
exports.dropAttribute = dropAttribute;
exports.createAttribute = createAttribute;
exports.getBackup = getBackup;
exports.cleanupOrphanBlobs = cleanupOrphanBlobs;
const schemaMetadataValidator = __importStar(require("../validation/schemaMetadataValidator.js"));
const validationWrapper_ts_1 = require("../validation/validationWrapper.js");
const common_validators_ts_1 = require("../validation/common_validators.js");
const joi_1 = __importDefault(require("joi"));
const harper_logger_ts_1 = __importDefault(require("../utility/logging/harper_logger.js"));
const uuid_1 = require("uuid");
const signalling = __importStar(require("../utility/signalling.js"));
const hdbTerms = __importStar(require("../utility/hdbTerms.js"));
const util = __importStar(require("util"));
const harperBridge = require('./harperBridge/harperBridge').default;
const hdbError_ts_1 = require("../utility/errors/hdbError.js");
const commonErrors_ts_1 = require("../utility/errors/commonErrors.js");
const itc_js_1 = require("../server/threads/itc.js");
const databases_ts_1 = require("../resources/databases.js");
const common_utils_ts_1 = require("../utility/common_utils.js");
const Server_ts_1 = require("../server/Server.js");
const blob_ts_1 = require("../resources/blob.js");
const DB_NAME_CONSTRAINTS = joi_1.default.string()
    .min(1)
    .max(common_validators_ts_1.commonValidators.schema_length.maximum)
    .pattern(common_validators_ts_1.schemaRegex)
    .messages({ 'string.pattern.base': '{:#label} ' + common_validators_ts_1.commonValidators.schema_format.message });
const TABLE_NAME_CONSTRAINTS = joi_1.default.string()
    .min(1)
    .max(common_validators_ts_1.commonValidators.schema_length.maximum)
    .pattern(common_validators_ts_1.schemaRegex)
    .messages({ 'string.pattern.base': '{:#label} ' + common_validators_ts_1.commonValidators.schema_format.message })
    .required();
const PRIMARY_KEY_CONSTRAINTS = joi_1.default.string()
    .min(1)
    .max(common_validators_ts_1.commonValidators.schema_length.maximum)
    .pattern(common_validators_ts_1.schemaRegex)
    .messages({
    'string.pattern.base': '{:#label} ' + common_validators_ts_1.commonValidators.schema_format.message,
    'any.required': "'primary_key' is required",
    'string.base': "'primary_key' must be a string",
})
    .required();
/** EXPORTED FUNCTIONS **/
async function createSchema(schemaCreateObject) {
    let schemaStructure = await createSchemaStructure(schemaCreateObject);
    signalling.signalSchemaChange(new itc_js_1.SchemaEventMsg(process.pid, schemaCreateObject.operation, schemaCreateObject.schema));
    return schemaStructure;
}
async function createSchemaStructure(schemaCreateObject) {
    const validation = (0, validationWrapper_ts_1.validateBySchema)(schemaCreateObject, joi_1.default.object({
        database: DB_NAME_CONSTRAINTS,
        schema: DB_NAME_CONSTRAINTS,
    }));
    if (validation)
        throw new hdbError_ts_1.ClientError(validation.message);
    (0, common_utils_ts_1.transformReq)(schemaCreateObject);
    if (!(await schemaMetadataValidator.checkSchemaExists(schemaCreateObject.schema))) {
        throw (0, hdbError_ts_1.handleHDBError)(new Error(), commonErrors_ts_1.HDB_ERROR_MSGS.SCHEMA_EXISTS_ERR(schemaCreateObject.schema), commonErrors_ts_1.HTTP_STATUS_CODES.BAD_REQUEST, hdbTerms.LOG_LEVELS.ERROR, commonErrors_ts_1.HDB_ERROR_MSGS.SCHEMA_EXISTS_ERR(schemaCreateObject.schema), true);
    }
    await harperBridge.createSchema(schemaCreateObject);
    return `database '${schemaCreateObject.schema}' successfully created`;
}
async function createTable(createTableObject) {
    (0, common_utils_ts_1.transformReq)(createTableObject);
    createTableObject.primary_key = createTableObject.primary_key ?? createTableObject.hash_attribute;
    return await createTableStructure(createTableObject);
}
async function createTableStructure(createTableObject) {
    const validation = (0, validationWrapper_ts_1.validateBySchema)(createTableObject, joi_1.default.object({
        database: DB_NAME_CONSTRAINTS,
        schema: DB_NAME_CONSTRAINTS,
        table: TABLE_NAME_CONSTRAINTS,
        residence: joi_1.default.array().items(joi_1.default.string().min(1)).optional(),
        primary_key: PRIMARY_KEY_CONSTRAINTS,
    }));
    if (validation)
        throw new hdbError_ts_1.ClientError(validation.message);
    let invalidTableMsg = await schemaMetadataValidator.checkSchemaTableExists(createTableObject.schema, createTableObject.table);
    if (!invalidTableMsg) {
        throw (0, hdbError_ts_1.handleHDBError)(new Error(), commonErrors_ts_1.HDB_ERROR_MSGS.TABLE_EXISTS_ERR(createTableObject.schema, createTableObject.table), commonErrors_ts_1.HTTP_STATUS_CODES.BAD_REQUEST, hdbTerms.LOG_LEVELS.ERROR, commonErrors_ts_1.HDB_ERROR_MSGS.TABLE_EXISTS_ERR(createTableObject.schema, createTableObject.table), true);
    }
    let tableSystemData = {
        name: createTableObject.table,
        schema: createTableObject.schema,
        id: (0, uuid_1.v4)(),
        primary_key: createTableObject.primary_key,
    };
    if (createTableObject.residence) {
        if (global.clustering_on) {
            tableSystemData.residence = createTableObject.residence;
            await harperBridge.createTable(tableSystemData, createTableObject);
        }
        else {
            throw (0, hdbError_ts_1.handleHDBError)(new Error(), `Clustering does not appear to be enabled. Cannot insert table with property 'residence'.`, commonErrors_ts_1.HTTP_STATUS_CODES.BAD_REQUEST);
        }
    }
    else {
        await harperBridge.createTable(tableSystemData, createTableObject);
    }
    return `table '${createTableObject.schema}.${createTableObject.table}' successfully created.`;
}
async function dropSchema(dropSchemaObject) {
    const validation = (0, validationWrapper_ts_1.validateBySchema)(dropSchemaObject, joi_1.default.object({
        database: joi_1.default.string(),
        schema: joi_1.default.string(),
    })
        .or('database', 'schema')
        .messages({
        'object.missing': "'database' is required",
    }));
    if (validation)
        throw new hdbError_ts_1.ClientError(validation.message);
    (0, common_utils_ts_1.transformReq)(dropSchemaObject);
    let invalidSchemaMsg = await schemaMetadataValidator.checkSchemaExists(dropSchemaObject.schema);
    if (invalidSchemaMsg) {
        throw (0, hdbError_ts_1.handleHDBError)(new Error(), invalidSchemaMsg, commonErrors_ts_1.HTTP_STATUS_CODES.NOT_FOUND, hdbTerms.LOG_LEVELS.ERROR, invalidSchemaMsg, true);
    }
    await harperBridge.dropSchema(dropSchemaObject);
    signalling.signalSchemaChange(new itc_js_1.SchemaEventMsg(process.pid, dropSchemaObject.operation, dropSchemaObject.schema));
    let response = await Server_ts_1.server.replication.replicateOperation(dropSchemaObject);
    response.message = `successfully deleted '${dropSchemaObject.schema}'`;
    return response;
}
async function dropTable(dropTableObject) {
    const validation = (0, validationWrapper_ts_1.validateBySchema)(dropTableObject, joi_1.default.object({
        database: joi_1.default.string(),
        schema: joi_1.default.string(),
        table: joi_1.default.string().required(),
    }));
    if (validation)
        throw new hdbError_ts_1.ClientError(validation.message);
    (0, common_utils_ts_1.transformReq)(dropTableObject);
    let invalidSchemaTableMsg = await schemaMetadataValidator.checkSchemaTableExists(dropTableObject.schema, dropTableObject.table);
    if (invalidSchemaTableMsg) {
        throw (0, hdbError_ts_1.handleHDBError)(new Error(), invalidSchemaTableMsg, commonErrors_ts_1.HTTP_STATUS_CODES.NOT_FOUND, hdbTerms.LOG_LEVELS.ERROR, invalidSchemaTableMsg, true);
    }
    await harperBridge.dropTable(dropTableObject);
    await (0, databases_ts_1.dropTableMeta)({ table: dropTableObject.table, database: dropTableObject.schema });
    let response = await Server_ts_1.server.replication.replicateOperation(dropTableObject);
    response.message = `successfully deleted table '${dropTableObject.schema}.${dropTableObject.table}'`;
    return response;
}
/**
 * Drops all files for the specified attribute.
 * @param dropAttributeObject - The JSON formatted inbound message.
 * @returns {Promise<*>}
 */
async function dropAttribute(dropAttributeObject) {
    const validation = (0, validationWrapper_ts_1.validateBySchema)(dropAttributeObject, joi_1.default.object({
        database: joi_1.default.string(),
        schema: joi_1.default.string(),
        table: joi_1.default.string().required(),
        attribute: joi_1.default.string().required(),
    }));
    if (validation)
        throw new hdbError_ts_1.ClientError(validation.message);
    (0, common_utils_ts_1.transformReq)(dropAttributeObject);
    let invalidSchemaTableMsg = await schemaMetadataValidator.checkSchemaTableExists(dropAttributeObject.schema, dropAttributeObject.table);
    if (invalidSchemaTableMsg) {
        throw (0, hdbError_ts_1.handleHDBError)(new Error(), invalidSchemaTableMsg, commonErrors_ts_1.HTTP_STATUS_CODES.NOT_FOUND, hdbTerms.LOG_LEVELS.ERROR, invalidSchemaTableMsg, true);
    }
    if (dropAttributeObject.attribute ===
        global.hdb_schema[dropAttributeObject.schema][dropAttributeObject.table].primary_key) {
        throw (0, hdbError_ts_1.handleHDBError)(new Error(), 'You cannot drop a hash attribute', commonErrors_ts_1.HTTP_STATUS_CODES.BAD_REQUEST, undefined, undefined, true);
    }
    if (hdbTerms.TIME_STAMP_NAMES.indexOf(dropAttributeObject.attribute) >= 0) {
        throw (0, hdbError_ts_1.handleHDBError)(new Error(), `cannot drop internal timestamp attribute: ${dropAttributeObject.attribute}`, commonErrors_ts_1.HTTP_STATUS_CODES.BAD_REQUEST, undefined, undefined, true);
    }
    try {
        await harperBridge.dropAttribute(dropAttributeObject);
        dropAttributeFromGlobal(dropAttributeObject);
        signalling.signalSchemaChange(new itc_js_1.SchemaEventMsg(process.pid, dropAttributeObject.operation, dropAttributeObject.schema, dropAttributeObject.table, dropAttributeObject.attribute));
        return `successfully deleted attribute '${dropAttributeObject.attribute}'`;
    }
    catch (err) {
        harper_logger_ts_1.default.error(`Got an error deleting attribute ${util.inspect(dropAttributeObject)}.`);
        throw err;
    }
}
/**
 * Removes the dropped attribute from the global hdb schema object.
 * @param dropAttributeObject
 */
function dropAttributeFromGlobal(dropAttributeObject) {
    let attributesObj = Object.values(global.hdb_schema[dropAttributeObject.schema][dropAttributeObject.table]['attributes']);
    for (let i = 0; i < attributesObj.length; i++) {
        if (attributesObj[i].attribute === dropAttributeObject.attribute) {
            global.hdb_schema[dropAttributeObject.schema][dropAttributeObject.table]['attributes'].splice(i, 1);
        }
    }
}
async function createAttribute(createAttributeObject) {
    (0, common_utils_ts_1.transformReq)(createAttributeObject);
    const tableAttr = (0, databases_ts_1.getDatabases)()[createAttributeObject.schema][createAttributeObject.table].attributes;
    for (const { name } of tableAttr) {
        if (name === createAttributeObject.attribute) {
            throw (0, hdbError_ts_1.handleHDBError)(new Error(), `attribute '${createAttributeObject.attribute}' already exists in ${createAttributeObject.schema}.${createAttributeObject.table}`, commonErrors_ts_1.HTTP_STATUS_CODES.BAD_REQUEST, undefined, undefined, true);
        }
    }
    await harperBridge.createAttribute(createAttributeObject);
    signalling.signalSchemaChange(new itc_js_1.SchemaEventMsg(process.pid, createAttributeObject.operation, createAttributeObject.schema, createAttributeObject.table, createAttributeObject.attribute));
    return `attribute '${createAttributeObject.schema}.${createAttributeObject.table}.${createAttributeObject.attribute}' successfully created.`;
}
function getBackup(getBackupObject) {
    return harperBridge.getBackup(getBackupObject);
}
function cleanupOrphanBlobs(request) {
    if (!request.database)
        throw new hdbError_ts_1.ClientError('Must provide "database" name for search for orphaned blobs');
    const database = databases[request.database];
    if (!database)
        throw new hdbError_ts_1.ClientError(`Unknown database '${request.database}'`);
    // don't await, it will probably take hours
    (0, blob_ts_1.cleanupOrphans)(databases[request.database], request.database);
    return { message: 'Orphaned blobs cleanup started, check logs for progress' };
}
//# sourceMappingURL=schema.js.map