<commit_before>package io.liveoak.scripts.resourcetriggered.resource;

import java.io.File;
import java.io.IOException;

import com.fasterxml.jackson.databind.node.ObjectNode;
import io.liveoak.common.util.ConversionUtils;
import io.liveoak.spi.util.ObjectMapperFactory;
import io.liveoak.scripts.resource.ScriptResource;
import io.liveoak.scripts.resource.ScriptsResource;
import io.liveoak.scripts.resource.ScriptsRootResource;
import io.liveoak.spi.exceptions.PropertyException;
import io.liveoak.spi.RequestContext;
import io.liveoak.spi.exceptions.ResourceAlreadyExistsException;
import io.liveoak.spi.ResourceParams;
import io.liveoak.spi.resource.async.PropertySink;
import io.liveoak.spi.resource.async.Resource;
import io.liveoak.spi.resource.async.ResourceSink;
import io.liveoak.spi.resource.async.Responder;
import io.liveoak.spi.state.ResourceState;
import org.jboss.logging.Logger;
import org.vertx.java.core.Vertx;

/**
 * @author <a href="mailto:<EMAIL>"><NAME></a>
 */
public class ResourceScripts extends ScriptsResource {

    private ScriptsRootResource parent;
    private static final String ID = "resource-triggered-scripts";

    private static final String RESOURCE_DIRNAME = "resource-based";
    private static final String METADATA_FILENAME = "metadata.json";
    private static final String SOURCE_FILENAME = "source.js";

    private static final String TARGET_PARAMETER = "target";

    private ScriptRegistry scripts;

    private File resourceDirectory;

    String applicationId;

    public ResourceScripts(String applicationId, ScriptRegistry scriptRegistry, Vertx vertx) {
        super(vertx);
        this.scripts = scriptRegistry;
        this.applicationId = applicationId;
    }

    protected File getScriptsDirectory() {
        if (resourceDirectory == null) {
            //get a reference to where the scripts should be held
            String resourceBasedDir = parent.getScriptConfig().getScriptDirectory() + File.separator + "/" + RESOURCE_DIRNAME;
            resourceDirectory = new File(resourceBasedDir);

            // create the directory if it doesn't already exist
            if (!resourceDirectory.exists()) {
                resourceDirectory.mkdirs();
            }
        }
        return resourceDirectory;
    }

    //TODO
    @Override
    protected ScriptResource generateChildResource(ResourceState state) {
        return null;
    }


    public void parent(ScriptsRootResource parent) {
        this.parent = parent;
    }

    // Read the configuration files from the filesystem [if any] and configure the resource based on them
    public void start() throws IOException {

        //get a reference to where the resource triggred scripts should be held
        String resourceBasedDir = parent.getScriptConfig().getScriptDirectory() + File.separator + "/" + RESOURCE_DIRNAME;
        resourceDirectory = new File(resourceBasedDir);

        // create the directory if it doesn't already exist
        if (!resourceDirectory.exists()) {
            resourceDirectory.mkdirs();
        }

        vertx.fileSystem().readDir(resourceDirectory.getPath(), (result) -> {
            if (!result.failed()) {

                for (String fileName : result.result()) {
                    File scriptDirectory = new File(fileName);
                    String resourceID = scriptDirectory.getName();

                    try {
                        // read the file containing the metadata for this script
                        File metadataFile = new File(scriptDirectory.getPath() + File.separator + "/" + METADATA_FILENAME);
                        if (metadataFile.exists()) {
                            ObjectNode objectNode = (ObjectNode) ObjectMapperFactory.create().readTree(metadataFile);

                            ResourceState state = ConversionUtils.convert(objectNode);
                            state.id(resourceID);

                            ResourceScript resourceScript = new ResourceScript(this, state);
                            // add this resource to the list of scripts available.
                            this.scripts.add(resourceScript.getScript());

                            // read the file containing the source for this script [if any]
                            File sourceFile = new File(scriptDirectory.getPath() + File.separator + "/" + SOURCE_FILENAME);
                            if (sourceFile.exists()) {
                                vertx.fileSystem().readFile(sourceFile.getPath(), (buffer) -> {
                                    resourceScript.getScript().setScriptBuffer(buffer.result().getByteBuf());
                                });
                            }
                        }
                    } catch (Exception e) {
                        logger().error("Error reading resource script files.", e);
                    }
                }

            } else {
                logger().error("Error reading resource script files.", result.cause());
            }
        });


    }

    @Override
    public Resource parent() {
        return parent;
    }

    @Override
    public String id() {
        return ID;
    }

    @Override
    public void readProperties(RequestContext ctx, PropertySink sink) throws Exception {
        sink.accept("name", "Resource Triggered Scripts");
        sink.accept("description", "Scripts which are run when a monitored resource is modified.");

        //TODO: it not very performant to have count as part of the properties (since have to query the size)
        //      count should be specified in the readMembers method response
        ResourceParams resourceParams = ctx.resourceParams();
        if (resourceParams != null && resourceParams.value(TARGET_PARAMETER) != null) {
            String target = resourceParams.value(TARGET_PARAMETER);
            sink.accept("count", scripts.getByTarget(target).size());
        } else {
            sink.accept("count", scripts.size());
        }
        sink.complete();
    }

