UNPKG

cumulocity-cypress

Version:
261 lines (260 loc) 10.3 kB
import * as fs from "fs"; import * as path from "path"; import * as glob from "glob"; import debug from "debug"; import { pactId } from "../c8ypact"; import * as yaml from "yaml"; import { safeStringify } from "../../util"; import lodash1 from "lodash"; import * as lodash2 from "lodash"; const _ = lodash1 || lodash2; /** * Default implementation of C8yPactFileAdapter which loads and saves C8yPact objects * Provide location of the files using folder option. Default location is * cypress/fixtures/c8ypact folder. * * This adapter supports loading of JSON and YAML pact files (.json, .yaml, .yml). When * saviing pact files, it saves them as JSON files (.json). * * By using C8yPactAdapterOptions you can enable loading of JavaScript pact files (.js, .cjs). * Use with caution, as this can lead to security issues if the files are not trusted. */ export class C8yPactDefaultFileAdapter { /** * Creates an instance of C8yPactDefaultFileAdapter. * * @param folder - The folder where pact files are stored. Can be an absolute or relative path. * @param options - Optional configuration for the adapter. * @param options.enableJavaScript - If true, enables loading of JavaScript pact files (.js, .cjs). Defaults to false. */ constructor(folder, options) { this.fileExtension = "json"; this.folder = path.isAbsolute(folder) ? folder : this.toAbsolutePath(folder); this.enabledExtensions = [`.${this.fileExtension}`, ".yaml", ".yml"]; if (options?.enableJavaScript) { this.enabledExtensions.push(".js", ".cjs"); } this.id = options?.id || "fileadapter"; this.log = debug(`c8y:${this.id}`); } description() { return `C8yPactDefaultFileAdapter: ${this.folder}`; } getFolder() { return this.folder; } loadPacts() { const pactObjects = this.loadPactObjects(); this.log(`loadPacts() - ${pactObjects.length} pact files from ${this.folder}`); return pactObjects.reduce((acc, obj) => { if (!obj?.info?.id) return acc; acc[obj.info.id] = obj; return acc; }, {}); } loadPact(id) { this.log(`loadPact() - ${id}`); const pId = pactId(id); if (pId == null) { this.log(`loadPact() - invalid pact id ${id} -> ${pId}`); return null; } if (!this.folder || !fs.existsSync(this.folder)) { this.log(`loadPact() - folder ${this.folder} does not exist`); return null; } // Try to find the file with different supported extensions const extensions = this.enabledExtensions; let loadedPact = null; for (const ext of extensions) { const file = path.join(this.folder, `${pId}${ext}`); if (fs.existsSync(file)) { try { loadedPact = this.loadPactFromFile(file); if (loadedPact) { this.log(`loadPact() - ${file} loaded successfully`); return loadedPact; } } catch (error) { this.log(`loadPact() - error loading ${file}: ${error}`); } } } this.log(`loadPact() - no valid pact file found for id ${pId}`); return null; } pactExists(id) { const pId = pactId(id); return this.enabledExtensions.some((ext) => fs.existsSync(path.join(this.folder, `${pId}${ext}`))); } savePact(pact) { this.createFolderRecursive(this.folder); const pId = pactId(pact.id); if (pId == null) { this.log(`savePact() - invalid pact id ${pact.id} -> ${pId}`); return; } const file = path.join(this.folder, `${pId}.${this.fileExtension}`); this.log(`savePact() - write ${file} (${pact.records?.length || 0} records)`); try { fs.writeFileSync(file, safeStringify({ id: pact.id, info: pact.info, records: pact.records, }, 2), "utf-8"); } catch (error) { console.error(`Failed to save pact.`, error); } } deletePact(id) { const pId = pactId(id); if (pId == null) { this.log(`deletePact() - invalid pact id ${id} -> ${pId}`); return; } const filePath = path.join(this.folder, `${pId}.${this.fileExtension}`); if (fs.existsSync(filePath)) { fs.unlinkSync(filePath); this.log(`deletePact() - deleted ${filePath}`); } else { this.log(`deletePact() - ${filePath} does not exist`); } } readPactFiles() { this.log(`readPactFiles() - ${this.folder}`); if (!this.folder || !fs.existsSync(this.folder)) { this.log(`readPactFiles() - ${this.folder} does not exist`); return []; } const pacts = this.loadPactObjects(); return pacts.map((pact) => { return safeStringify(pact); }); } /** * @deprecated Use readPactFiles() instead. */ readJsonFiles() { this.log(`readJsonFiles() - ${this.folder}`); if (!this.folder || !fs.existsSync(this.folder)) { this.log(`readJsonFiles() - ${this.folder} does not exist`); return []; } const jsonFiles = glob.sync(path.join(this.folder, `*.${this.fileExtension}`)); this.log(`readJsonFiles() - reading ${jsonFiles.length} ${this.fileExtension} files from ${this.folder}`); const pacts = jsonFiles.map((file) => { return fs.readFileSync(file, "utf-8"); }); return pacts; } deleteJsonFiles() { if (!this.folder || !fs.existsSync(this.folder)) { this.log(`deleteJsonFiles() - ${this.folder} does not exist`); return; } const jsonFiles = glob.sync(path.join(this.folder, `*.${this.fileExtension}`)); this.log(`deleteJsonFiles() - deleting ${jsonFiles.length} ${this.fileExtension} files from ${this.folder}`); jsonFiles.forEach((file) => { fs.unlinkSync(file); }); } loadPactObjects() { this.log(`loadPactObjects() - ${this.folder}`); if (!this.folder || !fs.existsSync(this.folder)) { this.log(`loadPactObjects() - ${this.folder} does not exist`); return []; } // Find all files with supported extensions const combinedPattern = path.join(this.folder, `*{${this.enabledExtensions.join(",")}}`); const allFiles = glob.sync(combinedPattern); this.log(`loadPactObjects() - reading ${allFiles.length} files from ${this.folder}`); // Load and parse each file based on its extension const pactObjects = allFiles .map((file) => { try { return this.loadPactFromFile(file); } catch (error) { this.log(`loadPactObjects() - error loading ${file}: ${error}`); return null; } }) .filter(Boolean); this.log(`loadPactObjects() - loaded ${pactObjects.length} valid pact objects`); return pactObjects; } loadPactFromFile(filePath) { if (!fs.existsSync(filePath)) { this.log(`loadPactFromFile() - file does not exist: ${filePath}`); return null; } const extension = path.extname(filePath).toLowerCase(); // Check if the extension is enabled if (!this.enabledExtensions.includes(extension)) { this.log(`loadPactFromFile() - file extension ${extension} is not supported or enabled for loading: ${filePath}`); return null; } const content = fs.readFileSync(filePath, "utf-8"); try { // Handle different file formats if (extension === `.${this.fileExtension}`) { // Load JSON file return JSON.parse(content); } else if (extension === ".yaml" || extension === ".yml") { // Load YAML file return yaml.parse(content); } else if (extension === ".js" || extension === ".cjs") { // CommonJS modules (.js, .cjs) can use require const absolutePath = path.isAbsolute(filePath) ? filePath : path.resolve(process.cwd(), filePath); try { // Clear cache if needed if (require.cache && require.cache[require.resolve(absolutePath)]) { delete require.cache[require.resolve(absolutePath)]; } // eslint-disable-next-line @typescript-eslint/no-require-imports const pactModule = require(absolutePath); return pactModule.default || pactModule; } catch (error) { this.log(`loadPactFromFile() - error loading ${extension} file ${absolutePath}: ${error}`); } } } catch (error) { this.log(`loadPactFromFile() - error parsing file ${filePath}: ${error}`); } return null; } createFolderRecursive(f) { this.log(`createFolderRecursive() - ${f}`); if (!f || !_.isString(f)) return undefined; const absolutePath = !path.isAbsolute(f) ? this.toAbsolutePath(f) : f; if (f !== absolutePath) { this.log(`createFolderRecursive() - resolved ${f} to ${absolutePath}`); } if (fs.existsSync(f)) return undefined; const result = fs.mkdirSync(absolutePath, { recursive: true }); if (result) { this.log(`createFolderRecursive() - created ${absolutePath}`); } return result; } toAbsolutePath(f) { return path.isAbsolute(f) ? f : path.resolve(process.cwd(), f); } isNodeError(error, type) { return error instanceof type; } }