io.crate.operation.language.JavascriptUserDefinedFunctionTest.java Source code

Java tutorial

Introduction

Here is the source code for io.crate.operation.language.JavascriptUserDefinedFunctionTest.java

Source

/*
 * This file is part of a module with proprietary Enterprise Features.
 *
 * Licensed to Crate.io Inc. ("Crate.io") under one or more contributor
 * license agreements.  See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 *
 * Unauthorized copying of this file, via any medium is strictly prohibited.
 *
 * To use this file, Crate.io must have given you permission to enable and
 * use such Enterprise Features and you must have a valid Enterprise or
 * Subscription Agreement with Crate.io.  If you enable or use the Enterprise
 * Features, you represent and warrant that you have a valid Enterprise or
 * Subscription Agreement with Crate.io.  Your use of the Enterprise Features
 * if governed by the terms and conditions of your Enterprise or Subscription
 * Agreement with Crate.io.
 */

package io.crate.operation.language;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import io.crate.analyze.FunctionArgumentDefinition;
import io.crate.analyze.symbol.Literal;
import io.crate.metadata.FunctionIdent;
import io.crate.metadata.FunctionImplementation;
import io.crate.metadata.Schemas;
import io.crate.operation.scalar.AbstractScalarFunctionsTest;
import io.crate.operation.udf.UserDefinedFunctionMetaData;
import io.crate.operation.udf.UserDefinedFunctionService;
import io.crate.types.ArrayType;
import io.crate.types.DataType;
import io.crate.types.DataTypes;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.cluster.service.ClusterService;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import javax.script.ScriptException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static io.crate.testing.SymbolMatchers.isLiteral;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.startsWith;
import static org.mockito.Mockito.mock;

public class JavascriptUserDefinedFunctionTest extends AbstractScalarFunctionsTest {

    @Rule
    public final ExpectedException exception = ExpectedException.none();

    private static final String JS = "javascript";
    private UserDefinedFunctionService udfService;

    @Override
    @Before
    public void setUp() throws Exception {
        super.setUp();
        udfService = new UserDefinedFunctionService(mock(ClusterService.class));
        udfService.registerLanguage(new JavaScriptLanguage(udfService));
    }

    private Map<FunctionIdent, FunctionImplementation> functionImplementations = new HashMap<>();

    private void registerUserDefinedFunction(String name, DataType returnType, List<DataType> types,
            String definition) throws ScriptException {
        UserDefinedFunctionMetaData udfMeta = new UserDefinedFunctionMetaData(Schemas.DEFAULT_SCHEMA_NAME, name,
                types.stream().map(FunctionArgumentDefinition::of).collect(Collectors.toList()), returnType, JS,
                definition);

        String validation = udfService.getLanguage(JS).validate(udfMeta);
        if (validation == null) {
            functionImplementations.put(new FunctionIdent(Schemas.DEFAULT_SCHEMA_NAME, name, types),
                    udfService.getLanguage(JS).createFunctionImplementation(udfMeta));
            functions.registerUdfResolversForSchema(Schemas.DEFAULT_SCHEMA_NAME, functionImplementations);
        } else {
            throw new ScriptException(validation);
        }
    }

    @After
    public void after() {
        functionImplementations.clear();
    }

    @Test
    public void testObjectReturnType() throws Exception {
        registerUserDefinedFunction("f", DataTypes.OBJECT, ImmutableList.of(),
                "function f() { return JSON.parse('{\"foo\": \"bar\"}'); }");
        assertEvaluate("f()", ImmutableMap.of("foo", "bar"));
    }

    @Test
    public void testInvalidJavascript() throws ScriptException {
        UserDefinedFunctionMetaData udfMeta = new UserDefinedFunctionMetaData(Schemas.DEFAULT_SCHEMA_NAME, "f",
                Collections.singletonList(FunctionArgumentDefinition.of(DataTypes.DOUBLE)), DataTypes.DOUBLE_ARRAY,
                JS, "function f(a) { return a[0]1*#?; }");

        String validation = udfService.getLanguage(JS).validate(udfMeta);
        assertThat(validation, startsWith("Invalid JavaScript in function 'doc.f(double)': <eval>:1:27"));
    }

