Skip to content

Commit

Permalink
#1693 API v3: some basic VersionedModelControllerV3 integration test
Browse files Browse the repository at this point in the history
 - login allowed at /api/login & /api-v3/login
 - v2/v3 BaseRestApiTest distinquished
  • Loading branch information
dk1844 committed Mar 7, 2022
1 parent f1798b2 commit 0f62851
Show file tree
Hide file tree
Showing 11 changed files with 217 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,19 @@ class WebSecurityConfig @Autowired()(beanFactory: BeanFactory,
.anyRequest()
.authenticated()
.and()
// v2 login
.formLogin()
.loginProcessingUrl("/api/login")
.successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler)
.permitAll()
.and()
// v3 login
.formLogin()
.loginProcessingUrl("/api-v3/login")
.successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler)
.permitAll()
.and()
.addFilterBefore(kerberosFilter, classOf[UsernamePasswordAuthenticationFilter])
.addFilterAfter(jwtAuthFilter, classOf[SpnegoAuthenticationProcessingFilter])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,11 @@ abstract class VersionedModelControllerV3[C <: VersionedModel with Product

@GetMapping(Array("/{name}"))
@ResponseStatus(HttpStatus.OK)
def getVersionsList(@PathVariable name: String): CompletableFuture[Seq[Int]] = {
versionedModelService.getAllVersionsValues(name)
def getVersionsList(@PathVariable name: String): CompletableFuture[VersionsList] = {
versionedModelService.getAllVersionsValues(name) map {
case Some(entity) => entity
case None => throw notFound()
}
}

@GetMapping(Array("/{name}/{version}"))
Expand Down Expand Up @@ -126,7 +129,7 @@ abstract class VersionedModelControllerV3[C <: VersionedModel with Product
def edit(@AuthenticationPrincipal user: UserDetails,
@RequestBody item: C): CompletableFuture[C] = {
versionedModelService.update(user.getUsername, item).map {
case Some(entity) => entity
case Some(entity) => entity // todo change not to return conent
case None => throw notFound()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,13 @@ abstract class VersionedMongoRepository[C <: VersionedModel](mongoDb: MongoDatab
collection.aggregate[VersionedSummary](pipeline).headOption().map(_.map(_.latestVersion))
}

def getAllVersionsValues(name: String): Future[Seq[Int]] = {
def getAllVersionsValues(name: String): Future[Option[VersionsList]] = {
val pipeline = Seq(
filter(getNameFilter(name)),
Aggregates.sort(Sorts.ascending("version")),
Aggregates.group("$name", Accumulators.push("versions", "$version")) // all versions into single array
)
collection.aggregate[VersionsList](pipeline).headOption().map {
case None => Seq.empty
case Some(verList) => verList.versions
}
collection.aggregate[VersionsList](pipeline).headOption().map(_.map(vlist => VersionsList("versions", vlist.versions)))
}

def getAllVersions(name: String, inclDisabled: Boolean = false): Future[Seq[C]] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.core.userdetails.UserDetails
import za.co.absa.enceladus.model.{ModelVersion, Schema, UsedIn, Validation}
import za.co.absa.enceladus.model.menas._
import za.co.absa.enceladus.model.versionedModel.{VersionedModel, VersionedSummary}
import za.co.absa.enceladus.model.versionedModel.{VersionedModel, VersionedSummary, VersionsList}
import za.co.absa.enceladus.rest_api.exceptions._
import za.co.absa.enceladus.rest_api.repositories.VersionedMongoRepository
import za.co.absa.enceladus.model.menas.audit._
Expand Down Expand Up @@ -56,7 +56,7 @@ abstract class VersionedModelService[C <: VersionedModel with Product with Audit
versionedMongoRepository.getAllVersions(name)
}

def getAllVersionsValues(name: String): Future[Seq[Int]] = {
def getAllVersionsValues(name: String): Future[Option[VersionsList]] = {
versionedMongoRepository.getAllVersionsValues(name)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import scala.concurrent.{Await, Future}
@RunWith(classOf[SpringRunner])
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles(Array("withEmbeddedMongo"))
class AuthenticationIntegrationSuite extends BaseRestApiTest {
class AuthenticationIntegrationSuite extends BaseRestApiTestV2 {

import scala.concurrent.ExecutionContext.Implicits.global

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ import za.co.absa.enceladus.rest_api.integration.repositories.BaseRepositoryTest
import scala.concurrent.Future
import scala.reflect.ClassTag

abstract class BaseRestApiTest extends BaseRepositoryTest {
abstract class BaseRestApiTestV2 extends BaseRestApiTest("/api")
abstract class BaseRestApiTestV3 extends BaseRestApiTest("/api-v3")

abstract class BaseRestApiTest(apiPath: String) extends BaseRepositoryTest {

import scala.concurrent.ExecutionContext.Implicits.global

Expand All @@ -50,7 +53,8 @@ abstract class BaseRestApiTest extends BaseRepositoryTest {
@Value("${menas.auth.inmemory.admin.password}")
val adminPasswd: String = ""

private lazy val baseUrl = s"http://localhost:$port/api"
// expecting apiPath to be /api for v2 and /api-v3 for v3
private lazy val baseUrl = s"http://localhost:$port$apiPath"
private lazy val authHeaders = getAuthHeaders(user, passwd)
private lazy val authHeadersAdmin = getAuthHeaders(adminUser, adminPasswd)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import za.co.absa.enceladus.rest_api.integration.fixtures._
@RunWith(classOf[SpringRunner])
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles(Array("withEmbeddedMongo"))
class DatasetApiIntegrationSuite extends BaseRestApiTest with BeforeAndAfterAll {
class DatasetApiIntegrationSuite extends BaseRestApiTestV2 with BeforeAndAfterAll {

@Autowired
private val datasetFixture: DatasetFixtureService = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import za.co.absa.enceladus.model.test.factories.PropertyDefinitionFactory
@RunWith(classOf[SpringRunner])
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles(Array("withEmbeddedMongo"))
class PropertyDefinitionApiIntegrationSuite extends BaseRestApiTest with BeforeAndAfterAll with Matchers {
class PropertyDefinitionApiIntegrationSuite extends BaseRestApiTestV2 with BeforeAndAfterAll with Matchers {

@Autowired
private val propertyDefinitionFixture: PropertyDefinitionFixtureService = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import za.co.absa.enceladus.model.{Run, SplineReference, Validation}
@RunWith(classOf[SpringRunner])
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles(Array("withEmbeddedMongo"))
class RunApiIntegrationSuite extends BaseRestApiTest {
class RunApiIntegrationSuite extends BaseRestApiTestV2 {

import za.co.absa.enceladus.rest_api.integration.RunImplicits.RunExtensions
import za.co.absa.enceladus.model.Validation._
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import scala.collection.immutable.HashMap
@RunWith(classOf[SpringRunner])
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles(Array("withEmbeddedMongo"))
class SchemaApiFeaturesIntegrationSuite extends BaseRestApiTest with BeforeAndAfterAll {
class SchemaApiFeaturesIntegrationSuite extends BaseRestApiTestV2 with BeforeAndAfterAll {

private val port = 8877 // same port as in test/resources/application.conf in the `menas.schemaRegistry.baseUrl` key
private val wireMockServer = new WireMockServer(WireMockConfiguration.wireMockConfig().port(port))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/*
* Copyright 2018 ABSA Group Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package za.co.absa.enceladus.rest_api.integration.controllers.v3

import org.junit.runner.RunWith
import org.scalatest.BeforeAndAfterAll
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.junit4.SpringRunner
import za.co.absa.enceladus.model.conformanceRule.MappingConformanceRule
import za.co.absa.enceladus.model.dataFrameFilter._
import za.co.absa.enceladus.model.properties.PropertyDefinition
import za.co.absa.enceladus.model.properties.essentiality.Essentiality
import za.co.absa.enceladus.model.properties.essentiality.Essentiality._
import za.co.absa.enceladus.model.properties.propertyType.{EnumPropertyType, PropertyType, StringPropertyType}
import za.co.absa.enceladus.model.test.factories.{DatasetFactory, PropertyDefinitionFactory}
import za.co.absa.enceladus.model.versionedModel.VersionsList
import za.co.absa.enceladus.model.{Dataset, Validation}
import za.co.absa.enceladus.rest_api.integration.fixtures._
import za.co.absa.enceladus.rest_api.integration.controllers.{BaseRestApiTest, BaseRestApiTestV3, toExpected}

@RunWith(classOf[SpringRunner])
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles(Array("withEmbeddedMongo"))
class DatasetControllerV3IntegrationSuite extends BaseRestApiTestV3 with BeforeAndAfterAll {

@Autowired
private val datasetFixture: DatasetFixtureService = null

@Autowired
private val propertyDefinitionFixture: PropertyDefinitionFixtureService = null

private val apiUrl = "/datasets"

// fixtures are cleared after each test
override def fixtures: List[FixtureService[_]] = List(datasetFixture, propertyDefinitionFixture)

s"POST $apiUrl" can {
"return 201" when {
"a Dataset is created" should {
"return the created Dataset (with empty properties stripped)" in {
val dataset = DatasetFactory.getDummyDataset("dummyDs",
properties = Some(Map("keyA" -> "valA", "keyB" -> "valB", "keyC" -> "")))

val response = sendPost[Dataset, Dataset](s"$apiUrl", bodyOpt = Some(dataset))
assertCreated(response)

val actual = response.getBody
val expected = toExpected(dataset, actual).copy(properties = Some(Map("keyA" -> "valA", "keyB" -> "valB"))) // keyC stripped
assert(actual == expected)
}
}
}
}

s"PUT $apiUrl" can {
"return 200" when {
"a Schema with the given name and version is the latest that exists" should {
"return the updated Schema (with empty properties stripped)" in {
val datasetA1 = DatasetFactory.getDummyDataset("datasetA",
description = Some("init version"), properties = Some(Map("keyA" -> "valA")))
datasetFixture.add(datasetA1)

val exampleMappingCr = MappingConformanceRule(0,
controlCheckpoint = true,
mappingTable = "CurrencyMappingTable",
mappingTableVersion = 9, //scalastyle:ignore magic.number
attributeMappings = Map("InputValue" -> "STRING_VAL"),
targetAttribute = "CCC",
outputColumn = "ConformedCCC",
isNullSafe = true,
mappingTableFilter = Some(
AndJoinedFilters(Set(
OrJoinedFilters(Set(
EqualsFilter("column1", "soughtAfterValue"),
EqualsFilter("column1", "alternativeSoughtAfterValue")
)),
DiffersFilter("column2", "anotherValue"),
NotFilter(IsNullFilter("col3"))
))
),
overrideMappingTableOwnFilter = Some(true)
)

val datasetA2 = DatasetFactory.getDummyDataset("datasetA",
description = Some("updated"),
properties = Some(Map("keyA" -> "valA", "keyB" -> "valB", "keyC" -> "")),
conformance = List(exampleMappingCr)
)

val response = sendPut[Dataset, Dataset](s"$apiUrl", bodyOpt = Some(datasetA2))
assertOk(response)

// todo should be ok/no content and then actually no content -> run get again
val actual = response.getBody
val expectedDs = DatasetFactory.getDummyDataset(
name = "datasetA",
version = 2,
description = Some("updated"),
parent = Some(DatasetFactory.toParent(datasetA1)),
properties = Some(Map("keyA" -> "valA", "keyB" -> "valB")),
conformance = List(exampleMappingCr)
)
val expected = toExpected(expectedDs, actual)
assert(actual == expected)
}
}
}
}

s"GET $apiUrl/{name}" should {
"return 200" when {
"a Dataset with the given name exists - so it gives versions" in {
val datasetV1 = DatasetFactory.getDummyDataset(name = "datasetA", version = 1)
val datasetV2 = DatasetFactory.getDummyDataset(name = "datasetA",
version = 2,
parent = Some(DatasetFactory.toParent(datasetV1)))
datasetFixture.add(datasetV1, datasetV2)

val response = sendGet[VersionsList](s"$apiUrl/datasetA")
assertOk(response)
assert(response.getBody == VersionsList("versions", Seq(1, 2)))
}
}

"return 404" when {
"a Dataset with the given name does not exist" in {
val dataset = DatasetFactory.getDummyDataset(name = "datasetA", version = 1)
datasetFixture.add(dataset)

val response = sendGet[String](s"$apiUrl/anotherDatasetName")
assertNotFound(response)
}
}
}

s"GET $apiUrl/{name}/{version}/export" should {
"return 404" when {
"when the name+version does not exist" in {
val response = sendGet[String](s"$apiUrl/notFoundDataset/2/export")
assertNotFound(response)
}
}

"return 200" when {
"there is a correct Dataset version" should {
"return the exported Dataset representation" in {
val dataset = DatasetFactory.getDummyDataset(name = "dataset", version = 2,
properties = Some(Map("key1" -> "val1", "key2" -> "val2")))
datasetFixture.add(dataset)
val response = sendGet[String](s"$apiUrl/dataset/2/export")

assertOk(response)

val body = response.getBody
assert(body ==
"""{"metadata":{"exportVersion":1},"item":{
|"name":"dataset",
|"hdfsPath":"/dummy/path",
|"hdfsPublishPath":"/dummy/publish/path",
|"schemaName":"dummySchema",
|"schemaVersion":1,
|"conformance":[],
|"properties":{"key2":"val2","key1":"val1"}
|}}""".stripMargin.replaceAll("[\\r\\n]", ""))
}
}
}

}

// todo properties test for datasets or in general for any VersionedModel

}

0 comments on commit 0f62851

Please sign in to comment.