Commit e2f14301 by baban

Merge branch 'master' of https://github.com/hortonworks/metadata

parents 7053d0e7 3e9bb4c2
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
# Maven
target
# IntelliJ
*.iml
*.ipr
*.iws
.idea
# Eclipse
.cache
.classpath
.project
.settings
.externalToolBuilders
maven-eclipse.xml
#ActiveMQ
activemq-data
build
#log files
logs
*.log
\ No newline at end of file
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
A. Building & Installing Metadata
=================================
0. Prerequisites
------------------
You would need the following installed:
* JDK 1.7
* Maven 3.x
1. Building Metadata
--------------------
Building metadata from the source repository
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* git clone git@github.com:hortonworks/metadata.git metadata
* cd metadata
* export MAVEN_OPTS="-Xmx1024m -XX:MaxPermSize=256m" && mvn clean install
2. Deploying Metadata
---------------------
Once the build successfully completes, artifacts can be packaged for deployment.
* mvn clean assembly:assembly -DskipTests -DskipITs
Tar can be found in {project dir}/target/apache-metadata-${project.version}-bin.tar.gz
Tar is structured as follows
|- bin
|- metadata-start.sh
|- metadata-stop.sh
|- conf
|- application.properties
|- graph.properties
|- log4j.xml
|- docs
|- server
|- webapp
|- metadata.war
|- README
|- NOTICE.txt
|- LICENSE.txt
|- DISCLAIMER.txt
|- CHANGES.txt
3. Installing & running Metadata
--------------------------------
a. Installing Metadata
~~~~~~~~~~~~~~~~~~~~~~
* tar -xzvf apache-metadata-${project.version}-bin.tar.gz
* cd metadata-${project.version}
b. Starting Metadata Server
~~~~~~~~~~~~~~~~~~~~~~~~~
* bin/metadata-start.sh
c. Using Falcon
~~~~~~~~~~~~~~~
* Verify if the server is up and running
curl -v http://localhost:21000/api/metadata/admin/version
{"Version":"v0.1"}
* List the types in the repository
curl -v http://localhost:21000/api/metadata/types/list
{"list":["biginteger","short","byte","int","string","bigdecimal","boolean","date","double","long","float"],"requestId":"902580786@qtp-1479771328-0"}
* List the instances for a given type
curl -v http://localhost:21000/api/metadata/entities/list/hive_table
{"requestId":"788558007@qtp-44808654-5","list":["cb9b5513-c672-42cb-8477-b8f3e537a162","ec985719-a794-4c98-b98f-0509bd23aac0","48998f81-f1d3-45a2-989a-223af5c1ed6e","a54b386e-c759-4651-8779-a099294244c4"]}
curl -v http://localhost:21000/api/metadata/entities/list/hive_database
d. Stopping Falcon Server
~~~~~~~~~~~~~~~~~~~~~~~~~
* bin/metadata-stop.sh
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
Metadata and Governance Overview
This aims to provide a simple repository for storing entities and associated
relationships among other entities.
The goal is to capture lineage for both entities and its associated instances.
It also captures provenance, lineage, classification, etc. associated with each
of the entities in th metadata repository.
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one
~ or more contributor license agreements. See the NOTICE file
~ distributed with this work for additional information
~ regarding copyright ownership. The ASF licenses this file
~ to you 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.hadoop.metadata</groupId>
<artifactId>metadata-governance</artifactId>
<version>0.1-incubating-SNAPSHOT</version>
</parent>
<artifactId>metadata-common</artifactId>
<description>Apache Metadata Common Module</description>
<name>Apache Metadata Commons</name>
<packaging>jar</packaging>
<profiles>
<profile>
<id>hadoop-2</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<classifier>tests</classifier>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<classifier>tests</classifier>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
<dependencies>
<dependency>
<groupId>commons-el</groupId>
<artifactId>commons-el</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.jettison</groupId>
<artifactId>jettison</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
</dependency>
<dependency>
<groupId>net.sourceforge.findbugs</groupId>
<artifactId>annotations</artifactId>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
</dependency>
<dependency>
<groupId>com.tinkerpop.blueprints</groupId>
<artifactId>blueprints-core</artifactId>
</dependency>
<dependency>
<groupId>com.thinkaurelius.titan</groupId>
<artifactId>titan-core</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<excludes>
<exclude>**/log4j.xml</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.hadoop.metadata;
public class MetadataException extends Exception {
/**
* Constructs a new exception with the specified detail message. The
* cause is not initialized, and may subsequently be initialized by
* a call to {@link #initCause}.
*
* @param message the detail message. The detail message is saved for
* later retrieval by the {@link #getMessage()} method.
*/
public MetadataException(String message) {
super(message);
}
/**
* Constructs a new exception with the specified detail message and
* cause. <p>Note that the detail message associated with
* {@code cause} is <i>not</i> automatically incorporated in
* this exception's detail message.
*
* @param message the detail message (which is saved for later retrieval
* by the {@link #getMessage()} method).
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A <tt>null</tt> value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public MetadataException(String message, Throwable cause) {
super(message, cause);
}
/**
* Constructs a new exception with the specified cause and a detail
* message of <tt>(cause==null ? null : cause.toString())</tt> (which
* typically contains the class and detail message of <tt>cause</tt>).
* This constructor is useful for exceptions that are little more than
* wrappers for other throwables (for example, {@link
* java.security.PrivilegedActionException}).
*
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A <tt>null</tt> value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public MetadataException(Throwable cause) {
super(cause);
}
}
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.hadoop.metadata.bridge;
public abstract class AEntityBean {
}
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.hadoop.metadata.service;
import java.io.Closeable;
import java.io.IOException;
/**
* Service interface that's initialized at startup.
*/
//todo: needs to be removed, as it serves no purpose now with Guice
@Deprecated
public interface Service extends Closeable {
/**
* Starts the service. This method blocks until the service has completely started.
*
* @throws Exception
*/
void start() throws Exception;
/**
* Stops the service. This method blocks until the service has completely shut down.
*/
void stop();
/**
* A version of stop() that is designed to be usable in Java7 closure
* clauses.
* Implementation classes MUST relay this directly to {@link #stop()}
* @throws java.io.IOException never
* @throws RuntimeException on any failure during the stop operation
*/
void close() throws IOException;
}
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.hadoop.metadata.util;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Support function to parse and format date in xsd string.
*/
public final class DateTimeHelper {
private static final String DATE_PATTERN =
"(2\\d\\d\\d|19\\d\\d)-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])T([0-1][0-9]|2[0-3]):([0-5][0-9])Z";
private static final Pattern PATTERN = Pattern.compile(DATE_PATTERN);
public static final String ISO8601_FORMAT = "yyyy-MM-dd'T'HH:mm'Z'";
private DateTimeHelper() {}
public static String getTimeZoneId(TimeZone tz) {
return tz.getID();
}
public static DateFormat getDateFormat() {
DateFormat dateFormat = new SimpleDateFormat(ISO8601_FORMAT);
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
return dateFormat;
}
public static String formatDateUTC(Date date) {
return (date != null) ? getDateFormat().format(date) : null;
}
public static Date parseDateUTC(String dateStr) {
if (!validate(dateStr)) {
throw new IllegalArgumentException(dateStr + " is not a valid UTC string");
}
try {
return getDateFormat().parse(dateStr);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
public static String formatDateUTCToISO8601(final String dateString, final String dateStringFormat) {
try {
DateFormat dateFormat = new SimpleDateFormat(dateStringFormat.substring(0, dateString.length()));
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
return DateTimeHelper.formatDateUTC(dateFormat.parse(dateString));
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
/**
* Validate date format with regular expression.
*
* @param date date address for validation
* @return true valid date fromat, false invalid date format
*/
public static boolean validate(final String date) {
Matcher matcher = PATTERN.matcher(date);
if (matcher.matches()) {
matcher.reset();
if (matcher.find()) {
int year = Integer.parseInt(matcher.group(1));
String month = matcher.group(2);
String day = matcher.group(3);
if (day.equals("31")
&& (month.equals("4") || month.equals("6")
|| month.equals("9") || month.equals("11")
|| month.equals("04") || month.equals("06") || month.equals("09"))) {
return false; // only 1,3,5,7,8,10,12 has 31 days
} else if (month.equals("2") || month.equals("02")) {
// leap year
if (year % 4 == 0) {
return !(day.equals("30") || day.equals("31"));
} else {
return !(day.equals("29") || day.equals("30") || day.equals("31"));
}
} else {
return true;
}
} else {
return false;
}
} else {
return false;
}
}
}
\ No newline at end of file
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.hadoop.metadata.util;
import org.apache.hadoop.metadata.MetadataException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* Helper methods for class instantiation through reflection.
*/
public final class ReflectionUtils {
private ReflectionUtils() {}
@SuppressWarnings("unchecked")
public static <T> T getInstanceByClassName(String clazzName) throws MetadataException {
try {
Class<T> clazz = (Class<T>) ReflectionUtils.class.getClassLoader().loadClass(clazzName);
try {
return clazz.newInstance();
} catch (IllegalAccessException e) {
Method method = clazz.getMethod("get");
return (T) method.invoke(null);
}
} catch (Exception e) {
throw new MetadataException("Unable to get instance for " + clazzName, e);
}
}
/**
* Invokes constructor with one argument.
* @param clazzName - classname
* @param argCls - Class of the argument
* @param arg - constructor argument
* @param <T> - instance type
* @return Class instance
* @throws MetadataException
*/
@SuppressWarnings("unchecked")
public static <T> T getInstanceByClassName(String clazzName, Class<?> argCls,
Object arg) throws MetadataException {
try {
Class<T> clazz = (Class<T>) ReflectionUtils.class.getClassLoader().loadClass(clazzName);
Constructor<T> constructor = clazz.getConstructor(argCls);
return constructor.newInstance(arg);
} catch (Exception e) {
throw new MetadataException("Unable to get instance for " + clazzName, e);
}
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one
~ or more contributor license agreements. See the NOTICE file
~ distributed with this work for additional information
~ regarding copyright ownership. The ASF licenses this file
~ to you 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.
-->
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %-5p - [%t:%x] ~ %m (%c{1}:%L)%n"/>
</layout>
</appender>
<appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="${user.dir}/target/logs/application.log"/>
<param name="Append" value="true"/>
<param name="Threshold" value="debug"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %-5p - [%t:%x] ~ %m (%c{1}:%L)%n"/>
</layout>
</appender>
<appender name="AUDIT" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="${user.dir}/target/logs/audit.log"/>
<param name="Append" value="true"/>
<param name="Threshold" value="debug"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %x %m%n"/>
</layout>
</appender>
<appender name="METRIC" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="${user.dir}/target/logs/metric.log"/>
<param name="Append" value="true"/>
<param name="Threshold" value="debug"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %m%n"/>
</layout>
</appender>
<logger name="AUDIT" additivity="false">
<level value="debug"/>
<appender-ref ref="console"/>
</logger>
<logger name="METRIC" additivity="false">
<level value="debug"/>
<appender-ref ref="console"/>
</logger>
<logger name="org.apache.hadoop.metadata" additivity="false">
<level value="debug"/>
<appender-ref ref="FILE"/>
</logger>
<logger name="com.thinkaurelius.titan" additivity="false">
<level value="warn"/>
<appender-ref ref="FILE"/>
</logger>
<logger name="org.elasticsearch" additivity="false">
<level value="warn"/>
<appender-ref ref="FILE"/>
</logger>
<logger name="org.apache.lucene" additivity="false">
<level value="warn"/>
<appender-ref ref="FILE"/>
</logger>
<root>
<priority value="debug"/>
<appender-ref ref="console"/>
</root>
</log4j:configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one
~ or more contributor license agreements. See the NOTICE file
~ distributed with this work for additional information
~ regarding copyright ownership. The ASF licenses this file
~ to you 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.
-->
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %-5p - [%t:%x] ~ %m (%c{1}:%L)%n"/>
</layout>
</appender>
<appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="${user.dir}/target/logs/application.log"/>
<param name="Append" value="true"/>
<param name="Threshold" value="debug"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %-5p - [%t:%x] ~ %m (%c{1}:%L)%n"/>
</layout>
</appender>
<appender name="AUDIT" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="${user.dir}/target/logs/audit.log"/>
<param name="Append" value="true"/>
<param name="Threshold" value="debug"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %x %m%n"/>
</layout>
</appender>
<appender name="METRIC" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="${user.dir}/target/logs/metric.log"/>
<param name="Append" value="true"/>
<param name="Threshold" value="debug"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %m%n"/>
</layout>
</appender>
<logger name="AUDIT" additivity="false">
<level value="debug"/>
<appender-ref ref="console"/>
</logger>
<logger name="METRIC" additivity="false">
<level value="debug"/>
<appender-ref ref="console"/>
</logger>
<logger name="org.apache.hadoop.metadata" additivity="false">
<level value="debug"/>
<appender-ref ref="FILE"/>
</logger>
<logger name="com.thinkaurelius.titan" additivity="false">
<level value="warn"/>
<appender-ref ref="FILE"/>
</logger>
<logger name="org.elasticsearch" additivity="false">
<level value="warn"/>
<appender-ref ref="FILE"/>
</logger>
<logger name="org.apache.lucene" additivity="false">
<level value="warn"/>
<appender-ref ref="FILE"/>
</logger>
<root>
<priority value="debug"/>
<appender-ref ref="console"/>
</root>
</log4j:configuration>
{
"directory": "public/lib",
"storage": {
"packages": ".bower-cache",
"registry": ".bower-registry"
},
"tmp": ".bower-tmp"
}
.DS_Store
.bower-*/
.idea/
node_modules/
lib/
public/lib
public/dist
*.log
*.tgz
\ No newline at end of file
test/coverage/**
\ No newline at end of file
{
"browser": true, // Standard browser globals e.g. `window`, `document`.
"bitwise": false, // Prohibit bitwise operators (&, |, ^, etc.).
"camelcase": false, // Permit only camelcase for `var` and `object indexes`.
"curly": false, // Require {} for every new block or scope.
"devel": true, // Allow development statements e.g. `console.log();`.
"esnext": true, // Allow ES.next specific features such as `const` and `let`.
"eqeqeq": true, // Require triple equals i.e. `===`.
"immed": true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );`
"indent": false, // Specify indentation spacing
"latedef": true, // Prohibit variable use before definition.
"node": true, // Enable globals available when code is running inside of the NodeJS runtime environment.
"newcap": false, // Require capitalization of all constructor functions e.g. `new F()`.
"noarg": true, // Prohibit use of `arguments.caller` and `arguments.callee`.
"noempty": true, // Prohibit use of empty blocks.
"quotmark": false, // Define quotes to string values.
"regexp": true, // Prohibit `.` and `[^...]` in regular expressions.
"strict": true, // Require `use strict` pragma in every file.
"smarttabs": false, // Suppresses warnings about mixed tabs and spaces
"trailing": true, // Prohibit trailing whitespaces.
"undef": true, // Require all non-global variables be declared before they are used.
"unused": true, // Warn unused variables.
"globals": { // Globals variables.
"angular": true
},
"predef": [ // Extra globals.
"define",
"require",
"exports",
"module",
"spyOn",
"describe",
"xdescribe",
"before",
"beforeEach",
"after",
"afterEach",
"jasmine",
"it",
"xit",
"inject",
"expect",
"ngGridFlexibleHeightPlugin"
]
}
\ No newline at end of file
# DGC Metadata
## Instructions
### Prerequisite
1. Nodejs (http://nodejs.org/download/)
2. ```npm install -g grunt-cli```
### Setup:
```
git clone https://github.com/hortonworks/dgi-metadata-ui.git
cd dgi-metadata-ui
npm install
grunt server
```
Server will start at:
<http://localhost:3020/>
{
"name": "dgc-metadata",
"description": "DGC Metadata",
"version": "1.0.0-SNAPSHOT",
"devDependencies": {
"angular": "~1.2.15",
"angular-resource": "~1.2.15",
"angular-cookies": "~1.2.15",
"angular-route": "~1.2.15",
"angular-sanitize": "~1.2.15",
"bootstrap": "~3.1.1",
"angular-bootstrap": "~0.12.0",
"angular-ui-router": "~0.2.13",
"d3": "~3.5.3",
"d3-tip": "~0.6.6",
"lodash": "~3.0.0",
"angular-ui-utils": "~0.1.1",
"font-awesome": "~4.2.0",
"closure-compiler": "https://dl.google.com/closure-compiler/compiler-20140814.zip",
"ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.3/assets/ng-closure-runner.zip"
},
"resolutions": {
"d3": "~3.5.3"
}
}
'use strict';
var git = require('git-rev');
module.exports = function (grunt) {
var classPathSep = (process.platform === "win32") ? ';' : ':',
gitHash = '',
pkg = grunt.file.readJSON('package.json');
grunt.initConfig({
watch: {
options: {
livereload: 35730
},
js: {
files: ['public/**/*.js', '!public/lib/**', '!public/dist/**'],
tasks: ['shell']
},
html: {
files: ['public/**/*.html']
},
css: {
files: ['public/**/*.css']
}
},
jshint: {
all: {
src: ['gruntfile.js', 'package.json', 'server.js', 'server/**/*.js', 'public/**/*.js', '!public/lib/**', '!public/dist/**'],
options: {
jshintrc: true
}
}
},
nodemon: {
local: {
script: 'server.js',
options: {
ext: 'js,json',
ignore: ['public/**', 'node_modules/**'],
nodeArgs: ['--debug=6868']
}
},
prod: {
script: 'server.js',
options: {
ignore: ['.'],
env: {
NODE_ENV: 'production'
}
}
}
},
concurrent: {
tasks: ['nodemon:local', 'watch'],
options: {
logConcurrentOutput: true
}
},
jsbeautifier: {
'default': {
src: ['<%= jshint.all.src %>', 'bower.json'],
options: {
js: {
preserveNewlines: true,
maxPreserveNewlines: 2
}
}
},
'build': {
src: '<%= jsbeautifier.default.src %>',
options: {
mode: 'VERIFY_ONLY',
js: '<%= jsbeautifier.default.options.js%>'
}
}
},
bower: {
install: {
options: {
verbose: true
}
}
},
dist: 'public/dist/app.min.js',
modules: grunt.file.expand(
'public/js/app.js',
'public/js/routes.js',
'public/modules/**/*Module.js',
'public/modules/**/*.js',
'public/js/init.js'
).join(' '),
shell: {
min: {
command: 'java ' +
'-cp public/lib/closure-compiler/compiler.jar' + classPathSep +
'public/lib/ng-closure-runner/ngcompiler.jar ' +
'org.angularjs.closurerunner.NgClosureRunner ' +
'--compilation_level SIMPLE_OPTIMIZATIONS ' +
//'--formatting PRETTY_PRINT ' +
'--language_in ECMASCRIPT5_STRICT ' +
'--angular_pass ' +
'--manage_closure_dependencies ' +
'--js <%= modules %> ' +
'--js_output_file <%= dist %>'
}
},
devUpdate: {
main: {
options: {
updateType: 'force'
}
}
},
compress: {
release: {
options: {
archive: function () {
return [pkg.name, pkg.version, gitHash].join('_') + '.tgz';
}
},
src: ['node_modules/**', 'package.json', 'server.js', 'server/**', 'public/**', '!public/js/**', '!public/modules/**/*.js']
}
}
});
require('load-grunt-tasks')(grunt);
grunt.registerTask('default', ['devUpdate', 'bower', 'jshint', 'jsbeautifier:default']);
grunt.registerTask('server', ['bower', 'jshint', 'minify', 'concurrent']);
grunt.registerTask('server:prod', ['nodemon:prod']);
grunt.registerTask('server:prod', ['nodemon:prod']);
grunt.registerTask('minify', 'Minify the all js', function () {
var done = this.async();
grunt.file.mkdir('public/dist');
grunt.task.run(['shell:min']);
done();
});
grunt.registerTask('release', 'Create release package', function () {
var done = this.async();
git.short(function (str) {
gitHash = str;
grunt.task.run(['minify', 'compress:release']);
done();
});
});
};
{
"name": "dgc-metadata",
"description": "DGC Metadata",
"version": "1.0.0-SNAPSHOT",
"private": true,
"bin": "server.js",
"repository": {
"type": "git",
"url": "https://github.com/hortonworks/metadata"
},
"engines": {
"node": "0.10.x",
"npm": "1.3.x"
},
"keywords": [
"DGC",
"HortonWorks"
],
"scripts": {
"postinstall": "node node_modules/bower/bin/bower install"
},
"dependencies": {
"body-parser": "^1.2.0",
"bower": "~1.3.1",
"compression": "^1.0.2",
"consolidate": "~0.10.0",
"cookie-parser": "^1.0.1",
"cookies": "~0.4.0",
"express": "~4.2.0",
"express-load": "^1.1.14",
"forever": "~0.11.1",
"lodash": "~2.4.1",
"method-override": "^1.0.0",
"morgan": "^1.0.1",
"path-extra": "~0.1.1",
"proxit": "^0.6.0",
"q": "~1.0.1",
"rc": "~0.3.4",
"serve-favicon": "^2.0.0",
"static-favicon": "^2.0.0-alpha",
"superagent": "^0.20.0",
"swig": "~1.3.2",
"view-helpers": "~0.1.4"
},
"devDependencies": {
"git-rev": "^0.2.1",
"grunt": "~0.4.2",
"grunt-bower-task": "~0.4.0",
"grunt-cli": "~0.1.11",
"grunt-concurrent": "^1.0.0",
"grunt-contrib-compress": "^0.13.0",
"grunt-contrib-jshint": "^0.11.0",
"grunt-contrib-watch": "^0.6.0",
"grunt-dev-update": "^1.0.2",
"grunt-jsbeautifier": "^0.2.6",
"grunt-nodemon": "^0.4.0",
"grunt-shell": "^1.1.1",
"load-grunt-tasks": "^3.1.0"
}
}
div.separator {
position: relative;
font-size: 18px;
color: #aaa;
margin-top: 10px;
margin-bottom: 10px;
padding-top: 10px;
padding-bottom: 10px;
}
span.separator {
display: block;
position: absolute;
left: 50%;
top: -2px;
margin-left: -25px;
background-color: #fff;
width: 50px;
text-align: center;
}
hr.separator {
background-color: #cdcdcd;
height: 1px;
margin-top: 0px !important;
margin-bottom: 0px !important;
}
.pointer {
cursor: pointer;
}
.form-control {
border-color: #5cbb5a;
border-width: 2px;
}
.small-txt {
color: #999999;
padding-left: 14px;
}
/* Header background */
header.navbar-top {
background-color: #fafafa;
border-bottom: solid 4px #5cbb5a;
margin-bottom: 0px;
}
.search {
padding-top: 20px;
background-color: #eeeded;
padding-bottom: 10px;
border-bottom: solid 1px #d9d9d8;
}
header .container {
padding: 12px;
}
/* Footer */
footer.navbar-bottom {
background-color: #fafafa;
border-top: solid 4px #5cbb5a;
}
footer.navbar-bottom p {
color: #333333;
margin: 23px 10px 10px;
}
footer.navbar-bottom img {
padding-left: 5px;
margin-top: -21px;
}
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "\25BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
\ No newline at end of file
g circle {
cursor: pointer;
stroke: green;
stroke-width: 2px;
fill: url(#process-image);
}
g circle.empty {
fill: #90ef96;
}
.link {
fill: none;
stroke: green;
stroke-width: 2px;
}
g text {
pointer-events: none;
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
}
.d3-tip pre {
max-width: 400px;
}
div.lineage {
border-bottom: 2px solid #006600;
margin-bottom: 30px;
}
\ No newline at end of file
/* Sticky footer styles
-------------------------------------------------- */
html {
position: relative;
min-height: 100%;
}
body {
/* Margin bottom by footer height */
margin-bottom: 60px;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
/* Set the fixed height of the footer here */
height: 60px;
background-color: #f5f5f5;
}
\ No newline at end of file
'use strict';
angular.module('dgc', ['ngCookies',
'ngResource',
'ui.bootstrap',
'ui.router',
'dgc.system',
'dgc.home',
'dgc.search'
]);
angular.module('dgc.system', ['dgc.system.notification']);
angular.module('dgc').factory('lodash', ['$window',
function($window) {
return $window._;
}
]).factory('d3', ['$window',
function($window) {
return $window.d3;
}
]).factory('Global', ['$window',
function($window) {
return {
user: $window.user,
authenticated: !!$window.user,
renderErrors: $window.renderErrors
};
}
]).run(['$rootScope', 'Global', 'NotificationService', 'lodash', 'd3', function($rootScope, Global, NotificationService, lodash, d3) {
var errors = Global.renderErrors;
if (angular.isArray(errors) || angular.isObject(errors)) {
lodash.forEach(errors, function(err) {
err = angular.isObject(err) ? err : {
message: err
};
err.timeout = false;
NotificationService.error(err);
});
} else {
errors.timeout = false;
NotificationService.error(errors);
}
$rootScope.$on('$stateChangeStart', function() {
d3.selectAll('.d3-tip').remove();
});
}]);
'use strict';
angular.element(document).ready(function() {
/* Fixing facebook bug with redirect */
if (window.location.hash === '#_=_') window.location.hash = '#!';
//Then init the app
angular.bootstrap(document, ['dgc'], {
strictDi: true
});
});
'use strict';
//Setting up route
angular.module('dgc').config(['$locationProvider', '$urlRouterProvider',
function($locationProvider, $urlRouterProvider) {
$locationProvider.hashPrefix('!');
// For unmatched routes:
$urlRouterProvider.otherwise('/');
}
]);
'use strict';
angular.module('dgc.details').controller('DetailsController', ['$scope', '$stateParams', 'DetailsResource',
function($scope, $stateParams, DetailsResource) {
$scope.details = DetailsResource.get({
id: $stateParams.id
});
$scope.isString = angular.isString;
}
]);
'use strict';
angular.module('dgc.details', ['dgc.lineage']);
'use strict';
angular.module('dgc.details').factory('DetailsResource', ['$resource', function($resource) {
return $resource('/api/metadata/entities/definition/:id', {}, {
get: {
method: 'GET',
transformResponse: function(data) {
if (data) {
return angular.fromJson(data.definition);
}
},
responseType: 'json'
}
});
}]);
'use strict';
angular.module('dgc.details').config(['$stateProvider',
function($stateProvider) {
// states for my app
$stateProvider.state('details', {
url: '/details/:id',
templateUrl: '/modules/details/views/details.html'
});
}
]);
<h4>{{key}}:</h4>
<p>{{value | date:'medium'}}</p>
\ No newline at end of file
<div class="container details" data-ng-controller="DetailsController">
<div class="col-lg-12">
<h3>{{details.$id$.id}}</h3>
<div class="lineage" data-ng-include="'/modules/lineage/views/lineage.html'"></div>
<div class="wiki">
<section data-ng-repeat="(key,value) in details" data-ng-if="isString(value)" data-ng-include="'/modules/details/views/attribute.html'"></section>
</div>
</div>
</div>
\ No newline at end of file
'use strict';
angular.module('dgc.home').controller('HeaderController', ['$scope', function($scope) {
$scope.menu = [];
$scope.isCollapsed = true;
$scope.isLoggedIn = function() {
return true;
};
}]);
'use strict';
angular.module('dgc.home', ['dgc.home.routes']);
'use strict';
//Setting up route
angular.module('dgc.home.routes', []).config(['$stateProvider',
function($stateProvider) {
// states for my app
$stateProvider.state('home', {
url: '/',
templateUrl: '/modules/home/views/home.html'
});
}
]);
<div data-ng-controller="HeaderController">
<div class="navbar-header">
<button class="navbar-toggle" type="button" data-ng-click="isCollapsed = !isCollapsed">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a data-ui-sref="home" data-ui-sref-active="active"><img src="modules/home/img/logo-sm.png"/></a>
</div>
<nav class="collapse navbar-collapse" data-collapse="isCollapsed" data-role="navigation">
<ul class="navbar-nav nav" data-ng-if="isLoggedIn()">
<li data-ng-repeat="item in menu" data-ui-sref-active="active">
<a data-ui-sref="{{item.state}}">{{item.title}}</a>
</li>
</ul>
</nav>
</div>
\ No newline at end of file
<section class="text-center">
<img data-ui-sref="search" data-ui-sref="search" class="pointer" src="modules/home/img/splash.png"/>
</section>
\ No newline at end of file
'use strict';
angular.module('dgc.lineage').controller('LineageController', ['$element', '$scope', '$state', '$stateParams', 'lodash', 'LineageResource', 'd3',
function($element, $scope, $state, $stateParams, _, LineageResource, d3) {
$scope.lineageData = LineageResource.get({
id: $stateParams.id
}, function(data) {
var nodes = {};
function getNode(nodeId) {
if (!nodes[nodeId]) {
var node;
if (data.vertices[nodeId]) {
node = angular.copy(data.vertices[nodeId]);
node.__key = nodeId;
node.__name = node['hive_table.name'] || node.__key;
node.__tooltip = node['hive_table.description'] || node['HiveLineage.query'];
} else {
node = {};
node.__key = nodeId;
node.__tooltip = node.__name = nodeId + ', Node Missing';
}
nodes[nodeId] = node;
}
return nodes[nodeId];
}
var edges = [],
edgeTypes = [];
angular.forEach(data.edges, function(edge) {
/* Put the head (edge) inside tail (edge)
* Tail is parent
* Head is child
* */
var parentNode = getNode(edge.tail);
edge.source = parentNode;
edge.target = getNode(edge.head);
parentNode.__hasChild = true;
edge.__type = edge.label;
edgeTypes.push(edge.label);
edges.push(edge);
});
edgeTypes = _.uniq(edgeTypes);
render(nodes, edges, edgeTypes);
});
function render(nodes, links, linkTypes) {
// Use elliptical arc path segments to doubly-encode directionality.
function click(node) {
if (node.guid) {
$state.go('details', {
id: node.guid
}, {
location: 'replace'
});
}
}
function tick() {
path.attr("d", linkArc);
circle.attr("transform", transform);
text.attr("transform", transform);
}
function linkArc(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
}
function transform(d) {
return "translate(" + d.x + "," + d.y + ")";
}
var width = Math.max($element[0].offsetWidth, 960),
height = Math.max($element[0].offsetHeight, 350);
var force = d3.layout.force()
.nodes(d3.values(nodes))
.links(links)
.size([width, height])
.linkDistance(200)
.charge(-120)
.gravity(0.05)
.on("tick", tick)
.start();
var svg = d3.select($element[0]).select('svg')
.attr("width", width)
.attr("height", height);
/* Initialize tooltip */
var tooltip = d3.tip()
.attr('class', 'd3-tip')
.html(function(d) {
return '<pre class="alert alert-success">' + d.__tooltip + '</pre>';
});
/* Invoke the tip in the context of your visualization */
svg.call(tooltip);
// Per-type markers, as they don't inherit styles.
var defs = svg.append("defs");
var imageDim = 10;
defs.append('svg:pattern')
.attr('id', 'process-image')
.attr('patternUnits', 'userSpaceOnUse')
.attr('width', imageDim)
.attr('height', imageDim)
.append('svg:image')
.attr('xlink:href', '/img/process.png')
.attr('x', 0)
.attr('y', 0)
.attr('width', imageDim)
.attr('height', imageDim);
defs.selectAll("marker")
.data(linkTypes)
.enter().append("marker")
.attr("id", function(d) {
return d;
})
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,-5L10,0L0,5");
var path = svg.append("g").selectAll("path")
.data(force.links())
.enter().append("path")
.attr("class", function(d) {
return "link " + d.__type;
})
.attr("marker-end", function(d) {
return "url(#" + d.__type + ")";
});
var circle = svg.append("g").selectAll("circle")
.data(force.nodes())
.enter().append("circle")
.on('click', click)
.on('mouseover', tooltip.show)
.on('mouseout', tooltip.hide)
.attr('class', function(d) {
return d.__hasChild ? '' : 'empty';
})
.attr("r", function(d) {
return d.__hasChild ? 15 : 10;
})
.call(force.drag);
var text = svg.append("g").selectAll("text")
.data(force.nodes())
.enter().append("text")
.attr('dy', '2em')
.text(function(d) {
return d.__name;
});
}
}
]);
'use strict';
angular.module('dgc.lineage', []);
'use strict';
angular.module('dgc.lineage').factory('LineageResource', ['$resource', function($resource) {
return $resource('/api/metadata/discovery/search/relationships/:id', {
depth: 3,
edgesToFollow: 'HiveLineage.sourceTables.0,HiveLineage.sourceTables.1,HiveLineage.sourceTables.2,HiveLineage.tableName'
});
}]);
<div data-ng-controller="LineageController">
<svg></svg>
</div>
'use strict';
angular.module('dgc.system.notification').controller('NotificationController', ['$scope', 'NotificationService',
function($scope, NotificationService) {
$scope.getNotifications = NotificationService.getAll;
$scope.close = function(notification) {
NotificationService.close(notification);
};
}
]);
'use strict';
angular.module('dgc.system.notification', ['ui.router']).constant('ColorCoding', {
info: 'success',
error: 'danger'
}).run(['$rootScope', 'NotificationService', function($rootScope, NotificationService) {
$rootScope.$on('$locationChangeSuccess', function(evt, from, to) {
if (from !== to) {
NotificationService.reset();
}
});
}]);
'use strict';
angular.module('dgc.system.notification').service('NotificationService', ['$timeout', 'lodash', 'ColorCoding', function($timeout, _, colorCoding) {
var notifications = [],
service = {
timeout: 2000,
getAll: function() {
return notifications;
},
reset: function() {
notifications = [];
},
close: function(notification) {
_.remove(notifications, notification);
}
};
_.each(colorCoding, function(value, key) {
service[key] = function(message, timeout) {
var notification = message;
if (_.isString(message)) {
notification = {
message: message
};
}
notification.message = notification.msg || notification.message;
delete notification.msg;
notification.type = value;
notification.timeout = _.isUndefined(timeout) ? (_.isUndefined(notification.timeout) ? true : notification.timeout) : timeout;
notifications.push(notification);
if (notification.timeout) {
$timeout(function() {
service.close(notification);
}, angular.isNumber(notification.timeout) ? notification.timeout : service.timeout);
}
};
});
return service;
}]);
<div data-ng-controller="NotificationController">
<alert data-ng-repeat="notification in getNotifications()" data-type="{{notification.type}}" data-close="close(notification)">
{{notification.message}}
</alert>
</div>
\ No newline at end of file
'use strict';
angular.module('dgc.search').controller('SearchController', ['$scope', '$location', '$http', '$state', '$stateParams', 'SearchResource', 'NotificationService',
function($scope, $location, $http, $state, $stateParams, SearchResource, NotificationService) {
$scope.types = [];
$scope.results = [];
$scope.search = function(query) {
$scope.results = [];
NotificationService.reset();
SearchResource.search($location.search(query).search(), function(response) {
$scope.results = response;
if ($scope.results.length < 1) {
NotificationService.error('No Result found', false);
}
$state.go('search.results', {}, {
location: false
});
});
};
$scope.typeAvailable = function() {
return ['hive_table'].indexOf(this.result.type && this.result.type.toLowerCase()) > -1;
};
var urlParts = $location.url().split('?');
$scope.query = urlParts.length > 1 ? urlParts[1] : null;
if ($scope.query) {
$scope.search($scope.query);
}
}
]);
'use strict';
angular.module('dgc.search', ['dgc.details']);
'use strict';
angular.module('dgc.search').factory('SearchResource', ['$resource', function($resource) {
return $resource('/api/metadata/discovery/search/fulltext', {}, {
search: {
'method': 'GET',
'responseType': 'json',
'isArray': true,
'transformResponse': function(data) {
var results = [];
angular.forEach(data && data.vertices, function(val) {
results.push(val);
});
return results;
}
}
});
}]);
'use strict';
//Setting up route
angular.module('dgc.search').config(['$stateProvider',
function($stateProvider) {
// states for my app
$stateProvider.state('search', {
url: '/search',
templateUrl: '/modules/search/views/search.html'
}).state('search.results', {
url: '/?',
templateUrl: '/modules/search/views/searchResult.html'
});
}
]);
<div data-ng-controller="SearchController">
<div class="search">
<form name="form" novalidate class="container">
<div class="col-lg-7">
<div class="row input-group">
<input type="text" class="form-control" placeholder="Search" data-ng-model="query" data-typeahead="type for type in types" required/>
<span class="input-group-btn">
<button class="btn btn-success" type="submit" data-ng-disabled="form.$invalid" data-ng-click="search(query)">
<i class="glyphicon glyphicon-search white "></i>
</button>
</span>
</div>
<div class="row">
<small class="small-txt">Examples : property=HiveLineage.executionEngine&text=tez</small>
<br/>
<small class="small-txt">property=type&text=HiveLineage</small>
<br/>
<small class="small-txt">property=type&text=hive_table</small>
</div>
</div>
</form>
</div>
<div class="container">
<div class="row">
<div class="col-lg-11" data-ui-view=""></div>
</div>
</div>
</div>
<h4>{{results.length}} results matching your search query "{{query}}" were found</h4>
<ul class="list-unstyled">
<li ng-repeat="result in results">
<div data-ng-if="typeAvailable()" data-ng-include="'/modules/search/views/types/'+result.type.toLowerCase()+'.html'"></div>
<div data-ng-if="!typeAvailable()" data-ng-include="'/modules/search/views/types/guid.html'"></div>
</li>
</ul>
\ No newline at end of file
<a data-ui-sref="details({id:result.guid})">{{result.guid}}</a>
\ No newline at end of file
<a data-ui-sref="details({id:result.guid})">{{result.guid}}</a>
\ No newline at end of file
<a data-ui-sref="details({id:result.guid})">{{result["hive_table.name"]}}</a>
\ No newline at end of file
<!-- Angular JS -->
<script type="text/javascript" src="/lib/angular/angular.min.js"></script>
<script type="text/javascript" src="/lib/lodash/lodash.min.js"></script>
<script type="text/javascript" src="/lib/angular-cookies/angular-cookies.min.js"></script>
<script type="text/javascript" src="/lib/angular-resource/angular-resource.min.js"></script>
<script type="text/javascript" src="/lib/angular-ui-router/release/angular-ui-router.js"></script>
<!-- Angular UI -->
<script type="text/javascript" src="/lib/angular-bootstrap/ui-bootstrap.js"></script>
<script type="text/javascript" src="/lib/angular-bootstrap/ui-bootstrap-tpls.js"></script>
<script type="text/javascript" src="/lib/d3/d3.min.js"></script>
<script type="text/javascript" src="/lib/d3-tip/index.js"></script>
<script type="text/javascript" src="dist/app.min.js"></script>
{% if (process.env.NODE_ENV == 'local') %}
<!-- Livereload script rendered -->
<script type="text/javascript" src="http://localhost:35730/livereload.js"></script>
{% endif %}
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>{{app.title}}</title>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta name="keywords" content="artifact-graph, Cengage, registry-services">
<meta name="description" content="Cengage registry services">
<link href="/img/favicon.ico" rel="shortcut icon" type="image/x-icon">
<link rel="stylesheet" href="/lib/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="/lib/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="/css/sticky-footer-navbar.css">
<link rel="stylesheet" href="/css/common.css">
<link rel="stylesheet" href="/css/lineage.css">
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
{% block content %}
<script type="text/javascript">
window.user = {{JSON.stringify(user)}};
window.renderErrors = {{JSON.stringify(renderErrors)}};
</script>
{% endblock %}
</head>
<!doctype html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" itemscope="itemscope" itemtype="http://schema.org/Product">
{% include 'includes/head.html' %}
<body>
<header class="navbar navbar-static-top navbar-top" data-role="navigation">
<div class="container" data-ng-include="'/modules/home/views/header.html'"></div>
</header>
<div class="content">
<div data-ng-include="'/modules/notification/views/notifications.html'"></div>
<div data-ui-view></div>
</div>
<footer class="footer navbar-bottom">
<div class="container">
<p align="right">Powered by<img src="modules/home/img/logo-green.png"></p>
</div>
</footer>
{% include 'includes/foot.html' %}
</body>
</html>
\ No newline at end of file
#!/usr/bin/env node
'use strict';
/**
* Module dependencies.
*/
var express = require('express');
/**
* Main application entry file.
* Please note that the order of loading is important.
*/
// Initializing system variables
var config = require('./server/config/config'),
app = express();
// Express settings
require('./server/config/express')(app);
// Start the app by listening on <port>
var port = process.env.PORT || config.port;
app.listen(port);
console.log('Environment is = "' + config.nodeEnv + '"');
console.log('Express app started on port ' + port + ' using config\n', JSON.stringify(config, null, 4));
// Expose app
module.exports = app;
'use strict';
// Extend the base configuration in all.js with environment
// specific configuration
var path = require('path'),
oneLevelUp = path.sep + '..',
rootPath = path.normalize(__dirname + oneLevelUp + oneLevelUp),
packageJson = require(rootPath + path.sep + 'package.json'),
config = require('rc')(packageJson.name, {
app: {
name: packageJson.name,
title: 'DGI | aetna'
},
nodeEnv: 'local',
root: rootPath,
port: process.env.PORT || 3010,
templateEngine: 'swig',
proxit: {
verbose: true,
hosts: [{
routes: {
'/api': 'http://162.249.6.76:21000/api'
}
}]
}
});
// Set the node environment variable if not set before
config.nodeEnv = process.env.NODE_ENV = process.env.NODE_ENV || config.nodeEnv;
module.exports = config;
'use strict';
/**
* Module dependencies.
*/
var express = require('express'),
expressLoad = require('express-load'),
consolidate = require('consolidate'),
helpers = require('view-helpers'),
cookieParser = require('cookie-parser'),
compress = require('compression'),
bodyParser = require('body-parser'),
favicon = require('serve-favicon'),
methodOverride = require('method-override'),
swig = require('swig'),
proxit = require('proxit'),
config = require('./config');
module.exports = function(app) {
app.set('showStackError', true);
// Prettify HTML
app.locals.pretty = true;
// cache=memory or swig dies in NODE_ENV=production
app.locals.cache = 'memory';
// The cookieParser should be above session
app.use(cookieParser());
app.use(proxit(config.proxit));
// Should be placed before express.static
// To ensure that all assets and data are compressed (utilize bandwidth)
app.use(compress({
filter: function(req, res) {
return (/json|text|javascript|css/).test(res.getHeader('Content-Type'));
},
// Levels are specified in a range of 0 to 9, where-as 0 is
// no compression and 9 is best compression, but slowest
level: 9
}));
// assign the template engine to .html files
app.engine('html', consolidate[config.templateEngine]);
// set .html as the default extension
app.set('view engine', 'html');
// Set views path, template engine and default layout
app.set('views', config.root + '/public/views');
// Enable jsonp
app.enable('jsonp callback');
app.use(methodOverride());
// Dynamic helpers
app.use(helpers(config.app.name));
// Connect flash for flash messages
//app.use(flash());
// Setting the fav icon and static folder
app.use(favicon(__dirname + '/../../public/img/favicon.ico'));
app.use(bodyParser.urlencoded());
/**
* User json parser after proxied requests. If its used before proxied requests post request doesn't work
* */
app.use(bodyParser.json());
/*
* Make app details available to routes
* */
app.config = config;
/**
* System Routes
* */
expressLoad('../routes/system', {
extlist: /(.*)\.(js$)/,
cwd: __dirname
}).into(app);
var viewsDir = config.root + '/public';
app.use(express.static(viewsDir));
if (process.env.NODE_ENV === 'local') {
// Swig will cache templates for you, but you can disable
// that and use Express's caching instead, if you like:
app.set('view cache', false);
// To disable Swig's cache, do the following:
swig.setDefaults({
cache: false
});
}
// Assume "not found" in the error message is a 404. this is somewhat
// silly, but valid, you can do whatever you like, set properties,
// use instanceof etc.
app.use(function(err, req, res, next) {
// Treat as 404
if (~err.message.indexOf('not found')) return next();
// Log it
console.error(err.stack);
// Error page
res.status(500).send('500', {
error: err.stack
});
});
// Assume 404 since no middleware responded
app.use(function(req, res) {
res.status(404).send('404', {
url: req.originalUrl,
error: 'Not found'
});
});
// Do not stop server on any unhandled error
process.on('uncaughtException', function(err) {
console.error('UNCAUGHT EXCEPTION\n' + err.stack || err.message);
});
};
'use strict';
module.exports = function(app) {
app.get('/', function(req, res) {
res.render('index', {
renderErrors: {}, //req.flash('error')
app: app.config.app
});
});
};
#+TITLE: Query DSL
#+AUTHOR: Harish Butani
#+EMAIL: hbutani@apache.org
#+LANGUAGE: en
#+INFOJS_OPT: view:showall toc:t ltoc:t mouse:underline path:http://orgmode.org/org-info.js
#+LINK_HOME: http://home.fnal.gov/~neilsen
#+LINK_UP: http://home.fnal.gov/~neilsen/notebook
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="http://orgmode.org/org-manual.css" />
#+LaTeX_CLASS: smarticle
#+LaTeX_HEADER: \pdfmapfile{/home/neilsen/texmf/fonts/map/dvips/libertine/libertine.map}
#+LaTeX_HEADER: \usepackage[ttscale=.875]{libertine}
#+LaTeX_HEADER: \usepackage{sectsty}
#+LaTeX_HEADER: \sectionfont{\normalfont\scshape}
#+LaTeX_HEADER: \subsectionfont{\normalfont\itshape}
#+EXPORT_SELECT_TAGS: export
#+EXPORT_EXCLUDE_TAGS: noexport
#+OPTIONS: H:2 num:nil toc:nil \n:nil @:t ::t |:t ^:{} _:{} *:t TeX:t LaTeX:t
#+STARTUP: showall
#+OPTIONS: html-postamble:nil
** Example Type Definitions
#+begin_src plantuml :file class_diagram.png
scale 1300 width
note left of Trait : traits are classifications/tags attached to Instances
class Trait
Trait <|-- JDbcAccess
Trait <|-- PII
Trait <|-- Dimension
Trait <|-- Metric
Trait <|-- ETL
class Object
Object --* Trait : traits >
Object <|-- DB
Object <|-- Table
Object <|-- Column
class DB {
name : String
owner : String
}
class StorageDescriptor {
inputFormat : String
outputFormat : String
}
class Column {
name : String
dataType : String
}
class Table {
name: String
db: DB
}
Table -> StorageDescriptor : storageDesc >
Table -> DB : db >
Column *-> StorageDescriptor : storageDesc >
class LoadProcess {
name String
}
LoadProcess -* Table : inputTables >
LoadProcess -> Table : outputTable >
class View {
name String
}
View -* Table : inputTables >
#+end_src
#+CAPTION: ETL and Reporting Scenario Types
#+LABEL: fig:sampleTypeDefs
#+results:
[[file:class_diagram.png]]
** Example Instance Graph
#+begin_src dot :file instanceGraph.png :cmdline -Kdot -Tpng
digraph G {
//size ="6 6";
nodesep=.2;
//rankdir=LR;
ranksep=.25;
node [shape=record fontsize=9];
compound=true;
subgraph cluster0 {
style=bold;
label = "Sales Database"; fontsize=18;
salesDB[label="DB(sales)"]
salesFact[label="Table(sales_fact)" style=filled; color="khaki"]
salesStorage[label="Storage(text,text)"]
sales_time_id[label="time_id" shape="circle" style=filled color="peachpuff"]
sales_product_id[label="product_id" shape="circle" style=filled color="peachpuff"]
sales_customer_id[label="customer_id" shape="circle" style=filled color="peachpuff"]
sales_sales[label="sales" shape="circle" style=filled color="peachpuff"]
sales_sales_metric[label="Metric" style=filled; shape="ellipse" color="turquoise"]
salesFact -> salesDB;
salesFact -> salesStorage;
sales_time_id -> salesStorage;
sales_product_id -> salesStorage;
sales_customer_id -> salesStorage;
sales_sales -> salesStorage;
sales_sales -> sales_sales_metric;
productDim[label="Table(product_dim)" style=filled; color="khaki"]
productStorage[label="Storage(text,text)"]
product_product_id[label="product_id" shape="circle" style=filled color="peachpuff"]
product_product_name[label="product_name" shape="circle" style=filled color="peachpuff"]
product_brand_name[label="brand_name" shape="circle" style=filled color="peachpuff"]
product_dimension[label="Dimension" style=filled; shape="ellipse" color="turquoise"]
productDim -> salesDB;
productDim -> productStorage;
product_product_id -> productStorage;
product_product_name -> productStorage;
product_brand_name -> productStorage;
productDim -> product_dimension;
productDim -> salesFact [style=invis];
timeDim[label="Table(time_dim)" style=filled; color="khaki"]
timeStorage[label="Storage(text,text)"]
time_time_id[label="time_id" shape="circle" style=filled color="peachpuff"]
time_dayOfYear[label="day_of_year" shape="circle" style=filled color="peachpuff"]
time_weekDay[label="week_day" shape="circle" style=filled color="peachpuff"]
time_dimension[label="Dimension" style=filled; shape="ellipse" color="turquoise"]
timeDim -> salesDB;
timeDim -> timeStorage;
time_time_id -> timeStorage;
time_dayOfYear -> timeStorage;
time_weekDay -> timeStorage;
timeDim -> time_dimension;
timeDim -> productDim [style=invis];
customerDim[label="Table(customer_dim)" style=filled; color="khaki"]
customerStorage[label="Storage(text,text)"]
customer_customer_id[label="customer_id" shape="circle" style=filled color="peachpuff"]
customer_name[label="name" shape="circle" style=filled color="peachpuff"]
customer_address[label="address" shape="circle" style=filled color="peachpuff"]
customer_dimension[label="Dimension" style=filled; shape="ellipse" color="turquoise"]
address_pii[label="PII" style=filled; shape="ellipse" color="turquoise"]
customerDim -> salesDB;
customerDim -> customerStorage;
customer_customer_id -> customerStorage;
customer_name -> customerStorage;
customer_address -> customerStorage;
customerDim -> customer_dimension;
customer_address -> address_pii;
customerDim -> timeDim [style=invis];
//{rank=min; salesDB};
{rank=min; salesDB};
};
subgraph cluster1 {
style=bold;
label = "Reporting Database"; fontsize=18;
reportingDB[label="DB(reporting)"]
salesFactDaily[label="Table(sales_daily_mv)" style=filled; color="khaki"]
salesDailyStorage[label="Storage(orc,orc)"]
salesD_time_id[label="time_id" shape="circle" style=filled color="peachpuff"]
salesD_product_id[label="product_id" shape="circle" style=filled color="peachpuff"]
salesD_customer_id[label="customer_id" shape="circle" style=filled color="peachpuff"]
salesD_sales[label="sales" shape="circle" style=filled color="peachpuff"]
salesD_sales_metric[label="Metric" style=filled; shape="ellipse" color="turquoise"]
salesFactDaily -> reportingDB;
salesFactDaily -> salesDailyStorage;
salesD_time_id -> salesDailyStorage;
salesD_product_id -> salesDailyStorage;
salesD_customer_id -> salesDailyStorage;
salesD_sales -> salesDailyStorage;
salesD_sales -> salesD_sales_metric;
salesFactDaily -> reportingDB [style=invis];
productDimView[label="View(product_dim_v)" style=filled; color="khaki"]
productDim -> productDimView [style=dotted];
productDimView_dim[label="Dimension" style=filled; shape="ellipse" color="turquoise"]
productDimView_jdbc[label="JdbcAccess" style=filled; shape="ellipse" color="turquoise"]
productDimView -> productDimView_dim;
productDimView -> productDimView_jdbc;
productDimView -> salesFactDaily [style=invis];
customerDimView[label="View(customer_dim_v)" style=filled; color="khaki"]
customerDim -> customerDimView [style=dotted];
customerDimView_dim[label="Dimension" style=filled; shape="ellipse" color="turquoise"]
customerDimView_jdbc[label="JdbcAccess" style=filled; shape="ellipse" color="turquoise"]
customerDimView -> customerDimView_dim;
customerDimView -> customerDimView_jdbc;
customerDimView -> salesFactDaily [style=invis];
salesMonthly[label="Table(sales_monthly_mv)" style=filled; color="khaki"]
salesMonthlyStorage[label="Storage(orc,orc)"]
salesM_time_id[label="time_id" shape="circle" style=filled color="peachpuff"]
salesM_product_id[label="product_id" shape="circle" style=filled color="peachpuff"]
salesM_customer_id[label="customer_id" shape="circle" style=filled color="peachpuff"]
salesM_sales[label="sales" shape="circle" style=filled color="peachpuff"]
salesM_sales_metric[label="Metric" style=filled; shape="ellipse" color="turquoise"]
salesMonthly -> reportingDB;
salesMonthly -> salesMonthlyStorage;
salesM_time_id -> salesMonthlyStorage;
salesM_product_id -> salesMonthlyStorage;
salesM_customer_id -> salesMonthlyStorage;
salesM_sales -> salesMonthlyStorage;
salesM_sales -> salesM_sales_metric;
salesMonthly -> customerDimView [style=invis];
{rank=min; reportingDB};
};
loadSalesDaily[label="LoadProcess(loadSalesDaily)" style=filled; color="seagreen"; shape="octagon"]
loadSalesDaily_etl[label="ETL" style=filled; shape="ellipse" color="turquoise"]
salesFact -> loadSalesDaily [style=dotted];
timeDim -> loadSalesDaily [style=dotted];
loadSalesDaily -> salesFactDaily [style=dotted];
loadSalesDaily -> loadSalesDaily_etl;
loadSalesMonthly[label="LoadProcess(loadSalesMonthly)" style=filled; color="seagreen"; shape="octagon"]
loadSalesMonthly_etl[label="ETL" style=filled; shape="ellipse" color="turquoise"]
salesFactDaily -> loadSalesMonthly [style=dotted];
timeDim -> loadSalesMonthly [style=dotted];
loadSalesMonthly -> salesMonthly [style=dotted];
loadSalesMonthly -> loadSalesMonthly_etl;
}
#+end_src
#+CAPTION: ETL and Reporting Scenario
#+LABEL: fig:sampleInstanceGraph
#+results:
[[file:instanceGraph.png]]
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one
~ or more contributor license agreements. See the NOTICE file
~ distributed with this work for additional information
~ regarding copyright ownership. The ASF licenses this file
~ to you 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.hadoop.metadata</groupId>
<artifactId>metadata-governance</artifactId>
<version>0.1-incubating-SNAPSHOT</version>
</parent>
<artifactId>metadata-docs</artifactId>
<description>Apache Metadata Documentation</description>
<name>Apache Metadata Documentation</name>
<properties>
<skipTests>true</skipTests>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.maven.doxia</groupId>
<artifactId>doxia-module-twiki</artifactId>
<version>1.3</version>
</dependency>
</dependencies>
<executions>
<execution>
<goals>
<goal>site</goal>
</goals>
<phase>prepare-package</phase>
</execution>
</executions>
<configuration>
<generateProjectInfo>false</generateProjectInfo>
<generateReports>false</generateReports>
<skip>false</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one
~ or more contributor license agreements. See the NOTICE file
~ distributed with this work for additional information
~ regarding copyright ownership. The ASF licenses this file
~ to you 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.
-->
<project name="Metadata and Governance" xmlns="http://maven.apache.org/DECORATION/1.3.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/DECORATION/1.3.0 http://maven.apache.org/xsd/decoration-1.3.0.xsd">
<skin>
<groupId>org.apache.maven.skins</groupId>
<artifactId>maven-fluido-skin</artifactId>
<version>1.3.0</version>
</skin>
<custom>
<fluidoSkin>
<project>Apache Metadata and Governance</project>
<sideBarEnabled>false</sideBarEnabled>
</fluidoSkin>
</custom>
<bannerLeft>
<name>DGC - Metadata</name>
<src>./images/metadata-logo.png</src>
<width>200px</width>
<height>45px</height>
</bannerLeft>
<bannerRight>
<name>Apache Incubator</name>
<src>./images/apache-incubator-logo.png</src>
<href>http://incubator.apache.org</href>
</bannerRight>
<publishDate position="right"/>
<version position="right"/>
<body>
<head>
<script type="text/javascript">
$( document ).ready( function() { $( '.carousel' ).carousel( { interval: 3500 } ) } );
</script>
</head>
<breadcrumbs position="left">
<item name="MetadataGovernance" title="Apache Metadata and Governance" href="index.html"/>
</breadcrumbs>
<footer>
© 2011-2012 The Apache Software Foundation. Apache Metadata and Governance, Apache,
the Apache feather logo, and the Apache Metadata and Governance project logo are
trademarks of The Apache Software Foundation.
</footer>
</body>
</project>
\ No newline at end of file
---+ Data Governance and Metadata platform for Hadoop
---++ Why?
*
* Captures Lineage information for data sets and processes
---+ Getting Started
#LicenseInfo
---+ Licensing Information
Metadata (DGC) is distributed under [[http://www.apache.org/licenses/LICENSE-2.0][Apache License 2.0]].
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment