package org.rundeck.plugin.example;

import com.dtolabs.rundeck.core.plugins.Plugin;
import com.dtolabs.rundeck.core.storage.ResourceMetaBuilder;
import com.dtolabs.rundeck.plugins.ServiceNameConstants;
import com.dtolabs.rundeck.plugins.storage.StorageConverterPlugin;
import org.apache.commons.codec.binary.Base64InputStream;
import org.rundeck.storage.api.HasInputStream;
import org.rundeck.storage.api.Path;

import java.io.IOException;
import java.io.InputStream;

/**
 * Example converter plugin that converts all resource content into base64-encoded form and back. When encoding the
 * data, it adds metadata it can read later.  When asked to decode, it first checks if the metadata indicates it was
 * encoded.
 */
@Plugin(service = ServiceNameConstants.StorageConverter, name = "example-b64-converter")
public class ExampleConverterPlugin implements StorageConverterPlugin {

    public static final String X_EXAMPLE_B64_CONVERTER_WAS_ENCODED = "x-example-b64-converter:is-b64-encoded";

    /**
     * Returns a {@link HasInputStream} as a lazy way to wrap an input stream with a base64 encode/decode stream
     *
     * @param hasResourceStream source
     * @param doEncode          true to encode
     *
     * @return lazy stream
     */
    private static HasInputStream wrap(final HasInputStream hasResourceStream, final boolean doEncode) {
        return new HasInputStream() {
            @Override
            public InputStream getInputStream() throws IOException {
                return new Base64InputStream(hasResourceStream.getInputStream(), doEncode);
            }
        };
    }

    private HasInputStream decode(HasInputStream hasResourceStream) {
        return wrap(hasResourceStream, false);
    }

    private HasInputStream encode(HasInputStream hasResourceStream) {
        return wrap(hasResourceStream, true);
    }


    /**
     * Returns true if the metadata indicates the data was encoded
     *
     * @param resourceMetaBuilder metadata
     *
     * @return true if encoded was to true
     */
    static boolean wasEncoded(ResourceMetaBuilder resourceMetaBuilder) {
        return Boolean.parseBoolean(resourceMetaBuilder.getResourceMeta().get(X_EXAMPLE_B64_CONVERTER_WAS_ENCODED));
    }

    /**
     * Add base64-encoded=true metadata
     *
     * @param resourceMetaBuilder metadata
     */
    static void addMetadataWasEncoded(ResourceMetaBuilder resourceMetaBuilder) {
        resourceMetaBuilder.setMeta(X_EXAMPLE_B64_CONVERTER_WAS_ENCODED, Boolean.TRUE.toString());
    }


    /**
     * Reads stored data, so decodes a base64 stream if the metadata indicates it has been encoded
     *
     * @param path
     * @param resourceMetaBuilder
     * @param hasResourceStream
     *
     * @return
     */
    @Override
    public HasInputStream readResource(Path path, ResourceMetaBuilder resourceMetaBuilder, final HasInputStream
            hasResourceStream) {
        if (wasEncoded(resourceMetaBuilder)) {
            return decode(hasResourceStream);
        }
        //return null to indicate no change was performed on the data
        return null;
    }

    @Override
    public HasInputStream createResource(Path path, ResourceMetaBuilder resourceMetaBuilder,
            HasInputStream hasResourceStream) {
        addMetadataWasEncoded(resourceMetaBuilder);
        return encode(hasResourceStream);
    }

    @Override
    public HasInputStream updateResource(Path path, ResourceMetaBuilder resourceMetaBuilder,
            HasInputStream hasResourceStream) {
        addMetadataWasEncoded(resourceMetaBuilder);
        return encode(hasResourceStream);
    }
}

@@ -313,32 +313,91 @@
 e64InputStream;%0A
+import org.apache.commons.codec.binary.Base64OutputStream;%0A
 import org.runde
@@ -463,16 +463,58 @@
 pi.Path;
+%0Aimport org.rundeck.storage.data.DataUtil;
 %0A%0Aimport
@@ -562,16 +562,45 @@
 tStream;
+%0Aimport java.io.OutputStream;
 %0A%0A/**%0A *
@@ -1707,32 +1707,449 @@
 );%0A            %7D
+%0A%0A            @Override%0A            public long writeContent(OutputStream outputStream) throws IOException %7B%0A                Base64OutputStream codec = new Base64OutputStream(outputStream, doEncode);%0A                try %7B%0A                    return hasResourceStream.writeContent(codec);%0A                %7D finally %7B%0A                    codec.flush();%0A                    codec.close();%0A                %7D%0A            %7D
 %0A        %7D;%0A    

Update example with updated interfaces