    @Test
    public void testValidJavascript() throws Exception {
        UserDefinedFunctionMetaData udfMeta = new UserDefinedFunctionMetaData(Schemas.DEFAULT_SCHEMA_NAME, "f",
                Collections.singletonList(FunctionArgumentDefinition.of(DataTypes.DOUBLE_ARRAY)), DataTypes.DOUBLE,
                JS, "function f(a) { return a[0]; }");

        String validation = udfService.getLanguage(JS).validate(udfMeta);
        assertNull(validation);
    }

    @Test
    public void testArrayReturnType() throws Exception {
        registerUserDefinedFunction("f", DataTypes.DOUBLE_ARRAY, ImmutableList.of(),
                "function f() { return [1, 2]; }");
        assertEvaluate("f()", new double[] { 1.0, 2.0 });
    }

    @Test
    public void testTimestampReturnType() throws Exception {
        registerUserDefinedFunction("f", DataTypes.TIMESTAMP, ImmutableList.of(),
                "function f() { return \"1990-01-01T00:00:00\"; }");
        assertEvaluate("f()", 631152000000L);
    }

    @Test
    public void testIpReturnType() throws Exception {
        registerUserDefinedFunction("f", DataTypes.IP, ImmutableList.of(),
                "function f() { return \"127.0.0.1\"; }");
        assertEvaluate("f()", DataTypes.IP.value("127.0.0.1"));
    }

    @Test
    public void testPrimitiveReturnType() throws Exception {
        registerUserDefinedFunction("f", DataTypes.INTEGER, ImmutableList.of(), "function f() { return 10; }");
        assertEvaluate("f()", 10);
    }

    @Test
    public void testObjectReturnTypeAndInputArguments() throws Exception {
        registerUserDefinedFunction("f", DataTypes.FLOAT, ImmutableList.of(DataTypes.DOUBLE, DataTypes.SHORT),
                "function f(x, y) { return x + y; }");
        assertEvaluate("f(double_val, short_val)", 3.0f, Literal.of(1), Literal.of(2));
    }

    @Test
    public void testPrimitiveReturnTypeAndInputArguments() throws Exception {
        registerUserDefinedFunction("f", DataTypes.FLOAT, ImmutableList.of(DataTypes.DOUBLE, DataTypes.SHORT),
                "function f(x, y) { return x + y; }");
        assertEvaluate("f(double_val, short_val)", 3.0f, Literal.of(1), Literal.of(2));
    }

    @Test
    public void testGeoTypeReturnTypeWithDoubleArray() throws Exception {
        registerUserDefinedFunction("f", DataTypes.GEO_POINT, ImmutableList.of(),
                "function f() { return [1, 1]; }");
        assertEvaluate("f()", new double[] { 1.0, 1.0 });
    }

    @Test
    public void testGeoTypeReturnTypeWithWKT() throws Exception {
        registerUserDefinedFunction("f", DataTypes.GEO_POINT, ImmutableList.of(),
                "function f() { return \"POINT (1.0 2.0)\"; }");
        assertEvaluate("f()", new double[] { 1.0, 2.0 });
    }

    @Test
    public void testOverloadingUserDefinedFunctions() throws Exception {
        registerUserDefinedFunction("f", DataTypes.LONG, ImmutableList.of(), "function f() { return 1; }");
        registerUserDefinedFunction("f", DataTypes.LONG, ImmutableList.of(DataTypes.LONG),
                "function f(x) { return x; }");
        registerUserDefinedFunction("f", DataTypes.LONG, ImmutableList.of(DataTypes.LONG, DataTypes.INTEGER),
                "function f(x, y) { return x + y; }");
        assertEvaluate("f()", 1L);
        assertEvaluate("f(x)", 2L, Literal.of(2));
        assertEvaluate("f(x, a)", 3L, Literal.of(2), Literal.of(1));
    }

