├── .gitignore ├── AUTHORS ├── LICENSE ├── NOTICE.txt ├── OSSMETADATA ├── README.md ├── build.gradle ├── clients ├── java │ ├── .gitignore │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── netflix │ │ │ └── lipstick │ │ │ ├── client │ │ │ ├── BaseClient.java │ │ │ └── Client.java │ │ │ ├── graph │ │ │ ├── Edge.java │ │ │ ├── Graph.java │ │ │ ├── Node.java │ │ │ ├── NodeGroup.java │ │ │ ├── Stage.java │ │ │ └── Status.java │ │ │ └── template │ │ │ └── Template.java │ │ └── test │ │ ├── java │ │ └── com │ │ │ └── netflix │ │ │ └── lipstick │ │ │ └── graph │ │ │ └── GraphTest.java │ │ └── resources │ │ └── graph.json └── python │ ├── .gitignore │ ├── README.md │ ├── lipstick │ ├── __init__.py │ ├── graph.py │ ├── lipstick.py │ └── template.py │ └── setup.py ├── codequality ├── HEADER ├── checkstyle.xml └── mysuppressions.xml ├── gradle.properties ├── gradle ├── buildscript.gradle ├── check.gradle ├── convention.gradle ├── license.gradle ├── maven.gradle ├── release.gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── lipstick-console ├── buildinfo │ └── lipstick_build.txt └── src │ ├── main │ ├── java │ │ ├── com │ │ │ └── netflix │ │ │ │ └── lipstick │ │ │ │ ├── MRPlanCalculator.java │ │ │ │ ├── Main.java │ │ │ │ ├── P2jPlanGenerator.java │ │ │ │ ├── Pig2DotGenerator.java │ │ │ │ ├── adaptors │ │ │ │ ├── LOCogroupJsonAdaptor.java │ │ │ │ ├── LOFilterJsonAdaptor.java │ │ │ │ ├── LOJoinJsonAdaptor.java │ │ │ │ ├── LOJsonAdaptor.java │ │ │ │ ├── LOLimitJsonAdaptor.java │ │ │ │ ├── LOLoadJsonAdaptor.java │ │ │ │ ├── LOSplitOutputJsonAdaptor.java │ │ │ │ ├── LOStoreJsonAdaptor.java │ │ │ │ └── package.html │ │ │ │ ├── listeners │ │ │ │ ├── LipstickPPNL.java │ │ │ │ ├── PPNLErrorHandler.java │ │ │ │ └── package.html │ │ │ │ ├── model │ │ │ │ ├── P2jCounters.java │ │ │ │ ├── P2jJobStatus.java │ │ │ │ ├── P2jPlan.java │ │ │ │ ├── P2jPlanPackage.java │ │ │ │ ├── P2jPlanStatus.java │ │ │ │ ├── P2jSampleOutput.java │ │ │ │ ├── P2jSampleOutputList.java │ │ │ │ ├── P2jScripts.java │ │ │ │ ├── P2jWarning.java │ │ │ │ ├── Utils.java │ │ │ │ ├── operators │ │ │ │ │ ├── P2jLOCogroup.java │ │ │ │ │ ├── P2jLOFilter.java │ │ │ │ │ ├── P2jLOJoin.java │ │ │ │ │ ├── P2jLOLimit.java │ │ │ │ │ ├── P2jLOLoad.java │ │ │ │ │ ├── P2jLOSplitOutput.java │ │ │ │ │ ├── P2jLOStore.java │ │ │ │ │ ├── P2jLogicalRelationalOperator.java │ │ │ │ │ ├── elements │ │ │ │ │ │ ├── JoinExpression.java │ │ │ │ │ │ ├── SchemaElement.java │ │ │ │ │ │ └── package.html │ │ │ │ │ └── package.html │ │ │ │ └── package.html │ │ │ │ ├── package.html │ │ │ │ ├── pigstatus │ │ │ │ ├── PigStatusClient.java │ │ │ │ ├── RestfulPigStatusClient.java │ │ │ │ └── package.html │ │ │ │ ├── pigtolipstick │ │ │ │ ├── BasicP2LClient.java │ │ │ │ ├── P2LClient.java │ │ │ │ └── package.html │ │ │ │ ├── util │ │ │ │ ├── EzIterable.java │ │ │ │ ├── OutputSampler.java │ │ │ │ └── package.html │ │ │ │ └── warnings │ │ │ │ └── JobWarnings.java │ │ └── org │ │ │ └── apache │ │ │ └── pig │ │ │ ├── LipstickPigServer.java │ │ │ └── tools │ │ │ └── grunt │ │ │ └── LipstickGrunt.java │ ├── resources │ │ ├── META-INF │ │ │ ├── beans.xml │ │ │ └── persistence.xml │ │ └── MessageResources.properties │ └── webapp │ │ ├── META-INF │ │ ├── MANIFEST.MF │ │ └── context.xml │ │ ├── WEB-INF │ │ ├── faces-config.xml │ │ ├── jetty-env.xml │ │ ├── templates │ │ │ └── template.xhtml │ │ └── web.xml │ │ ├── graph.html │ │ ├── home.xhtml │ │ ├── index.html │ │ └── resources │ │ ├── css │ │ ├── bootstrap-responsive.css │ │ ├── bootstrap.css │ │ ├── codemirror.css │ │ ├── screen.css │ │ └── tossboss.css │ │ ├── img │ │ ├── 404.gif │ │ ├── error.gif │ │ ├── glyphicons-halflings-white.png │ │ ├── glyphicons-halflings.png │ │ ├── netflix_logo_small.png │ │ └── pending.gif │ │ └── js │ │ ├── backbone-min.js │ │ ├── bootstrap.js │ │ ├── codemirror.js │ │ ├── d3.v2.min.js │ │ ├── hive.js │ │ ├── jquery-1.8.2.min.js │ │ ├── jquery.transit.js │ │ ├── lodash.min.js │ │ ├── tossboss-graph.js │ │ └── tossboss-main.js │ └── test │ ├── java │ └── com │ │ └── netflix │ │ ├── dse │ │ └── pig2json │ │ │ └── model │ │ │ └── PersistTest.java │ │ └── lipstick │ │ ├── MRPlanCalculatorTest.java │ │ ├── P2jPlanGeneratorTest.java │ │ ├── adaptors │ │ └── LOJsonAdaptorTest.java │ │ ├── model │ │ └── PersistTest.java │ │ ├── pigtolipstick │ │ └── BasicP2LClientTest.java │ │ ├── test │ │ └── util │ │ │ └── Util.java │ │ ├── util │ │ └── OutputSamplerTest.java │ │ └── warnings │ │ └── JobWarningsTest.java │ └── resources │ ├── complex.pig │ ├── complex2.pig │ ├── status.json │ ├── test.json │ └── test.pig ├── lipstick-server ├── Gemfile ├── Gemfile.lock ├── app │ ├── controllers │ │ ├── api_docs.rb │ │ ├── job_controller.rb │ │ └── template_controller.rb │ ├── helpers │ │ ├── elasticsearch.rb │ │ ├── elasticsearch_adaptor.rb │ │ ├── graph.rb │ │ └── p2j_translator.rb │ ├── public │ │ ├── WEB-INF │ │ │ ├── applicationContext.xml │ │ │ ├── sitemesh.xml │ │ │ └── tld │ │ │ │ ├── c.tld │ │ │ │ ├── fmt.tld │ │ │ │ ├── grails.tld │ │ │ │ └── spring.tld │ │ ├── bootstrap │ │ │ ├── css │ │ │ │ ├── bootstrap-responsive.css │ │ │ │ ├── bootstrap.css │ │ │ │ ├── bootstrap.css1 │ │ │ │ └── bootstrap.min.css │ │ │ ├── fonts │ │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ │ └── glyphicons-halflings-regular.woff │ │ │ ├── img │ │ │ │ ├── 404.gif │ │ │ │ ├── apple-touch-icon-retina.png │ │ │ │ ├── apple-touch-icon.png │ │ │ │ ├── error.gif │ │ │ │ ├── favicon.ico │ │ │ │ ├── glyphicons-halflings-white.png │ │ │ │ ├── glyphicons-halflings.png │ │ │ │ ├── grails_logo.jpg │ │ │ │ ├── grails_logo.png │ │ │ │ ├── leftnav_btm.png │ │ │ │ ├── leftnav_midstretch.png │ │ │ │ ├── leftnav_top.png │ │ │ │ ├── netflix_logo_small.png │ │ │ │ ├── pending.gif │ │ │ │ ├── spinner.gif │ │ │ │ └── springsource.png │ │ │ └── js │ │ │ │ ├── bootstrap.3.1.1.min.js │ │ │ │ └── bootstrap.js │ │ ├── codemirror │ │ │ ├── css │ │ │ │ ├── codemirror.css │ │ │ │ └── codemirror.css1 │ │ │ └── js │ │ │ │ ├── codemirror.js │ │ │ │ └── hive.js │ │ ├── css │ │ │ ├── dashboard.css │ │ │ ├── errors.css │ │ │ ├── jquery.treetable.css │ │ │ ├── jquery.treetable.theme.default.css │ │ │ ├── main.css │ │ │ ├── mobile.css │ │ │ ├── screen.css │ │ │ └── workflow.css │ │ ├── foo.html │ │ ├── fuelux-3.1.0 │ │ │ ├── css │ │ │ │ ├── fuelux.css │ │ │ │ ├── fuelux.css.map │ │ │ │ └── fuelux.min.css │ │ │ ├── fonts │ │ │ │ ├── fuelux.eot │ │ │ │ ├── fuelux.svg │ │ │ │ ├── fuelux.ttf │ │ │ │ └── fuelux.woff │ │ │ └── js │ │ │ │ ├── fuelux.js │ │ │ │ └── fuelux.min.js │ │ ├── fuelux │ │ │ ├── all.js │ │ │ ├── all.min.js │ │ │ ├── checkbox.js │ │ │ ├── combobox.js │ │ │ ├── css │ │ │ │ ├── fuelux-responsive.css │ │ │ │ ├── fuelux-responsive.min.css │ │ │ │ ├── fuelux.css │ │ │ │ └── fuelux.min.css │ │ │ ├── datagrid.js │ │ │ ├── img │ │ │ │ ├── form.png │ │ │ │ ├── glyphicons-halflings-white.png │ │ │ │ └── glyphicons-halflings.png │ │ │ ├── less │ │ │ │ ├── checkbox.less │ │ │ │ ├── combobox.less │ │ │ │ ├── datagrid.less │ │ │ │ ├── fuelux-responsive.less │ │ │ │ ├── fuelux.less │ │ │ │ ├── pillbox.less │ │ │ │ ├── radio.less │ │ │ │ ├── search.less │ │ │ │ ├── select.less │ │ │ │ ├── spinner.less │ │ │ │ ├── tree.less │ │ │ │ ├── variables.less │ │ │ │ └── wizard.less │ │ │ ├── loader.js │ │ │ ├── loader.min.js │ │ │ ├── pillbox.js │ │ │ ├── radio.js │ │ │ ├── search.js │ │ │ ├── select.js │ │ │ ├── spinner.js │ │ │ ├── tree.js │ │ │ ├── util.js │ │ │ └── wizard.js │ │ ├── graph.html │ │ ├── images │ │ │ ├── 404.gif │ │ │ ├── apple-touch-icon-retina.png │ │ │ ├── apple-touch-icon.png │ │ │ ├── error.gif │ │ │ ├── favicon.ico │ │ │ ├── grails_logo.jpg │ │ │ ├── grails_logo.png │ │ │ ├── leftnav_btm.png │ │ │ ├── leftnav_midstretch.png │ │ │ ├── leftnav_top.png │ │ │ ├── loading.gif │ │ │ ├── netflix_logo_small.png │ │ │ ├── pending.gif │ │ │ ├── skin │ │ │ │ ├── database_add.png │ │ │ │ ├── database_delete.png │ │ │ │ ├── database_edit.png │ │ │ │ ├── database_save.png │ │ │ │ ├── database_table.png │ │ │ │ ├── exclamation.png │ │ │ │ ├── house.png │ │ │ │ ├── information.png │ │ │ │ ├── shadow.jpg │ │ │ │ ├── sorted_asc.gif │ │ │ │ └── sorted_desc.gif │ │ │ ├── spinner.gif │ │ │ └── springsource.png │ │ ├── index.html │ │ ├── js │ │ │ ├── application.js │ │ │ ├── backbone-min.js │ │ │ ├── d3.v2.min.js │ │ │ ├── jquery-1.8.2.min.js │ │ │ ├── jquery.js │ │ │ ├── jquery.transit.js │ │ │ ├── jquery.transit.min.js │ │ │ ├── lib │ │ │ │ ├── d3.v3.min.js │ │ │ │ ├── dagre-d3.min.js │ │ │ │ ├── jquery.treetable.js │ │ │ │ ├── knockout-3.0.0.js │ │ │ │ └── mustache.js │ │ │ ├── lodash.min.js │ │ │ ├── moment.min.js │ │ │ ├── require.js │ │ │ ├── tossboss-history.js │ │ │ ├── workflow-history.js │ │ │ ├── workflow.js │ │ │ └── workflow │ │ │ │ ├── graph │ │ │ │ ├── edge.js │ │ │ │ ├── graph.js │ │ │ │ ├── node.js │ │ │ │ ├── node_group.js │ │ │ │ ├── stage.js │ │ │ │ └── status.js │ │ │ │ ├── templates.js │ │ │ │ ├── utils.js │ │ │ │ └── viewmodel.js │ │ ├── tossboss-detail-view │ │ │ ├── css │ │ │ │ └── tossboss-detail-view.css │ │ │ └── js │ │ │ │ └── tossboss-detail-view.js │ │ ├── tossboss-drawers │ │ │ ├── css │ │ │ │ └── tossboss-drawers.css │ │ │ ├── img │ │ │ │ ├── handle-horizontal.png │ │ │ │ └── handle-vertical.png │ │ │ └── js │ │ │ │ └── tossboss-drawers.js │ │ ├── tossboss-graph-model │ │ │ └── js │ │ │ │ └── tossboss-graph-model.js │ │ ├── tossboss-graph-view │ │ │ ├── css │ │ │ │ └── tossboss-graph-view.css │ │ │ └── js │ │ │ │ └── tossboss-graph-view.js │ │ ├── tossboss-main │ │ │ ├── css │ │ │ │ └── tossboss-main.css │ │ │ ├── img │ │ │ │ ├── lipstick_140.png │ │ │ │ ├── lipstick_404.png │ │ │ │ └── lipstick_940.png │ │ │ └── js │ │ │ │ ├── tossboss-main.js │ │ │ │ └── tossboss-templates.js │ │ ├── tossboss-modal │ │ │ ├── css │ │ │ │ └── tossboss-modal.css │ │ │ └── js │ │ │ │ └── tossboss-modal.js │ │ ├── tossboss-script │ │ │ ├── css │ │ │ │ └── tossboss-script.css │ │ │ └── js │ │ │ │ └── tossboss-script.js │ │ ├── workflow.html │ │ └── workflows.html │ ├── templates │ │ ├── PigEdge.js │ │ ├── PigEdge.mustache │ │ ├── PigNode.js │ │ └── PigNode.mustache │ └── test │ │ ├── lipstick_test.rb │ │ ├── test_job_controller.rb │ │ └── test_template_controller.rb ├── config.ru ├── config │ ├── graph-mapping.json │ ├── lipstick_es5_template.json │ ├── plan-mapping.json │ ├── template-mapping.json │ └── warble.rb ├── examples │ ├── graphs │ │ ├── big.json │ │ ├── doubly-nested.json │ │ ├── example.json │ │ └── example2.json │ └── templates │ │ ├── pig-edge-template.json │ │ └── pig-template.json └── init.rb ├── quickstart ├── 1.dat ├── 2.dat ├── example1 ├── pig.properties ├── test_local.pig └── test_no_output.pig └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.rar 19 | *.tar 20 | *.zip 21 | 22 | # Logs and databases # 23 | ###################### 24 | *.log 25 | 26 | # OS generated files # 27 | ###################### 28 | .DS_Store* 29 | ehthumbs.db 30 | Icon? 31 | Thumbs.db 32 | 33 | # Editor Files # 34 | ################ 35 | *~ 36 | *.swp 37 | 38 | # Gradle Files # 39 | ################ 40 | .gradle 41 | 42 | # Build output directies 43 | /target 44 | */target 45 | /build 46 | */build 47 | 48 | # IntelliJ specific files/directories 49 | out 50 | .idea 51 | *.ipr 52 | *.iws 53 | *.iml 54 | atlassian-ide-plugin.xml 55 | 56 | # Eclipse specific files/directories 57 | .classpath 58 | .project 59 | .settings 60 | .metadata 61 | 62 | # NetBeans specific files/directories 63 | .nbattrs 64 | 65 | #lipstick 66 | *.war 67 | .yardoc 68 | lipstick-server/app/public/doc 69 | lipstick-server/.gem 70 | lipstick-server/.es-data 71 | lipstick-server/lib/*.jar 72 | lipstick-console/bin 73 | lipstick-console/test-output 74 | lipstick-console/src/main/resources/lipstick_build.txt 75 | 76 | /bin 77 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Netflix 2 | Google Inc. 3 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | Lipstick 2 | Copyright 2013 Netflix, Inc. 3 | 4 | ------------------------------------------------ 5 | Attribution for JavaScript Libraries and Plugins 6 | ------------------------------------------------ 7 | 8 | jQuery JavaScript Library provided by the jQuery Foundation 9 | http://jquery.com/ 10 | License: MIT License (http://jquery.org/license) 11 | Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors 12 | 13 | Bootstrap front-end framework provided by Mark Otto, Jacob Thornton, and Twitter 14 | http://twitter.github.io/bootstrap/index.html 15 | License: Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) 16 | Copyright 2012 Twitter, Inc 17 | 18 | CodeMirror provided by Marijn Haverbeke 19 | http://codemirror.net/ 20 | License: MIT License (http://codemirror.net/LICENSE) 21 | Copyright 2013 Marijn Haverbeke 22 | 23 | Fuel UX provided by ExactTarget 24 | http://exacttarget.github.io/fuelux/ 25 | License: MIT License (https://github.com/ExactTarget/fuelux/blob/master/COPYING) 26 | Copyright 2012 ExactTarget 27 | 28 | D3.js JavaScript library provided by Michael Bostock 29 | http://d3js.org/ 30 | License: BSD License (https://github.com/mbostock/d3/blob/master/LICENSE) 31 | Copyright 2013 Michael Bostock 32 | 33 | Lo-Dash JavaScript library provided by John-David Dalton, Kit Cambridge, and Mathias Bynens 34 | http://lodash.com/ 35 | License: https://github.com/bestiejs/lodash/blob/master/LICENSE.txt 36 | Copyright 2012-2013 The Dojo Foundation 37 | 38 | Transit provided by Rico Sta. Cruz 39 | http://ricostacruz.com/jquery.transit/ 40 | License: MIT License (http://opensource.org/licenses/mit-license.php) 41 | Copyright 2011-2013 Rico Sta. Cruz 42 | 43 | Backbone.js provided by contributors (https://github.com/documentcloud/backbone/graphs/contributors) 44 | http://backbonejs.org/ 45 | License: MIT License (https://github.com/documentcloud/backbone/blob/master/LICENSE) 46 | Copyright 2010-2013 Jeremy Ashkenas, DocumentCloud 47 | 48 | RequireJS provided by James Burke 49 | http://requirejs.org/ 50 | License: MIT License (https://github.com/jrburke/requirejs/blob/master/LICENSE) 51 | Copyright 2010-2011 The Dojo Foundation 52 | -------------------------------------------------------------------------------- /OSSMETADATA: -------------------------------------------------------------------------------- 1 | osslifecycle=active 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Lipstick 2 | ======== 3 | 4 | Master build status: [![Master Build Status](https://netflixoss.ci.cloudbees.com/job/Lipstick-master/badge/icon)](https://netflixoss.ci.cloudbees.com/job/Lipstick-master/) 5 | Pull requests build status: [![Pull Requests Build Status](https://netflixoss.ci.cloudbees.com/job/Lipstick-pull-requests/badge/icon)](https://netflixoss.ci.cloudbees.com/job/Lipstick-pull-requests/) 6 | 7 | Lipstick combines a graphical depiction of a Pig workflow with information about the job as it executes. 8 | 9 | To find out more [see the wiki.](https://github.com/Netflix/Lipstick/wiki) 10 | 11 | For support and development discussion, join the [Lipstick google group](https://groups.google.com/forum/#!forum/lipstick-oss). 12 | 13 | ![Lipstick UI](https://raw.github.com/wiki/Netflix/Lipstick/screenshot.png) 14 | -------------------------------------------------------------------------------- /clients/java/.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /bin/ 3 | -------------------------------------------------------------------------------- /clients/java/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'eclipse' 3 | 4 | sourceCompatibility = 1.6 5 | version = '0.1' 6 | 7 | repositories { 8 | mavenCentral() 9 | } 10 | 11 | dependencies { 12 | compile 'com.google.guava:guava:12.0' 13 | compile 'commons-logging:commons-logging:1.1.1' 14 | compile 'org.glassfish.jersey.core:jersey-common:2.13' 15 | compile 'org.glassfish.jersey.core:jersey-client:2.13' 16 | compile 'com.fasterxml.jackson.core:jackson-core:2.1.4' 17 | compile 'com.fasterxml.jackson.core:jackson-annotations:2.1.4' 18 | compile 'com.fasterxml.jackson.core:jackson-databind:2.1.4' 19 | testCompile 'org.testng:testng:6.1.1' 20 | testCompile 'org.skyscreamer:jsonassert:1.2.3' 21 | } 22 | 23 | task fullJar(type: Jar) { 24 | baseName = project.name + '-withDependencies' 25 | from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }} 26 | with jar 27 | } 28 | 29 | test { 30 | useTestNG() 31 | } 32 | -------------------------------------------------------------------------------- /clients/java/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/clients/java/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /clients/java/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Nov 25 11:06:46 PST 2014 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-bin.zip 7 | -------------------------------------------------------------------------------- /clients/java/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /clients/java/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name="lipstick-java" 2 | -------------------------------------------------------------------------------- /clients/java/src/main/java/com/netflix/lipstick/client/Client.java: -------------------------------------------------------------------------------- 1 | package com.netflix.lipstick.client; 2 | 3 | import com.netflix.lipstick.graph.Graph; 4 | import com.netflix.lipstick.template.Template; 5 | 6 | public class Client extends BaseClient { 7 | 8 | protected static String JOB_PATH = "/v1/job"; 9 | protected static String TEMPLATE_PATH = "/template"; 10 | 11 | public Client(String serviceUrl) { 12 | super(serviceUrl); 13 | } 14 | 15 | public Client(String serviceUrl, int connectTimeout, int readTimeout) { 16 | super(serviceUrl, connectTimeout, readTimeout); 17 | } 18 | 19 | public Graph get(String graphId) { 20 | String path = String.format("%s/%s", JOB_PATH, graphId); 21 | String response = makeRequest(path, null, RequestVerb.GET); 22 | if (response != null) { 23 | return Graph.fromJson(response); 24 | } 25 | return null; 26 | } 27 | 28 | public String list() { 29 | return makeRequest(JOB_PATH, null, RequestVerb.GET); 30 | } 31 | 32 | public String save(Graph graph) { 33 | return makeRequest(JOB_PATH, graph, RequestVerb.POST); 34 | } 35 | 36 | public String update(Graph graph) { 37 | String path = String.format("%s/%s", JOB_PATH, graph.id); 38 | return makeRequest(path, graph, RequestVerb.PUT); 39 | } 40 | 41 | public Template getTemplate(String name) { 42 | String path = String.format("%s/%s", TEMPLATE_PATH, name); 43 | String response = makeRequest(path, null, RequestVerb.GET); 44 | if (response != null) { 45 | return Template.fromJson(response); 46 | } 47 | return null; 48 | } 49 | 50 | public String saveTemplate(Template template) { 51 | String path = String.format("%s/%s", TEMPLATE_PATH, template.name); 52 | return makeRequest(path, template, RequestVerb.POST); 53 | } 54 | 55 | public String listTemplates() { 56 | return makeRequest(TEMPLATE_PATH, null, RequestVerb.GET); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /clients/java/src/main/java/com/netflix/lipstick/graph/Edge.java: -------------------------------------------------------------------------------- 1 | package com.netflix.lipstick.graph; 2 | 3 | import java.util.Map; 4 | 5 | import jersey.repackaged.com.google.common.collect.Maps; 6 | 7 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 8 | import com.fasterxml.jackson.annotation.JsonInclude; 9 | 10 | @JsonInclude(value=JsonInclude.Include.NON_EMPTY) 11 | @JsonIgnoreProperties(ignoreUnknown=true) 12 | public class Edge { 13 | public String u; 14 | public String v; 15 | public String type; 16 | public String label; 17 | public Map properties; 18 | 19 | public Edge() { 20 | this.properties = Maps.newHashMap(); 21 | } 22 | 23 | public Edge(String u, String v) { 24 | this.u = u; 25 | this.v = v; 26 | this.properties = Maps.newHashMap(); 27 | } 28 | 29 | public Edge u(String u) { 30 | this.u = u; 31 | return this; 32 | } 33 | 34 | public Edge v(String v) { 35 | this.v = v; 36 | return this; 37 | } 38 | 39 | public Edge type(String type) { 40 | this.type = type; 41 | return this; 42 | } 43 | 44 | public Edge label(String label) { 45 | this.label = label; 46 | return this; 47 | } 48 | 49 | public Object property(String key) { 50 | return this.properties.get(key); 51 | } 52 | 53 | public Edge property(String key, Object value) { 54 | this.properties.put(key, value); 55 | return this; 56 | } 57 | 58 | public Edge properties(Map properties) { 59 | this.properties = properties; 60 | return this; 61 | } 62 | 63 | public boolean equals(Object other) { 64 | if (this == other) return true; 65 | if (!(other instanceof Edge)) return false; 66 | 67 | Edge e = (Edge)other; 68 | 69 | return 70 | this.u == null ? e.u == null : this.u.equals(e.u) && 71 | this.v == null ? e.v == null : this.v.equals(e.v) && 72 | this.type == null ? e.type == null : this.type.equals(e.type) && 73 | this.label == null ? e.label == null : this.label.equals(e.label) && 74 | this.properties.equals(e.properties); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /clients/java/src/main/java/com/netflix/lipstick/graph/Node.java: -------------------------------------------------------------------------------- 1 | package com.netflix.lipstick.graph; 2 | 3 | import java.util.Map; 4 | 5 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 6 | import com.fasterxml.jackson.annotation.JsonInclude; 7 | import com.google.common.collect.Maps; 8 | 9 | @JsonInclude(value=JsonInclude.Include.NON_EMPTY) 10 | @JsonIgnoreProperties(ignoreUnknown=true) 11 | public class Node { 12 | public String id; 13 | public String url; 14 | public String type; 15 | public String child; 16 | public Status status; 17 | public Map properties; 18 | 19 | public Node() { 20 | this.properties = Maps.newHashMap(); 21 | } 22 | 23 | public Node(String id) { 24 | this.id = id; 25 | this.properties = Maps.newHashMap(); 26 | } 27 | 28 | public Node id(String id) { 29 | this.id = id; 30 | return this; 31 | } 32 | 33 | public Node url(String url) { 34 | this.url = url; 35 | return this; 36 | } 37 | 38 | public Node type(String type) { 39 | this.type = type; 40 | return this; 41 | } 42 | 43 | public Node child(String child) { 44 | this.child = child; 45 | return this; 46 | } 47 | 48 | public Node status(Status status) { 49 | this.status = status; 50 | return this; 51 | } 52 | 53 | public Object property(String key) { 54 | return this.properties.get(key); 55 | } 56 | 57 | public Node property(String key, Object value) { 58 | this.properties.put(key, value); 59 | return this; 60 | } 61 | 62 | public Node properties(Map properties) { 63 | this.properties = properties; 64 | return this; 65 | } 66 | 67 | public boolean equals(Object other) { 68 | if (this == other) return true; 69 | if (!(other instanceof Node)) return false; 70 | 71 | Node n = (Node)other; 72 | 73 | return 74 | this.id == null ? n.id == null : this.id.equals(n.id) && 75 | this.url == null ? n.url == null : this.url.equals(n.url) && 76 | this.type == null ? n.type == null : this.type.equals(n.type) && 77 | this.child == null ? n.child == null : this.child.equals(n.child) && 78 | this.status == null ? n.status == null : this.status.equals(n.status) && 79 | this.properties.equals(n.properties); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /clients/java/src/main/java/com/netflix/lipstick/graph/NodeGroup.java: -------------------------------------------------------------------------------- 1 | package com.netflix.lipstick.graph; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 7 | import com.fasterxml.jackson.annotation.JsonInclude; 8 | import com.google.common.collect.Lists; 9 | import com.google.common.collect.Maps; 10 | 11 | @JsonInclude(value=JsonInclude.Include.NON_EMPTY) 12 | @JsonIgnoreProperties(ignoreUnknown=true) 13 | public class NodeGroup { 14 | public String id; 15 | public String url; 16 | public Status status; 17 | public List stages; 18 | public List children; 19 | public Map properties; 20 | 21 | public NodeGroup() { 22 | this.stages = Lists.newArrayList(); 23 | this.properties = Maps.newHashMap(); 24 | this.children = Lists.newArrayList(); 25 | } 26 | 27 | public NodeGroup(String id) { 28 | this.id = id; 29 | this.stages = Lists.newArrayList(); 30 | this.properties = Maps.newHashMap(); 31 | this.children = Lists.newArrayList(); 32 | } 33 | 34 | public NodeGroup id(String id) { 35 | this.id = id; 36 | return this; 37 | } 38 | 39 | public NodeGroup url(String url) { 40 | this.url = url; 41 | return this; 42 | } 43 | 44 | public NodeGroup status(Status status) { 45 | this.status = status; 46 | return this; 47 | } 48 | 49 | public NodeGroup stage(Stage stage) { 50 | this.stages.add(stage); 51 | return this; 52 | } 53 | 54 | public NodeGroup stages(List stages) { 55 | this.stages = stages; 56 | return this; 57 | } 58 | 59 | public Boolean hasChild(String child) { 60 | return this.children.contains(child); 61 | } 62 | 63 | public NodeGroup child(String child) { 64 | if (!hasChild(child)) { 65 | this.children.add(child); 66 | } 67 | return this; 68 | } 69 | 70 | public NodeGroup children(List children) { 71 | this.children = children; 72 | return this; 73 | } 74 | 75 | public Object property(String key) { 76 | return this.properties.get(key); 77 | } 78 | 79 | public NodeGroup property(String key, Object value) { 80 | this.properties.put(key, value); 81 | return this; 82 | } 83 | 84 | public NodeGroup properties(Map properties) { 85 | this.properties = properties; 86 | return this; 87 | } 88 | 89 | public boolean equals(Object other) { 90 | if (this == other) return true; 91 | if (!(other instanceof NodeGroup)) return false; 92 | 93 | NodeGroup ng = (NodeGroup)other; 94 | 95 | return 96 | this.id == null ? ng.id == null : this.id.equals(ng.id) && 97 | this.url == null ? ng.url == null : this.url.equals(ng.url) && 98 | this.status == null ? ng.status == null : this.status.equals(ng.status) && 99 | this.stages.equals(ng.stages) && 100 | this.children.equals(ng.children) && 101 | this.properties.equals(ng.properties); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /clients/java/src/main/java/com/netflix/lipstick/graph/Stage.java: -------------------------------------------------------------------------------- 1 | package com.netflix.lipstick.graph; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | 6 | @JsonInclude(value=JsonInclude.Include.NON_EMPTY) 7 | @JsonIgnoreProperties(ignoreUnknown=true) 8 | public class Stage { 9 | public String name; 10 | public Status status; 11 | 12 | public Stage() { 13 | } 14 | 15 | public Stage(String name) { 16 | this(name, new Status()); 17 | } 18 | 19 | public Stage(String name, Status status) { 20 | this.name = name; 21 | this.status = status; 22 | } 23 | 24 | public Stage name(String name) { 25 | this.name = name; 26 | return this; 27 | } 28 | 29 | public Stage status(Status status) { 30 | this.status = status; 31 | return this; 32 | } 33 | 34 | public boolean equals(Object other) { 35 | if (this == other) return true; 36 | if (!(other instanceof Stage)) return false; 37 | 38 | Stage s = (Stage)other; 39 | 40 | return 41 | this.name == null ? s.name == null : this.name.equals(s.name) && 42 | this.status == null ? s.status == null : this.status.equals(s.status); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /clients/java/src/main/java/com/netflix/lipstick/graph/Status.java: -------------------------------------------------------------------------------- 1 | package com.netflix.lipstick.graph; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | 6 | @JsonInclude(value=JsonInclude.Include.NON_EMPTY) 7 | @JsonIgnoreProperties(ignoreUnknown=true) 8 | public class Status { 9 | public Integer progress; 10 | public Long startTime; 11 | public Long endTime; 12 | public Long heartbeatTime; 13 | public String statusText; 14 | 15 | public Status() { 16 | Long now = System.currentTimeMillis(); 17 | progress = 0; 18 | startTime = now; 19 | heartbeatTime = now; 20 | } 21 | 22 | public Status(Integer progress, Long startTime, Long heartbeatTime, String statusText) { 23 | this.progress = progress; 24 | this.startTime = startTime; 25 | this.heartbeatTime = heartbeatTime; 26 | this.statusText = statusText; 27 | } 28 | 29 | public Status progress(Integer progress) { 30 | this.progress = progress; 31 | return this; 32 | } 33 | 34 | public Status startTime(Long startTime) { 35 | this.startTime = startTime; 36 | return this; 37 | } 38 | 39 | public Status endTime(Long endTime) { 40 | this.endTime = endTime; 41 | return this; 42 | } 43 | 44 | public Status heartbeatTime(Long heartbeatTime) { 45 | this.heartbeatTime = heartbeatTime; 46 | return this; 47 | } 48 | 49 | public Status statusText(String statusText) { 50 | this.statusText = statusText; 51 | return this; 52 | } 53 | 54 | public boolean equals(Object other) { 55 | if (this == other) return true; 56 | if (!(other instanceof Status)) return false; 57 | 58 | Status s = (Status)other; 59 | 60 | return 61 | this.progress.equals(s.progress) && 62 | this.startTime.equals(s.startTime) && 63 | this.endTime == null ? s.endTime == null : this.endTime.equals(s.endTime) && 64 | this.heartbeatTime.equals(s.heartbeatTime) && 65 | this.statusText.equals(s.statusText); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /clients/java/src/main/java/com/netflix/lipstick/template/Template.java: -------------------------------------------------------------------------------- 1 | package com.netflix.lipstick.template; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.charset.Charset; 6 | 7 | import org.apache.commons.logging.Log; 8 | import org.apache.commons.logging.LogFactory; 9 | 10 | import com.fasterxml.jackson.databind.ObjectMapper; 11 | import com.google.common.io.Files; 12 | 13 | public class Template { 14 | 15 | private static final Log LOG = LogFactory.getLog(Template.class); 16 | 17 | public String name; 18 | public String view; 19 | public String template; 20 | 21 | public Template() { 22 | } 23 | 24 | public Template(String name) { 25 | this.name = name; 26 | } 27 | 28 | public Template name(String name) { 29 | this.name = name; 30 | return this; 31 | } 32 | 33 | public Template view(String view) { 34 | this.view = view; 35 | return this; 36 | } 37 | 38 | public Template template(String template) { 39 | this.template = template; 40 | return this; 41 | } 42 | 43 | protected String readFile(String uri) throws IOException { 44 | return Files.toString(new File(uri), Charset.defaultCharset()); 45 | } 46 | 47 | public Template loadView(String viewUri) throws IOException { 48 | this.view = readFile(viewUri); 49 | return this; 50 | } 51 | 52 | public Template loadTemplate(String templateUri) throws IOException { 53 | this.template = readFile(templateUri); 54 | return this; 55 | } 56 | 57 | public static Template fromJson(String json) { 58 | try { 59 | Template t = (new ObjectMapper()).readValue(json, Template.class); 60 | return t; 61 | } catch (IOException e) { 62 | LOG.error("Error deserializing Template", e); 63 | } 64 | return null; 65 | } 66 | 67 | public String toString() { 68 | String result = null; 69 | try { 70 | result = (new ObjectMapper()).writeValueAsString(this); 71 | } catch (IOException e) { 72 | throw new RuntimeException(e); 73 | } 74 | return result; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /clients/java/src/test/resources/graph.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "0", 3 | "name": "test", 4 | "user": "testuser", 5 | "properties": { 6 | "something":"else" 7 | }, 8 | "status": { 9 | "progress":20, 10 | "startTime":1412354951, 11 | "heartbeatTime": 1412354796, 12 | "statusText":"running" 13 | }, 14 | "nodes": [ 15 | {"id": "a", "properties": {"alias": "one", "operation":"start"}}, 16 | {"id": "b", "properties": {"alias": "two", "operation":"hop"}}, 17 | {"id": "c", "properties": {"alias": "three", "operation":"skip"}}, 18 | {"id": "d", "properties": {"alias": "four", "operation":"join"}}, 19 | {"id": "e", "properties": {"alias": "five", "operation":"read"}}, 20 | {"id": "f", "properties": {"alias": "six", "operation":"fly"}}, 21 | {"id": "g", "properties": {"alias": "seven", "operation":"dance"}}, 22 | {"id": "h", "properties": {"alias": "eight", "operation":"rollerskate"}}, 23 | {"id": "i", "properties": {"alias": "nine", "operation":"roll"}}, 24 | {"id": "j", "properties": {"alias": "ten", "operation":"dive"}}, 25 | {"id": "k", 26 | "type":"Plain", 27 | "url":"http://www.netflix.com", 28 | "properties": {"alias": "eleven", "operation":"tuck", "step_type" : "mapper"} 29 | }, 30 | {"id": "l", "properties": {"alias": "twelve", "operation":"pushup"}}, 31 | {"id": "m", "properties": {"alias": "thirteen", "operation":"twist"}}, 32 | {"id": "n", "properties": {"alias": "fourteen", "operation":"kick"}}, 33 | {"id": "o", "properties": {"alias": "fifteen", "operation":"shout"}}, 34 | {"id": "p", "properties": {"alias": "sixteen", "operation":"finish"}}, 35 | {"id": "job1", "child": "1"}, 36 | {"id": "job2", "child": "2"}, 37 | {"id": "job3", "child": "3"}, 38 | {"id": "job4", "child": "4"}, 39 | {"id": "job5", "child": "5"} 40 | ], 41 | "edges": [ 42 | {"u": "a", "v": "d"}, 43 | {"u": "b", "v": "d"}, 44 | {"u": "c", "v": "e", 45 | "label":"testlabel", 46 | "type": "Something", 47 | "properties": { 48 | "edgeWeight": 2 49 | } 50 | }, 51 | {"u": "d", "v": "f"}, 52 | {"u": "f", "v": "g"}, 53 | {"u": "g", "v": "h"}, 54 | {"u": "h", "v": "i"}, 55 | {"u": "i", "v": "j"}, 56 | {"u": "j", "v": "k"}, 57 | {"u": "k", "v": "l"}, 58 | {"u": "l", "v": "m"}, 59 | {"u": "m", "v": "n"}, 60 | {"u": "n", "v": "o"}, 61 | {"u": "o", "v": "p"} 62 | ], 63 | "node_groups": [ 64 | {"id": "1", "children": ["a","b"], "status":{ 65 | "progress":10, 66 | "startTime":1412354951, 67 | "heartbeatTime": 1412354796, 68 | "statusText":"failed" 69 | } 70 | }, 71 | {"id": "2", "children": ["c","d"], "stages":[{ 72 | "name":"map", 73 | "status":{"progress":100,"startTime":0,"heartbeatTime":0, "statusText":"finished"} 74 | }]}, 75 | {"id": "3", "children": ["e","f","g"]}, 76 | {"id": "4", "children": ["h","i","j"]}, 77 | {"id": "5", "children": ["k","l","m","n","o","p"], 78 | "url": "http://localhost:8080", 79 | "properties": { 80 | "counters":{ 81 | "num_records":2 82 | } 83 | }, 84 | "status":{ 85 | "progress":30, 86 | "startTime":1412354951, 87 | "heartbeatTime": 1412354796, 88 | "statusText":"running" 89 | } 90 | } 91 | ] 92 | } 93 | -------------------------------------------------------------------------------- /clients/python/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | bin/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # Installer logs 26 | pip-log.txt 27 | pip-delete-this-directory.txt 28 | 29 | # Unit test / coverage reports 30 | htmlcov/ 31 | .tox/ 32 | .coverage 33 | .cache 34 | nosetests.xml 35 | coverage.xml 36 | 37 | # Translations 38 | *.mo 39 | 40 | # Mr Developer 41 | .mr.developer.cfg 42 | .project 43 | .pydevproject 44 | 45 | # Rope 46 | .ropeproject 47 | 48 | # Django stuff: 49 | *.log 50 | *.pot 51 | 52 | # Sphinx documentation 53 | docs/_build/ 54 | MANIFEST 55 | 56 | #ipython notebook 57 | *.ipynb 58 | .ipynb_checkpoints 59 | -------------------------------------------------------------------------------- /clients/python/README.md: -------------------------------------------------------------------------------- 1 | # Lipstick 2 | 3 | A simple python client library for lipstick workflow graph visualization 4 | -------------------------------------------------------------------------------- /clients/python/lipstick/__init__.py: -------------------------------------------------------------------------------- 1 | from .lipstick import * 2 | -------------------------------------------------------------------------------- /clients/python/lipstick/lipstick.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from .graph import * 3 | from .template import Template 4 | 5 | class BaseClient(object): 6 | def __init__(self, base_url): 7 | if base_url.startswith("http"): 8 | self.base_url = base_url 9 | else: 10 | self.base_url = "http://"+base_url 11 | 12 | def request(self, method, path, **kwargs): 13 | url = self.base_url+path 14 | return requests.request(method=method, url=url, **kwargs) 15 | 16 | def get(self, path, **kwargs): 17 | kwargs.setdefault('allow_redirects', True) 18 | return self.request('get', path, **kwargs) 19 | 20 | def post(self, path, data=None, **kwargs): 21 | return self.request('post', path, data=data, **kwargs) 22 | 23 | def put(self, path, data=None, **kwargs): 24 | return self.request('put', path, data=data, **kwargs) 25 | 26 | class Client(BaseClient): 27 | job_path = '/v1/job' 28 | template_path = '/template' 29 | 30 | def get(self, graph_id): 31 | path = '%s/%s' % (Client.job_path, graph_id) 32 | response = super(Client, self).get(path) 33 | if (response.ok): 34 | return graph(response.json()) 35 | else: 36 | response.raise_for_status() 37 | 38 | def list(self): 39 | response = super(Client, self).get(Client.job_path) 40 | if (response.ok): 41 | return response.json() 42 | else: 43 | response.raise_for_status() 44 | 45 | def save(self, graph): 46 | return self.post(Client.job_path, data=graph.json(), headers={'content-type': 'application/json'}) 47 | 48 | def update(self, graph): 49 | path = '%s/%s' % (Client.job_path, graph.id()) 50 | return self.put(path, data=graph.json()) 51 | 52 | def create_template(self, template): 53 | path = '%s/%s' % (Client.template_path, template.name) 54 | return self.post(path, data=template.json()) 55 | 56 | def get_template(self, name): 57 | path = '%s/%s' % (Client.template_path, name) 58 | response = super(Client, self).get(path) 59 | if (response.ok): 60 | return template(response.json()) 61 | else: 62 | response.raise_for_status() 63 | 64 | def list_templates(self): 65 | response = super(Client, self).get(Client.template_path) 66 | if (response.ok): 67 | return response.json() 68 | else: 69 | response.raise_for_status() 70 | -------------------------------------------------------------------------------- /clients/python/lipstick/template.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | def template(data): 4 | return Template(data.pop('name'), **data) 5 | 6 | class Template(): 7 | def __init__(self, name, view=None, template=None): 8 | self.name = name 9 | self.view = view 10 | self.template = template 11 | 12 | def view(self, view): 13 | self.view = view 14 | return self 15 | 16 | def template(self, template): 17 | self.template = template 18 | return self 19 | 20 | def load_view(self, view_file): 21 | self.view = open(view_file).read() 22 | return self 23 | 24 | def load_template(self, template_file): 25 | self.template = open(template_file).read() 26 | return self 27 | 28 | def json(self): 29 | return json.dumps(self.__dict__) 30 | -------------------------------------------------------------------------------- /clients/python/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name = 'lipstick', 5 | version = '0.0.1', 6 | url = 'http://github.com/Netflix/Lipstick/clients/python', 7 | description = 'Lipstick client library', 8 | license = 'Apache Software License', 9 | author = 'Netflix Data Platform Architecture', 10 | author_email = 'dataplatformarchitecture@netflix.com', 11 | packages = find_packages(), 12 | install_requires = [ 13 | 'requests>=1.0.0' 14 | ], 15 | zip_safe = False 16 | ) 17 | -------------------------------------------------------------------------------- /codequality/HEADER: -------------------------------------------------------------------------------- 1 | Copyright ${year} Netflix, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /codequality/mysuppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | version=0.6-SNAPSHOT 2 | 3 | -------------------------------------------------------------------------------- /gradle/buildscript.gradle: -------------------------------------------------------------------------------- 1 | // Executed in context of buildscript 2 | repositories { 3 | // Repo in addition to maven central 4 | repositories { maven { url 'http://dl.bintray.com/content/netflixoss/external-gradle-plugins/' } } // For gradle-release 5 | } 6 | dependencies { 7 | classpath 'nl.javadude.gradle.plugins:license-gradle-plugin:0.6.1' 8 | classpath 'com.mapvine:gradle-cobertura-plugin:0.1' 9 | classpath 'gradle-release:gradle-release:1.1.5' 10 | classpath 'org.ajoberstar:gradle-git:0.5.0' 11 | } 12 | -------------------------------------------------------------------------------- /gradle/check.gradle: -------------------------------------------------------------------------------- 1 | // Checkstyle 2 | apply plugin: 'checkstyle' 3 | checkstyle { 4 | ignoreFailures = true 5 | configFile = rootProject.file('codequality/checkstyle.xml') 6 | } 7 | 8 | // FindBugs 9 | apply plugin: 'findbugs' 10 | findbugs { 11 | ignoreFailures = true 12 | } 13 | 14 | // PMD 15 | apply plugin: 'pmd' 16 | //tasks.withType(Pmd) { reports.html.enabled true } 17 | 18 | apply plugin: 'cobertura' 19 | cobertura { 20 | sourceDirs = sourceSets.main.java.srcDirs 21 | format = 'html' 22 | includes = ['**/*.java', '**/*.groovy'] 23 | excludes = [] 24 | } 25 | -------------------------------------------------------------------------------- /gradle/convention.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' // Plugin as major conventions, overwrites status 2 | 3 | sourceCompatibility = 1.6 4 | 5 | // GRADLE-2087 workaround, perform after java plugin 6 | status = project.hasProperty('preferredStatus')?project.preferredStatus:(version.contains('SNAPSHOT')?'snapshot':'release') 7 | 8 | // Indenting to align with multi-project branch 9 | task sourcesJar(type: Jar, dependsOn:classes) { 10 | from sourceSets.main.allSource 11 | classifier 'sources' 12 | extension 'jar' 13 | } 14 | 15 | task javadocJar(type: Jar, dependsOn:javadoc) { 16 | from javadoc.destinationDir 17 | classifier 'javadoc' 18 | extension 'jar' 19 | } 20 | 21 | configurations.add('sources') 22 | configurations.add('javadoc') 23 | configurations.archives { 24 | extendsFrom configurations.sources 25 | extendsFrom configurations.javadoc 26 | } 27 | 28 | // When outputing to an Ivy repo, we want to use the proper type field 29 | gradle.taskGraph.whenReady { 30 | def isNotMaven = !it.hasTask(project.uploadMavenCentral) 31 | if (isNotMaven) { 32 | def artifacts = project.configurations.sources.artifacts 33 | def sourceArtifact = artifacts.iterator().next() 34 | sourceArtifact.type = 'sources' 35 | } 36 | } 37 | 38 | artifacts { 39 | sources(sourcesJar) { 40 | // Weird Gradle quirk where type will be used for the extension, but only for sources 41 | type 'jar' 42 | } 43 | javadoc(javadocJar) { 44 | type 'javadoc' 45 | } 46 | } 47 | 48 | configurations { 49 | provided { 50 | description = 'much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive.' 51 | transitive = true 52 | visible = true 53 | } 54 | } 55 | 56 | project.sourceSets { 57 | main.compileClasspath += project.configurations.provided 58 | main.runtimeClasspath -= project.configurations.provided 59 | test.compileClasspath += project.configurations.provided 60 | test.runtimeClasspath += project.configurations.provided 61 | } 62 | 63 | apply plugin: 'github-pages' // Used to create publishGhPages task 64 | 65 | def docTasks = [:] 66 | [Javadoc,ScalaDoc,Groovydoc].each{ Class docClass -> 67 | tasks.withType(docClass).each { docTask -> 68 | docTasks[docTask.name] = docTask 69 | processGhPages.dependsOn(docTask) 70 | } 71 | } 72 | 73 | githubPages { 74 | repoUri = "git@github.com:Netflix/${rootProject.githubProjectName}.git" 75 | pages { 76 | docTasks.each { shortName, docTask -> 77 | from(docTask.outputs.files) { 78 | into "docs/${shortName}" 79 | } 80 | } 81 | } 82 | } 83 | 84 | // Generate wrapper, which is distributed as part of source to alleviate the need of installing gradle 85 | task createWrapper(type: Wrapper) { 86 | gradleVersion = '1.9' 87 | } 88 | -------------------------------------------------------------------------------- /gradle/license.gradle: -------------------------------------------------------------------------------- 1 | // Dependency for plugin was set in buildscript.gradle 2 | 3 | apply plugin: 'license' //nl.javadude.gradle.plugins.license.LicensePlugin 4 | license { 5 | header rootProject.file('codequality/HEADER') 6 | ext.year = Calendar.getInstance().get(Calendar.YEAR) 7 | skipExistingHeaders true 8 | } 9 | -------------------------------------------------------------------------------- /gradle/release.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'release' 2 | 3 | [ uploadIvyLocal: 'uploadLocal', uploadArtifactory: 'artifactoryPublish', buildWithArtifactory: 'build' ].each { key, value -> 4 | // Call out to compile against internal repository 5 | task "${key}"(type: GradleBuild) { 6 | startParameter = project.gradle.startParameter.newInstance() 7 | doFirst { 8 | startParameter.projectProperties = [status: project.status, preferredStatus: project.status] 9 | } 10 | startParameter.getExcludedTaskNames().add('check') 11 | tasks = [ 'build', value ] 12 | } 13 | } 14 | 15 | // Marker task for following code to key in on 16 | task releaseCandidate(dependsOn: release) 17 | task forceCandidate { 18 | onlyIf { gradle.taskGraph.hasTask(releaseCandidate) } 19 | doFirst { project.status = 'candidate' } 20 | } 21 | task forceRelease { 22 | onlyIf { !gradle.taskGraph.hasTask(releaseCandidate) } 23 | doFirst { project.status = 'release' } 24 | } 25 | release.dependsOn([forceCandidate, forceRelease]) 26 | 27 | task uploadMavenCentral(dependsOn: [':lipstick-console:uploadMavenCentral']) 28 | task releaseSnapshot(dependsOn: [uploadMavenCentral]) 29 | 30 | // Ensure our versions look like the project status before publishing 31 | task verifyStatus << { 32 | def hasSnapshot = version.contains('-SNAPSHOT') 33 | if (project.status == 'snapshot' && !hasSnapshot) { 34 | throw new GradleException("Version (${version}) needs -SNAPSHOT if publishing snapshot") 35 | } 36 | } 37 | uploadArtifactory.dependsOn(verifyStatus) 38 | uploadMavenCentral.dependsOn(verifyStatus) 39 | 40 | // Ensure upload happens before taggging, hence upload failures will leave repo in a revertable state 41 | preTagCommit.dependsOn([uploadMavenCentral]) 42 | 43 | gradle.taskGraph.whenReady { taskGraph -> 44 | def hasRelease = taskGraph.hasTask('commitNewVersion') 45 | def indexOf = { return taskGraph.allTasks.indexOf(it) } 46 | 47 | if (hasRelease) { 48 | assert indexOf(build) < indexOf(unSnapshotVersion), 'build target has to be after unSnapshotVersion' 49 | assert indexOf(uploadMavenCentral) < indexOf(preTagCommit), 'preTagCommit has to be after uploadMavenCentral' 50 | } 51 | } 52 | 53 | // Prevent plugin from asking for a version number interactively 54 | ext.'gradle.release.useAutomaticVersion' = "true" 55 | 56 | release { 57 | git.requireBranch = null 58 | } 59 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Apr 02 11:45:56 PDT 2013 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=http\://services.gradle.org/distributions/gradle-1.5-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /lipstick-console/buildinfo/lipstick_build.txt: -------------------------------------------------------------------------------- 1 | build version: ${version}, ts: ${ts}, git: ${commitid} 2 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/adaptors/LOFilterJsonAdaptor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.adaptors; 17 | 18 | import org.apache.pig.impl.logicalLayer.FrontendException; 19 | import org.apache.pig.newplan.logical.relational.LOFilter; 20 | import org.apache.pig.newplan.logical.relational.LogicalPlan; 21 | 22 | import com.netflix.lipstick.model.operators.P2jLOFilter; 23 | 24 | /** 25 | * LOFilter to Lipstick model adaptor. 26 | * 27 | * @author jmagnusson 28 | * 29 | */ 30 | public class LOFilterJsonAdaptor extends LOJsonAdaptor { 31 | 32 | /** 33 | * Instantiate a new LOFilterJsonAdaptor and populate the filter expression field. 34 | * 35 | * @param node LOFilterJsonAdaptor operator to convert to P2jLOCogroup. 36 | * @param lp the LogicalPlan containing node 37 | * @throws FrontendException 38 | */ 39 | public LOFilterJsonAdaptor(LOFilter node, LogicalPlan lp) throws FrontendException { 40 | super(node, new P2jLOFilter(), lp); 41 | P2jLOFilter filter = (P2jLOFilter) p2j; 42 | filter.setExpression(LogicalExpressionPlanSerializer.serialize(node.getFilterPlan())); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/adaptors/LOLimitJsonAdaptor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.adaptors; 17 | 18 | import org.apache.pig.impl.logicalLayer.FrontendException; 19 | import org.apache.pig.newplan.logical.relational.LOLimit; 20 | import org.apache.pig.newplan.logical.relational.LogicalPlan; 21 | 22 | import com.netflix.lipstick.model.operators.P2jLOLimit; 23 | 24 | /** 25 | * LOLimit to Lipstick model adaptor. 26 | * 27 | * @author jmagnusson 28 | * 29 | */ 30 | public class LOLimitJsonAdaptor extends LOJsonAdaptor { 31 | 32 | /** 33 | * Initializes a new LOLimitJsonAdaptor and sets the row limit on the P2jLOLimit object created. 34 | * 35 | * @param node the LOLimit operator to convert to P2jLOLimit 36 | * @param lp the logical plan containing node 37 | * @throws FrontendException 38 | */ 39 | public LOLimitJsonAdaptor(LOLimit node, LogicalPlan lp) throws FrontendException { 40 | super(node, new P2jLOLimit(), lp); 41 | ((P2jLOLimit) p2j).setRowLimit(node.getLimit()); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/adaptors/LOLoadJsonAdaptor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.adaptors; 17 | 18 | import org.apache.commons.lang.StringUtils; 19 | import org.apache.pig.impl.logicalLayer.FrontendException; 20 | import org.apache.pig.newplan.logical.relational.LOLoad; 21 | import org.apache.pig.newplan.logical.relational.LogicalPlan; 22 | 23 | import com.netflix.lipstick.model.operators.P2jLOLoad; 24 | 25 | /** 26 | * LOLoad to Lipstick model adaptor. 27 | * 28 | * @author jmagnusson 29 | * 30 | */ 31 | public class LOLoadJsonAdaptor extends LOJsonAdaptor { 32 | /** 33 | * Initializes a new LOLoadJsonAdaptor and additionally sets the storage 34 | * function and location on the P2jLOLoad object being created. 35 | * 36 | * @param node 37 | * @param lp 38 | * @throws FrontendException 39 | */ 40 | public LOLoadJsonAdaptor(LOLoad node, LogicalPlan lp) throws FrontendException { 41 | super(node, new P2jLOLoad(), lp); 42 | P2jLOLoad load = (P2jLOLoad) p2j; 43 | load.setStorageLocation(node.getFileSpec().getFileName()); 44 | String[] funcList = StringUtils.split(node.getFileSpec().getFuncName(), "."); 45 | load.setStorageFunction(funcList[funcList.length - 1]); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/adaptors/LOSplitOutputJsonAdaptor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.adaptors; 17 | 18 | import org.apache.pig.impl.logicalLayer.FrontendException; 19 | import org.apache.pig.newplan.logical.relational.LOSplitOutput; 20 | import org.apache.pig.newplan.logical.relational.LogicalPlan; 21 | 22 | import com.netflix.lipstick.model.operators.P2jLOSplitOutput; 23 | 24 | /** 25 | * LOSplit to Lipstick model adaptor. 26 | * 27 | * @author jmagnusson 28 | */ 29 | public class LOSplitOutputJsonAdaptor extends LOJsonAdaptor { 30 | 31 | /** 32 | * Initializes a new LOSplitOutputJsonAdaptor and additionally sets the 33 | * filter expression on the P2jLOSplitOutput object that is created. 34 | * 35 | * @param node the LOSplitOutput to adapt to P2jLOSplitOutput 36 | * @param lp the logical plan containing node 37 | * @throws FrontendException 38 | */ 39 | public LOSplitOutputJsonAdaptor(LOSplitOutput node, LogicalPlan lp) throws FrontendException { 40 | super(node, new P2jLOSplitOutput(), lp); 41 | ((P2jLOSplitOutput) p2j).setExpression(LogicalExpressionPlanSerializer.serialize(node.getFilterPlan())); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/adaptors/LOStoreJsonAdaptor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.adaptors; 17 | 18 | import org.apache.commons.lang.StringUtils; 19 | import org.apache.pig.impl.logicalLayer.FrontendException; 20 | import org.apache.pig.newplan.logical.relational.LOStore; 21 | import org.apache.pig.newplan.logical.relational.LogicalPlan; 22 | 23 | import com.netflix.lipstick.model.operators.P2jLOStore; 24 | 25 | /** 26 | * LOStore to Lipstick model adaptor. 27 | * 28 | * @author jmagnusson 29 | * 30 | */ 31 | public class LOStoreJsonAdaptor extends LOJsonAdaptor { 32 | 33 | /** 34 | * Initializes a new LOStoreJsonAdaptor and additionally sets the storage 35 | * function and location on the P2jLOStore object being created. 36 | * 37 | * @param node the LOStore object to convert to P2jLOStore 38 | * @param lp the logical plan containing node 39 | * @throws FrontendException 40 | */ 41 | public LOStoreJsonAdaptor(LOStore node, LogicalPlan lp) throws FrontendException { 42 | super(node, new P2jLOStore(), lp); 43 | P2jLOStore store = (P2jLOStore) p2j; 44 | store.setStorageLocation(node.getFileSpec().getFileName()); 45 | String[] funcList = StringUtils.split(node.getFileSpec().getFuncName(), "."); 46 | store.setStorageFunction(funcList[funcList.length - 1]); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/adaptors/package.html: -------------------------------------------------------------------------------- 1 |

