{"action":"created","comment":{"url":"https://api.github.com/repos/yahoo/fili/pulls/comments/88785933","pull_request_review_id":9351120,"id":88785933,"diff_hunk":"@@ -0,0 +1,297 @@\n+// Copyright 2016 Yahoo Inc.\n+// Licensed under the terms of the Apache license. Please see LICENSE.md file distributed with this work for terms.\n+\n+package com.yahoo.bard.webservice.data.config.metric.parser;\n+\n+import com.yahoo.bard.webservice.data.config.metric.parser.lexer.LexException;\n+import com.yahoo.bard.webservice.data.config.metric.parser.lexer.Lexeme;\n+import com.yahoo.bard.webservice.data.config.metric.parser.lexer.LexemeType;\n+import com.yahoo.bard.webservice.data.config.metric.parser.operand.ConstantMetricNode;\n+import com.yahoo.bard.webservice.data.config.metric.parser.operand.IdentifierNode;\n+import com.yahoo.bard.webservice.data.config.metric.parser.operand.MetricNode;\n+import com.yahoo.bard.webservice.data.config.metric.parser.operand.Operand;\n+import com.yahoo.bard.webservice.data.config.metric.parser.operator.ArithmeticOperator;\n+import com.yahoo.bard.webservice.data.config.metric.parser.operator.BinaryFilterOperator;\n+import com.yahoo.bard.webservice.data.config.metric.parser.operator.FilterOperator;\n+import com.yahoo.bard.webservice.data.config.metric.parser.operator.FunctionOperator;\n+import com.yahoo.bard.webservice.data.config.metric.parser.operator.Operator;\n+import com.yahoo.bard.webservice.data.config.metric.parser.operator.Precedence;\n+import com.yahoo.bard.webservice.data.config.metric.parser.operator.Sentinel;\n+import com.yahoo.bard.webservice.data.config.provider.MakerDictionary;\n+import com.yahoo.bard.webservice.data.dimension.DimensionDictionary;\n+import com.yahoo.bard.webservice.data.metric.LogicalMetric;\n+import com.yahoo.bard.webservice.data.metric.MetricDictionary;\n+\n+import java.io.IOException;\n+import java.util.ArrayDeque;\n+import java.util.Deque;\n+import java.util.LinkedList;\n+import java.util.Queue;\n+\n+/**\n+ * Metric parser.\n+ */\n+public class MetricParser {\n+\n+    private String name;\n+\n+    // All lookups happen here; this is where the metrics all end up\n+    private final MetricDictionary dict;\n+    private final MetricDictionary tempDict;\n+\n+    static int TEMP_IDENTIFIER = 0;\n+    static String TEMP_BASE = \"__temp_metric_\";\n+\n+    private final Deque<Operator> operatorStack = new ArrayDeque<>();\n+    private final Deque<Operand> operandStack = new ArrayDeque<>();\n+\n+    private final DimensionDictionary dimensionDictionary;\n+    private final MakerDictionary makerDict;\n+    private final String metricDefinition;\n+\n+    private Queue<Lexeme> items;\n+\n+    /**\n+     * Parse a metric.\n+     *\n+     * @param metricName The metric name\n+     * @param metricDefinition The metric definition, as a string\n+     * @param dict the base metric dictionary\n+     * @param tempDict the temporary scoped metric dictionary\n+     * @param makerDict the metric maker dictionary\n+     * @param dimensionDictionary the dimension dictionary\n+     */\n+    public MetricParser(\n+            String metricName,\n+            String metricDefinition,\n+            MetricDictionary dict,\n+            MetricDictionary tempDict,\n+            MakerDictionary makerDict,\n+            DimensionDictionary dimensionDictionary\n+    ) {\n+        this.name = metricName;\n+        this.dict = dict;\n+        this.tempDict = tempDict;\n+        this.makerDict = makerDict;\n+        this.dimensionDictionary = dimensionDictionary;\n+        this.metricDefinition = metricDefinition;\n+    }\n+\n+    /**\n+     * Parses the metric def and returns a LogicalMetric; caller should add to dictionary.\n+     *\n+     * @return a logical metric\n+     * @throws IOException when a lexing or parsing error occurs\n+     */\n+    public LogicalMetric parse() throws IOException {\n+\n+        try {\n+            items = Lexeme.lex(metricDefinition);\n+        } catch (LexException e) {\n+            throw new ParsingException(\n+                    \"Error occurred while lexing metric \" + name + \" with definition \" + metricDefinition,\n+                    e\n+            );\n+        }\n+\n+        try {\n+            MetricNode node = innerParse();\n+            return node.make(name, tempDict);\n+        } catch (ParsingException e) {\n+            throw new ParsingException(\n+                    \"Error occurred while parsing metric \" + name + \" with definition \" + metricDefinition,\n+                    e\n+            );\n+        }\n+    }\n+\n+    /**\n+     * Parses the metric def and returns a MetricNode.\n+     *\n+     * @return a Metric Node\n+     * @throws ParsingException when a parsing error occurs\n+     */\n+    protected MetricNode innerParse() throws ParsingException {\n+        operatorStack.push(new Sentinel());\n+        parseExpression();\n+        Operand o = operandStack.peek();\n+        return o.getMetricNode();\n+    }\n+\n+    /**\n+     * Parse an expression\n+     *\n+     * Uses the shunting yard algorithm ( https://www.engr.mun.ca/~theo/Misc/exp_parsing.htm#shunting_yard or wiki),\n+     * with some minor changes to handle things like function calls.\n+     *\n+     * @throws ParsingException when a parsing error occurs\n+     */\n+    public void parseExpression() throws ParsingException {\n+        consumeNext();\n+\n+        // Handle most operators; functions are handled inside consumeNext()\n+        while (true) {\n+            Lexeme current;\n+            if ((current = peekPop(LexemeType.BINARY_OPERATOR)) != null) {\n+                pushOperator(ArithmeticOperator.fromString(current.getToken()));\n+                consumeNext();\n+            } else if ((current = peekPop(LexemeType.FILTER_OPERATOR)) != null) {\n+                pushOperator(BinaryFilterOperator.fromString(current.getToken()));\n+                consumeNext();\n+            } else if ((peekPop(LexemeType.PIPE)) != null) {\n+                pushOperator(new FilterOperator());\n+                consumeNext();\n+            } else {\n+                break;\n+            }\n+        }\n+\n+        while (operatorStack.peek().getPrecedence().greaterThan(Precedence.SENTINEL)) {\n+            popOperator();\n+        }\n+    }\n+\n+    /**\n+     * Consume and parse the next token/lexeme on the stack.\n+     *\n+     * @throws ParsingException when a parsing error occurs\n+     */\n+    private void consumeNext() throws ParsingException {\n+        Lexeme current = items.remove();\n+        LexemeType type = current.getType();\n+\n+        switch (type) {\n+            case NUMBER:\n+            case DOUBLE_QUOTED_STRING:\n+            case SINGLE_QUOTED_STRING:\n+                operandStack.push(new ConstantMetricNode(current.getToken()));\n+                break;\n+            case L_PAREN:\n+                operatorStack.push(new Sentinel());\n+                parseExpression();\n+                expect(LexemeType.R_PAREN);\n+                operatorStack.pop();\n+                break;\n+\n+            case IDENTIFIER:\n+                if (peekType(LexemeType.L_PAREN)) {\n+                    expect(LexemeType.L_PAREN);\n+                    int nArgs = parseFuncArgs();\n+                    expect(LexemeType.R_PAREN);\n+                    pushOperator(new FunctionOperator(makerDict.get(current.getToken()), nArgs));\n+                } else {\n+                    operandStack.push(new IdentifierNode(current.getToken(), dimensionDictionary, dict));\n+                }\n+                break;\n+            default:\n+                throw new ParsingException(\"Unexpected token:\" + current.getToken());\n+        }\n+    }\n+\n+    /**\n+     * Helper function to parse arguments to a function.\n+     *\n+     * @return the number of args parsed and placed onto the operand stack\n+     * @throws ParsingException when a parsing error occurs\n+     */\n+    private int parseFuncArgs() throws ParsingException {\n+        int argc = 0;\n+\n+        while (true) {\n+            operatorStack.push(new Sentinel());\n+            parseExpression();\n+            argc += 1;\n+            operatorStack.pop();\n+\n+            if (!peekType(LexemeType.COMMA)) {\n+                break;\n+            }\n+            expect(LexemeType.COMMA);\n+        }\n+\n+        return argc;\n+    }\n+\n+    /**\n+     * Get the next item on the top of the lexeme stack if it matches `type`, or throw an exception.\n+     *\n+     * @param type Lexeme type\n+     * @return the item of the specified type\n+     * @throws ParsingException when a parsing error occurs\n+     */\n+    private Lexeme expect(LexemeType type) throws ParsingException {\n+        Lexeme current = items.remove();\n+        if (!(current.getType() == type)) {","path":"fili-core/src/main/java/com/yahoo/bard/webservice/data/config/metric/parser/MetricParser.java","position":224,"original_position":224,"commit_id":"f5cdc3fa3e53accac7d0f5d362fbf79ff1b95b41","original_commit_id":"f5cdc3fa3e53accac7d0f5d362fbf79ff1b95b41","user":{"login":"codacy-bot","id":19940114,"avatar_url":"https://avatars.githubusercontent.com/u/19940114?v=3","gravatar_id":"","url":"https://api.github.com/users/codacy-bot","html_url":"https://github.com/codacy-bot","followers_url":"https://api.github.com/users/codacy-bot/followers","following_url":"https://api.github.com/users/codacy-bot/following{/other_user}","gists_url":"https://api.github.com/users/codacy-bot/gists{/gist_id}","starred_url":"https://api.github.com/users/codacy-bot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/codacy-bot/subscriptions","organizations_url":"https://api.github.com/users/codacy-bot/orgs","repos_url":"https://api.github.com/users/codacy-bot/repos","events_url":"https://api.github.com/users/codacy-bot/events{/privacy}","received_events_url":"https://api.github.com/users/codacy-bot/received_events","type":"User","site_admin":false},"body":"![Codacy](https://www.codacy.com/assets/images/favicon.png) Issue found: [Use opposite operator instead of the logic complement operator.](https://www.codacy.com/app/Fili/fili/file/4476468334/issues/source?bid=3750985&fileBranchId=3923196#l224)","created_at":"2016-11-19T19:23:43Z","updated_at":"2016-11-19T19:23:43Z","html_url":"https://github.com/yahoo/fili/pull/104#discussion_r88785933","pull_request_url":"https://api.github.com/repos/yahoo/fili/pulls/104","_links":{"self":{"href":"https://api.github.com/repos/yahoo/fili/pulls/comments/88785933"},"html":{"href":"https://github.com/yahoo/fili/pull/104#discussion_r88785933"},"pull_request":{"href":"https://api.github.com/repos/yahoo/fili/pulls/104"}}},"pull_request":{"url":"https://api.github.com/repos/yahoo/fili/pulls/104","id":94492151,"html_url":"https://github.com/yahoo/fili/pull/104","diff_url":"https://github.com/yahoo/fili/pull/104.diff","patch_url":"https://github.com/yahoo/fili/pull/104.patch","issue_url":"https://api.github.com/repos/yahoo/fili/issues/104","number":104,"state":"open","locked":false,"title":"YAML-based configuration","user":{"login":"jacobtolar","id":1390277,"avatar_url":"https://avatars.githubusercontent.com/u/1390277?v=3","gravatar_id":"","url":"https://api.github.com/users/jacobtolar","html_url":"https://github.com/jacobtolar","followers_url":"https://api.github.com/users/jacobtolar/followers","following_url":"https://api.github.com/users/jacobtolar/following{/other_user}","gists_url":"https://api.github.com/users/jacobtolar/gists{/gist_id}","starred_url":"https://api.github.com/users/jacobtolar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jacobtolar/subscriptions","organizations_url":"https://api.github.com/users/jacobtolar/orgs","repos_url":"https://api.github.com/users/jacobtolar/repos","events_url":"https://api.github.com/users/jacobtolar/events{/privacy}","received_events_url":"https://api.github.com/users/jacobtolar/received_events","type":"User","site_admin":false},"body":"This change is quite large (sorry about that...) but there are (almost) zero changes to the core code, so at least it shouldn't break any existing functionality; it just adds new broken functionality :).\r\n\r\n* Adds a set of interfaces for setting up Fili via configuration (`ConfigProvider`). You can currently configure metrics, dimensions, physical and logical tables, and register custom metric makers. Notably, I've never had to set up dimension loading or search providers before, so those aren't supported yet but could presumably be added without too much trouble.\r\n\r\n* There is also an implementation of AbstractBinderFactory that uses reflection to instantiate a specified ConfigProvider and provide all the necessary Fili configuration. With this, you can get Fili up and running in front of your Druid instance without writing a single line of code.\r\n\r\n* There is an implementation of `ConfigProvider` for a YAML-driven configuration via Jackson's yaml support (jackson-dataformat-yaml).\r\n\r\n* There's a new subproject, 'fili-yaml', that is set up to with the ConfigBinderFactory (this repo contains no actual java code). It just builds a war that can be configured easily with yaml. It also includes a couple scripts and a Dockerfile that can be used to stand up Fili via Docker, and a few instructions on how to do so. \r\n\r\n* Distinct from the YAML config, there is also a metric parser included (it's used by the YAML config provider, but could also be used independently of it). It lets you define metrics in a special DSL; for example, `(impressions + 3)/5 | gender == 2` would produce a filtered aggregate. There's not really any docs on the DSL included in this PR yet, but it's hopefully fairly intuitive. It doesn't support 100% of what you could do programmatically, but it's hopefully a good start.\r\n\r\n* One of the few changes to existing code is that `ShardSpecMixIn` was updated to include 'linear' which is used for streaming.\r\n\r\n* A couple assorted documentation updates.\r\n\r\n* Updated the ClassScanner to handle array argument types.\r\n\r\nThe test coverage isn't perfect, but most things have at least one test :) (Notable exception: the metric parser; I've written tests for most of the classes it uses, just not the top-level parser). I have also tested this against our Druid instance, which was where I found the ShardSpecMixIn change. \r\n\r\nThere's also a couple things in here (especially the metric parser) that I'm not 100% sure I have correct -- Fili/Druid-isms that I didn't dig into fully. I tried to make notes when I wasn't sure of something so there's a few FIXMEs scattered around.","created_at":"2016-11-19T19:20:05Z","updated_at":"2016-11-19T19:23:44Z","closed_at":null,"merged_at":null,"merge_commit_sha":"5fb7487b44aa307776deb37b93412e892c4e14e7","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/yahoo/fili/pulls/104/commits","review_comments_url":"https://api.github.com/repos/yahoo/fili/pulls/104/comments","review_comment_url":"https://api.github.com/repos/yahoo/fili/pulls/comments{/number}","comments_url":"https://api.github.com/repos/yahoo/fili/issues/104/comments","statuses_url":"https://api.github.com/repos/yahoo/fili/statuses/f5cdc3fa3e53accac7d0f5d362fbf79ff1b95b41","head":{"label":"jacobtolar:yaml-config","ref":"yaml-config","sha":"f5cdc3fa3e53accac7d0f5d362fbf79ff1b95b41","user":{"login":"jacobtolar","id":1390277,"avatar_url":"https://avatars.githubusercontent.com/u/1390277?v=3","gravatar_id":"","url":"https://api.github.com/users/jacobtolar","html_url":"https://github.com/jacobtolar","followers_url":"https://api.github.com/users/jacobtolar/followers","following_url":"https://api.github.com/users/jacobtolar/following{/other_user}","gists_url":"https://api.github.com/users/jacobtolar/gists{/gist_id}","starred_url":"https://api.github.com/users/jacobtolar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jacobtolar/subscriptions","organizations_url":"https://api.github.com/users/jacobtolar/orgs","repos_url":"https://api.github.com/users/jacobtolar/repos","events_url":"https://api.github.com/users/jacobtolar/events{/privacy}","received_events_url":"https://api.github.com/users/jacobtolar/received_events","type":"User","site_admin":false},"repo":{"id":74225846,"name":"fili","full_name":"jacobtolar/fili","owner":{"login":"jacobtolar","id":1390277,"avatar_url":"https://avatars.githubusercontent.com/u/1390277?v=3","gravatar_id":"","url":"https://api.github.com/users/jacobtolar","html_url":"https://github.com/jacobtolar","followers_url":"https://api.github.com/users/jacobtolar/followers","following_url":"https://api.github.com/users/jacobtolar/following{/other_user}","gists_url":"https://api.github.com/users/jacobtolar/gists{/gist_id}","starred_url":"https://api.github.com/users/jacobtolar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jacobtolar/subscriptions","organizations_url":"https://api.github.com/users/jacobtolar/orgs","repos_url":"https://api.github.com/users/jacobtolar/repos","events_url":"https://api.github.com/users/jacobtolar/events{/privacy}","received_events_url":"https://api.github.com/users/jacobtolar/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/jacobtolar/fili","description":"Easily make RESTful web services for time series reporting with Big Data analytics engines like Druid and Hive.","fork":true,"url":"https://api.github.com/repos/jacobtolar/fili","forks_url":"https://api.github.com/repos/jacobtolar/fili/forks","keys_url":"https://api.github.com/repos/jacobtolar/fili/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jacobtolar/fili/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jacobtolar/fili/teams","hooks_url":"https://api.github.com/repos/jacobtolar/fili/hooks","issue_events_url":"https://api.github.com/repos/jacobtolar/fili/issues/events{/number}","events_url":"https://api.github.com/repos/jacobtolar/fili/events","assignees_url":"https://api.github.com/repos/jacobtolar/fili/assignees{/user}","branches_url":"https://api.github.com/repos/jacobtolar/fili/branches{/branch}","tags_url":"https://api.github.com/repos/jacobtolar/fili/tags","blobs_url":"https://api.github.com/repos/jacobtolar/fili/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jacobtolar/fili/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jacobtolar/fili/git/refs{/sha}","trees_url":"https://api.github.com/repos/jacobtolar/fili/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jacobtolar/fili/statuses/{sha}","languages_url":"https://api.github.com/repos/jacobtolar/fili/languages","stargazers_url":"https://api.github.com/repos/jacobtolar/fili/stargazers","contributors_url":"https://api.github.com/repos/jacobtolar/fili/contributors","subscribers_url":"https://api.github.com/repos/jacobtolar/fili/subscribers","subscription_url":"https://api.github.com/repos/jacobtolar/fili/subscription","commits_url":"https://api.github.com/repos/jacobtolar/fili/commits{/sha}","git_commits_url":"https://api.github.com/repos/jacobtolar/fili/git/commits{/sha}","comments_url":"https://api.github.com/repos/jacobtolar/fili/comments{/number}","issue_comment_url":"https://api.github.com/repos/jacobtolar/fili/issues/comments{/number}","contents_url":"https://api.github.com/repos/jacobtolar/fili/contents/{+path}","compare_url":"https://api.github.com/repos/jacobtolar/fili/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jacobtolar/fili/merges","archive_url":"https://api.github.com/repos/jacobtolar/fili/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jacobtolar/fili/downloads","issues_url":"https://api.github.com/repos/jacobtolar/fili/issues{/number}","pulls_url":"https://api.github.com/repos/jacobtolar/fili/pulls{/number}","milestones_url":"https://api.github.com/repos/jacobtolar/fili/milestones{/number}","notifications_url":"https://api.github.com/repos/jacobtolar/fili/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jacobtolar/fili/labels{/name}","releases_url":"https://api.github.com/repos/jacobtolar/fili/releases{/id}","deployments_url":"https://api.github.com/repos/jacobtolar/fili/deployments","created_at":"2016-11-19T18:05:01Z","updated_at":"2016-11-19T18:05:04Z","pushed_at":"2016-11-19T19:21:35Z","git_url":"git://github.com/jacobtolar/fili.git","ssh_url":"git@github.com:jacobtolar/fili.git","clone_url":"https://github.com/jacobtolar/fili.git","svn_url":"https://github.com/jacobtolar/fili","homepage":"","size":2103,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"yahoo:master","ref":"master","sha":"5bd948beffb4a4f19ebcf06a9984bc34845bf374","user":{"login":"yahoo","id":16574,"avatar_url":"https://avatars.githubusercontent.com/u/16574?v=3","gravatar_id":"","url":"https://api.github.com/users/yahoo","html_url":"https://github.com/yahoo","followers_url":"https://api.github.com/users/yahoo/followers","following_url":"https://api.github.com/users/yahoo/following{/other_user}","gists_url":"https://api.github.com/users/yahoo/gists{/gist_id}","starred_url":"https://api.github.com/users/yahoo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yahoo/subscriptions","organizations_url":"https://api.github.com/users/yahoo/orgs","repos_url":"https://api.github.com/users/yahoo/repos","events_url":"https://api.github.com/users/yahoo/events{/privacy}","received_events_url":"https://api.github.com/users/yahoo/received_events","type":"Organization","site_admin":false},"repo":{"id":60879636,"name":"fili","full_name":"yahoo/fili","owner":{"login":"yahoo","id":16574,"avatar_url":"https://avatars.githubusercontent.com/u/16574?v=3","gravatar_id":"","url":"https://api.github.com/users/yahoo","html_url":"https://github.com/yahoo","followers_url":"https://api.github.com/users/yahoo/followers","following_url":"https://api.github.com/users/yahoo/following{/other_user}","gists_url":"https://api.github.com/users/yahoo/gists{/gist_id}","starred_url":"https://api.github.com/users/yahoo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yahoo/subscriptions","organizations_url":"https://api.github.com/users/yahoo/orgs","repos_url":"https://api.github.com/users/yahoo/repos","events_url":"https://api.github.com/users/yahoo/events{/privacy}","received_events_url":"https://api.github.com/users/yahoo/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/yahoo/fili","description":"Easily make RESTful web services for time series reporting with Big Data analytics engines like Druid and Hive.","fork":false,"url":"https://api.github.com/repos/yahoo/fili","forks_url":"https://api.github.com/repos/yahoo/fili/forks","keys_url":"https://api.github.com/repos/yahoo/fili/keys{/key_id}","collaborators_url":"https://api.github.com/repos/yahoo/fili/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/yahoo/fili/teams","hooks_url":"https://api.github.com/repos/yahoo/fili/hooks","issue_events_url":"https://api.github.com/repos/yahoo/fili/issues/events{/number}","events_url":"https://api.github.com/repos/yahoo/fili/events","assignees_url":"https://api.github.com/repos/yahoo/fili/assignees{/user}","branches_url":"https://api.github.com/repos/yahoo/fili/branches{/branch}","tags_url":"https://api.github.com/repos/yahoo/fili/tags","blobs_url":"https://api.github.com/repos/yahoo/fili/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/yahoo/fili/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/yahoo/fili/git/refs{/sha}","trees_url":"https://api.github.com/repos/yahoo/fili/git/trees{/sha}","statuses_url":"https://api.github.com/repos/yahoo/fili/statuses/{sha}","languages_url":"https://api.github.com/repos/yahoo/fili/languages","stargazers_url":"https://api.github.com/repos/yahoo/fili/stargazers","contributors_url":"https://api.github.com/repos/yahoo/fili/contributors","subscribers_url":"https://api.github.com/repos/yahoo/fili/subscribers","subscription_url":"https://api.github.com/repos/yahoo/fili/subscription","commits_url":"https://api.github.com/repos/yahoo/fili/commits{/sha}","git_commits_url":"https://api.github.com/repos/yahoo/fili/git/commits{/sha}","comments_url":"https://api.github.com/repos/yahoo/fili/comments{/number}","issue_comment_url":"https://api.github.com/repos/yahoo/fili/issues/comments{/number}","contents_url":"https://api.github.com/repos/yahoo/fili/contents/{+path}","compare_url":"https://api.github.com/repos/yahoo/fili/compare/{base}...{head}","merges_url":"https://api.github.com/repos/yahoo/fili/merges","archive_url":"https://api.github.com/repos/yahoo/fili/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/yahoo/fili/downloads","issues_url":"https://api.github.com/repos/yahoo/fili/issues{/number}","pulls_url":"https://api.github.com/repos/yahoo/fili/pulls{/number}","milestones_url":"https://api.github.com/repos/yahoo/fili/milestones{/number}","notifications_url":"https://api.github.com/repos/yahoo/fili/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/yahoo/fili/labels{/name}","releases_url":"https://api.github.com/repos/yahoo/fili/releases{/id}","deployments_url":"https://api.github.com/repos/yahoo/fili/deployments","created_at":"2016-06-10T22:07:59Z","updated_at":"2016-11-19T11:23:52Z","pushed_at":"2016-11-19T19:21:36Z","git_url":"git://github.com/yahoo/fili.git","ssh_url":"git@github.com:yahoo/fili.git","clone_url":"https://github.com/yahoo/fili.git","svn_url":"https://github.com/yahoo/fili","homepage":"","size":2103,"stargazers_count":18,"watchers_count":18,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":17,"mirror_url":null,"open_issues_count":21,"forks":17,"open_issues":21,"watchers":18,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/yahoo/fili/pulls/104"},"html":{"href":"https://github.com/yahoo/fili/pull/104"},"issue":{"href":"https://api.github.com/repos/yahoo/fili/issues/104"},"comments":{"href":"https://api.github.com/repos/yahoo/fili/issues/104/comments"},"review_comments":{"href":"https://api.github.com/repos/yahoo/fili/pulls/104/comments"},"review_comment":{"href":"https://api.github.com/repos/yahoo/fili/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/yahoo/fili/pulls/104/commits"},"statuses":{"href":"https://api.github.com/repos/yahoo/fili/statuses/f5cdc3fa3e53accac7d0f5d362fbf79ff1b95b41"}}}}
{"actor":{"display_login":"codacy-bot"}}