Skip to content

Commit

Permalink
Make grant statement optional (#3054)
Browse files Browse the repository at this point in the history
  • Loading branch information
Yasirmod17 authored Aug 29, 2024
1 parent 13279f6 commit 23c7195
Show file tree
Hide file tree
Showing 6 changed files with 347 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ public MutableList<? extends FunctionActivatorError> validate(Identity identity,
{
MutableList<FunctionActivatorError> errors = Lists.mutable.empty();
SnowflakeAppContent content = (SnowflakeAppContent)artifact.content;
if (content.applicationName.trim().equals(""))
{
errors.add(new SnowflakeAppError("Application name cannot be empty"));
}
if (!content.sqlExpressions.isEmpty())
{
int size = content.sqlExpressions.select(e -> !e.toLowerCase().endsWith("to role public;")).size();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,10 @@ public MutableList<String> generateStatements(String catalogName, SnowflakeAppCo
else
{
statements.add(String.format(content.createStatement, catalogName));
statements.add(String.format(content.grantStatement, catalogName));
if (content.grantStatement != null)
{
statements.add(String.format(content.grantStatement, catalogName));
}
}
return statements;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,18 @@
<!-- Pure -->

<!-- ENGINE -->
<!-- <dependency>-->
<!-- <groupId>org.finos.legend.engine</groupId>-->
<!-- <artifactId>legend-engine-language-pure-grammar</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.finos.legend.engine</groupId>
<artifactId>legend-engine-language-pure-grammar</artifactId>
</dependency>
<dependency>
<groupId>org.finos.legend.engine</groupId>
<artifactId>legend-engine-language-pure-compiler</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.finos.legend.engine</groupId>-->
<!-- <artifactId>legend-engine-language-pure-modelManager</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.finos.legend.engine</groupId>
<artifactId>legend-engine-identity-core</artifactId>
</dependency>
<dependency>
<groupId>org.finos.legend.engine</groupId>
<artifactId>legend-engine-pure-code-compiled-core</artifactId>
Expand Down Expand Up @@ -137,11 +137,6 @@
<artifactId>legend-engine-xt-relationalStore-snowflake-pure</artifactId>
</dependency>
<!-- ENGINE -->

<!-- <dependency>-->
<!-- <groupId>javax.ws.rs</groupId>-->
<!-- <artifactId>javax.ws.rs-api</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
Expand Down Expand Up @@ -172,6 +167,11 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.finos.legend.engine</groupId>
<artifactId>legend-engine-xt-relationalStore-snowflake-grammar</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.finos.legend.engine</groupId>
<artifactId>legend-engine-xt-relationalStore-javaPlatformBinding-pure</artifactId>
Expand All @@ -196,6 +196,34 @@
<groupId>org.finos.legend.engine</groupId>
<artifactId>legend-engine-language-pure-dsl-generation</artifactId>
</dependency>

<dependency>
<groupId>org.finos.legend.engine</groupId>
<artifactId>legend-engine-xt-json-pure</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.finos.legend.engine</groupId>
<artifactId>legend-engine-xt-json-model</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.finos.legend.engine</groupId>
<artifactId>legend-engine-xt-json-javaPlatformBinding-pure</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.finos.legend.engine</groupId>
<artifactId>legend-engine-pure-runtime-java-extension-compiled-functions-pureExtensions</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.finos.legend.engine</groupId>
<artifactId>legend-engine-xt-javaPlatformBinding-pure</artifactId>
<scope>test</scope>
</dependency>

<!-- TEST -->
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright 2023 Goldman Sachs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.finos.legend.engine.language.snowflakeApp.generator.test;

import org.eclipse.collections.api.RichIterable;
import org.eclipse.collections.api.block.function.Function;
import org.finos.legend.engine.language.pure.compiler.Compiler;
import org.finos.legend.engine.language.pure.compiler.toPureGraph.PureModel;
import org.finos.legend.engine.language.pure.grammar.from.PureGrammarParser;
import org.finos.legend.engine.language.snowflakeApp.generator.SnowflakeAppGenerator;
import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContextData;
import org.finos.legend.engine.protocol.snowflakeApp.deployment.SnowflakeAppArtifact;
import org.finos.legend.engine.protocol.snowflakeApp.deployment.SnowflakeAppContent;
import org.finos.legend.engine.pure.code.core.PureCoreExtensionLoader;
import org.finos.legend.engine.shared.core.identity.Identity;
import org.finos.legend.pure.generated.Root_meta_external_function_activator_snowflakeApp_SnowflakeApp;
import org.finos.legend.pure.generated.Root_meta_pure_extension_Extension;
import org.junit.Assert;
import org.junit.Test;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Objects;
import java.util.stream.Collectors;

import static org.finos.legend.pure.generated.platform_pure_essential_meta_graph_pathToElement.Root_meta_pure_functions_meta_pathToElement_String_1__PackageableElement_1_;

public class TestSnowflakeAppGenerator
{
private final PureModelContextData contextData;
private final PureModel pureModel;
private final Function<PureModel, RichIterable<? extends Root_meta_pure_extension_Extension>> routerExtensions = (PureModel pureModel) -> PureCoreExtensionLoader.extensions().flatCollect(e -> e.extraPureCoreExtensions(pureModel.getExecutionSupport()));

public TestSnowflakeAppGenerator()
{
this.contextData = PureGrammarParser.newInstance().parseModel(readModelContentFromResource(this.modelResourcePath()));
this.pureModel = Compiler.compile(contextData, null, Identity.getAnonymousIdentity().getName());
}

public String modelResourcePath()
{
return "/org/finos/legend/engine/language/snowflakeApp/generator/snowflakeAppTestModels.pure";
}

private String readModelContentFromResource(String resourcePath)
{
try (BufferedReader buffer = new BufferedReader(new InputStreamReader(Objects.requireNonNull(TestSnowflakeAppGenerator.class.getResourceAsStream(resourcePath)))))
{
return buffer.lines().collect(Collectors.joining("\n"));
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}

private SnowflakeAppArtifact generateForActivator(String activatorPath, PureModel pureModel)
{
Root_meta_external_function_activator_snowflakeApp_SnowflakeApp app = (Root_meta_external_function_activator_snowflakeApp_SnowflakeApp) Root_meta_pure_functions_meta_pathToElement_String_1__PackageableElement_1_(activatorPath, pureModel.getExecutionSupport());
return SnowflakeAppGenerator.generateArtifact(pureModel, app, this.contextData, routerExtensions);
}

@Test
public void testNoParamActivator()
{
SnowflakeAppArtifact artifact = generateForActivator("demo::activators::snowflakeApp::App1", this.pureModel);
String expected = "CREATE OR REPLACE SECURE FUNCTION %S.LEGEND_NATIVE_APPS.APP1_REVISED() RETURNS TABLE (\"APP NAME\" VARCHAR,\"QUERY\" VARCHAR,\"OWNER\" VARCHAR,\"VERSION\" VARCHAR,\"DOC\" VARCHAR) LANGUAGE SQL AS $$ select \"root\".APP_NAME as \"App Name\", \"root\".SQL_FRAGMENT as \"Query\", \"root\".OWNER as \"Owner\", \"root\".VERSION_NUMBER as \"Version\", \"root\".DESCRIPTION as \"Doc\" from LEGEND_GOVERNANCE.BUSINESS_OBJECTS as \"root\" $$;";
Assert.assertEquals(expected, ((SnowflakeAppContent)artifact.content).createStatement);
Assert.assertNull(((SnowflakeAppContent) artifact.content).grantStatement);
}

@Test
public void testParamActivator()
{
SnowflakeAppArtifact artifact = generateForActivator("demo::activators::snowflakeApp::UDTFWithParam", this.pureModel);
String expected = "CREATE OR REPLACE SECURE FUNCTION %S.LEGEND_NATIVE_APPS.UDTFWITHPARAMETER(\"nameLength\" INTEGER,\"nameStart\" VARCHAR) RETURNS TABLE (\"APP NAME\" VARCHAR,\"QUERY\" VARCHAR,\"OWNER\" VARCHAR,\"VERSION\" VARCHAR,\"DOC\" VARCHAR) LANGUAGE SQL AS $$ select \"root\".APP_NAME as \"App Name\", \"root\".SQL_FRAGMENT as \"Query\", \"root\".OWNER as \"Owner\", \"root\".VERSION_NUMBER as \"Version\", \"root\".DESCRIPTION as \"Doc\" from LEGEND_GOVERNANCE.BUSINESS_OBJECTS as \"root\" where (length(\"root\".APP_NAME) > nameLength and startswith(\"root\".APP_NAME,nameStart)) $$;";
Assert.assertEquals(expected, ((SnowflakeAppContent)artifact.content).createStatement);
Assert.assertNull(((SnowflakeAppContent) artifact.content).grantStatement);
}

@Test
public void testGrantGenerated()
{
SnowflakeAppArtifact artifact = generateForActivator("demo::activators::snowflakeApp::App2", this.pureModel);
String expected = "CREATE OR REPLACE SECURE FUNCTION %S.LEGEND_NATIVE_APPS.APP1_REVISED() RETURNS TABLE (\"APP NAME\" VARCHAR,\"QUERY\" VARCHAR,\"OWNER\" VARCHAR,\"VERSION\" VARCHAR,\"DOC\" VARCHAR) LANGUAGE SQL AS $$ select \"root\".APP_NAME as \"App Name\", \"root\".SQL_FRAGMENT as \"Query\", \"root\".OWNER as \"Owner\", \"root\".VERSION_NUMBER as \"Version\", \"root\".DESCRIPTION as \"Doc\" from LEGEND_GOVERNANCE.BUSINESS_OBJECTS as \"root\" $$;";
String expectedGrant = "GRANT USAGE ON FUNCTION %S.LEGEND_NATIVE_APPS.APP1_REVISED() to role PUBLIC;";
Assert.assertEquals(expected, ((SnowflakeAppContent)artifact.content).createStatement);
Assert.assertEquals(expectedGrant, ((SnowflakeAppContent) artifact.content).grantStatement);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
###Relational

Database demo::stores::DemoDb
(
Schema LEGEND_GOVERNANCE
(
Table BUSINESS_OBJECTS
(
BUSINESS_OBJECT_UDTF_NAME VARCHAR(16777216),
APP_NAME VARCHAR(16777216) PRIMARY KEY,
ALLOY_SERVICE_NAME VARCHAR(16777216),
VERSION_NUMBER VARCHAR(16777216),
OWNER VARCHAR(16777216),
CREATE_DATETIME TIMESTAMP,
MODIFIED_DATETIME TIMESTAMP,
DESCRIPTION VARCHAR(16777216),
TO_BE_UPDATED VARCHAR(16777216),
SQL_FRAGMENT VARCHAR(16777216)
)

)
)


###Snowflake
SnowflakeApp demo::activators::snowflakeApp::App1
{
applicationName : 'App1_revised';
function : demo::functions::NativeApp_QueryFunction():TabularDataSet[1];
ownership : Deployment { identifier: 'id1'};
description : 'test App';
activationConfiguration : demo::connections::DeploymentConnection;
}

SnowflakeApp demo::activators::snowflakeApp::App2
{
applicationName : 'App1_revised';
function : demo::functions::NativeApp_QueryFunction():TabularDataSet[1];
ownership : Deployment { identifier: 'id1'};
description : 'test App';
usageRole: 'PUBLIC';
activationConfiguration : demo::connections::DeploymentConnection;
}

SnowflakeApp demo::activators::snowflakeApp::UDTFWithParam
{
applicationName : 'UDTFWithParameter';
function : demo::functions::NativeApp_QueryFunction(Integer[1],String[1]):TabularDataSet[1];
ownership : Deployment { identifier: 'id1'};
description : 'UDTFWithParameter';
activationConfiguration : demo::connections::DeploymentConnection;
}



###Pure

Class demo::models::NativeApp
{
AppName: String[1];
Doc: String[1];
Query: String[1];
Owner: String[1];
Version: String[1];
}


function demo::functions::NativeApp_QueryFunction(): meta::pure::tds::TabularDataSet[1]
{
demo::models::NativeApp.all()->project(
[
x|$x.AppName,
x|$x.Query,
x|$x.Owner,
x|$x.Version,
x|$x.Doc
],
[
'App Name',
'Query',
'Owner',
'Version',
'Doc'
]
)->from(
demo::mappings::DemoMapping,
demo::runtimes::DemoRuntime
)
}

function demo::functions::NativeApp_QueryFunction(nameLength: Integer[1], nameStart: String[1]): meta::pure::tds::TabularDataSet[1]
{
demo::models::NativeApp.all()->filter(
x|($x.AppName->length() >
$nameLength) &&
$x.AppName->startsWith(
$nameStart
)
)->project(
[
x|$x.AppName,
x|$x.Query,
x|$x.Owner,
x|$x.Version,
x|$x.Doc
],
[
'App Name',
'Query',
'Owner',
'Version',
'Doc'
]
)->from(
demo::mappings::DemoMapping,
demo::runtimes::DemoRuntime
)
}



###Mapping
Mapping demo::mappings::DemoMapping
(
*demo::models::NativeApp: Relational
{
~primaryKey
(
[demo::stores::DemoDb]LEGEND_GOVERNANCE.BUSINESS_OBJECTS.APP_NAME
)
~mainTable [demo::stores::DemoDb]LEGEND_GOVERNANCE.BUSINESS_OBJECTS
AppName: [demo::stores::DemoDb]LEGEND_GOVERNANCE.BUSINESS_OBJECTS.APP_NAME,
Doc: [demo::stores::DemoDb]LEGEND_GOVERNANCE.BUSINESS_OBJECTS.DESCRIPTION,
Query: [demo::stores::DemoDb]LEGEND_GOVERNANCE.BUSINESS_OBJECTS.SQL_FRAGMENT,
Owner: [demo::stores::DemoDb]LEGEND_GOVERNANCE.BUSINESS_OBJECTS.OWNER,
Version: [demo::stores::DemoDb]LEGEND_GOVERNANCE.BUSINESS_OBJECTS.VERSION_NUMBER
}
)


###Connection
RelationalDatabaseConnection demo::connections::DemoSnowflakeConnection
{
store: demo::stores::DemoDb;
type: Snowflake;
specification: Snowflake
{
name: 'dbName';
account: 'account';
warehouse: 'warehouse';
region: 'region';
};
auth: DefaultH2;
}

RelationalDatabaseConnection demo::connections::DeploymentConnection
{
store: demo::stores::DemoDb;
type: Snowflake;
specification: Snowflake
{
name: 'dbName';
account: 'account';
warehouse: 'warehouse';
region: 'region';
};
auth: DefaultH2;
}


###Runtime
Runtime demo::runtimes::DemoRuntime
{
mappings:
[
demo::mappings::DemoMapping
];
connections:
[
demo::stores::DemoDb:
[
connection_1: demo::connections::DemoSnowflakeConnection
]
];
}
Loading

0 comments on commit 23c7195

Please sign in to comment.