    @Test
    public void testFunctionWrongNameInFunctionBody() throws Exception {
        exception.expect(io.crate.exceptions.ScriptException.class);
        exception.expectMessage("The name of the function signature doesn't match");
        registerUserDefinedFunction("test", DataTypes.LONG, ImmutableList.of(), "function f() { return 1; }");
        assertEvaluate("test()", 1L);
    }

    @Test
    public void testNormalizeOnObjectInput() throws Exception {
        registerUserDefinedFunction("f", DataTypes.OBJECT, ImmutableList.of(DataTypes.OBJECT),
                "function f(x) { return x; }");
        assertNormalize("f({})", isLiteral(new HashMap<>()));
    }

    @Test
    public void testNormalizeOnArrayInput() throws Exception {
        registerUserDefinedFunction("f", DataTypes.LONG, ImmutableList.of(DataTypes.DOUBLE_ARRAY),
                "function f(x) { return x[1]; }");
        assertNormalize("f([1.0, 2.0])", isLiteral(2L));
    }

    @Test
    public void testNormalizeOnStringInputs() throws Exception {
        registerUserDefinedFunction("f", DataTypes.STRING, ImmutableList.of(DataTypes.STRING),
                "function f(x) { return x; }");
        assertNormalize("f('bar')", isLiteral("bar"));
    }

    @Test
    public void testAccessJavaClasses() throws Exception {
        exception.expect(io.crate.exceptions.ScriptException.class);
        exception.expectMessage(containsString("has no such function \"type\""));
        registerUserDefinedFunction("f", DataTypes.LONG, ImmutableList.of(DataTypes.LONG),
                "function f(x) { var File = Java.type(\"java.io.File\"); return x; }");
        assertEvaluate("f(x)", 1L, Literal.of(1L));
    }

    @Test
    public void testEvaluateBytesRefConvertedToString() throws Exception {
        registerUserDefinedFunction("f", DataTypes.STRING, ImmutableList.of(DataTypes.STRING),
                "function f(name) { return 'foo' + name; }");
        assertEvaluate("f(name)", "foobar", Literal.of("bar"));
    }

    @Test
    public void testJavaScriptFunctionReturnsUndefined() throws Exception {
        registerUserDefinedFunction("f", DataTypes.STRING, ImmutableList.of(DataTypes.STRING),
                "function f(name) { }");
        assertEvaluate("f(name)", null, Literal.of("bar"));
    }

    @Test
    public void testJavaScriptFunctionReturnsNull() throws Exception {
        registerUserDefinedFunction("f", DataTypes.STRING, ImmutableList.of(), "function f() { return null; }");
        assertEvaluate("f()", null);
    }

    @Test
    public void testEvaluateBytesRefInObjectIsConvertedToString() throws Exception {
        registerUserDefinedFunction("f", DataTypes.OBJECT, ImmutableList.of(DataTypes.OBJECT),
                "function f(o) { return {'key1' : o['inner']['key1'][0], 'key2': o['inner']['key2']}; }");
        // the map will be modified
        Map<String, Object> inner = new HashMap<>();
        inner.put("key1", new Object[] { new BytesRef("bar") });
        inner.put("key2", new BytesRef("foo"));
        assertEvaluate("f(obj)", ImmutableMap.of("key1", "bar", "key2", "foo"),
                Literal.of(ImmutableMap.of("inner", inner)));
    }

    @Test
    public void testEvaluateBytesRefInArrayIsConvertedToString() throws Exception {
        registerUserDefinedFunction("f", DataTypes.STRING,
                ImmutableList.of(new ArrayType(new ArrayType(DataTypes.STRING))),
                "function f(arr) { return arr[0][0]; }");
        assertEvaluate("f(array_string_array)", "foo",
                Literal.of(new Object[][] { new Object[] { new BytesRef("foo") } },
                        new ArrayType(new ArrayType(DataTypes.STRING))));
    }
}