    @Override
    public void createMember(RequestContext ctx, ResourceState state, Responder responder) throws Exception {<commit_msg>Temporary fix for script resources not being initialized through wrapped config resources - parse the state to convert ${application.id} into a value<commit_after>package io.liveoak.scripts.resourcetriggered.resource;

import java.io.File;
import java.io.IOException;
import java.util.Properties;

import com.fasterxml.jackson.databind.node.ObjectNode;
import io.liveoak.common.util.ConversionUtils;
import io.liveoak.common.util.JsonFilterUtils;
import io.liveoak.spi.util.ObjectMapperFactory;
import io.liveoak.scripts.resource.ScriptResource;
import io.liveoak.scripts.resource.ScriptsResource;
import io.liveoak.scripts.resource.ScriptsRootResource;
import io.liveoak.spi.exceptions.PropertyException;
import io.liveoak.spi.RequestContext;
import io.liveoak.spi.exceptions.ResourceAlreadyExistsException;
import io.liveoak.spi.ResourceParams;
import io.liveoak.spi.resource.async.PropertySink;
import io.liveoak.spi.resource.async.Resource;
import io.liveoak.spi.resource.async.ResourceSink;
import io.liveoak.spi.resource.async.Responder;
import io.liveoak.spi.state.ResourceState;
import org.jboss.logging.Logger;
import org.vertx.java.core.Vertx;

/**
 * @author <a href="mailto:<EMAIL>"><NAME></a>
 */
public class ResourceScripts extends ScriptsResource {

    private ScriptsRootResource parent;
    private static final String ID = "resource-triggered-scripts";

    private static final String RESOURCE_DIRNAME = "resource-based";
    private static final String METADATA_FILENAME = "metadata.json";
    private static final String SOURCE_FILENAME = "source.js";

    private static final String TARGET_PARAMETER = "target";

    private ScriptRegistry scripts;

    private File resourceDirectory;

    String applicationId;

    public ResourceScripts(String applicationId, ScriptRegistry scriptRegistry, Vertx vertx) {
        super(vertx);
        this.scripts = scriptRegistry;
        this.applicationId = applicationId;
    }

    protected File getScriptsDirectory() {
        if (resourceDirectory == null) {
            //get a reference to where the scripts should be held
            String resourceBasedDir = parent.getScriptConfig().getScriptDirectory() + File.separator + "/" + RESOURCE_DIRNAME;
            resourceDirectory = new File(resourceBasedDir);

            // create the directory if it doesn't already exist
            if (!resourceDirectory.exists()) {
                resourceDirectory.mkdirs();
            }
        }
        return resourceDirectory;
    }

    //TODO
    @Override
    protected ScriptResource generateChildResource(ResourceState state) {
        return null;
    }


    public void parent(ScriptsRootResource parent) {
        this.parent = parent;
    }

    // Read the configuration files from the filesystem [if any] and configure the resource based on them
    public void start() throws IOException {

        //get a reference to where the resource triggred scripts should be held
        String resourceBasedDir = parent.getScriptConfig().getScriptDirectory() + File.separator + "/" + RESOURCE_DIRNAME;
        resourceDirectory = new File(resourceBasedDir);

        // create the directory if it doesn't already exist
        if (!resourceDirectory.exists()) {
            resourceDirectory.mkdirs();
        }

        vertx.fileSystem().readDir(resourceDirectory.getPath(), (result) -> {
            if (!result.failed()) {

                for (String fileName : result.result()) {
                    File scriptDirectory = new File(fileName);
                    String resourceID = scriptDirectory.getName();

                    try {
                        // read the file containing the metadata for this script
                        File metadataFile = new File(scriptDirectory.getPath() + File.separator + "/" + METADATA_FILENAME);
                        if (metadataFile.exists()) {
                            ObjectNode objectNode = (ObjectNode) ObjectMapperFactory.create().readTree(metadataFile);

                            ResourceState state = ConversionUtils.convert(objectNode);
                            state.id(resourceID);

                            // TODO This is temporary until proper handling of config resources with their own directory structure is implemented
                            state = JsonFilterUtils.filter(state, buildProperties());

                            ResourceScript resourceScript = new ResourceScript(this, state);
                            // add this resource to the list of scripts available.
                            this.scripts.add(resourceScript.getScript());

                            // read the file containing the source for this script [if any]
                            File sourceFile = new File(scriptDirectory.getPath() + File.separator + "/" + SOURCE_FILENAME);
                            if (sourceFile.exists()) {
                                vertx.fileSystem().readFile(sourceFile.getPath(), (buffer) -> {
                                    resourceScript.getScript().setScriptBuffer(buffer.result().getByteBuf());
                                });
                            }
                        }
                    } catch (Exception e) {
                        logger().error("Error reading resource script files.", e);
                    }
                }

            } else {
                logger().error("Error reading resource script files.", result.cause());
            }
        });


    }

    private Properties buildProperties() {
        Properties props = new Properties();
        props.put("application.id", this.applicationId);
        return props;
    }

    @Override
    public Resource parent() {
        return parent;
    }

    @Override
    public String id() {
        return ID;
    }

    @Override
    public void readProperties(RequestContext ctx, PropertySink sink) throws Exception {
        sink.accept("name", "Resource Triggered Scripts");
        sink.accept("description", "Scripts which are run when a monitored resource is modified.");

        //TODO: it not very performant to have count as part of the properties (since have to query the size)
        //      count should be specified in the readMembers method response
        ResourceParams resourceParams = ctx.resourceParams();
        if (resourceParams != null && resourceParams.value(TARGET_PARAMETER) != null) {
            String target = resourceParams.value(TARGET_PARAMETER);
            sink.accept("count", scripts.getByTarget(target).size());
        } else {
            sink.accept("count", scripts.size());
        }
        sink.complete();
    }

    @Override
    public void createMember(RequestContext ctx, ResourceState state, Responder responder) throws Exception {
{'fraction_non_alphanumeric': 0.08968713789107764, 'fraction_numerical': 0.0, 'mean_word_length': 2.3194871794871794, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 5, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6966210', 'n_tokens_mistral': 3183, 'n_tokens_neox': 3060, 'n_words': 835}