"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.SchemaEditor = exports.SchemaSummarizer = void 0;
const common_utils_1 = require("@fluidframework/common-utils");
const runtime_utils_1 = require("@fluidframework/runtime-utils");
const core_1 = require("../core");
const util_1 = require("../util");
const schemaIndexFormat_1 = require("./schemaIndexFormat");
/**
 * The storage key for the blob in the summary containing schema data
 */
const schemaBlobKey = "SchemaBlob";
const schemaStringKey = "SchemaString";
/**
 * Provides methods for summarizing and loading a schema repository.
 */
class SchemaSummarizer {
    constructor(runtime, schema) {
        this.runtime = runtime;
        this.schema = schema;
        this.key = "Schema";
        this.schemaBlob = (0, core_1.cachedValue)(async (observer) => {
            (0, core_1.recordDependency)(observer, this.schema);
            const schemaText = (0, schemaIndexFormat_1.getSchemaString)(this.schema);
            // For now we are not chunking the the schema, but still put it in a reusable blob:
            return this.runtime.uploadBlob(common_utils_1.IsoBuffer.from(schemaText));
        });
    }
    getAttachSummary(stringify, fullTree, trackState, telemetryContext) {
        const builder = new runtime_utils_1.SummaryTreeBuilder();
        const dataString = (0, schemaIndexFormat_1.getSchemaString)(this.schema);
        builder.addBlob(schemaStringKey, dataString);
        return builder.getSummaryTree();
    }
    async summarize(stringify, fullTree, trackState, telemetryContext) {
        const schemaBlobHandle = await this.schemaBlob.get();
        const builder = new runtime_utils_1.SummaryTreeBuilder();
        builder.addBlob(schemaBlobKey, stringify(schemaBlobHandle));
        return builder.getSummaryTree();
    }
    getGCData(fullGC) {
        // TODO: Properly implement garbage collection. Right now, garbage collection is performed automatically
        // by the code in SharedObject (from which SharedTreeCore extends). The `runtime.uploadBlob` API delegates
        // to the `BlobManager`, which automatically populates the summary with ISummaryAttachment entries for each
        // blob.
        return {
            gcNodes: {},
        };
    }
    async load(services, parse) {
        const [hasString, hasBlob] = await Promise.all([
            services.contains(schemaStringKey),
            services.contains(schemaBlobKey),
        ]);
        (0, common_utils_1.assert)(hasString || hasBlob, 0x3d8 /* Schema is required in summary */);
        let schemaBuffer;
        if (hasBlob) {
            const handleBuffer = await services.readBlob(schemaBlobKey);
            const handleString = (0, common_utils_1.bufferToString)(handleBuffer, "utf-8");
            const handle = parse(handleString);
            schemaBuffer = await handle.get();
        }
        else {
            schemaBuffer = await services.readBlob(schemaStringKey);
        }
        // After the awaits, validate that the schema is in a clean state.
        // This detects any schema that could have been accidentally added through
        // invalid means and are about to be overwritten.
        (0, common_utils_1.assert)((0, core_1.schemaDataIsEmpty)(this.schema), 0x3da /* there should not already be stored schema when loading stored schema */);
        const schemaString = (0, common_utils_1.bufferToString)(schemaBuffer, "utf-8");
        const decoded = (0, schemaIndexFormat_1.parseSchemaString)(schemaString);
        this.schema.update(decoded);
    }
}
exports.SchemaSummarizer = SchemaSummarizer;
/**
 * Wraps a StoredSchemaRepository, adjusting its "update" function to hook into Fluid Ops.
 *
 * TODO: this should be more integrated with transactions.
 */
class SchemaEditor {
    constructor(inner, submit) {
        this.inner = inner;
        this.submit = submit;
    }
    on(eventName, listener) {
        return this.inner.on(eventName, listener);
    }
    /**
     * @returns true if this is a schema op and was handled.
     *
     * TODO: Shared tree needs a pattern for handling non-changeset operations.
     * See TODO on `SharedTree.processCore`.
     */
    tryHandleOp(message) {
        const op = message.contents;
        if ((0, util_1.isJsonObject)(op) && op.type === "SchemaOp") {
            const data = (0, schemaIndexFormat_1.parseSchemaString)(op.data);
            // TODO: This does not correctly handle concurrency of schema edits.
            this.inner.update(data);
            return true;
        }
        return false;
    }
    /**
     * @returns true iff this is a schema op and was submitted.
     *
     * TODO: Shared tree needs a pattern for handling non-changeset operations.
     * See TODO on `SharedTree.processCore`.
     */
    tryResubmitOp(content) {
        const op = content;
        if ((0, util_1.isJsonObject)(op) && op.type === "SchemaOp") {
            (0, common_utils_1.assert)(typeof op.data === "string", 0x5e3 /* expected string data for resubmitted schema op */);
            const schemaOp = {
                type: op.type,
                data: op.data,
            };
            this.submit(schemaOp);
            return true;
        }
        return false;
    }
    update(newSchema) {
        const op = { type: "SchemaOp", data: (0, schemaIndexFormat_1.getSchemaString)(newSchema) };
        this.submit(op);
        this.inner.update(newSchema);
    }
    registerDependent(dependent) {
        return this.inner.registerDependent(dependent);
    }
    removeDependent(dependent) {
        return this.inner.removeDependent(dependent);
    }
    get computationName() {
        return this.inner.computationName;
    }
    get listDependees() {
        var _a;
        return (_a = this.inner.listDependees) === null || _a === void 0 ? void 0 : _a.bind(this.inner);
    }
    get policy() {
        return this.inner.policy;
    }
    get globalFieldSchema() {
        return this.inner.globalFieldSchema;
    }
    get treeSchema() {
        return this.inner.treeSchema;
    }
}
exports.SchemaEditor = SchemaEditor;
//# sourceMappingURL=schemaSummarizer.js.map