"use strict";
/*!
 * Copyright 2022 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.resetInstrumentationStatus = exports.getNodejsLibraryVersion = exports.createDiagnosticEntry = exports.populateInstrumentationInfo = exports.getInstrumentationInfoStatus = exports.NODEJS_LIBRARY_NAME_PREFIX = exports.INSTRUMENTATION_SOURCE_KEY = exports.DIAGNOSTIC_INFO_KEY = void 0;
const arrify = require("arrify");
const path = require("path");
const protos_1 = require("../../protos/protos");
const entry_1 = require("../entry");
// The global variable keeping track if instrumentation record was already written or not.
// The instrumentation record should be generated only once per process and contain logging
// libraries related info.
global.instrumentationAdded = false;
// The variable to hold cached library version
let libraryVersion;
// Max length for instrumentation library name and version values
const maxDiagnosticValueLen = 14;
exports.DIAGNOSTIC_INFO_KEY = 'logging.googleapis.com/diagnostic';
exports.INSTRUMENTATION_SOURCE_KEY = 'instrumentation_source';
exports.NODEJS_LIBRARY_NAME_PREFIX = 'nodejs';
/**
 * This method returns the status if instrumentation info was already added or not.
 * @returns true if the log record with instrumentation info was already added, false otherwise.
 */
function getInstrumentationInfoStatus() {
    return global.instrumentationAdded;
}
exports.getInstrumentationInfoStatus = getInstrumentationInfoStatus;
/**
 * This method helps to populate entries with instrumentation data
 * @param entry {Entry} The entry or array of entries to be populated with instrumentation info
 * @returns {Entry} Array of entries which contains an entry with current library instrumentation info
 */
function populateInstrumentationInfo(entry) {
    var _a, _b;
    // Update the flag indicating that instrumentation entry was already added once,
    // so any subsequent calls to this method will not add a separate instrumentation log entry
    let isWritten = global.instrumentationAdded;
    global.instrumentationAdded = true;
    const entries = [];
    if (entry) {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        for (const entryItem of arrify(entry)) {
            if (entryItem) {
                const info = (_b = (_a = entryItem.data) === null || _a === void 0 ? void 0 : _a[exports.DIAGNOSTIC_INFO_KEY]) === null || _b === void 0 ? void 0 : _b[exports.INSTRUMENTATION_SOURCE_KEY];
                if (info) {
                    // Validate and update the instrumentation info with current library info
                    entryItem.data[exports.DIAGNOSTIC_INFO_KEY][exports.INSTRUMENTATION_SOURCE_KEY] =
                        validateAndUpdateInstrumentation(info);
                    // Indicate that instrumentation info log entry already exists
                    // and that current library info was added to existing log entry
                    isWritten = true;
                }
                entries.push(entryItem);
            }
        }
    }
    // If no instrumentation info was added before, append a separate log entry with
    // instrumentation data for this library
    if (!isWritten) {
        entries.push(createDiagnosticEntry(undefined, undefined));
    }
    return entries;
}
exports.populateInstrumentationInfo = populateInstrumentationInfo;
/**
 * The helper method to generate a log entry with diagnostic instrumentation data.
 * @param libraryName {string} The name of the logging library to be reported. Should be prefixed with 'nodejs'.
 *        Will be truncated if longer than 14 characters.
 * @param libraryVersion {string} The version of the logging library to be reported. Will be truncated if longer than 14 characters.
 * @returns {Entry} The entry with diagnostic instrumentation data.
 */
function createDiagnosticEntry(libraryName, libraryVersion) {
    // Validate the libraryName first and make sure it starts with 'nodejs' prefix.
    if (!libraryName || !libraryName.startsWith(exports.NODEJS_LIBRARY_NAME_PREFIX)) {
        libraryName = exports.NODEJS_LIBRARY_NAME_PREFIX;
    }
    const entry = new entry_1.Entry({
        severity: protos_1.google.logging.type.LogSeverity.INFO,
    }, {
        [exports.DIAGNOSTIC_INFO_KEY]: {
            [exports.INSTRUMENTATION_SOURCE_KEY]: [
                {
                    // Truncate libraryName and libraryVersion if more than 14 characters length
                    name: truncateValue(libraryName, maxDiagnosticValueLen),
                    version: truncateValue(libraryVersion !== null && libraryVersion !== void 0 ? libraryVersion : getNodejsLibraryVersion(), maxDiagnosticValueLen),
                },
            ],
        },
    });
    return entry;
}
exports.createDiagnosticEntry = createDiagnosticEntry;
/**
 * This method validates that provided instrumentation info list is valid and also adds current library info to a list.
 * @param infoList {InstrumentationInfo} The array of InstrumentationInfo to be validated and updated.
 * @returns {InstrumentationInfo} The updated list of InstrumentationInfo.
 */
function validateAndUpdateInstrumentation(infoList) {
    const finalInfo = [];
    // First, add current library information
    finalInfo.push({
        name: exports.NODEJS_LIBRARY_NAME_PREFIX,
        version: getNodejsLibraryVersion(),
    });
    // Iterate through given list of libraries and for each entry perform validations and transformations
    // Limit amount of entries to be up to 3
    let count = 1;
    for (const info of infoList) {
        if (isValidInfo(info)) {
            finalInfo.push({
                name: truncateValue(info.name, maxDiagnosticValueLen),
                version: truncateValue(info.version, maxDiagnosticValueLen),
            });
        }
        if (++count === 3)
            break;
    }
    return finalInfo;
}
/**
 * A helper function to truncate a value (library name or version for example). The value is truncated
 * when it is longer than {maxLen} chars and '*' is added instead of truncated suffix.
 * @param value {string} The value to be truncated.
 * @param maxLen {number} The max length to be used for truncation.
 * @returns {string} The truncated value.
 */
function truncateValue(value, maxLen) {
    if (value && value.length > maxLen) {
        return value.substring(0, maxLen).concat('*');
    }
    return value;
}
/**
 * The helper function to retrieve current library version from 'package.json' file. Note that
 * since we use {path.resolve}, the search for 'package.json' could be impacted by current working directory.
 * @returns {string} A current library version.
 */
function getNodejsLibraryVersion() {
    if (libraryVersion) {
        return libraryVersion;
    }
    libraryVersion = require(path.resolve(__dirname, '../../../', 'package.json')).version;
    return libraryVersion;
}
exports.getNodejsLibraryVersion = getNodejsLibraryVersion;
/**
 * The helper function which checks if given InstrumentationInfo is valid.
 * @param info {InstrumentationInfo} The info to be validated.
 * @returns true if given info is valid, false otherwise
 */
function isValidInfo(info) {
    if (!info ||
        !info.name ||
        !info.version ||
        !info.name.startsWith(exports.NODEJS_LIBRARY_NAME_PREFIX)) {
        return false;
    }
    return true;
}
/**
 * The helper method used to reset a status of a flag which indicates if instrumentation info already written or not.
 */
function resetInstrumentationStatus() {
    global.instrumentationAdded = false;
}
exports.resetInstrumentationStatus = resetInstrumentationStatus;
//# sourceMappingURL=instrumentation.js.map