Pig logical operator to Lipstick model operator object adaptors.

2 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/listeners/PPNLErrorHandler.java: -------------------------------------------------------------------------------- 1 | package com.netflix.lipstick.listeners; 2 | 3 | /** 4 | * Lipstick Pig Progress notification listener Error Handler 5 | * 6 | * For finer grain error hanlding from the LipstickPPNL, implement 7 | * this interface, then pass an instance to LipstickPPNL.addErrorHandler() 8 | */ 9 | public interface PPNLErrorHandler { 10 | 11 | /** 12 | * Called when an unhandled excepction from LipstickPPNL.setPlanGenerators() occurs 13 | */ 14 | public void handlePlanGeneratorsError(Exception e); 15 | 16 | /** 17 | * Called when an unhandled excepction from LipstickPPNL.initialPlanNotification() occurs 18 | */ 19 | public void handleInitialPlanNotificationError(Exception e); 20 | 21 | /** 22 | * Called when an unhandled excepction from LipstickPPNL.jobStartedNotification() occurs 23 | */ 24 | public void handleJobStartedNotificationError(Exception e); 25 | 26 | /** 27 | * Called when an unhandled excepction from LipstickPPNL.jobFinishedNotification() occurs 28 | */ 29 | public void handleJobFinishedNotificationError(Exception e); 30 | 31 | /** 32 | * Called when an unhandled excepction from LipstickPPNL.jobFailedNotification() occurs 33 | */ 34 | public void handleJobFailedNotificationError(Exception e); 35 | 36 | /** 37 | * Called when an unhandled excepction from LipstickPPNL.progressUpdatedNotification() occurs 38 | */ 39 | public void handleProgressUpdatedNotificationError(Exception e); 40 | 41 | /** 42 | * Called when an unhandled excepction from LipstickPPNL.launchCompletedNotification() occurs 43 | */ 44 | public void handleLaunchCompletedNotificationError(Exception e); 45 | 46 | } 47 | 48 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/listeners/package.html: -------------------------------------------------------------------------------- 1 |

