walker.js

import { Bucket } from "./s3.js";
import EventEmitter from "events";

/** Class representing an S3 walker. */
export class Walker extends EventEmitter {
    /**
     * Walk a repository in an S3 bucket
     * @constructor
     * @param {Object} params
     * @param {Credentials} params.credentials - the AWS credentials to use for the connection
     */
    constructor({ credentials }) {
        super();

        if (!credentials) throw new Error(`Missing required property: 'credentials'`);
        const requiredProperties = ["bucket", "accessKeyId", "secretAccessKey", "region"];
        requiredProperties.forEach((property) => {
            if (!credentials[property]) {
                throw new Error(`Missing required property: '${property}'`);
            }
        });

        this.roCrateFile = "ro-crate-metadata.json";
        this.inventoryFile = "nocfl.inventory.json";
        this.identifierFile = "nocfl.identifier.json";

        this.credentials = credentials;
        this.bucket = new Bucket(credentials);
    }

    /**
     * Walk the repository and emit when an object is located. The object data
     *   to set up a store connection to it is emitted.
     * @param {Object} params
     * @param {string} [params.prefix] - Walk only the defined prefix. This can be any path from the root of the bucket.
     */

    async walk({ domain = undefined, prefix = undefined }) {
        const walker = __walker.bind(this);
        await walker({});

        async function __walker({ continuationToken }) {
            prefix = prefix ? prefix : domain;
            let objects = await this.bucket.listObjects({ continuationToken });
            if (objects?.Contents?.length) {
                for (let entry of objects?.Contents) {
                    let match = false;
                    if (
                        prefix &&
                        entry.Key.match(`${prefix}/`) &&
                        entry.Key.match(this.identifierFile)
                    ) {
                        match = true;
                    } else if (!prefix && entry.Key.match(this.identifierFile)) {
                        match = true;
                    }
                    if (match) {
                        let inventory = await this.bucket.readJSON({
                            target: entry.Key,
                        });
                        this.emit("object", inventory);
                    }
                }
            }
            if (objects.NextContinuationToken) {
                await walker({ domain, continuationToken: objects.NextContinuationToken });
            }
        }
    }
}