b/159709036 create pipeline's document base parser.

Change-Id: Icc2999dfefd887547ae9dc86992b89213c9462a1
diff --git a/build.gradle b/build.gradle
index 5d69eca..5b3649f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -36,6 +36,7 @@
     // Test
     "junit": "4.13", // 22 June 2020
     "mockito": "3.3.3", // 22 June 2020
+    "assertj": "3.16.1", // 26 June 2020
 
     // Command Line Utility Libraries
     "jcommander": "1.78", // 22 June 2020
diff --git a/oas-core/src/main/java/com/apigee/security/oas/parser/BaseParser.java b/oas-core/src/main/java/com/apigee/security/oas/parser/BaseParser.java
index 9204695..c070896 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/parser/BaseParser.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/parser/BaseParser.java
@@ -1,3 +1,15 @@
 package com.apigee.security.oas.parser;
 
-public class BaseParser {}
+import java.io.File;
+import java.net.URL;
+import org.openapi4j.parser.model.v3.OpenApi3;
+
+/** Parses a {@linkplain File file} or {@link URL} into an {@code OpenApi3} object. */
+public interface BaseParser {
+
+  /** Parses a valid OpenAPI Specification (v3) document from a {@link URL}. */
+  OpenApi3 parse(URL url);
+
+  /** Parses a valid OpenAPI Specification (v3) document from a {@linkplain File file}. */
+  OpenApi3 parse(File specFile);
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/parser/BaseParserModule.java b/oas-core/src/main/java/com/apigee/security/oas/parser/BaseParserModule.java
new file mode 100644
index 0000000..92c705e
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/parser/BaseParserModule.java
@@ -0,0 +1,19 @@
+package com.apigee.security.oas.parser;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
+import org.openapi4j.parser.OpenApi3Parser;
+
+/** Module with instructions for instantiating instances of {@code BaseParser}. */
+public final class BaseParserModule extends AbstractModule {
+
+  @Override
+  protected void configure() {
+    bind(BaseParser.class).to(DefaultBaseParser.class);
+  }
+
+  @Provides
+  OpenApi3Parser provideOpenApiForJavaOasParser() {
+    return new OpenApi3Parser();
+  }
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/parser/DefaultBaseParser.java b/oas-core/src/main/java/com/apigee/security/oas/parser/DefaultBaseParser.java
new file mode 100644
index 0000000..f3404f9
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/parser/DefaultBaseParser.java
@@ -0,0 +1,44 @@
+package com.apigee.security.oas.parser;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import javax.inject.Inject;
+import org.openapi4j.core.exception.ResolutionException;
+import org.openapi4j.core.validation.ValidationException;
+import org.openapi4j.parser.OpenApi3Parser;
+import org.openapi4j.parser.model.v3.OpenApi3;
+
+/** Validates format of and parses an OpenAPI specification (v3) JSON or YAML document. */
+final class DefaultBaseParser implements BaseParser {
+
+  private static final String PARSER_ERROR_MSG = "Failed to parse OAS document.";
+  private final OpenApi3Parser openApiParser;
+
+  @Inject
+  DefaultBaseParser(OpenApi3Parser openApiParser) {
+    this.openApiParser = openApiParser;
+  }
+
+  @Override
+  public OpenApi3 parse(URL url) {
+    checkNotNull(url);
+    try {
+      return openApiParser.parse(url, /* validate= */ true);
+    } catch (ResolutionException | ValidationException e) {
+      throw new ParserException(PARSER_ERROR_MSG, e);
+    }
+  }
+
+  @Override
+  public OpenApi3 parse(File specFile) {
+    checkNotNull(specFile);
+    try {
+      return parse(specFile.toURI().toURL());
+    } catch (MalformedURLException e) {
+      throw new ParserException(PARSER_ERROR_MSG, e);
+    }
+  }
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/parser/ParserException.java b/oas-core/src/main/java/com/apigee/security/oas/parser/ParserException.java
new file mode 100644
index 0000000..601c41b
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/parser/ParserException.java
@@ -0,0 +1,11 @@
+package com.apigee.security.oas.parser;
+
+/**
+ * Exception thrown when an error has occurred while parsing and validating an OpenApi Specification
+ * document.
+ */
+public final class ParserException extends RuntimeException {
+  ParserException(String s, Exception childException) {
+    super(s, childException);
+  }
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/parser/ParserModule.java b/oas-core/src/main/java/com/apigee/security/oas/parser/ParserModule.java
new file mode 100644
index 0000000..4c7600a
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/parser/ParserModule.java
@@ -0,0 +1,12 @@
+package com.apigee.security.oas.parser;
+
+import com.google.inject.AbstractModule;
+
+/** Top level module that imports other Guice modules relied upon. */
+public final class ParserModule extends AbstractModule {
+  @Override
+  protected void configure() {
+    install(new BaseParserModule());
+    binder().requireExplicitBindings();
+  }
+}
diff --git a/oas-core/src/test/java/com/apigee/security/oas/parser/BaseParserTest.java b/oas-core/src/test/java/com/apigee/security/oas/parser/BaseParserTest.java
deleted file mode 100644
index 9f28094..0000000
--- a/oas-core/src/test/java/com/apigee/security/oas/parser/BaseParserTest.java
+++ /dev/null
@@ -1,3 +0,0 @@
-package com.apigee.security.oas.parser;
-
-public class BaseParserTest {}
diff --git a/oas-core/src/test/java/com/apigee/security/oas/parser/DefaultBaseParserTest.java b/oas-core/src/test/java/com/apigee/security/oas/parser/DefaultBaseParserTest.java
new file mode 100644
index 0000000..1c914ac
--- /dev/null
+++ b/oas-core/src/test/java/com/apigee/security/oas/parser/DefaultBaseParserTest.java
@@ -0,0 +1,96 @@
+package com.apigee.security.oas.parser;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.ThrowableAssert.catchThrowable;
+
+import com.google.common.io.Resources;
+import com.google.inject.Guice;
+import java.io.File;
+import java.net.URL;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.openapi4j.parser.model.v3.OpenApi3;
+
+@RunWith(JUnit4.class)
+public class DefaultBaseParserTest {
+  private BaseParser defaultBaseParser;
+
+  private URL validUrl;
+  private URL invalidUrl;
+
+  private File validOasDocYaml;
+  private File validOasDocJson;
+  private File invalidOasDocYaml;
+  private File invalidOasDocJson;
+
+  /** Sets up variables tests need. */
+  @Before
+  public void setup() {
+    defaultBaseParser = Guice.createInjector(new ParserModule()).getInstance(BaseParser.class);
+
+    URL validOasJsonUrl = Resources.getResource("Valid_OAS_v3.json");
+    validOasDocJson = new File(validOasJsonUrl.getFile());
+    URL validOasYamlUrl = Resources.getResource("Valid_OAS_v3.yaml");
+    validOasDocYaml = new File(validOasYamlUrl.getFile());
+    URL invalidOasYamlUrl = Resources.getResource("Invalid_OAS_v3.yaml");
+    invalidOasDocYaml = new File(invalidOasYamlUrl.getFile());
+    URL invalidOasJsonUrl = Resources.getResource("Invalid_OAS_v3.json");
+    invalidOasDocJson = new File(invalidOasJsonUrl.getFile());
+
+    validUrl = validOasJsonUrl;
+    invalidUrl = invalidOasJsonUrl;
+  }
+
+  @Test
+  public void parse_nullOasFile_throwsNullPointerException() {
+    assertThat(catchThrowable(() -> defaultBaseParser.parse((File) null)))
+        .isInstanceOf(NullPointerException.class);
+  }
+
+  @Test
+  public void parse_nullOasUrl_throwsNullPointerException() {
+    assertThat(catchThrowable(() -> defaultBaseParser.parse((URL) null)))
+        .isInstanceOf(NullPointerException.class);
+  }
+
+  @Test
+  public void parse_urlWithInvalidFormattedOas_throwsParserExceptionWithCause() {
+    assertThat(catchThrowable(() -> defaultBaseParser.parse(invalidUrl)))
+        .isInstanceOf(ParserException.class)
+        .getCause()
+        .isNotNull();
+  }
+
+  @Test
+  public void parse_jsonFileWithInvalidFormattedOas_throwsParserExceptionWithCause() {
+    assertThat(catchThrowable(() -> defaultBaseParser.parse(invalidOasDocJson)))
+        .isInstanceOf(ParserException.class)
+        .getCause()
+        .isNotNull();
+  }
+
+  @Test
+  public void parse_yamlFileWithInvalidFormattedOas_throwsParserExceptionWithCause() {
+    assertThat(catchThrowable(() -> defaultBaseParser.parse(invalidOasDocYaml)))
+        .isInstanceOf(ParserException.class)
+        .getCause()
+        .isNotNull();
+  }
+
+  @Test
+  public void parse_validOasYamlDoc_returnsOpenApiObject() {
+    assertThat(defaultBaseParser.parse(validOasDocYaml)).isInstanceOf(OpenApi3.class);
+  }
+
+  @Test
+  public void parse_validOasJsonDoc_returnsOpenApiObject() {
+    assertThat(defaultBaseParser.parse(validOasDocJson)).isInstanceOf(OpenApi3.class);
+  }
+
+  @Test
+  public void parse_validOasUrl_returnsOpenApiObject() {
+    assertThat(defaultBaseParser.parse(validUrl)).isInstanceOf(OpenApi3.class);
+  }
+}
diff --git a/oas-core/src/test/java/com/apigee/security/oas/parser/ParserModuleTest.java b/oas-core/src/test/java/com/apigee/security/oas/parser/ParserModuleTest.java
new file mode 100644
index 0000000..79e3f4f
--- /dev/null
+++ b/oas-core/src/test/java/com/apigee/security/oas/parser/ParserModuleTest.java
@@ -0,0 +1,21 @@
+package com.apigee.security.oas.parser;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.ThrowableAssert.catchThrowable;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ParserModuleTest {
+  @Test
+  public void createDefaultBaseParserShouldNotFail() {
+    Injector injector = Guice.createInjector(new ParserModule());
+
+    assertThat(catchThrowable(() -> injector.getInstance(BaseParser.class)))
+        .doesNotThrowAnyException();
+  }
+}
diff --git a/oas-core/src/test/resources/Invalid_OAS_v3.json b/oas-core/src/test/resources/Invalid_OAS_v3.json
new file mode 100644
index 0000000..577cd49
--- /dev/null
+++ b/oas-core/src/test/resources/Invalid_OAS_v3.json
@@ -0,0 +1,54 @@
+{
+  "openapi": "3.0.0",
+  "info": {
+    "title": "Simple API overview",
+    "version": "2.0.0"
+  },
+  "/": {
+    "paths": {
+      "summary": "List API versions",
+      "get": {
+        "operationId": "listVersionsv2",
+        "responses": {
+          "200": {
+            "description": "200 response",
+            "application/json": {
+              "content": {
+                "examples": {
+                  "value": {
+                    "foo": {
+                      "versions": [
+                        {
+                          "status": "CURRENT",
+                          "updated": "2011-01-21T11:33:21Z",
+                          "id": "v2.0",
+                          "links": [
+                            {
+                              "href": "http://127.0.0.1:8774/v2/",
+                              "rel": "self"
+                            }
+                          ]
+                        },
+                        {
+                          "status": "EXPERIMENTAL",
+                          "updated": "2013-07-23T11:33:21Z",
+                          "id": "v3.0",
+                          "links": [
+                            {
+                              "href": "http://127.0.0.1:8774/v3/",
+                              "rel": "self"
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/oas-core/src/test/resources/Invalid_OAS_v3.yaml b/oas-core/src/test/resources/Invalid_OAS_v3.yaml
new file mode 100644
index 0000000..a9f23ae
--- /dev/null
+++ b/oas-core/src/test/resources/Invalid_OAS_v3.yaml
@@ -0,0 +1,79 @@
+---
+openapi: "3.0.0"
+info:
+  title: Simple API overview
+  version: 2.0.0
+summary: List API versions
+paths:
+  /:
+    operationId: listVersionsv2
+    get:
+      responses:
+        '200':
+          description: |-
+            200 response
+          application/json:
+          content:
+              examples:
+                foo:
+                  value:
+                    {
+                      "versions": [
+                      {
+                        "status": "CURRENT",
+                        "updated": "2011-01-21T11:33:21Z",
+                        "id": "v2.0",
+                        "links": [
+                        {
+                          "href": "http://127.0.0.1:8774/v2/",
+                          "rel": "self"
+                        }
+                        ]
+                      },
+                      {
+                        "status": "EXPERIMENTAL",
+                        "updated": "2013-07-23T11:33:21Z",
+                        "id": "v3.0",
+                        "links": [
+                        {
+                          "href": "http://127.0.0.1:8774/v3/",
+                          "rel": "self"
+                        }
+                        ]
+                      }
+                      ]
+                    }
+        '300':
+          description: |-
+            300 response
+          content:
+            application/json:
+              examples:
+                foo:
+                  value: |
+                    {
+                     "versions": [
+                           {
+                             "status": "CURRENT",
+                             "updated": "2011-01-21T11:33:21Z",
+                             "id": "v2.0",
+                             "links": [
+                                 {
+                                     "href": "http://127.0.0.1:8774/v2/",
+                                     "rel": "self"
+                                 }
+                             ]
+                         },
+                         {
+                             "status": "EXPERIMENTAL",
+                             "updated": "2013-07-23T11:33:21Z",
+                             "id": "v3.0",
+                             "links": [
+                                 {
+                                     "href": "http://127.0.0.1:8774/v3/",
+                                     "rel": "self"
+                                 }
+                             ]
+                         }
+                     ]
+                    }
diff --git a/oas-core/src/test/resources/Valid_OAS_v3.json b/oas-core/src/test/resources/Valid_OAS_v3.json
new file mode 100644
index 0000000..1d40e7e
--- /dev/null
+++ b/oas-core/src/test/resources/Valid_OAS_v3.json
@@ -0,0 +1,54 @@
+{
+  "openapi": "3.0.0",
+  "info": {
+    "title": "Simple API overview",
+    "version": "2.0.0"
+  },
+  "paths": {
+    "/": {
+      "get": {
+        "operationId": "listVersionsv2",
+        "summary": "List API versions",
+        "responses": {
+          "200": {
+            "description": "200 response",
+            "content": {
+              "application/json": {
+                "examples": {
+                  "foo": {
+                    "value": {
+                      "versions": [
+                        {
+                          "status": "CURRENT",
+                          "updated": "2011-01-21T11:33:21Z",
+                          "id": "v2.0",
+                          "links": [
+                            {
+                              "href": "http://127.0.0.1:8774/v2/",
+                              "rel": "self"
+                            }
+                          ]
+                        },
+                        {
+                          "status": "EXPERIMENTAL",
+                          "updated": "2013-07-23T11:33:21Z",
+                          "id": "v3.0",
+                          "links": [
+                            {
+                              "href": "http://127.0.0.1:8774/v3/",
+                              "rel": "self"
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/oas-core/src/test/resources/Valid_OAS_v3.yaml b/oas-core/src/test/resources/Valid_OAS_v3.yaml
new file mode 100644
index 0000000..d853ab6
--- /dev/null
+++ b/oas-core/src/test/resources/Valid_OAS_v3.yaml
@@ -0,0 +1,79 @@
+---
+openapi: "3.0.0"
+info:
+  title: Simple API overview
+  version: 2.0.0
+paths:
+  /:
+    get:
+      operationId: listVersionsv2
+      summary: List API versions
+      responses:
+        '200':
+          description: |-
+            200 response
+          content:
+            application/json:
+              examples:
+                foo:
+                  value:
+                    {
+                      "versions": [
+                      {
+                        "status": "CURRENT",
+                        "updated": "2011-01-21T11:33:21Z",
+                        "id": "v2.0",
+                        "links": [
+                        {
+                          "href": "http://127.0.0.1:8774/v2/",
+                          "rel": "self"
+                        }
+                        ]
+                      },
+                      {
+                        "status": "EXPERIMENTAL",
+                        "updated": "2013-07-23T11:33:21Z",
+                        "id": "v3.0",
+                        "links": [
+                        {
+                          "href": "http://127.0.0.1:8774/v3/",
+                          "rel": "self"
+                        }
+                        ]
+                      }
+                      ]
+                    }
+        '300':
+          description: |-
+            300 response
+          content:
+            application/json:
+              examples:
+                foo:
+                  value: |
+                    {
+                     "versions": [
+                           {
+                             "status": "CURRENT",
+                             "updated": "2011-01-21T11:33:21Z",
+                             "id": "v2.0",
+                             "links": [
+                                 {
+                                     "href": "http://127.0.0.1:8774/v2/",
+                                     "rel": "self"
+                                 }
+                             ]
+                         },
+                         {
+                             "status": "EXPERIMENTAL",
+                             "updated": "2013-07-23T11:33:21Z",
+                             "id": "v3.0",
+                             "links": [
+                                 {
+                                     "href": "http://127.0.0.1:8774/v3/",
+                                     "rel": "self"
+                                 }
+                             ]
+                         }
+                     ]
+                    }
diff --git a/oas-test/build.gradle b/oas-test/build.gradle
index 377662e..4324ede 100644
--- a/oas-test/build.gradle
+++ b/oas-test/build.gradle
@@ -1,4 +1,9 @@
+plugins {
+    id 'java-library'
+}
+
 dependencies {
-    implementation "junit:junit:${libVersions.junit}"
-    implementation "org.mockito:mockito-core:${libVersions.mockito}"
+    api "junit:junit:${libVersions.junit}"
+    api "org.mockito:mockito-core:${libVersions.mockito}"
+    api "org.assertj:assertj-core:${libVersions.assertj}"
 }
\ No newline at end of file