Pig progress notification listeners.

2 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/model/P2jCounters.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.model; 17 | 18 | import java.util.Map; 19 | 20 | import javax.persistence.ElementCollection; 21 | import javax.persistence.Entity; 22 | import javax.persistence.GeneratedValue; 23 | import javax.persistence.Id; 24 | 25 | import com.google.common.collect.Maps; 26 | /** 27 | * Container for Map/Reduce job counters. 28 | * @author jmagnusson 29 | * 30 | */ 31 | @Entity 32 | public class P2jCounters { 33 | private Map counters = Maps.newHashMap(); 34 | private long id; 35 | 36 | @ElementCollection 37 | public Map getCounters() { 38 | return counters; 39 | } 40 | 41 | @Id 42 | @GeneratedValue 43 | public long getId() { 44 | return id; 45 | } 46 | 47 | public void setCounters(Map counters) { 48 | this.counters = counters; 49 | } 50 | 51 | public void setId(long id) { 52 | this.id = id; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/model/P2jPlan.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.model; 17 | 18 | import java.util.Map; 19 | 20 | import javax.persistence.CascadeType; 21 | import javax.persistence.Entity; 22 | import javax.persistence.GeneratedValue; 23 | import javax.persistence.Id; 24 | import javax.persistence.Lob; 25 | import javax.persistence.OneToMany; 26 | 27 | import com.netflix.lipstick.model.operators.P2jLogicalRelationalOperator; 28 | 29 | /** 30 | * Container for a logical plan. 31 | * 32 | * @author jmagnusson 33 | * 34 | */ 35 | @Entity 36 | public class P2jPlan { 37 | 38 | private long id; 39 | private Map plan = null; 40 | private String svg = null; 41 | 42 | /** 43 | * Construct an empty P2jPlan object. 44 | */ 45 | public P2jPlan() { 46 | } 47 | 48 | /** 49 | * Construct a new P2jPlan object and set the plan. 50 | * @param plan 51 | */ 52 | public P2jPlan(Map plan) { 53 | super(); 54 | setPlan(plan); 55 | } 56 | 57 | /** 58 | * Construct a new P2jPlan object and set both the plan and svg representation of the plan. 59 | * 60 | * @param plan 61 | * @param svg 62 | */ 63 | public P2jPlan(Map plan, String svg) { 64 | super(); 65 | setPlan(plan); 66 | setSvg(svg); 67 | } 68 | 69 | @Id 70 | @GeneratedValue 71 | public long getId() { 72 | return id; 73 | } 74 | 75 | /** 76 | * Get the logical plan. 77 | * 78 | * @return a representation of the logical plan as a map of uid to P2jLogicalRelationalOperator. 79 | */ 80 | @OneToMany(cascade = CascadeType.ALL) 81 | public Map getPlan() { 82 | return plan; 83 | } 84 | 85 | /** 86 | * Get the graphical representation of the plan in svg format. 87 | * 88 | * @return a string containing the svg representation of the logical plan 89 | */ 90 | @Lob 91 | public String getSvg() { 92 | return svg; 93 | } 94 | 95 | public void setId(long id) { 96 | this.id = id; 97 | } 98 | 99 | public void setPlan(Map plan) { 100 | this.plan = plan; 101 | } 102 | 103 | public void setSvg(String svg) { 104 | this.svg = svg; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/model/P2jSampleOutput.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.model; 17 | 18 | import javax.persistence.Entity; 19 | import javax.persistence.GeneratedValue; 20 | import javax.persistence.Id; 21 | import javax.persistence.Lob; 22 | 23 | /** 24 | * Container for sample output of a store operation in a map/reduce job. 25 | * 26 | * @author jmagnusson 27 | * 28 | */ 29 | @Entity 30 | public class P2jSampleOutput { 31 | 32 | private long id; 33 | private String schemaString; 34 | private String sampleOutput; 35 | 36 | @Id 37 | @GeneratedValue 38 | public long getId() { 39 | return id; 40 | } 41 | 42 | public void setId(long id) { 43 | this.id = id; 44 | } 45 | 46 | @Lob 47 | public String getSchemaString() { 48 | return schemaString; 49 | } 50 | 51 | public void setSchemaString(String schemaString) { 52 | this.schemaString = schemaString; 53 | } 54 | 55 | @Lob 56 | public String getSampleOutput() { 57 | return sampleOutput; 58 | } 59 | 60 | public void setSampleOutput(String sampleOutput) { 61 | this.sampleOutput = sampleOutput; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/model/P2jSampleOutputList.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.model; 17 | 18 | import java.util.List; 19 | 20 | import javax.persistence.CascadeType; 21 | import javax.persistence.Entity; 22 | import javax.persistence.GeneratedValue; 23 | import javax.persistence.Id; 24 | import javax.persistence.OneToMany; 25 | 26 | import com.google.common.collect.Lists; 27 | 28 | /** 29 | * Container for the set of sample output data produced by a map/reduce job. 30 | * 31 | * @author jmagnusson 32 | * 33 | */ 34 | @Entity 35 | public class P2jSampleOutputList { 36 | 37 | private List sampleOutputList; 38 | private long id; 39 | 40 | /** 41 | * Construct an empty P2jSampleOutputList. 42 | */ 43 | public P2jSampleOutputList() { 44 | } 45 | 46 | @OneToMany(cascade = CascadeType.ALL) 47 | public List getSampleOutputList() { 48 | return sampleOutputList; 49 | } 50 | 51 | public void setSampleOutputList(List sampleOutputList) { 52 | this.sampleOutputList = sampleOutputList; 53 | } 54 | 55 | /** 56 | * Add a new P2jSampleOutput to the P2jSampleOutputList. 57 | * 58 | * @param sampleOutput the P2jSampleOutput to add 59 | */ 60 | public void add(P2jSampleOutput sampleOutput) { 61 | if (sampleOutputList == null) { 62 | sampleOutputList = Lists.newArrayList(); 63 | } 64 | sampleOutputList.add(sampleOutput); 65 | } 66 | 67 | @Id 68 | @GeneratedValue 69 | public long getId() { 70 | return id; 71 | } 72 | 73 | public void setId(long id) { 74 | this.id = id; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/model/P2jScripts.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.model; 17 | 18 | import javax.persistence.Entity; 19 | import javax.persistence.GeneratedValue; 20 | import javax.persistence.Id; 21 | import javax.persistence.Lob; 22 | 23 | /** 24 | * Model object for pig scripts. 25 | * 26 | * @author jmagnusson 27 | * 28 | */ 29 | @Entity 30 | public class P2jScripts { 31 | private long id; 32 | private String script; 33 | 34 | /** 35 | * Create an empty P2jScripts. 36 | */ 37 | public P2jScripts() { 38 | } 39 | 40 | /** 41 | * Create a new P2jScripts and set script. 42 | * 43 | * @param script a pig script 44 | */ 45 | public P2jScripts(String script) { 46 | this.script = script; 47 | } 48 | 49 | @Id 50 | @GeneratedValue 51 | public long getId() { 52 | return id; 53 | } 54 | 55 | @Lob 56 | public String getScript() { 57 | return script; 58 | } 59 | 60 | public void setId(long id) { 61 | this.id = id; 62 | } 63 | 64 | public void setScript(String script) { 65 | this.script = script; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/model/P2jWarning.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.model; 17 | 18 | import java.util.Map; 19 | 20 | import javax.persistence.ElementCollection; 21 | import javax.persistence.Entity; 22 | import javax.persistence.GeneratedValue; 23 | import javax.persistence.Id; 24 | 25 | import com.google.common.collect.Maps; 26 | 27 | /** 28 | * Container for a warning, a map containing attributes specific to 29 | * a single warning about a pig script or map/reduce job. 30 | * @author mroddy 31 | * 32 | */ 33 | @Entity 34 | public class P2jWarning { 35 | private Map warningAttributes = Maps.newHashMap(); 36 | private long id; 37 | private String jobId; 38 | private String warningKey; 39 | 40 | @ElementCollection 41 | public Map getWarningAttributes() { 42 | return warningAttributes; 43 | } 44 | 45 | @Id 46 | @GeneratedValue 47 | public long getId() { 48 | return id; 49 | } 50 | 51 | public void setWarningAttributes(Map warningAttributes) { 52 | this.warningAttributes = warningAttributes; 53 | } 54 | 55 | public void setId(long id) { 56 | this.id = id; 57 | } 58 | 59 | public String getJobId() { 60 | return jobId; 61 | } 62 | 63 | public void setJobId(String jobId) { 64 | this.jobId = jobId; 65 | } 66 | 67 | public String getWarningKey() { 68 | return warningKey; 69 | } 70 | 71 | public void setWarningKey(String warningKey) { 72 | this.warningKey = warningKey; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/model/Utils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.model; 17 | 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | import org.apache.pig.data.DataType; 22 | import org.apache.pig.impl.logicalLayer.schema.Schema; 23 | import org.apache.pig.newplan.logical.Util; 24 | import org.apache.pig.newplan.logical.relational.LogicalSchema; 25 | import org.apache.pig.newplan.logical.relational.LogicalSchema.LogicalFieldSchema; 26 | import org.apache.pig.parser.ParserException; 27 | 28 | import com.google.common.collect.Lists; 29 | import com.netflix.lipstick.model.operators.elements.SchemaElement; 30 | 31 | /** 32 | * Utilities related to the lipstick model. 33 | * 34 | * @author jmagnusson 35 | */ 36 | public final class Utils { 37 | 38 | /** Mapping of type id to string type. */ 39 | protected static final Map TYPESMAP = DataType.genTypeToNameMap(); 40 | 41 | private Utils() { } 42 | 43 | /** 44 | * Produces a list of Lipstick SchemaElements from a string representation of a schema. 45 | * 46 | * @param schemaString the schema string 47 | * @return a list of schema elements 48 | * @throws ParserException the parser exception 49 | */ 50 | public static List processSchema(String schemaString) throws ParserException { 51 | String tempString = schemaString.replace(".", "_"); 52 | tempString = tempString.substring(1, tempString.length() - 1); 53 | Schema temp = org.apache.pig.impl.util.Utils.getSchemaFromString(tempString); 54 | return processSchema(Util.translateSchema(temp)); 55 | } 56 | 57 | /** 58 | * Produces a list of Lipstick SchemaElements given a LogicalSchema. 59 | * 60 | * @param src the LogicalSchema 61 | * @return a list of SchemaElements 62 | */ 63 | public static List processSchema(LogicalSchema src) { 64 | 65 | if (src == null) { 66 | return null; 67 | } 68 | 69 | List schemaList = Lists.newArrayList(); 70 | List fields = src.getFields(); 71 | for (LogicalFieldSchema f : fields) { 72 | SchemaElement ele = new SchemaElement(); 73 | if (f.alias != null) { 74 | ele.setAlias(f.alias.toString()); 75 | } 76 | ele.setType(TYPESMAP.get(f.type)); 77 | if (f.schema != null) { 78 | ele.setSchemaElements(processSchema(f.schema)); 79 | } 80 | schemaList.add(ele); 81 | } 82 | return schemaList; 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/model/operators/P2jLOCogroup.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.model.operators; 17 | 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | import javax.persistence.Entity; 22 | 23 | /** 24 | * Lipstick model object for LOCogroup operator. 25 | * 26 | * @author jmagnusson 27 | * 28 | */ 29 | @Entity 30 | public class P2jLOCogroup extends P2jLogicalRelationalOperator { 31 | private Join group; 32 | 33 | public Join getGroup() { 34 | return group; 35 | } 36 | 37 | public void setGroup(Join group) { 38 | this.group = group; 39 | } 40 | 41 | /** 42 | * Set the group attribute. 43 | * 44 | * @param strategy the join strategy used 45 | * @param type the join type used 46 | * @param expression a map of alias to list of fields being joined 47 | */ 48 | public void setGroup(String strategy, String type, Map> expression) { 49 | group = new Join(strategy, type, expression); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/model/operators/P2jLOFilter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.model.operators; 17 | 18 | import javax.persistence.Column; 19 | 20 | import javax.persistence.Entity; 21 | /** 22 | * Lipstick model object for LOFilter operator. 23 | * 24 | * @author jmagnusson 25 | * 26 | */ 27 | @Entity 28 | public class P2jLOFilter extends P2jLogicalRelationalOperator { 29 | private String expression; 30 | 31 | @Column(length = 2048) 32 | public String getExpression() { 33 | return expression; 34 | } 35 | 36 | public void setExpression(String expression) { 37 | this.expression = expression; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/model/operators/P2jLOJoin.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.model.operators; 17 | 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | import javax.persistence.Entity; 22 | 23 | /** 24 | * Lipstick model object for LOJoin operator. 25 | * 26 | * @author jmagnusson 27 | * 28 | */ 29 | @Entity 30 | public class P2jLOJoin extends P2jLogicalRelationalOperator { 31 | private Join join; 32 | 33 | public Join getJoin() { 34 | return join; 35 | } 36 | 37 | public void setJoin(Join join) { 38 | this.join = join; 39 | } 40 | 41 | /** 42 | * Set the join attribute. 43 | * 44 | * @param strategy the join strategy used 45 | * @param type the join type used 46 | * @param expression a map of alias to list of fields being joined 47 | */ 48 | public void setJoin(String strategy, String type, Map> expression) { 49 | join = new Join(strategy, type, expression); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/model/operators/P2jLOLimit.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.model.operators; 17 | 18 | import javax.persistence.Entity; 19 | 20 | /** 21 | * Lipstick model object for LOLimit operator. 22 | * 23 | * @author jmagnusson 24 | * 25 | */ 26 | @Entity 27 | public class P2jLOLimit extends P2jLogicalRelationalOperator { 28 | private long rowLimit; 29 | 30 | public long getRowLimit() { 31 | return rowLimit; 32 | } 33 | 34 | public void setRowLimit(long rowLimit) { 35 | this.rowLimit = rowLimit; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/model/operators/P2jLOLoad.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.model.operators; 17 | 18 | import javax.persistence.Column; 19 | import javax.persistence.Entity; 20 | 21 | /** 22 | * Lipstick model object for LOLoad operator. 23 | * 24 | * @author jmagnusson 25 | * 26 | */ 27 | @Entity 28 | public class P2jLOLoad extends P2jLogicalRelationalOperator { 29 | @Column(length = 2048) 30 | public String getStorageLocation() { 31 | return storageLocation; 32 | } 33 | 34 | public void setStorageLocation(String storageLocation) { 35 | this.storageLocation = storageLocation; 36 | } 37 | 38 | public String getStorageFunction() { 39 | return storageFunction; 40 | } 41 | 42 | public void setStorageFunction(String storageFunction) { 43 | this.storageFunction = storageFunction; 44 | } 45 | 46 | private String storageLocation; 47 | private String storageFunction; 48 | } 49 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/model/operators/P2jLOSplitOutput.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.model.operators; 17 | 18 | import javax.persistence.Column; 19 | import javax.persistence.Entity; 20 | 21 | /** 22 | * Lipstick model object for LOSplitOutput operator. 23 | * 24 | * @author jmagnusson 25 | * 26 | */ 27 | @Entity 28 | public class P2jLOSplitOutput extends P2jLogicalRelationalOperator { 29 | private String expression; 30 | 31 | @Column(length = 2048) 32 | public String getExpression() { 33 | return expression; 34 | } 35 | 36 | public void setExpression(String expression) { 37 | this.expression = expression; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/model/operators/P2jLOStore.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.model.operators; 17 | 18 | import javax.persistence.Column; 19 | import javax.persistence.Entity; 20 | 21 | /** 22 | * Lipstick model object for LOStore operator. 23 | * 24 | * @author jmagnusson 25 | * 26 | */ 27 | @Entity 28 | public class P2jLOStore extends P2jLogicalRelationalOperator { 29 | @Column(length = 2048) 30 | public String getStorageLocation() { 31 | return storageLocation; 32 | } 33 | 34 | public void setStorageLocation(String storageLocation) { 35 | this.storageLocation = storageLocation; 36 | } 37 | 38 | public String getStorageFunction() { 39 | return storageFunction; 40 | } 41 | 42 | public void setStorageFunction(String storageFunction) { 43 | this.storageFunction = storageFunction; 44 | } 45 | 46 | private String storageLocation; 47 | private String storageFunction; 48 | } 49 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/model/operators/elements/JoinExpression.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.model.operators.elements; 17 | 18 | import java.util.List; 19 | 20 | import javax.persistence.ElementCollection; 21 | import javax.persistence.Entity; 22 | import javax.persistence.GeneratedValue; 23 | import javax.persistence.Id; 24 | 25 | /** 26 | * Lipstick model object representing a join expression. 27 | * 28 | * @author jmagnusson 29 | * 30 | */ 31 | @Entity 32 | public class JoinExpression { 33 | private List fields; 34 | private long id; 35 | 36 | /** 37 | * Construct an empty JoinExpression object. 38 | */ 39 | public JoinExpression() { 40 | } 41 | 42 | /** 43 | * Construct a new JoinExpression object and set fields attribute. 44 | * @param fields the list of fields being joined 45 | */ 46 | public JoinExpression(List fields) { 47 | this.fields = fields; 48 | } 49 | 50 | @ElementCollection 51 | public List getFields() { 52 | return fields; 53 | } 54 | 55 | public void setFields(List fields) { 56 | this.fields = fields; 57 | } 58 | 59 | @Id 60 | @GeneratedValue 61 | public long getId() { 62 | return id; 63 | } 64 | 65 | public void setId(long id) { 66 | this.id = id; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/model/operators/elements/SchemaElement.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.model.operators.elements; 17 | 18 | import java.util.List; 19 | 20 | import org.codehaus.jackson.annotate.JsonProperty; 21 | 22 | /** 23 | * Lipstick model object representing an individual field in a schema. 24 | * 25 | * @author jmagnusson 26 | * 27 | */ 28 | public class SchemaElement { 29 | private String alias = null; 30 | private String type = null; 31 | private List schemaElements = null; 32 | 33 | 34 | /** 35 | * Construct an empty SchemaElement. 36 | */ 37 | public SchemaElement() { 38 | } 39 | 40 | /** 41 | * Construct a new schema element. 42 | * 43 | * @param alias element alias 44 | * @param type element type 45 | * @param uid uid of this element 46 | * @param schemaElements a schema embedded in this element, expressed as a list of SchemaElements 47 | */ 48 | public SchemaElement(String alias, String type, Long uid, List schemaElements) { 49 | this.alias = alias; 50 | this.type = type; 51 | this.schemaElements = schemaElements; 52 | } 53 | 54 | public String getAlias() { 55 | return alias; 56 | } 57 | 58 | public void setAlias(String alias) { 59 | this.alias = alias; 60 | } 61 | 62 | public String getType() { 63 | return type; 64 | } 65 | 66 | public void setType(String type) { 67 | this.type = type; 68 | } 69 | 70 | @JsonProperty("schema") 71 | public List getSchemaElements() { 72 | return schemaElements; 73 | } 74 | 75 | @JsonProperty("schema") 76 | public void setSchemaElements(List schemaElements) { 77 | this.schemaElements = schemaElements; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/model/operators/elements/package.html: -------------------------------------------------------------------------------- 1 |

Lipstick model objects that are elements of logical operators.

2 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/model/operators/package.html: -------------------------------------------------------------------------------- 1 |

Lipstick model objects representing logical operators.

2 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/model/package.html: -------------------------------------------------------------------------------- 1 |

Lipstick model objects.

2 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/package.html: -------------------------------------------------------------------------------- 1 |

Top level package for Lipstick.

2 | 3 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/pigstatus/PigStatusClient.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.pigstatus; 17 | 18 | import com.netflix.lipstick.model.P2jPlanPackage; 19 | import com.netflix.lipstick.model.P2jPlanStatus; 20 | import com.netflix.lipstick.model.P2jSampleOutputList; 21 | 22 | /** 23 | * 24 | * Interface for Lipstick client communication to server. 25 | * 26 | * @author nbates 27 | * 28 | */ 29 | public interface PigStatusClient { 30 | /** 31 | * Persists a P2JPlanPackage which will presumably be used 32 | * by the server with which the client is interacting. 33 | * 34 | * @param plans 35 | * @return 36 | */ 37 | String savePlan(P2jPlanPackage plans); 38 | 39 | /** 40 | * Saves the status of the P2jPlanStatus. 41 | * It's expected that this will trigger an update of the 42 | * P2jPlanPackage with the given uuid. 43 | * 44 | * @param uuid 45 | * @param status 46 | */ 47 | void saveStatus(String uuid, P2jPlanStatus status); 48 | 49 | /** 50 | * Saves the sample output for a given job. 51 | * It's expected that this will trigger an update of the 52 | * P2jPlanPackage with the given uuid. 53 | * 54 | * @param uuid 55 | * @param jobId 56 | * @param sampleOutputList 57 | */ 58 | void saveSampleOutput(String uuid, String jobId, P2jSampleOutputList sampleOutputList); 59 | } 60 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/pigstatus/package.html: -------------------------------------------------------------------------------- 1 |

Lipstick clients.

2 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/pigtolipstick/package.html: -------------------------------------------------------------------------------- 1 |

Converters of listener notifications to lipstick model objects sent across a lipstick client.

2 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/util/EzIterable.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.util; 17 | 18 | import java.util.Iterator; 19 | 20 | /** 21 | * Utility to facilitate easy iteration. 22 | * @author nbates 23 | * 24 | * @param 25 | */ 26 | public class EzIterable implements Iterable { 27 | private final Iterator iter; 28 | 29 | /** 30 | * Constructs an EzIterable with the given iterator. 31 | * 32 | * @param iter 33 | */ 34 | public EzIterable(Iterator iter) { 35 | this.iter = iter; 36 | } 37 | 38 | @Override 39 | public Iterator iterator() { 40 | return iter; 41 | } 42 | 43 | /** 44 | * Returns a strongly typed EzIterable based on the input iterator. 45 | * 46 | * @param iter 47 | * @return 48 | */ 49 | public static EzIterable getIterable(Iterator iter) { 50 | return new EzIterable(iter); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/com/netflix/lipstick/util/package.html: -------------------------------------------------------------------------------- 1 | 2 |

Utility objects.

3 | 4 | -------------------------------------------------------------------------------- /lipstick-console/src/main/java/org/apache/pig/tools/grunt/LipstickGrunt.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.apache.pig.tools.grunt; 19 | 20 | import java.io.BufferedReader; 21 | 22 | import org.apache.pig.LipstickPigServer; 23 | import org.apache.pig.backend.executionengine.ExecException; 24 | import org.apache.pig.impl.PigContext; 25 | 26 | public class LipstickGrunt extends Grunt { 27 | 28 | public LipstickGrunt(BufferedReader in, PigContext pigContext) throws ExecException { 29 | super(in, pigContext); 30 | this.pig = new LipstickPigServer(pigContext); 31 | if (this.in != null) { 32 | parser = new GruntParser(this.in, pig); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lipstick-console/src/main/resources/META-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /lipstick-console/src/main/resources/MessageResources.properties: -------------------------------------------------------------------------------- 1 | footerText=This text is defined in the MessageResource.properties file 2 | -------------------------------------------------------------------------------- /lipstick-console/src/main/webapp/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Class-Path: 3 | 4 | -------------------------------------------------------------------------------- /lipstick-console/src/main/webapp/META-INF/context.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /lipstick-console/src/main/webapp/WEB-INF/faces-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | MessageResources 13 | messages 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /lipstick-console/src/main/webapp/WEB-INF/jetty-env.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | BeanManager 9 | 10 | 11 | javax.enterprise.inject.spi.BeanManager 12 | org.jboss.weld.resources.ManagerObjectFactory 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /lipstick-console/src/main/webapp/WEB-INF/templates/template.xhtml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | ${project.artifactId} Application 8 | 9 | 10 | 11 | 12 | 13 | 14 |

15 | pig2json 16 |

17 |
18 | 19 | 20 |

Sidebar

Content for the sidebar goes here 21 |
22 | 23 | Main Content 24 | 25 |
26 | #{messages.footerText}
Generated using the Knappsack Java EE Maven Archetypes from www.AndyGibson.net 28 |
29 |
30 | 31 |
32 | 33 | -------------------------------------------------------------------------------- /lipstick-console/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 14 | 17 | 18 | Pig2Json 19 | 20 | com.netflix.dse.pig2json.web.Pig2JsonServlet 21 | 22 | 1 23 | 24 | 25 | 26 | Pig2Json 27 | /p2j/* 28 | 29 | 30 | 31 | 90 32 | 33 | -------------------------------------------------------------------------------- /lipstick-console/src/main/webapp/home.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 |

9 |
10 |
11 | -------------------------------------------------------------------------------- /lipstick-console/src/main/webapp/resources/css/screen.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | background-color: #EAECEE; 5 | font-family: Verdana, sans-serif; 6 | font-size: 10px; 7 | } 8 | 9 | #header { 10 | height: 120px; 11 | border-bottom: 3px solid #c0c0c0; 12 | background: #204080; 13 | text-align: center; 14 | color: #f0f0f0; 15 | margin: 0; 16 | padding: 0; 17 | } 18 | 19 | #header h1 { 20 | padding-top: 24px; 21 | font-size: 250%; 22 | } 23 | 24 | #header a { 25 | color : #f0f0f0; 26 | } 27 | 28 | #page { 29 | background: #ffffff; 30 | width: 960px; 31 | margin-left: auto; 32 | margin-right: auto; 33 | border-right: solid 2px #a0a0a0; 34 | border-bottom: solid 2px #a0a0a0; 35 | } 36 | 37 | #sidebar { 38 | float: right; 39 | width: 200px; 40 | border: 1px solid #d0d0d0; 41 | min-height: 240px; 42 | padding: 4px; 43 | } 44 | 45 | #sidebar h1 { 46 | display: block; 47 | font-size: 120%; 48 | border-bottom: 1px solid #d0d0d0; 49 | } 50 | 51 | #container { 52 | padding: 8px; 53 | } 54 | 55 | #content { 56 | min-height: 320px; 57 | font-size: 120%; 58 | width: 720px; 59 | } 60 | 61 | #footer { 62 | text-align: center; 63 | padding: 8px; 64 | margin: 8px; 65 | border-top: 1px solid #c0c0c0; 66 | clear : both; 67 | } 68 | 69 | .panel { 70 | border: 1px solid #d0d0d0; 71 | padding: 8px; 72 | margin: 8px 4px 16px 4px; 73 | background: #fafafa; 74 | } 75 | 76 | .panel h1 { 77 | font-size: 120%; 78 | display: block; 79 | background: #f0f0f0; 80 | padding: 6px; 81 | margin: -8px; 82 | border-bottom: 1px solid #d0d0d0; 83 | margin-bottom: 8px; 84 | } 85 | 86 | .odd { 87 | background: #f0f0f0; 88 | } 89 | 90 | .even { 91 | background: #ffffff; 92 | } 93 | 94 | .dataTable { 95 | border-collapse: collapse;; 96 | width: 100%; 97 | } 98 | 99 | .dataTable th { 100 | background: #204080; 101 | color: #f0f0f0; 102 | padding: 6px; 103 | border: 1px solid #f0f0f0; 104 | } 105 | 106 | .dataTable td { 107 | border: 1px solid #d0d0d0; 108 | padding: 4px; 109 | } 110 | 111 | .caption { 112 | float: left; 113 | width: 100px; 114 | } 115 | 116 | .formCaption { 117 | float: left; 118 | width: 100px; 119 | padding-top: 4px; 120 | } 121 | 122 | .value { 123 | margin-right: 8px; 124 | } 125 | 126 | .errorMessage { 127 | color: #ff0000; 128 | font-weight: bold; 129 | } 130 | 131 | .property { 132 | margin-bottom: 12px; 133 | } 134 | -------------------------------------------------------------------------------- /lipstick-console/src/main/webapp/resources/img/404.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-console/src/main/webapp/resources/img/404.gif -------------------------------------------------------------------------------- /lipstick-console/src/main/webapp/resources/img/error.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-console/src/main/webapp/resources/img/error.gif -------------------------------------------------------------------------------- /lipstick-console/src/main/webapp/resources/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-console/src/main/webapp/resources/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /lipstick-console/src/main/webapp/resources/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-console/src/main/webapp/resources/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /lipstick-console/src/main/webapp/resources/img/netflix_logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-console/src/main/webapp/resources/img/netflix_logo_small.png -------------------------------------------------------------------------------- /lipstick-console/src/main/webapp/resources/img/pending.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-console/src/main/webapp/resources/img/pending.gif -------------------------------------------------------------------------------- /lipstick-console/src/test/java/com/netflix/dse/pig2json/model/PersistTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.dse.pig2json.model; 17 | 18 | import java.io.IOException; 19 | import java.io.StringWriter; 20 | 21 | import javax.persistence.EntityManager; 22 | import javax.persistence.EntityManagerFactory; 23 | import javax.persistence.EntityTransaction; 24 | import javax.persistence.Persistence; 25 | 26 | import org.apache.commons.io.IOUtils; 27 | import org.codehaus.jackson.JsonParseException; 28 | import org.codehaus.jackson.map.JsonMappingException; 29 | import org.codehaus.jackson.map.ObjectMapper; 30 | import org.testng.Assert; 31 | import org.testng.annotations.AfterClass; 32 | import org.testng.annotations.BeforeClass; 33 | import org.testng.annotations.Test; 34 | 35 | import com.netflix.lipstick.model.P2jPlanPackage; 36 | import com.netflix.lipstick.model.P2jPlanStatus; 37 | 38 | public class PersistTest { 39 | EntityManagerFactory emf; 40 | EntityManager em; 41 | 42 | @BeforeClass 43 | public void beforeTests() { 44 | emf = Persistence.createEntityManagerFactory("pu"); 45 | em = emf.createEntityManager(); 46 | } 47 | @AfterClass 48 | public void afterTests() { 49 | em.clear(); 50 | em.close(); 51 | } 52 | 53 | @Test 54 | public void persistPlan() throws JsonParseException, JsonMappingException, IOException { 55 | ObjectMapper mapper = new ObjectMapper(); 56 | P2jPlanPackage p = mapper.readValue(PersistTest.class.getResourceAsStream("/test.json"), P2jPlanPackage.class); 57 | 58 | EntityTransaction et = em.getTransaction(); 59 | et.begin(); 60 | em.persist(p); 61 | em.flush(); 62 | et.commit(); 63 | P2jPlanPackage p2 = em.find(P2jPlanPackage.class, p.getId()); 64 | 65 | String j = mapper.writeValueAsString(p); 66 | String j2 = mapper.writeValueAsString(p2); 67 | Assert.assertEquals(j2, j); 68 | } 69 | 70 | @Test 71 | public void persistStatus() throws JsonParseException, JsonMappingException, IOException { 72 | ObjectMapper mapper = new ObjectMapper(); 73 | StringWriter writer = new StringWriter(); 74 | IOUtils.copy(PersistTest.class.getResourceAsStream("/status.json"), writer, "UTF-8"); 75 | P2jPlanStatus p = mapper.readValue(PersistTest.class.getResourceAsStream("/status.json"), P2jPlanStatus.class); 76 | 77 | EntityTransaction et = em.getTransaction(); 78 | et.begin(); 79 | em.persist(p); 80 | em.flush(); 81 | et.commit(); 82 | P2jPlanStatus p2 = em.find(P2jPlanStatus.class, p.getId()); 83 | 84 | String j = mapper.writeValueAsString(p); 85 | String j2 = mapper.writeValueAsString(p2); 86 | Assert.assertEquals(j2, j); 87 | } 88 | } -------------------------------------------------------------------------------- /lipstick-console/src/test/java/com/netflix/lipstick/test/util/Util.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.test.util; 17 | 18 | import java.util.Collection; 19 | import java.util.HashSet; 20 | import java.util.Set; 21 | 22 | import com.google.common.collect.Sets; 23 | import com.google.common.collect.Sets.SetView; 24 | 25 | public class Util { 26 | public static HashSet safeNewSet(Collection coll) { 27 | if (coll == null) { 28 | return new HashSet(); 29 | } 30 | 31 | return new HashSet(coll); 32 | } 33 | 34 | public static SetView safeDiffSets(Set left, Set right) { 35 | if (left == null) { 36 | left = new HashSet(); 37 | } 38 | if (right == null) { 39 | right = new HashSet(); 40 | } 41 | 42 | return Sets.difference(left, right); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lipstick-console/src/test/java/com/netflix/lipstick/warnings/JobWarningsTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.netflix.lipstick.warnings; 17 | 18 | import static org.testng.Assert.*; 19 | import org.testng.annotations.Test; 20 | import static org.mockito.Mockito.*; 21 | 22 | import java.util.List; 23 | import org.apache.pig.tools.pigstats.JobStats; 24 | import com.google.common.collect.Lists; 25 | 26 | public class JobWarningsTest { 27 | 28 | @Test 29 | public void testFindSkewedReducersNotEnoughTasks() throws Exception { 30 | JobWarnings jw = new JobWarnings(); 31 | List reducerTimes = Lists.newLinkedList(); 32 | List taskIds; 33 | JobWarnings.ReducerDuration rd; 34 | 35 | for (int i = 0; i < JobWarnings.MIN_REDUCERS_FOR_SKEW; i++) { 36 | rd = new JobWarnings.ReducerDuration("Some Job", i); 37 | reducerTimes.add(rd); 38 | taskIds = jw.findSkewedReducers(reducerTimes); 39 | assertEquals(0, taskIds.size()); 40 | } 41 | 42 | /* Now that we've trie the most number of reducers w/o checking, lets 43 | try again with a duration value that is obviously skewed. */ 44 | rd = new JobWarnings.ReducerDuration("Some Job", 10000); 45 | reducerTimes.add(rd); 46 | taskIds = jw.findSkewedReducers(reducerTimes); 47 | assertEquals(1, taskIds.size()); 48 | } 49 | 50 | @Test 51 | public void testFindSkewedReducersSkewedReducersPresent() throws Exception { 52 | List reducerTimes = Lists.newLinkedList(); 53 | reducerTimes.add(new JobWarnings.ReducerDuration("task_201310241542_0008_r_000005", (1382638023848l))); 54 | reducerTimes.add(new JobWarnings.ReducerDuration("task_201310241542_0008_r_000006", (1382638023848l))); 55 | reducerTimes.add(new JobWarnings.ReducerDuration("task_201310241542_0008_r_000007", (1382638023849l))); 56 | reducerTimes.add(new JobWarnings.ReducerDuration("task_201310241542_0008_r_000009", (1382638023849l))); 57 | // This should be detected as a skew 58 | reducerTimes.add(new JobWarnings.ReducerDuration("task_201310241542_0008_r_000008", (138263802384800l))); 59 | 60 | JobWarnings jw = new JobWarnings(); 61 | List taskIds = jw.findSkewedReducers(reducerTimes); 62 | assertEquals(1, taskIds.size()); 63 | assertEquals("task_201310241542_0008_r_000008", taskIds.get(0)); 64 | } 65 | 66 | } 67 | 68 | -------------------------------------------------------------------------------- /lipstick-console/src/test/resources/status.json: -------------------------------------------------------------------------------- 1 | { 2 | "endTime": null, 3 | "id": 0, 4 | "jobStatusMap": { 5 | "job_201302111846_63797": { 6 | "counters": { 7 | "FileSystemCounters": { 8 | "counters": { 9 | "FILE_BYTES_WRITTEN": 84187, 10 | "HDFS_BYTES_READ": 5450, 11 | "HDFS_BYTES_WRITTEN": 17097, 12 | "S3N_BYTES_READ": 2252959 13 | } 14 | } 15 | }, 16 | "id": 0, 17 | "isComplete": false, 18 | "isSuccessful": false, 19 | "jobId": "job_201302111846_63797", 20 | "jobName": "PigLatin:stingstats.pig", 21 | "mapProgress": 0.05072874, 22 | "reduceProgress": 0.0, 23 | "scope": "scope-33", 24 | "totalMappers": 9, 25 | "totalReducers": 0, 26 | "trackingUrl": "http://ip-10-102-29-203.ec2.internal:9100/jobdetails.jsp?jobid=job_201302111846_63797" 27 | } 28 | }, 29 | "progress": 2, 30 | "startTime": null, 31 | "statusText": null 32 | } 33 | -------------------------------------------------------------------------------- /lipstick-console/src/test/resources/test.pig: -------------------------------------------------------------------------------- 1 | tiny = load 'tiny' as (plan:chararray, score:int, jk1:int); 2 | colors = load 'colors' as (plan:chararray, color:chararray, jk2:int); 3 | colors2 = load 'colors' as (plan:chararray, color:chararray, jk2:int); 4 | colors3 = load 'colors'; 5 | tiny_colors = join tiny by (plan, 1, 100*200+$1, $2), colors by (plan, 1, 1/200+jk2, jk2); 6 | colors_filtered = filter colors by $1 == 'red' and $1 == 'blue' or $2 == 'green' or $2 * 2 + 5 / ($2 + 3) != 3; 7 | tiny_colors_cogrp = cogroup tiny by (score, plan) inner, colors by (jk2+1, plan), colors2 by (50, plan); 8 | tiny_colors_join = join tiny by (score, plan), colors by (jk2+1, plan), colors2 by (50, plan), colors3 by ($3, $1); 9 | out = limit tiny_colors_cogrp 10; 10 | store out into 'test_out_cogrp'; 11 | store tiny_colors_join into 'test_out_join'; 12 | store tiny_colors into 'test_out_tiny_colors'; 13 | --dump out; 14 | -------------------------------------------------------------------------------- /lipstick-server/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem 'sinatra' 4 | gem 'warbler', '>= 1.4.8' 5 | gem 'yard' 6 | gem 'rack-test' 7 | -------------------------------------------------------------------------------- /lipstick-server/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | jruby-jars (1.7.22) 5 | jruby-rack (1.1.19) 6 | rack (1.5.2) 7 | rack-protection (1.5.3) 8 | rack 9 | rack-test (0.6.2) 10 | rack (>= 1.0) 11 | rake (10.4.2) 12 | rubyzip (1.1.7) 13 | sinatra (1.4.5) 14 | rack (~> 1.4) 15 | rack-protection (~> 1.4) 16 | tilt (~> 1.3, >= 1.3.4) 17 | tilt (1.4.1) 18 | warbler (1.4.9) 19 | jruby-jars (>= 1.5.6, < 2.0) 20 | jruby-rack (>= 1.1.1, < 1.3) 21 | rake (>= 0.9.6) 22 | rubyzip (>= 0.9, < 1.2) 23 | yard (0.8.7.6) 24 | 25 | PLATFORMS 26 | java 27 | ruby 28 | 29 | DEPENDENCIES 30 | rack-test 31 | sinatra 32 | warbler (>= 1.4.8) 33 | yard 34 | 35 | BUNDLED WITH 36 | 1.10.6 37 | -------------------------------------------------------------------------------- /lipstick-server/app/controllers/api_docs.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2014 Netflix, Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | get '/apidocs' do 17 | redirect to('/doc/top-level-namespace.html') 18 | end 19 | -------------------------------------------------------------------------------- /lipstick-server/app/public/WEB-INF/applicationContext.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | Grails application factory bean 9 | 10 | 11 | 12 | 13 | 14 | A bean that manages Grails plugins 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | utf-8 31 | 32 | 33 | -------------------------------------------------------------------------------- /lipstick-server/app/public/WEB-INF/sitemesh.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /lipstick-server/app/public/bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /lipstick-server/app/public/bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /lipstick-server/app/public/bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /lipstick-server/app/public/bootstrap/img/404.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/bootstrap/img/404.gif -------------------------------------------------------------------------------- /lipstick-server/app/public/bootstrap/img/apple-touch-icon-retina.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/bootstrap/img/apple-touch-icon-retina.png -------------------------------------------------------------------------------- /lipstick-server/app/public/bootstrap/img/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/bootstrap/img/apple-touch-icon.png -------------------------------------------------------------------------------- /lipstick-server/app/public/bootstrap/img/error.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/bootstrap/img/error.gif -------------------------------------------------------------------------------- /lipstick-server/app/public/bootstrap/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/bootstrap/img/favicon.ico -------------------------------------------------------------------------------- /lipstick-server/app/public/bootstrap/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/bootstrap/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /lipstick-server/app/public/bootstrap/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/bootstrap/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /lipstick-server/app/public/bootstrap/img/grails_logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/bootstrap/img/grails_logo.jpg -------------------------------------------------------------------------------- /lipstick-server/app/public/bootstrap/img/grails_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/bootstrap/img/grails_logo.png -------------------------------------------------------------------------------- /lipstick-server/app/public/bootstrap/img/leftnav_btm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/bootstrap/img/leftnav_btm.png -------------------------------------------------------------------------------- /lipstick-server/app/public/bootstrap/img/leftnav_midstretch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/bootstrap/img/leftnav_midstretch.png -------------------------------------------------------------------------------- /lipstick-server/app/public/bootstrap/img/leftnav_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/bootstrap/img/leftnav_top.png -------------------------------------------------------------------------------- /lipstick-server/app/public/bootstrap/img/netflix_logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/bootstrap/img/netflix_logo_small.png -------------------------------------------------------------------------------- /lipstick-server/app/public/bootstrap/img/pending.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/bootstrap/img/pending.gif -------------------------------------------------------------------------------- /lipstick-server/app/public/bootstrap/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/bootstrap/img/spinner.gif -------------------------------------------------------------------------------- /lipstick-server/app/public/bootstrap/img/springsource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/bootstrap/img/springsource.png -------------------------------------------------------------------------------- /lipstick-server/app/public/css/dashboard.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Base structure 3 | */ 4 | 5 | /* Move down content because we have a fixed navbar that is 50px tall */ 6 | body { 7 | padding-top: 50px; 8 | } 9 | 10 | 11 | /* 12 | * Global add-ons 13 | */ 14 | 15 | .sub-header { 16 | padding-bottom: 10px; 17 | border-bottom: 1px solid #eee; 18 | } 19 | 20 | /* 21 | * Top navigation 22 | * Hide default border to remove 1px line. 23 | */ 24 | .navbar-fixed-top { 25 | border: 0; 26 | } 27 | 28 | /* 29 | * Sidebar 30 | */ 31 | 32 | /* Hide for mobile, show later */ 33 | .sidebar { 34 | display: none; 35 | } 36 | @media (min-width: 768px) { 37 | .sidebar { 38 | position: fixed; 39 | top: 51px; 40 | bottom: 0; 41 | left: 0; 42 | z-index: 1000; 43 | display: block; 44 | padding: 20px; 45 | overflow-x: hidden; 46 | overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */ 47 | background-color: #f5f5f5; 48 | border-right: 1px solid #eee; 49 | } 50 | } 51 | 52 | /* Sidebar navigation */ 53 | .nav-sidebar { 54 | margin-right: -21px; /* 20px padding + 1px border */ 55 | margin-bottom: 20px; 56 | margin-left: -20px; 57 | } 58 | .nav-sidebar > li > a { 59 | padding-right: 20px; 60 | padding-left: 20px; 61 | } 62 | .nav-sidebar > .active > a, 63 | .nav-sidebar > .active > a:hover, 64 | .nav-sidebar > .active > a:focus { 65 | color: #fff; 66 | background-color: #428bca; 67 | } 68 | 69 | 70 | /* 71 | * Main content 72 | */ 73 | 74 | .main { 75 | padding: 20px; 76 | } 77 | @media (min-width: 768px) { 78 | .main { 79 | padding-right: 40px; 80 | padding-left: 40px; 81 | } 82 | } 83 | .main .page-header { 84 | margin-top: 0; 85 | } 86 | 87 | 88 | /* 89 | * Placeholder dashboard ideas 90 | */ 91 | 92 | .placeholders { 93 | margin-bottom: 30px; 94 | text-align: center; 95 | } 96 | .placeholders h4 { 97 | margin-bottom: 0; 98 | } 99 | .placeholder { 100 | margin-bottom: 20px; 101 | } 102 | .placeholder img { 103 | display: inline-block; 104 | border-radius: 50%; 105 | } 106 | -------------------------------------------------------------------------------- /lipstick-server/app/public/css/errors.css: -------------------------------------------------------------------------------- 1 | h1, h2 { 2 | margin: 10px 25px 5px; 3 | } 4 | 5 | h2 { 6 | font-size: 1.1em; 7 | } 8 | 9 | .filename { 10 | font-style: italic; 11 | } 12 | 13 | .exceptionMessage { 14 | margin: 10px; 15 | border: 1px solid #000; 16 | padding: 5px; 17 | background-color: #E9E9E9; 18 | } 19 | 20 | .stack, 21 | .snippet { 22 | margin: 0 25px 10px; 23 | } 24 | 25 | .stack, 26 | .snippet { 27 | border: 1px solid #ccc; 28 | -mox-box-shadow: 0 0 2px rgba(0,0,0,0.2); 29 | -webkit-box-shadow: 0 0 2px rgba(0,0,0,0.2); 30 | box-shadow: 0 0 2px rgba(0,0,0,0.2); 31 | } 32 | 33 | /* error details */ 34 | .error-details { 35 | border-top: 1px solid #FFAAAA; 36 | -mox-box-shadow: 0 0 2px rgba(0,0,0,0.2); 37 | -webkit-box-shadow: 0 0 2px rgba(0,0,0,0.2); 38 | box-shadow: 0 0 2px rgba(0,0,0,0.2); 39 | border-bottom: 1px solid #FFAAAA; 40 | -mox-box-shadow: 0 0 2px rgba(0,0,0,0.2); 41 | -webkit-box-shadow: 0 0 2px rgba(0,0,0,0.2); 42 | box-shadow: 0 0 2px rgba(0,0,0,0.2); 43 | background-color:#FFF3F3; 44 | line-height: 1.5; 45 | overflow: hidden; 46 | padding: 5px; 47 | padding-left:25px; 48 | } 49 | 50 | .error-details dt { 51 | clear: left; 52 | float: left; 53 | font-weight: bold; 54 | margin-right: 5px; 55 | } 56 | 57 | .error-details dt:after { 58 | content: ":"; 59 | } 60 | 61 | .error-details dd { 62 | display: block; 63 | } 64 | 65 | /* stack trace */ 66 | .stack { 67 | padding: 5px; 68 | overflow: auto; 69 | height: 150px; 70 | } 71 | 72 | /* code snippet */ 73 | .snippet { 74 | background-color: #fff; 75 | font-family: monospace; 76 | } 77 | 78 | .snippet .line { 79 | display: block; 80 | } 81 | 82 | .snippet .lineNumber { 83 | background-color: #ddd; 84 | color: #999; 85 | display: inline-block; 86 | margin-right: 5px; 87 | padding: 0 3px; 88 | text-align: right; 89 | width: 3em; 90 | } 91 | 92 | .snippet .error { 93 | background-color: #fff3f3; 94 | font-weight: bold; 95 | } 96 | 97 | .snippet .error .lineNumber { 98 | background-color: #faa; 99 | color: #333; 100 | font-weight: bold; 101 | } 102 | 103 | .snippet .line:first-child .lineNumber { 104 | padding-top: 5px; 105 | } 106 | 107 | .snippet .line:last-child .lineNumber { 108 | padding-bottom: 5px; 109 | } -------------------------------------------------------------------------------- /lipstick-server/app/public/css/jquery.treetable.css: -------------------------------------------------------------------------------- 1 | table.treetable span.indenter { 2 | display: inline-block; 3 | margin: 0; 4 | padding: 0; 5 | text-align: right; 6 | 7 | /* Disable text selection of nodes (for better D&D UX) */ 8 | user-select: none; 9 | -khtml-user-select: none; 10 | -moz-user-select: none; 11 | -o-user-select: none; 12 | -webkit-user-select: none; 13 | 14 | /* Force content-box box model for indenter (Bootstrap compatibility) */ 15 | -webkit-box-sizing: content-box; 16 | -moz-box-sizing: content-box; 17 | box-sizing: content-box; 18 | 19 | width: 19px; 20 | } 21 | 22 | table.treetable span.indenter a { 23 | background-position: left center; 24 | background-repeat: no-repeat; 25 | display: inline-block; 26 | text-decoration: none; 27 | width: 19px; 28 | } 29 | -------------------------------------------------------------------------------- /lipstick-server/app/public/css/mobile.css: -------------------------------------------------------------------------------- 1 | /* Styles for mobile devices */ 2 | 3 | @media screen and (max-width: 480px) { 4 | .nav { 5 | padding: 0.5em; 6 | } 7 | 8 | .nav li { 9 | margin: 0 0.5em 0 0; 10 | padding: 0.25em; 11 | } 12 | 13 | /* Hide individual steps in pagination, just have next & previous */ 14 | .pagination .step, .pagination .currentStep { 15 | display: none; 16 | } 17 | 18 | .pagination .prevLink { 19 | float: left; 20 | } 21 | 22 | .pagination .nextLink { 23 | float: right; 24 | } 25 | 26 | /* pagination needs to wrap around floated buttons */ 27 | .pagination { 28 | overflow: hidden; 29 | } 30 | 31 | /* slightly smaller margin around content body */ 32 | fieldset, 33 | .property-list { 34 | padding: 0.3em 1em 1em; 35 | } 36 | 37 | input, textarea { 38 | width: 100%; 39 | -moz-box-sizing: border-box; 40 | -webkit-box-sizing: border-box; 41 | -ms-box-sizing: border-box; 42 | box-sizing: border-box; 43 | } 44 | 45 | select, input[type=checkbox], input[type=radio], input[type=submit], input[type=button], input[type=reset] { 46 | width: auto; 47 | } 48 | 49 | /* hide all but the first column of list tables */ 50 | .scaffold-list td:not(:first-child), 51 | .scaffold-list th:not(:first-child) { 52 | display: none; 53 | } 54 | 55 | .scaffold-list thead th { 56 | text-align: center; 57 | } 58 | 59 | /* stack form elements */ 60 | .fieldcontain { 61 | margin-top: 0.6em; 62 | } 63 | 64 | .fieldcontain label, 65 | .fieldcontain .property-label, 66 | .fieldcontain .property-value { 67 | display: block; 68 | float: none; 69 | margin: 0 0 0.25em 0; 70 | text-align: left; 71 | width: auto; 72 | } 73 | 74 | .errors ul, 75 | .message p { 76 | margin: 0.5em; 77 | } 78 | 79 | .error ul { 80 | margin-left: 0; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lipstick-server/app/public/css/screen.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | background-color: #EAECEE; 5 | font-family: Verdana, sans-serif; 6 | font-size: 10px; 7 | } 8 | 9 | #header { 10 | height: 120px; 11 | border-bottom: 3px solid #c0c0c0; 12 | background: #204080; 13 | text-align: center; 14 | color: #f0f0f0; 15 | margin: 0; 16 | padding: 0; 17 | } 18 | 19 | #header h1 { 20 | padding-top: 24px; 21 | font-size: 250%; 22 | } 23 | 24 | #header a { 25 | color : #f0f0f0; 26 | } 27 | 28 | #page { 29 | background: #ffffff; 30 | width: 960px; 31 | margin-left: auto; 32 | margin-right: auto; 33 | border-right: solid 2px #a0a0a0; 34 | border-bottom: solid 2px #a0a0a0; 35 | } 36 | 37 | #sidebar { 38 | float: right; 39 | width: 200px; 40 | border: 1px solid #d0d0d0; 41 | min-height: 240px; 42 | padding: 4px; 43 | } 44 | 45 | #sidebar h1 { 46 | display: block; 47 | font-size: 120%; 48 | border-bottom: 1px solid #d0d0d0; 49 | } 50 | 51 | #container { 52 | padding: 8px; 53 | } 54 | 55 | #content { 56 | min-height: 320px; 57 | font-size: 120%; 58 | width: 720px; 59 | } 60 | 61 | #footer { 62 | text-align: center; 63 | padding: 8px; 64 | margin: 8px; 65 | border-top: 1px solid #c0c0c0; 66 | clear : both; 67 | } 68 | 69 | .panel { 70 | border: 1px solid #d0d0d0; 71 | padding: 8px; 72 | margin: 8px 4px 16px 4px; 73 | background: #fafafa; 74 | } 75 | 76 | .panel h1 { 77 | font-size: 120%; 78 | display: block; 79 | background: #f0f0f0; 80 | padding: 6px; 81 | margin: -8px; 82 | border-bottom: 1px solid #d0d0d0; 83 | margin-bottom: 8px; 84 | } 85 | 86 | .odd { 87 | background: #f0f0f0; 88 | } 89 | 90 | .even { 91 | background: #ffffff; 92 | } 93 | 94 | .dataTable { 95 | border-collapse: collapse;; 96 | width: 100%; 97 | } 98 | 99 | .dataTable th { 100 | background: #204080; 101 | color: #f0f0f0; 102 | padding: 6px; 103 | border: 1px solid #f0f0f0; 104 | } 105 | 106 | .dataTable td { 107 | border: 1px solid #d0d0d0; 108 | padding: 4px; 109 | } 110 | 111 | .caption { 112 | float: left; 113 | width: 100px; 114 | } 115 | 116 | .formCaption { 117 | float: left; 118 | width: 100px; 119 | padding-top: 4px; 120 | } 121 | 122 | .value { 123 | margin-right: 8px; 124 | } 125 | 126 | .errorMessage { 127 | color: #ff0000; 128 | font-weight: bold; 129 | } 130 | 131 | .property { 132 | margin-bottom: 12px; 133 | } 134 | -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux-3.1.0/fonts/fuelux.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/fuelux-3.1.0/fonts/fuelux.eot -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux-3.1.0/fonts/fuelux.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by Fontastic.me 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux-3.1.0/fonts/fuelux.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/fuelux-3.1.0/fonts/fuelux.ttf -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux-3.1.0/fonts/fuelux.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/fuelux-3.1.0/fonts/fuelux.woff -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux/checkbox.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Fuel UX Checkbox 3 | * https://github.com/ExactTarget/fuelux 4 | * 5 | * Copyright (c) 2012 ExactTarget 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | define(['require','jquery'],function (require) { 10 | 11 | var $ = require('jquery'); 12 | 13 | 14 | // CHECKBOX CONSTRUCTOR AND PROTOTYPE 15 | 16 | var Checkbox = function (element, options) { 17 | 18 | this.$element = $(element); 19 | this.options = $.extend({}, $.fn.checkbox.defaults, options); 20 | 21 | // cache elements 22 | this.$label = this.$element.parent(); 23 | this.$icon = this.$label.find('i'); 24 | this.$chk = this.$label.find('input[type=checkbox]'); 25 | 26 | // set default state 27 | this.setState(this.$chk); 28 | 29 | // handle events 30 | this.$chk.on('change', $.proxy(this.itemchecked, this)); 31 | }; 32 | 33 | Checkbox.prototype = { 34 | 35 | constructor: Checkbox, 36 | 37 | setState: function ($chk) { 38 | var checked = $chk.is(':checked'); 39 | var disabled = $chk.is(':disabled'); 40 | 41 | // reset classes 42 | this.$icon.removeClass('checked').removeClass('disabled'); 43 | 44 | // set state of checkbox 45 | if (checked === true) { 46 | this.$icon.addClass('checked'); 47 | } 48 | if (disabled === true) { 49 | this.$icon.addClass('disabled'); 50 | } 51 | }, 52 | 53 | enable: function () { 54 | this.$chk.attr('disabled', false); 55 | this.$icon.removeClass('disabled'); 56 | }, 57 | 58 | disable: function () { 59 | this.$chk.attr('disabled', true); 60 | this.$icon.addClass('disabled'); 61 | }, 62 | 63 | toggle: function () { 64 | this.$chk.click(); 65 | }, 66 | 67 | itemchecked: function (e) { 68 | var chk = $(e.target); 69 | this.setState(chk); 70 | } 71 | }; 72 | 73 | 74 | // CHECKBOX PLUGIN DEFINITION 75 | 76 | $.fn.checkbox = function (option, value) { 77 | var methodReturn; 78 | 79 | var $set = this.each(function () { 80 | var $this = $(this); 81 | var data = $this.data('checkbox'); 82 | var options = typeof option === 'object' && option; 83 | 84 | if (!data) $this.data('checkbox', (data = new Checkbox(this, options))); 85 | if (typeof option === 'string') methodReturn = data[option](value); 86 | }); 87 | 88 | return (methodReturn === undefined) ? $set : methodReturn; 89 | }; 90 | 91 | $.fn.checkbox.defaults = {}; 92 | 93 | $.fn.checkbox.Constructor = Checkbox; 94 | 95 | 96 | // CHECKBOX DATA-API 97 | 98 | $(function () { 99 | $(window).on('load', function () { 100 | //$('i.checkbox').each(function () { 101 | $('.checkbox-custom > input[type=checkbox]').each(function () { 102 | var $this = $(this); 103 | if ($this.data('checkbox')) return; 104 | $this.checkbox($this.data()); 105 | }); 106 | }); 107 | }); 108 | 109 | }); 110 | -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux/img/form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/fuelux/img/form.png -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/fuelux/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/fuelux/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux/less/checkbox.less: -------------------------------------------------------------------------------- 1 | .form-inline { 2 | .checkbox-custom { 3 | padding-left: 20px; 4 | } 5 | 6 | .checkbox-custom .checkbox { 7 | padding-left: 16px; 8 | } 9 | } 10 | 11 | .checkbox-custom { 12 | input[type=checkbox] { 13 | display: none; 14 | } 15 | 16 | i { 17 | background-image: url(../img/form.png); 18 | background-position: 0 1px; 19 | background-repeat: no-repeat; 20 | margin-left: -20px; 21 | margin-right: 4px; 22 | padding-left: 16px; 23 | width: 16px; 24 | height: 16px; 25 | 26 | &.checked { 27 | /* checked */ 28 | background-position: -48px 1px; 29 | } 30 | 31 | &.disabled { 32 | /* disabled */ 33 | background-position: -64px 1px; 34 | 35 | &.checked { 36 | /* disabled and checked */ 37 | background-position: -80px 1px; 38 | } 39 | } 40 | } 41 | } 42 | 43 | .checkbox-custom:hover { 44 | i { 45 | background-position: -16px 1px; 46 | 47 | &.checked { 48 | /* checked */ 49 | background-position: -32px 1px; 50 | } 51 | 52 | &.disabled { 53 | /* disabled */ 54 | background-position: -64px 1px; 55 | 56 | &.checked { 57 | /* disabled and checked */ 58 | background-position: -80px 1px; 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux/less/combobox.less: -------------------------------------------------------------------------------- 1 | .combobox { 2 | display: inline-block; 3 | 4 | a { 5 | font-size: @baseFontSize; 6 | } 7 | 8 | button.btn { 9 | border-radius: 0 @inputBorderRadius @inputBorderRadius 0; 10 | } 11 | } -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux/less/datagrid.less: -------------------------------------------------------------------------------- 1 | @tableBackgroundAccentDark: darken(@tableBackgroundAccent, 8%); 2 | 3 | .datagrid { 4 | 5 | thead { 6 | 7 | background-color: @tableBackgroundAccent; 8 | 9 | .datagrid-header-title { 10 | float: left; 11 | line-height: 28px; 12 | font-weight: normal; 13 | font-size: 14px; 14 | margin-right: 10px; 15 | } 16 | 17 | .datagrid-header-left { 18 | float: left; 19 | } 20 | 21 | .datagrid-header-right { 22 | float: right; 23 | } 24 | 25 | .datagrid-header-right, .datagrid-header-left { 26 | .search, .filter { 27 | margin-left: 8px; 28 | margin-bottom: 0; 29 | 30 | .dropdown-menu { 31 | top: auto; 32 | left: auto; 33 | } 34 | } 35 | } 36 | 37 | .sorted { 38 | .gradientBar(@tableBackgroundAccent, @tableBackgroundAccentDark, @textColor, 'none'); 39 | 40 | padding-right: 30px; 41 | 42 | i { 43 | float: right; 44 | margin-top: 2px; 45 | margin-right: -22px; 46 | } 47 | } 48 | 49 | .sortable { 50 | cursor: pointer; 51 | 52 | &:hover { 53 | .gradientBar(@tableBackgroundAccent, @tableBackgroundAccentDark, @textColor, 'none'); 54 | } 55 | } 56 | 57 | } 58 | 59 | tfoot { 60 | 61 | background-color: @tableBackgroundAccent; 62 | 63 | .datagrid-footer-left { 64 | 65 | float: left; 66 | 67 | .grid-controls { 68 | margin-top: 7px; 69 | 70 | select { 71 | margin: 0 5px 1px; 72 | width: 60px; 73 | } 74 | 75 | .grid-pagesize { 76 | display: inline-block; 77 | margin-bottom: 5px; 78 | vertical-align: middle; 79 | 80 | .dropdown-menu { 81 | top: auto; 82 | left: auto; 83 | } 84 | } 85 | 86 | span { 87 | font-weight: normal; 88 | } 89 | } 90 | 91 | } 92 | 93 | .datagrid-footer-right { 94 | 95 | float: right; 96 | 97 | .grid-pager { 98 | > span { 99 | font-weight: normal; 100 | position: relative; 101 | top: 8px; 102 | } 103 | 104 | .dropdown-menu { 105 | min-width: 50px; 106 | } 107 | 108 | .combobox { 109 | display: inline-block; 110 | position: relative; 111 | top: -2px; 112 | vertical-align: baseline; 113 | margin-bottom: 4px; 114 | } 115 | 116 | > button { 117 | position: relative; 118 | top: 7px; 119 | } 120 | } 121 | 122 | } 123 | 124 | } 125 | 126 | } 127 | 128 | .datagrid-stretch-header { 129 | border-bottom: 0; 130 | .border-bottom-radius(0); 131 | 132 | margin-bottom: 0; 133 | 134 | thead:last-child tr:last-child > th:first-child, 135 | thead:last-child tr:last-child > th:last-child { 136 | .border-bottom-radius(0); 137 | } 138 | } 139 | 140 | .datagrid-stretch-wrapper { 141 | border: 1px solid @tableBorder; 142 | overflow: auto; 143 | 144 | .datagrid { 145 | border: none; 146 | border-collapse: collapse; 147 | .border-radius(0); 148 | 149 | margin-bottom: 0; 150 | 151 | td, th { 152 | border-bottom: 1px solid @tableBorder; 153 | 154 | &:first-child { 155 | border-left: none; 156 | .border-radius(0); 157 | } 158 | } 159 | } 160 | } 161 | 162 | .datagrid-stretch-footer { 163 | border-top: 0; 164 | .border-top-radius(0); 165 | 166 | th { 167 | border-top: 0; 168 | } 169 | } 170 | 171 | -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux/less/fuelux-responsive.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Responsive v2.3.1 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */ 10 | 11 | .fuelux { 12 | 13 | // Responsive.less 14 | // For phone and tablet devices 15 | // ------------------------------------------------------------- 16 | 17 | 18 | // REPEAT VARIABLES & MIXINS 19 | // ------------------------- 20 | // Required since we compile the responsive stuff separately 21 | 22 | @import "variables.less"; // Modify this for custom colors, font-sizes, etc 23 | @import "../../lib/bootstrap/less/mixins.less"; 24 | 25 | 26 | // RESPONSIVE CLASSES 27 | // ------------------ 28 | 29 | @import "../../lib/bootstrap/less/responsive-utilities.less"; 30 | 31 | 32 | // MEDIA QUERIES 33 | // ------------------ 34 | 35 | // Large desktops 36 | @import "../../lib/bootstrap/less/responsive-1200px-min.less"; 37 | 38 | // Tablets to regular desktops 39 | @import "../../lib/bootstrap/less/responsive-768px-979px.less"; 40 | 41 | // Phones to portrait tablets and narrow desktops 42 | @import "../../lib/bootstrap/less/responsive-767px-max.less"; 43 | 44 | 45 | // RESPONSIVE NAVBAR 46 | // ------------------ 47 | 48 | // From 979px and below, show a button to toggle navbar contents 49 | @import "../../lib/bootstrap/less/responsive-navbar.less"; 50 | 51 | } 52 | -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux/less/fuelux.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v2.3.1 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */ 10 | 11 | .fuelux { 12 | 13 | // Core variables and mixins 14 | @import "variables.less"; // Modify this for custom colors, font-sizes, etc 15 | @import "../../lib/bootstrap/less/mixins.less"; 16 | 17 | // CSS Reset 18 | @import "../../lib/bootstrap/less/reset.less"; 19 | 20 | // Grid system and page structure 21 | @import "../../lib/bootstrap/less/scaffolding.less"; 22 | @import "../../lib/bootstrap/less/grid.less"; 23 | @import "../../lib/bootstrap/less/layouts.less"; 24 | 25 | // Base CSS 26 | @import "../../lib/bootstrap/less/type.less"; 27 | @import "../../lib/bootstrap/less/code.less"; 28 | @import "../../lib/bootstrap/less/forms.less"; 29 | @import "../../lib/bootstrap/less/tables.less"; 30 | 31 | // Components: common 32 | @import "../../lib/bootstrap/less/sprites.less"; 33 | @import "../../lib/bootstrap/less/dropdowns.less"; 34 | @import "../../lib/bootstrap/less/wells.less"; 35 | @import "../../lib/bootstrap/less/component-animations.less"; 36 | @import "../../lib/bootstrap/less/close.less"; 37 | 38 | // Components: Buttons & Alerts 39 | @import "../../lib/bootstrap/less/buttons.less"; 40 | @import "../../lib/bootstrap/less/button-groups.less"; 41 | @import "../../lib/bootstrap/less/alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less 42 | 43 | // Components: Nav 44 | @import "../../lib/bootstrap/less/navs.less"; 45 | @import "../../lib/bootstrap/less/navbar.less"; 46 | @import "../../lib/bootstrap/less/breadcrumbs.less"; 47 | @import "../../lib/bootstrap/less/pagination.less"; 48 | @import "../../lib/bootstrap/less/pager.less"; 49 | 50 | // Components: Popovers 51 | @import "../../lib/bootstrap/less/modals.less"; 52 | @import "../../lib/bootstrap/less/tooltip.less"; 53 | @import "../../lib/bootstrap/less/popovers.less"; 54 | 55 | // Components: Misc 56 | @import "../../lib/bootstrap/less/thumbnails.less"; 57 | @import "../../lib/bootstrap/less/media.less"; 58 | @import "../../lib/bootstrap/less/labels-badges.less"; 59 | @import "../../lib/bootstrap/less/progress-bars.less"; 60 | @import "../../lib/bootstrap/less/accordion.less"; 61 | @import "../../lib/bootstrap/less/carousel.less"; 62 | @import "../../lib/bootstrap/less/hero-unit.less"; 63 | 64 | // Utility classes 65 | @import "../../lib/bootstrap/less/utilities.less"; // Has to be last to override when necessary 66 | 67 | // Fuel UX controls 68 | @import "checkbox.less"; 69 | @import "combobox.less"; 70 | @import "datagrid.less"; 71 | @import "pillbox.less"; 72 | @import "radio.less"; 73 | @import "spinner.less"; 74 | @import "search.less"; 75 | @import "select.less"; 76 | @import "tree.less"; 77 | @import "wizard.less"; 78 | 79 | } 80 | -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux/less/pillbox.less: -------------------------------------------------------------------------------- 1 | .pillbox { 2 | 3 | padding: 3px; 4 | 5 | ul { 6 | display: inline-block; 7 | margin: 0; 8 | } 9 | 10 | li { 11 | 12 | // Begin Bootstrap .label 13 | font-size: @baseFontSize * .846; 14 | font-weight: bold; 15 | line-height: 21px; // modified 16 | color: @white; 17 | vertical-align: baseline; 18 | //white-space: nowrap; //removed 19 | text-shadow: 0 -1px 0 rgba(0,0,0,.25); 20 | background-color: @grayLight; 21 | padding: 1px 4px 2px; 22 | .border-radius(3px); 23 | // End Bootstrap .label 24 | 25 | display: inline-block; 26 | margin: 2px; 27 | cursor: pointer; 28 | float:left; 29 | 30 | &:after { 31 | 32 | // Begin Bootstrap .close 33 | float: right; 34 | font-size: 20px; 35 | font-weight: bold; 36 | line-height: @baseLineHeight; 37 | color: @black; 38 | text-shadow: 0 1px 0 rgba(255,255,255,1); 39 | .opacity(20); 40 | // End Bootstrap .close 41 | 42 | padding-left: 4px; 43 | position: relative; 44 | top: -2px; 45 | content: " \00D7" 46 | } 47 | 48 | &:hover { 49 | &:after { 50 | .opacity(40); 51 | } 52 | } 53 | 54 | &.status { 55 | &-important { 56 | background-color: @errorText; 57 | } 58 | 59 | &-warning { 60 | background-color: @orange; 61 | } 62 | 63 | &-success { 64 | background-color: @successText; 65 | } 66 | 67 | &-info { 68 | background-color: @infoText; 69 | } 70 | } 71 | 72 | } 73 | 74 | } -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux/less/radio.less: -------------------------------------------------------------------------------- 1 | .radio-custom { 2 | input[type=radio] { 3 | display: none; 4 | } 5 | 6 | i { 7 | background-image: url(../img/form.png); 8 | background-position: 0 -15px; 9 | background-repeat: no-repeat; 10 | margin-left: -20px; 11 | margin-right: 4px; 12 | padding-left: 16px; 13 | width: 16px; 14 | height: 16px; 15 | 16 | &.checked { 17 | /* checked */ 18 | background-position: -48px -15px; 19 | } 20 | 21 | &.disabled { 22 | /* disabled */ 23 | background-position: -64px -15px; 24 | 25 | &.checked { 26 | /* disabled and checked */ 27 | background-position: -80px -15px; 28 | } 29 | } 30 | } 31 | } 32 | 33 | .radio-custom:hover { 34 | i { 35 | background-position: -16px -15px; 36 | 37 | &.checked { 38 | /* checked */ 39 | background-position: -32px -15px; 40 | } 41 | 42 | &.disabled { 43 | /* disabled */ 44 | background-position: -64px -15px; 45 | 46 | &.checked { 47 | /* disabled and checked */ 48 | background-position: -80px -15px; 49 | } 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux/less/search.less: -------------------------------------------------------------------------------- 1 | .search { 2 | display: inline-block; 3 | } -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux/less/select.less: -------------------------------------------------------------------------------- 1 | .select { 2 | .dropdown-label { 3 | padding: 0 10px 0 0; 4 | margin: 0; 5 | display: inline-block; 6 | text-align: left; 7 | font-weight: normal; 8 | color: #333; 9 | } 10 | } 11 | 12 | #selectTextSize { 13 | display: inline-block; 14 | position: absolute; 15 | visibility: hidden; 16 | top: 0; 17 | } -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux/less/spinner.less: -------------------------------------------------------------------------------- 1 | .spinner{ 2 | 3 | input { 4 | width: 43px; 5 | float: left; 6 | } 7 | 8 | .btn { 9 | position: relative; 10 | width: 20px; 11 | height: 14px; 12 | padding-top: 0; 13 | padding-right: 9px; 14 | padding-left: 9px; 15 | 16 | &.disabled { 17 | cursor: not-allowed; 18 | } 19 | } 20 | 21 | .spinner-buttons { 22 | position: relative; 23 | float: left; 24 | height: 28px; 25 | width: 20px; 26 | left: -22px; 27 | } 28 | 29 | .spinner-up { 30 | 31 | padding: 0 0 4px 1px; 32 | top: 2px; 33 | 34 | i { 35 | position:relative; 36 | top: -4px; 37 | } 38 | } 39 | 40 | .spinner-down { 41 | 42 | padding: 0 0 4px 1px; 43 | top: 2px; 44 | height: 13px; 45 | 46 | i { 47 | position:relative; 48 | top: -5px; 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux/less/tree.less: -------------------------------------------------------------------------------- 1 | .tree { 2 | 3 | border: 1px solid #BBBBBB; 4 | border-radius: 4px 4px 4px 4px; 5 | overflow-y: auto; 6 | overflow-x: hidden; 7 | padding: 10px 15px 0 15px; 8 | position: relative; 9 | 10 | .tree-folder { 11 | 12 | width: 100%; 13 | min-height: 20px; 14 | cursor: pointer; 15 | margin-top: 1px; 16 | 17 | .tree-folder-header { 18 | 19 | position: relative; 20 | height: 20px; 21 | -webkit-border-radius: 6px; 22 | -moz-border-radius: 6px; 23 | border-radius: 6px; 24 | 25 | &:hover { 26 | background-color: @treeBackgroundHover; 27 | } 28 | 29 | i { 30 | position: absolute; 31 | float: left; 32 | top: 1px; 33 | left: 5px; 34 | } 35 | 36 | .tree-folder-name { 37 | padding-left: 29px; 38 | white-space: nowrap; 39 | overflow: hidden; 40 | text-overflow: ellipsis; 41 | } 42 | 43 | } 44 | 45 | .tree-folder-content { 46 | margin-left: 23px; 47 | } 48 | 49 | } 50 | 51 | .tree-item { 52 | 53 | position: relative; 54 | width: 100%; 55 | height: 20px; 56 | cursor: pointer; 57 | margin-top: 1px; 58 | -webkit-border-radius: 6px; 59 | -moz-border-radius: 6px; 60 | border-radius: 6px; 61 | 62 | &:hover { 63 | background-color: @treeBackgroundHover; 64 | } 65 | 66 | .tree-item-name { 67 | position: absolute; 68 | left: 29px; 69 | } 70 | 71 | .tree-dot { 72 | position: absolute; 73 | top: 8px; 74 | left: 10px; 75 | display: block; 76 | width: 4px; 77 | height: 4px; 78 | background-color: @grayDark; 79 | -webkit-border-radius: 6px; 80 | -moz-border-radius: 6px; 81 | border-radius: 6px; 82 | } 83 | 84 | .icon-ok { 85 | position: absolute; 86 | top: 1px; 87 | left: 5px; 88 | } 89 | 90 | } 91 | 92 | .tree-selected { 93 | background-color: @treeBackgroundSelect; 94 | 95 | &:hover { 96 | background-color: @treeBackgroundSelect; 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux/less/wizard.less: -------------------------------------------------------------------------------- 1 | .wizard { 2 | 3 | .clearfix; 4 | 5 | border: 1px solid @navbarBorder; 6 | .border-radius(@baseBorderRadius); 7 | .box-shadow(0 1px 4px rgba(0,0,0,.065)); 8 | background-color: @tableBackgroundAccent; 9 | 10 | ul { 11 | list-style: none outside none; 12 | padding: 0; 13 | margin: 0; 14 | 15 | li { 16 | float: left; 17 | margin: 0; 18 | padding: 0 20px 0 30px; 19 | height: 46px; 20 | line-height: 46px; 21 | position: relative; 22 | background: #ededed; 23 | color: @grayLight; 24 | font-size: 16px; 25 | cursor: default; 26 | 27 | .chevron { 28 | border: 24px solid transparent; 29 | border-left: 14px solid @navbarBorder; 30 | border-right: 0; 31 | display: block; 32 | position: absolute; 33 | right: -14px; 34 | top: 0; 35 | } 36 | 37 | .chevron:before { 38 | border: 24px solid transparent; 39 | border-left: 14px solid #ededed; 40 | border-right: 0; 41 | content: ""; 42 | display: block; 43 | position: absolute; 44 | right: 1px; 45 | top: -24px; 46 | } 47 | 48 | &.complete { 49 | background: #f3f4f5; 50 | color: @successText; 51 | 52 | &:hover { 53 | background: #e7eff8; 54 | cursor: pointer; 55 | 56 | .chevron:before { 57 | border-left: 14px solid #e7eff8; 58 | } 59 | } 60 | 61 | .chevron:before { 62 | border-left: 14px solid #f3f4f5; 63 | } 64 | } 65 | &.active { 66 | background: #f1f6fc; 67 | color: @infoText; 68 | 69 | .chevron:before { 70 | border-left: 14px solid #f1f6fc; 71 | } 72 | } 73 | .badge { 74 | margin-right: 8px; 75 | } 76 | } 77 | 78 | /* set z-index on steps for chevron overlap */ 79 | li:nth-child(1) { 80 | border-radius: 4px 0 0 4px; 81 | padding-left: 20px; 82 | z-index: 10; 83 | } 84 | li:nth-child(2) { 85 | z-index: 9; 86 | } 87 | li:nth-child(3) { 88 | z-index: 8; 89 | } 90 | li:nth-child(4) { 91 | z-index: 7; 92 | } 93 | li:nth-child(5) { 94 | z-index: 6; 95 | } 96 | li:nth-child(6) { 97 | z-index: 5; 98 | } 99 | li:nth-child(7) { 100 | z-index: 4; 101 | } 102 | li:nth-child(8) { 103 | z-index: 3; 104 | } 105 | li:nth-child(9) { 106 | z-index: 2; 107 | } 108 | li:nth-child(10) { 109 | z-index: 1; 110 | } 111 | } 112 | 113 | .actions { 114 | line-height: 44px; 115 | float: right; 116 | padding-right: 15px; 117 | vertical-align: middle; 118 | 119 | a { 120 | line-height: 45px; 121 | font-size: 12px; 122 | margin-right: 8px; 123 | } 124 | 125 | .btn-prev { 126 | i { 127 | margin-right: 5px; 128 | } 129 | } 130 | 131 | .btn-next { 132 | i { 133 | margin-left: 5px; 134 | } 135 | } 136 | } 137 | } 138 | 139 | .step-content { 140 | .step-pane { 141 | display: none; 142 | } 143 | 144 | .active { 145 | display: block; 146 | } 147 | } -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux/pillbox.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Fuel UX Pillbox 3 | * https://github.com/ExactTarget/fuelux 4 | * 5 | * Copyright (c) 2012 ExactTarget 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | define(['require','jquery'],function(require) { 10 | 11 | var $ = require('jquery'); 12 | 13 | 14 | // PILLBOX CONSTRUCTOR AND PROTOTYPE 15 | 16 | var Pillbox = function (element, options) { 17 | this.$element = $(element); 18 | this.options = $.extend({}, $.fn.pillbox.defaults, options); 19 | this.$element.on('click', 'li', $.proxy(this.itemclicked, this)); 20 | }; 21 | 22 | Pillbox.prototype = { 23 | constructor: Pillbox, 24 | 25 | items: function() { 26 | return this.$element.find('li').map(function() { 27 | var $this = $(this); 28 | return $.extend({ text: $this.text() }, $this.data()); 29 | }).get(); 30 | }, 31 | 32 | itemclicked: function (e) { 33 | $(e.currentTarget).remove(); 34 | e.preventDefault(); 35 | } 36 | }; 37 | 38 | 39 | // PILLBOX PLUGIN DEFINITION 40 | 41 | $.fn.pillbox = function (option) { 42 | var methodReturn; 43 | 44 | var $set = this.each(function () { 45 | var $this = $(this); 46 | var data = $this.data('pillbox'); 47 | var options = typeof option === 'object' && option; 48 | 49 | if (!data) $this.data('pillbox', (data = new Pillbox(this, options))); 50 | if (typeof option === 'string') methodReturn = data[option](); 51 | }); 52 | 53 | return (methodReturn === undefined) ? $set : methodReturn; 54 | }; 55 | 56 | $.fn.pillbox.defaults = {}; 57 | 58 | $.fn.pillbox.Constructor = Pillbox; 59 | 60 | 61 | // PILLBOX DATA-API 62 | 63 | $(function () { 64 | $('body').on('mousedown.pillbox.data-api', '.pillbox', function (e) { 65 | var $this = $(this); 66 | if ($this.data('pillbox')) return; 67 | $this.pillbox($this.data()); 68 | }); 69 | }); 70 | 71 | }); 72 | 73 | -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux/radio.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Fuel UX Radio 3 | * https://github.com/ExactTarget/fuelux 4 | * 5 | * Copyright (c) 2012 ExactTarget 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | define(['require','jquery'],function (require) { 10 | 11 | var $ = require('jquery'); 12 | 13 | 14 | // RADIO CONSTRUCTOR AND PROTOTYPE 15 | 16 | var Radio = function (element, options) { 17 | this.$element = $(element); 18 | this.options = $.extend({}, $.fn.radio.defaults, options); 19 | 20 | // cache elements 21 | this.$label = this.$element.parent(); 22 | this.$icon = this.$label.find('i'); 23 | this.$radio = this.$label.find('input[type=radio]'); 24 | this.groupName = this.$radio.attr('name'); 25 | 26 | // set default state 27 | this.setState(this.$radio); 28 | 29 | // handle events 30 | this.$radio.on('change', $.proxy(this.itemchecked, this)); 31 | }; 32 | 33 | Radio.prototype = { 34 | 35 | constructor: Radio, 36 | 37 | setState: function ($radio, resetGroupState) { 38 | var checked = $radio.is(':checked'); 39 | var disabled = $radio.is(':disabled'); 40 | 41 | // set state of radio 42 | if (checked === true) { 43 | this.$icon.addClass('checked'); 44 | } 45 | if (disabled === true) { 46 | this.$icon.addClass('disabled'); 47 | } 48 | }, 49 | 50 | resetGroup: function () { 51 | // reset all radio buttons in group 52 | $('input[name=' + this.groupName + ']').next().removeClass('checked'); 53 | }, 54 | 55 | enable: function () { 56 | this.$radio.attr('disabled', false); 57 | this.$icon.removeClass('disabled'); 58 | }, 59 | 60 | disable: function () { 61 | this.$radio.attr('disabled', true); 62 | this.$icon.addClass('disabled'); 63 | }, 64 | 65 | itemchecked: function (e) { 66 | var radio = $(e.target); 67 | 68 | this.resetGroup(); 69 | this.setState(radio); 70 | } 71 | }; 72 | 73 | 74 | // RADIO PLUGIN DEFINITION 75 | 76 | $.fn.radio = function (option, value) { 77 | var methodReturn; 78 | 79 | var $set = this.each(function () { 80 | var $this = $(this); 81 | var data = $this.data('radio'); 82 | var options = typeof option === 'object' && option; 83 | 84 | if (!data) $this.data('radio', (data = new Radio(this, options))); 85 | if (typeof option === 'string') methodReturn = data[option](value); 86 | }); 87 | 88 | return (methodReturn === undefined) ? $set : methodReturn; 89 | }; 90 | 91 | $.fn.radio.defaults = {}; 92 | 93 | $.fn.radio.Constructor = Radio; 94 | 95 | 96 | // RADIO DATA-API 97 | 98 | $(function () { 99 | $(window).on('load', function () { 100 | //$('i.radio').each(function () { 101 | $('.radio-custom > input[type=radio]').each(function () { 102 | var $this = $(this); 103 | if ($this.data('radio')) return; 104 | $this.radio($this.data()); 105 | }); 106 | }); 107 | }); 108 | 109 | }); 110 | -------------------------------------------------------------------------------- /lipstick-server/app/public/fuelux/util.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Fuel UX Utilities 3 | * https://github.com/ExactTarget/fuelux 4 | * 5 | * Copyright (c) 2012 ExactTarget 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | define(['require','jquery'],function (require) { 10 | 11 | var $ = require('jquery'); 12 | 13 | // custom case-insensitive match expression 14 | function fuelTextExactCI(elem, text) { 15 | return (elem.textContent || elem.innerText || $(elem).text() || '').toLowerCase() === (text || '').toLowerCase(); 16 | } 17 | 18 | $.expr[':'].fuelTextExactCI = $.expr.createPseudo ? 19 | $.expr.createPseudo(function (text) { 20 | return function (elem) { 21 | return fuelTextExactCI(elem, text); 22 | }; 23 | }) : 24 | function (elem, i, match) { 25 | return fuelTextExactCI(elem, match[3]); 26 | }; 27 | 28 | }); -------------------------------------------------------------------------------- /lipstick-server/app/public/images/404.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/404.gif -------------------------------------------------------------------------------- /lipstick-server/app/public/images/apple-touch-icon-retina.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/apple-touch-icon-retina.png -------------------------------------------------------------------------------- /lipstick-server/app/public/images/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/apple-touch-icon.png -------------------------------------------------------------------------------- /lipstick-server/app/public/images/error.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/error.gif -------------------------------------------------------------------------------- /lipstick-server/app/public/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/favicon.ico -------------------------------------------------------------------------------- /lipstick-server/app/public/images/grails_logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/grails_logo.jpg -------------------------------------------------------------------------------- /lipstick-server/app/public/images/grails_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/grails_logo.png -------------------------------------------------------------------------------- /lipstick-server/app/public/images/leftnav_btm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/leftnav_btm.png -------------------------------------------------------------------------------- /lipstick-server/app/public/images/leftnav_midstretch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/leftnav_midstretch.png -------------------------------------------------------------------------------- /lipstick-server/app/public/images/leftnav_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/leftnav_top.png -------------------------------------------------------------------------------- /lipstick-server/app/public/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/loading.gif -------------------------------------------------------------------------------- /lipstick-server/app/public/images/netflix_logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/netflix_logo_small.png -------------------------------------------------------------------------------- /lipstick-server/app/public/images/pending.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/pending.gif -------------------------------------------------------------------------------- /lipstick-server/app/public/images/skin/database_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/skin/database_add.png -------------------------------------------------------------------------------- /lipstick-server/app/public/images/skin/database_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/skin/database_delete.png -------------------------------------------------------------------------------- /lipstick-server/app/public/images/skin/database_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/skin/database_edit.png -------------------------------------------------------------------------------- /lipstick-server/app/public/images/skin/database_save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/skin/database_save.png -------------------------------------------------------------------------------- /lipstick-server/app/public/images/skin/database_table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/skin/database_table.png -------------------------------------------------------------------------------- /lipstick-server/app/public/images/skin/exclamation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/skin/exclamation.png -------------------------------------------------------------------------------- /lipstick-server/app/public/images/skin/house.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/skin/house.png -------------------------------------------------------------------------------- /lipstick-server/app/public/images/skin/information.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/skin/information.png -------------------------------------------------------------------------------- /lipstick-server/app/public/images/skin/shadow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/skin/shadow.jpg -------------------------------------------------------------------------------- /lipstick-server/app/public/images/skin/sorted_asc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/skin/sorted_asc.gif -------------------------------------------------------------------------------- /lipstick-server/app/public/images/skin/sorted_desc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/skin/sorted_desc.gif -------------------------------------------------------------------------------- /lipstick-server/app/public/images/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/spinner.gif -------------------------------------------------------------------------------- /lipstick-server/app/public/images/springsource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/images/springsource.png -------------------------------------------------------------------------------- /lipstick-server/app/public/js/application.js: -------------------------------------------------------------------------------- 1 | if (typeof jQuery !== 'undefined') { 2 | (function($) { 3 | $('#spinner').ajaxStart(function() { 4 | $(this).fadeIn(); 5 | }).ajaxStop(function() { 6 | $(this).fadeOut(); 7 | }); 8 | })(jQuery); 9 | } 10 | -------------------------------------------------------------------------------- /lipstick-server/app/public/js/workflow.js: -------------------------------------------------------------------------------- 1 | requirejs.config({ 2 | baseUrl: 'js', 3 | paths: { 4 | lib: 'lib', 5 | bootstrap: '../bootstrap/js/bootstrap.3.1.1.min', 6 | jquery: 'jquery-1.8.2.min', 7 | transit: 'jquery.transit.min', 8 | knockout: 'lib/knockout-3.0.0', 9 | treetable: 'lib/jquery.treetable', 10 | d3: 'lib/d3.v3.min', 11 | }, 12 | shim : { 13 | 'backbone-min' : { 14 | deps : [ 'lodash.min', 'jquery' ], 15 | exports : 'Backbone' 16 | } 17 | } 18 | }); 19 | 20 | requirejs( 21 | ['jquery', 'backbone-min', 'knockout', 22 | 'workflow/templates', 'workflow/viewmodel'], 23 | function($, Backbone, ko, templates, viewmodel) { 24 | 25 | templates.initialize(); 26 | 27 | var getJobInfo = function(uuid) { 28 | window.uuid = uuid; 29 | viewmodel.initialize(uuid); 30 | ko.applyBindings(viewmodel); 31 | }; 32 | 33 | var TestRouter = Backbone.Router.extend({ 34 | routes: { 35 | "job/:uuid": "getJobInfo", 36 | ":page/:uuid": "drawPageWithInfo" 37 | }, 38 | getJobInfo: function(uuid) { 39 | getJobInfo(uuid); 40 | window.location.hash = '#graph/'+uuid; 41 | }, 42 | drawPageWithInfo: function(page, uuid) { 43 | if (window.uuid != uuid) { 44 | getJobInfo(uuid); 45 | } 46 | scrollTo(0,0); 47 | $('div .progress').show(); 48 | $('div.page').hide(); 49 | $('div.page#'+page).show(); 50 | $('#graph-menu').width($('#graph-type button').width() * 2.45); 51 | } 52 | }); 53 | router = new TestRouter(); 54 | Backbone.history.start(); 55 | } 56 | ); 57 | -------------------------------------------------------------------------------- /lipstick-server/app/public/js/workflow/graph/edge.js: -------------------------------------------------------------------------------- 1 | define(['knockout', 'lib/mustache', '../templates'], 2 | function(ko, Mustache, templates) { 3 | function Edge(data) { 4 | var self = this; 5 | self.type = ko.observable(data.type); 6 | self.u = ko.observable(data.u); 7 | self.v = ko.observable(data.v); 8 | self.label = ko.observable(data.label); 9 | self.properties = ko.observable(data.properties); 10 | self.title = ko.computed(function() { 11 | return "["+self.u()+"] -> ["+self.v()+"]"; 12 | }); 13 | self.render = ko.computed(function() { 14 | var template = templates.templates[self.type()]; 15 | if (template) { 16 | return Mustache.render(template.template, new template.view(self.properties())); 17 | } 18 | return; 19 | }); 20 | }; 21 | return Edge; 22 | }); 23 | -------------------------------------------------------------------------------- /lipstick-server/app/public/js/workflow/graph/node.js: -------------------------------------------------------------------------------- 1 | define(['knockout', 'lib/mustache', './status', '../utils', '../templates'], 2 | function(ko, Mustache, Status, utils, templates) { 3 | 4 | function Node(data) { 5 | var self = this; 6 | self.status = ko.observable(new Status(data.status || {})); 7 | self.id = ko.observable(data.id); 8 | self.child = ko.observable(data.child); 9 | self.properties = ko.observable(data.properties); 10 | self.type = ko.observable(data.type); 11 | self.url = ko.observable(data.url); 12 | self.treeTable = ko.observableArray( 13 | utils.sortTreeTable(utils.flatten(data.properties)) 14 | ); 15 | 16 | self.render = function() { 17 | var template = templates.templates[self.type()]; 18 | if (template) { 19 | return Mustache.render(template.template, new template.view(self.properties())); 20 | } else { 21 | return Mustache.render("

{{id}}

", self); 22 | } 23 | }; 24 | }; 25 | return Node; 26 | }); 27 | -------------------------------------------------------------------------------- /lipstick-server/app/public/js/workflow/graph/node_group.js: -------------------------------------------------------------------------------- 1 | define(['knockout', './status', './stage', '../utils'], function(ko, Status, Stage, utils) { 2 | function NodeGroup(data) { 3 | var self = this; 4 | self.url = ko.observable(data.url); 5 | self.name = ko.observable(data.name || ''); 6 | self.status = ko.observable(new Status(data.status || {})); 7 | self.properties = ko.observable(data.properties); 8 | self.children = ko.observableArray(data.children); 9 | self.id = ko.observable(data.id); 10 | self.immediateParent = ko.observable(data.immediateParent); 11 | self.treeTable = ko.observableArray( 12 | utils.sortTreeTable(utils.flatten(data.properties)) 13 | ); 14 | 15 | self.stages = ko.observableArray((data.stages ? data.stages : []).map( 16 | function(stage) { 17 | return new Stage(stage); 18 | } 19 | )); 20 | 21 | self.title = ko.computed(function() { 22 | if (self.url()) { 23 | // HACK - handles legacy MRJob case 24 | if (self.properties() && self.properties().jobId) 25 | return ''+self.properties().jobId+''; 26 | else { 27 | return ''+self.name()+''; 28 | } 29 | } else if (self.properties() && self.properties().jobId) { 30 | return self.properties().jobId; 31 | } else { 32 | return self.name(); 33 | } 34 | }); 35 | 36 | }; 37 | return NodeGroup; 38 | }); 39 | -------------------------------------------------------------------------------- /lipstick-server/app/public/js/workflow/graph/stage.js: -------------------------------------------------------------------------------- 1 | define(['knockout', './status'], function(ko, Status) { 2 | function Stage(data) { 3 | var self = this; 4 | self.name = ko.observable(data.name || ''); 5 | self.status = ko.observable(new Status(data.status || {})); 6 | }; 7 | return Stage; 8 | }); 9 | -------------------------------------------------------------------------------- /lipstick-server/app/public/js/workflow/graph/status.js: -------------------------------------------------------------------------------- 1 | define(['knockout'], function(ko) { 2 | function Status(data) { 3 | var self = this; 4 | self.progress = ko.observable(data.progress ? data.progress : 0); 5 | self.startTime = ko.observable(data.startTime); 6 | self.heartbeatTime = ko.observable(data.heartbeatTime); 7 | self.endTime = ko.observable(data.endTime); 8 | self.statusText = ko.observable(data.statusText); 9 | 10 | self.timeString = function(ms) { 11 | if (ms && ms > 0) { 12 | var d = new Date(ms); 13 | return d.toLocaleDateString()+' '+d.toLocaleTimeString(); 14 | } 15 | return; 16 | }; 17 | 18 | self.startTimeString = ko.computed(function() { 19 | return self.timeString(self.startTime()); 20 | }); 21 | 22 | self.endTimeString = ko.computed(function() { 23 | if (self.endTime() && self.endTime() > 0) { 24 | return self.timeString(self.endTime()); 25 | } else { 26 | return; 27 | } 28 | }); 29 | 30 | self.heartbeatTimeString = ko.computed(function() { 31 | return self.timeString(self.heartbeatTime()); 32 | }); 33 | 34 | self.progressText = ko.computed(function() { 35 | if (self.progress()) { 36 | return self.progress()+"%"; 37 | } else { 38 | return; 39 | } 40 | }); 41 | self.progressCss = ko.computed(function() { 42 | switch(self.statusText()) 43 | { 44 | case "finished": 45 | return 'progress-bar-success'; 46 | case "failed": 47 | return 'progress-bar-danger'; 48 | case "terminated": 49 | return 'progress-bar-warning'; 50 | default: 51 | return 'progress-bar-striped'; 52 | } 53 | }); 54 | self.updateWith = function(data) { 55 | if (data) { 56 | if (data.progress) {self.progress(data.progress);} 57 | if (data.startTime) {self.startTime(data.startTime);} 58 | if (data.endTime) {self.endTime(data.endTime);} 59 | if (data.heartbeatTime) {self.heartbeatTime(data.heartbeatTime);} 60 | if (data.statusText) {self.statusText(data.statusText);} 61 | } 62 | }; 63 | }; 64 | return Status; 65 | }); 66 | -------------------------------------------------------------------------------- /lipstick-server/app/public/js/workflow/templates.js: -------------------------------------------------------------------------------- 1 | define(['jquery'], function($) { 2 | 3 | var Template = function(data) { 4 | eval("this.view = "+data.view); // yup. 5 | this.name = data.name; 6 | this.template = data.template; 7 | }; 8 | 9 | function WorkflowTemplates() { 10 | var self = this; 11 | self.baseUrl = './template'; 12 | self.templates = {}; 13 | self.initialize = function() { 14 | $.ajax({ 15 | type: 'GET', 16 | url: self.baseUrl 17 | }).done(function(json) { 18 | $.each(json.templates, function (i, template) { 19 | self.templates[template.name] = new Template(template) ; 20 | }); 21 | }).fail(function() { 22 | }); 23 | }; 24 | }; 25 | 26 | return new WorkflowTemplates(); 27 | }); 28 | -------------------------------------------------------------------------------- /lipstick-server/app/public/tossboss-detail-view/css/tossboss-detail-view.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* DetailView SPECIFIC */ 17 | .detail-view-object-body { 18 | font-size: 0.75em; 19 | } 20 | 21 | .LO-schema { 22 | overflow: auto; 23 | } 24 | 25 | .graph-view-options { 26 | padding-top: 0px; 27 | } 28 | -------------------------------------------------------------------------------- /lipstick-server/app/public/tossboss-drawers/img/handle-horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/tossboss-drawers/img/handle-horizontal.png -------------------------------------------------------------------------------- /lipstick-server/app/public/tossboss-drawers/img/handle-vertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/tossboss-drawers/img/handle-vertical.png -------------------------------------------------------------------------------- /lipstick-server/app/public/tossboss-graph-view/css/tossboss-graph-view.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pig-graph { 17 | -webkit-transform-origin: 0% 0%; 18 | transform-origin: 0% 0%; 19 | height: 100%; 20 | width: 100%; 21 | } 22 | 23 | #graph-type { 24 | padding-bottom: 7px; 25 | } 26 | 27 | .mouseover { 28 | cursor: pointer; 29 | } 30 | 31 | #mr-job-details { 32 | display: none; 33 | position: absolute; 34 | top: 100px; 35 | left: 272px; 36 | z-index: 1030; 37 | padding: 10px; 38 | background: whitesmoke; 39 | border: 1px; 40 | border-style: solid; 41 | border-radius: 4px; 42 | } 43 | 44 | #sampleOutputDataModal { 45 | font-size: 0.75em; 46 | } 47 | 48 | img.center { 49 | display: block; 50 | margin-left: auto; 51 | margin-right: auto; 52 | } 53 | 54 | .sample-output-icon { 55 | margin-left: 5px; 56 | } 57 | -------------------------------------------------------------------------------- /lipstick-server/app/public/tossboss-main/img/lipstick_140.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/tossboss-main/img/lipstick_140.png -------------------------------------------------------------------------------- /lipstick-server/app/public/tossboss-main/img/lipstick_404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/tossboss-main/img/lipstick_404.png -------------------------------------------------------------------------------- /lipstick-server/app/public/tossboss-main/img/lipstick_940.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/Lipstick/d610c5d224c68e29940e6cbbb765ddb0f9f01359/lipstick-server/app/public/tossboss-main/img/lipstick_940.png -------------------------------------------------------------------------------- /lipstick-server/app/public/tossboss-modal/css/tossboss-modal.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* Override some defaults */ 17 | #myModal { 18 | font-size: 0.75em; 19 | } 20 | -------------------------------------------------------------------------------- /lipstick-server/app/public/tossboss-modal/js/tossboss-modal.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** tossboss-modal.js 17 | * Responsible for the initialization of objects and the page. 18 | * 19 | * LISTENS FOR EVENTS: 20 | * - loadGraphModel.tossboss-graph-model 21 | * 22 | * TIGGERS EVENTS: 23 | */ 24 | 25 | ;Modal = { 26 | options: { 27 | modalDefaults: { 28 | title: '', 29 | html: '' 30 | } 31 | }, 32 | /** 33 | * Start all custom event listeners. 34 | */ 35 | startListeners: function() { 36 | // On getting graph data from GraphModel, show menus. 37 | $(document).on('loadGraphModel.tossboss-graph-model', function(event) { 38 | $('.zoom-menu').show(); 39 | $('.graph-type-menu').show(); 40 | }); 41 | }, 42 | /** 43 | * Initialize the Modal object. 44 | */ 45 | initialize: function() { 46 | Modal.startListeners(); 47 | }, 48 | /** 49 | * Add and display a modal. 50 | * Example: displayModal(options) 51 | * options: { 52 | * title: '', 53 | * html: '' 54 | * } 55 | * 56 | * options.title = modal title 57 | * options.html = html markup for modal body 58 | * 59 | * @param {Object} options The options for the modal 60 | */ 61 | displayModal: function(options) { 62 | var opts = $.extend({}, Modal.options.modalDefaults, options); 63 | $('body').prepend(_.template(Templates.modalTmpl, opts, {variable:'data'})); 64 | $('#myModal').css({ 65 | 'width': function () { 66 | return ($(document).width() * .9) + 'px'; 67 | }, 68 | 'margin-left': function () { 69 | return -($(this).width() / 2); 70 | } 71 | }); 72 | $('#myModal').modal('toggle'); 73 | $('#myModal').on('hidden', function(event) { 74 | $('#myModal').remove(); 75 | }); 76 | } 77 | }; 78 | -------------------------------------------------------------------------------- /lipstick-server/app/public/tossboss-script/css/tossboss-script.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | .well { 17 | padding: 0px; 18 | border: 3px solid #f3f3f3; 19 | background-color: #ffffff; 20 | } 21 | 22 | /* OVERWRITE codemirror DEFAULTS */ 23 | .CodeMirror-gutter { 24 | border-right: 1px solid #dddddd; 25 | background-color: #f3f3f3; 26 | } 27 | 28 | .CodeMirror-scroll { 29 | border: 1px solid #dddddd; 30 | height: 240px; 31 | } 32 | -------------------------------------------------------------------------------- /lipstick-server/app/templates/PigEdge.js: -------------------------------------------------------------------------------- 1 | function PigEdge(properties) { 2 | var self = this; 3 | self.properties = properties; 4 | self.hasSampleData = function() { 5 | if (self.properties.sampleOutput) { 6 | return true; 7 | } 8 | return false; 9 | }; 10 | 11 | self.hasSchema = function() { 12 | if (self.properties.schema) { 13 | return true; 14 | } 15 | return false; 16 | }; 17 | 18 | self.sampleOutput = function() { 19 | var sol = self.properties.sampleOutput; 20 | var result = sol.map(function(so, i) { 21 | return { 22 | data: so.split("\n").map(function(record, j) { 23 | return {fields: record.split("\u0001")}; 24 | }) 25 | }; 26 | }); 27 | return result; 28 | }; 29 | 30 | self.schema = properties.schema; 31 | } 32 | -------------------------------------------------------------------------------- /lipstick-server/app/templates/PigEdge.mustache: -------------------------------------------------------------------------------- 1 | {{#hasSampleData}} 2 |

Sample Data:

3 | {{#sampleOutput}} 4 | 5 | {{#hasSchema}} 6 | 7 | 8 | {{#schema}} 9 | 10 | {{/schema}} 11 | 12 | 13 | {{/hasSchema}} 14 | {{^hasSchema}} 15 | 16 | {{/hasSchema}} 17 | 18 | {{#data}} 19 | 20 | {{#fields}} 21 | 22 | {{/fields}} 23 | 24 | {{/data}} 25 | 26 |
{{alias}}
 
{{.}}
27 | {{/sampleOutput}} 28 | {{/hasSampleData}} 29 | {{^hasSampleData}} 30 |
No sample output available at this time.
31 | {{/hasSampleData}} 32 | -------------------------------------------------------------------------------- /lipstick-server/app/templates/PigNode.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | {{#expression}} 10 | 11 | {{/expression}} 12 | 13 | {{#storage_location}} 14 | 15 | {{/storage_location}} 16 | 17 | {{#storage_function}} 18 | 19 | {{/storage_function}} 20 | 21 | 22 | 25 | 26 | 27 | {{#join}} 28 | 29 | {{#relations}} 30 | 31 | {{/relations}} 32 | 33 | 34 | {{#by}} 35 | 36 | {{#fields}} 37 | 38 | {{/fields}} 39 | 40 | {{/by}} 41 | {{/join}} 42 | 43 | {{#schema}} 44 | {{#columns}} 45 | 46 | 47 | 48 | {{/columns}} 49 | {{/schema}} 50 | 51 |
5 | {{operation}}{{additional_info}} 6 |
{{expression}}
{{storage_location}}
{{storage_function}}
23 | {{alias}} 24 |
{{.}}
{{.}}
{{alias}}{{type}}
52 | -------------------------------------------------------------------------------- /lipstick-server/app/test/lipstick_test.rb: -------------------------------------------------------------------------------- 1 | require 'init' 2 | require 'test/unit' 3 | require 'rack/test' 4 | require 'app/test/test_template_controller' 5 | require 'app/test/test_job_controller' 6 | -------------------------------------------------------------------------------- /lipstick-server/app/test/test_template_controller.rb: -------------------------------------------------------------------------------- 1 | class TemplateControllerTest < Test::Unit::TestCase 2 | include Rack::Test::Methods 3 | 4 | def app 5 | Sinatra::Application 6 | end 7 | 8 | def setup 9 | @sample = { 10 | 'name' => 'Simple', 11 | 'view' => 'function Simple(properties) {self.name = properties.name}', 12 | 'template' => '{{name}}' 13 | } 14 | @expected_post_response = {'name' => @sample['name']} 15 | @expected_get_total = 2 16 | @expected_names = ['PigEdge', 'PigNode'] 17 | ElasticSearchAdaptor.instance.refresh! 18 | end 19 | 20 | def test_should_list_templates 21 | get '/template' 22 | assert last_response.ok? 23 | response = JSON.parse(last_response.body) 24 | response_names = response['templates'].map{|t| t['name']} 25 | assert_equal @expected_get_total, response['total'] 26 | assert_equal (@expected_names - response_names).size, 0 27 | end 28 | 29 | def test_should_post_templates 30 | post '/template/'+@sample['name'], @sample.to_json 31 | assert last_response.ok? 32 | assert_equal @expected_post_response, JSON.parse(last_response.body) 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lipstick-server/config.ru: -------------------------------------------------------------------------------- 1 | require 'init' 2 | 3 | set :run, false 4 | set :environment, :production 5 | 6 | # deploy httpd server 7 | run Sinatra::Application 8 | -------------------------------------------------------------------------------- /lipstick-server/config/graph-mapping.json: -------------------------------------------------------------------------------- 1 | { 2 | "graph": { 3 | "properties": { 4 | "created_at": { 5 | "type": "date", 6 | "format": "epoch_millis" 7 | }, 8 | "edges": { 9 | "properties": { 10 | "properties": { 11 | "type": "object" 12 | }, 13 | "u": { 14 | "type": "keyword", 15 | "index": true 16 | }, 17 | "v": { 18 | "type": "keyword", 19 | "index": true 20 | } 21 | } 22 | }, 23 | "id": { 24 | "type": "keyword", 25 | "index": true 26 | }, 27 | "name": { 28 | "type": "keyword", 29 | "index": true 30 | }, 31 | "nodes": { 32 | "properties": { 33 | "child": { 34 | "type": "text" 35 | }, 36 | "id": { 37 | "type": "keyword", 38 | "index": true 39 | }, 40 | "properties": { 41 | "type": "object" 42 | }, 43 | "type": { 44 | "type": "text" 45 | } 46 | } 47 | }, 48 | "properties": { 49 | "type": "object" 50 | }, 51 | "status": { 52 | "properties": { 53 | "endTime": { 54 | "type": "date", 55 | "format": "epoch_millis" 56 | }, 57 | "heartbeatTime": { 58 | "type": "date", 59 | "format": "epoch_millis" 60 | }, 61 | "progress": { 62 | "type": "integer" 63 | }, 64 | "startTime": { 65 | "type": "date", 66 | "format": "epoch_millis" 67 | }, 68 | "statusText": { 69 | "type": "keyword" 70 | } 71 | } 72 | }, 73 | "updated_at": { 74 | "type": "date", 75 | "format": "epoch_millis" 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lipstick-server/config/plan-mapping.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "id": { 4 | "type": "keyword" 5 | }, 6 | "jobName": { 7 | "type": "keyword", 8 | "index": true 9 | }, 10 | "optimized": { 11 | "properties": { 12 | "id": { 13 | "type": "keyword" 14 | }, 15 | "plan": { 16 | "type": "object", 17 | "enabled": false 18 | }, 19 | "svg": { 20 | "type": "text", 21 | "index": false 22 | } 23 | } 24 | }, 25 | "scripts": { 26 | "properties": { 27 | "id": { 28 | "type": "keyword" 29 | }, 30 | "script": { 31 | "type": "text" 32 | } 33 | } 34 | }, 35 | "status": { 36 | "properties": { 37 | "endTime": { 38 | "type": "date", 39 | "format": "epoch_millis" 40 | }, 41 | "heartbeatTime": { 42 | "type": "date", 43 | "format": "epoch_millis" 44 | }, 45 | "id": { 46 | "type": "keyword" 47 | }, 48 | "jobStatusMap": { 49 | "type": "object", 50 | "enabled": false 51 | }, 52 | "progress": { 53 | "type": "integer" 54 | }, 55 | "startTime": { 56 | "type": "date", 57 | "format": "epoch_millis" 58 | }, 59 | "statusText": { 60 | "type": "keyword", 61 | "index": true 62 | } 63 | } 64 | }, 65 | "unoptimized": { 66 | "properties": { 67 | "id": { 68 | "type": "keyword" 69 | }, 70 | "plan": { 71 | "type": "object", 72 | "enabled": false 73 | }, 74 | "svg": { 75 | "type": "text", 76 | "index": false 77 | } 78 | } 79 | }, 80 | "userName": { 81 | "type": "keyword", 82 | "index": true 83 | }, 84 | "uuid": { 85 | "type": "keyword", 86 | "index": true 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lipstick-server/config/template-mapping.json: -------------------------------------------------------------------------------- 1 | { 2 | "node_template": { 3 | "properties": { 4 | "name": { 5 | "type": "keyword" 6 | }, 7 | "template": { 8 | "type": "keyword", 9 | "index": true 10 | }, 11 | "view": { 12 | "type": "keyword", 13 | "index": true 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lipstick-server/config/warble.rb: -------------------------------------------------------------------------------- 1 | Warbler::Config.new do |config| 2 | config.dirs = %w(app config lib) 3 | config.includes = FileList["init.rb"] 4 | config.excludes = FileList["lib/*jruby*.jar"] 5 | config.gems -= ["rails"] 6 | config.jar_name = "pigstats" 7 | end 8 | -------------------------------------------------------------------------------- /lipstick-server/examples/graphs/doubly-nested.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "doublynested", 3 | "name": "doubly-nested-graph", 4 | "nodes": [ 5 | {"id": "optimized-a", "properties": {"alias": "one", "operation":"operation-A", "step_type" : "mapper"}}, 6 | {"id": "optimized-b", "properties": {"alias": "two", "operation":"operation-B", "step_type" : "mapper"}}, 7 | 8 | {"id": "unoptimized-a", "properties": {"alias": "one", "operation":"operation-A", "step_type" : "mapper"}}, 9 | {"id": "unoptimized-b", "properties": {"alias": "two", "operation":"operation-B", "step_type" : "mapper"}}, 10 | 11 | 12 | {"id": "optimized-job1", "child": "1"}, 13 | {"id": "optimized-job2", "child": "2"}, 14 | 15 | {"id": "unoptimized-job1", "child": "3"}, 16 | {"id": "unoptimized-job2", "child": "4"}, 17 | 18 | {"id": "optimized", "child": "5"}, 19 | {"id": "unoptimized", "child": "6"} 20 | 21 | ], 22 | "edges": [ 23 | {"u": "optimized-a", "v": "optimized-b", 24 | "properties":{ 25 | "sampleOutput":["a\u00011\nb\u00012"], 26 | "schema":[ 27 | {"type":"CHARARRAY", "alias":"name"}, 28 | {"type":"INTEGER", "alias":"value"} 29 | ] 30 | }, 31 | "label":"shoebox" 32 | }, 33 | {"u": "unoptimized-a", "v": "unoptimized-b"} 34 | ], 35 | "node_groups": [ 36 | {"id": "1", "children": ["optimized-a"]}, 37 | {"id": "2", "children": ["optimized-b"]}, 38 | {"id": "3", "children": ["unoptimized-a"]}, 39 | {"id": "4", "children": ["unoptimized-b"]}, 40 | {"id": "5", "children": ["optimized-job1","optimized-job2"]}, 41 | {"id": "6", "children": ["unoptimized-job1","unoptimized-job2"]} 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /lipstick-server/examples/graphs/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "8", 3 | "status": { 4 | "progress":40, 5 | "startTime":1412354951, 6 | "heartbeatTime": 1412354796, 7 | "statusText":"running" 8 | }, 9 | "nodes": [ 10 | {"id": "a", "properties": {"alias": "uno", "operation":"start", "step_type" : "mapper"}}, 11 | {"id": "b", "properties": { 12 | "alias": "dos", "operation":"join", "join": { 13 | "by":[ 14 | {"alias":"a", "fields":["fieldA"]} 15 | ], 16 | "type":"hash", 17 | "strategy":"replicated" 18 | } 19 | } 20 | }, 21 | {"id": "c", "properties": {"alias": "tres", "operation":"do"}}, 22 | {"id": "f", "properties": {"alias": "cuatro", "operation":"end"}, "child": "1"}, 23 | {"id": "g", "properties": {"alias": "cinco", "operation":"do"}} 24 | ], 25 | "edges": [ 26 | {"u": "a", "v": "b"}, 27 | {"u": "b", "v": "c"} 28 | ], 29 | "node_groups": [ 30 | {"id": "1", "children": ["a","b"], 31 | "properties": { 32 | "a":{"b":"c"}, 33 | "foo":[1,2,3], 34 | "bar":{"many":{"levels":{"of":"stuff"}}} 35 | }, 36 | "status":{ 37 | "progress":30, 38 | "startTime":1412354951, 39 | "heartbeatTime": 1412354796, 40 | "statusText":"running" 41 | } 42 | } 43 | ], 44 | "properties": { 45 | "mygraphname": "example", 46 | "userName":"jacob" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lipstick-server/examples/graphs/example2.json: -------------------------------------------------------------------------------- 1 | {"status":{"statusText":"running", "progress":63}, "node_groups": [], "nodes": [{"type": "PythonNode", "id": "0", "properties": {"operation": "Module"}}, {"type": "PythonNode", "id": "1", "properties": {"operation": "Assign"}}, {"type": "PythonNode", "id": "2", "properties": {"operation": "Name"}}, {"type": "PythonNode", "id": "3", "properties": {"operation": "Store"}}, {"type": "PythonNode", "id": "4", "properties": {"operation": "List"}}, {"type": "PythonNode", "id": "5", "properties": {"operation": "Str"}}, {"type": "PythonNode", "id": "6", "properties": {"operation": "Str"}}, {"type": "PythonNode", "id": "7", "properties": {"operation": "Str"}}, {"type": "PythonNode", "id": "8", "properties": {"operation": "Load"}}, {"type": "PythonNode", "id": "9", "properties": {"operation": "For"}}, {"type": "PythonNode", "id": "10", "properties": {"operation": "Name"}}, {"type": "PythonNode", "id": "11", "properties": {"operation": "Store"}}, {"type": "PythonNode", "id": "12", "properties": {"operation": "Name"}}, {"type": "PythonNode", "id": "13", "properties": {"operation": "Load"}}, {"type": "PythonNode", "id": "14", "properties": {"operation": "Print"}}, {"type": "PythonNode", "id": "15", "properties": {"operation": "Name"}}, {"type": "PythonNode", "id": "16", "properties": {"operation": "Load"}}], "edges": [{"u": "0", "v": "1"}, {"u": "0", "v": "9"}, {"u": "1", "v": "2"}, {"u": "1", "v": "4"}, {"u": "2", "v": "11"}, {"u": "4", "v": "5"}, {"u": "4", "v": "6"}, {"u": "4", "v": "7"}, {"u": "4", "v": "16"}, {"u": "9", "v": "10"}, {"u": "9", "v": "12"}, {"u": "9", "v": "14"}, {"u": "10", "v": "11"}, {"u": "12", "v": "16"}, {"u": "14", "v": "15"}, {"u": "15", "v": "16"}], "id": "1e28498c-4e6c-11e4-8215-c8e0eb148f15"} 2 | -------------------------------------------------------------------------------- /lipstick-server/examples/templates/pig-edge-template.json: -------------------------------------------------------------------------------- 1 | {"name":"PigEdge","view":"function PigEdge(properties) {\n var self = this;\n self.properties = properties; \n self.hasSampleData = function() {\n if (self.properties.sampleOutput) {\n return true;\n } \n return false;\n };\n\n self.hasSchema = function() {\n if (self.properties.schema) {\n return true;\n }\n return false;\n };\n\n self.sampleOutput = function() {\n var sol = self.properties.sampleOutput;\n var result = sol.map(function(so, i) {\n return {\n data: so.split(\"\\n\").map(function(record, j) {\n return {fields: record.split(\"\\u0001\")};\n })\n };\n });\n return result;\n };\n\n self.schema = properties.schema;\n}\n","template":"{{#hasSampleData}}\n

Sample Data:

\n{{#sampleOutput}}\n\n {{#hasSchema}}\n \n \n {{#schema}}\n \n {{/schema}}\n \n \n {{/hasSchema}}\n {{^hasSchema}}\n \n {{/hasSchema}}\n \n {{#data}}\n \n {{#fields}}\n \n {{/fields}}\n \n {{/data}}\n \n
{{alias}}
 
{{.}}
\n{{/sampleOutput}}\n{{/hasSampleData}}\n{{^hasSampleData}}\n
No sample output available at this time.
\n{{/hasSampleData}}\n"} 2 | -------------------------------------------------------------------------------- /lipstick-server/init.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'sinatra' 3 | require 'java' 4 | require 'json' 5 | 6 | jars = Dir["lib/*.jar"] 7 | jars.each{|j| require j} 8 | 9 | $CLASSPATH << '/etc' 10 | java.lang.System.set_property('eureka.client.props', 'lipstick') 11 | 12 | configure do 13 | root = File.expand_path(File.dirname(__FILE__)) 14 | set :public_folder, File.join(root, 'app', 'public') 15 | set :protection, :except => [:json_csrf] 16 | end 17 | 18 | # Load the helpers 19 | load "app/helpers/elasticsearch.rb" # load this first 20 | 21 | Dir["app/helpers/*.rb"].each { |file| load file } 22 | 23 | # Load the controllers. 24 | Dir["app/controllers/*.rb"].each { |file| load file } 25 | 26 | at_exit do 27 | PlanService.close 28 | end 29 | -------------------------------------------------------------------------------- /quickstart/1.dat: -------------------------------------------------------------------------------- 1 | 1,orange 2 | 2,apple 3 | 3,orange -------------------------------------------------------------------------------- /quickstart/2.dat: -------------------------------------------------------------------------------- 1 | 1,orange 2 | 2,apple 3 | 3,orange -------------------------------------------------------------------------------- /quickstart/example1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 4 | cygwin=false 5 | case "`uname`" in 6 | CYGWIN* ) 7 | cygwin=true 8 | ;; 9 | esac 10 | if $cygwin ; then 11 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 12 | fi 13 | 14 | # Determine the Java command to use to start the JVM. 15 | if [ -n "$JAVA_HOME" ] ; then 16 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 17 | # IBM's JDK on AIX uses strange locations for the executables 18 | JAVACMD="$JAVA_HOME/jre/sh/java" 19 | else 20 | JAVACMD="$JAVA_HOME/bin/java" 21 | fi 22 | if [ ! -x "$JAVACMD" ] ; then 23 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 24 | 25 | Please set the JAVA_HOME variable in your environment to match the 26 | location of your Java installation." 27 | fi 28 | else 29 | JAVACMD="java" 30 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 31 | 32 | Please set the JAVA_HOME variable in your environment to match the 33 | location of your Java installation." 34 | fi 35 | 36 | exec "$JAVACMD" -jar ../lipstick-console/build/libs/lipstick-console-*-withHadoop.jar -x local -propertyFile ./pig.properties ./test_local.pig 37 | -------------------------------------------------------------------------------- /quickstart/pig.properties: -------------------------------------------------------------------------------- 1 | lipstick.server.url=http://localhost:9292 2 | lipstick.uuid.prop.name=lipstick.uuid 3 | -------------------------------------------------------------------------------- /quickstart/test_local.pig: -------------------------------------------------------------------------------- 1 | fruits = load './1.dat' using PigStorage(',') as (id:int, fruit:chararray); 2 | names = load './2.dat' using PigStorage(',') as (id:int, name:chararray); 3 | names = filter names by id == 1 and not id == 2 or id * .5 == 1 and name == 'jeff'; 4 | fruit_names_join = join fruits by id, names by id; 5 | limited = limit fruit_names_join 100; 6 | fruit_names = foreach limited generate fruit, UPPER(name); 7 | dump fruit_names; 8 | --store fruit_names into 'blah2013'; 9 | -------------------------------------------------------------------------------- /quickstart/test_no_output.pig: -------------------------------------------------------------------------------- 1 | fruits = load './1.dat' using PigStorage(',') as (id:int, fruit:chararray); 2 | names = load './2.dat' using PigStorage(',') as (id:int, name:chararray); 3 | names = filter names by id == 1 and not id == 2 or id * .5 == 1 and name == 'jeff'; 4 | fruit_names_join = join fruits by id, names by id; 5 | limited = limit fruit_names_join 100; 6 | fruit_names = foreach limited generate fruit, UPPER(name); 7 | no_fruit_names = FILTER fruit_names by 1 == 2; 8 | dump no_fruit_names; 9 | --store fruit_names into 'blah2013'; 10 | 11 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name='lipstick' 2 | include 'lipstick-console','lipstick-server' 3 | --------------------------------------------------------------------------------