Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion database.env
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
DATABASE_DRIVER=org.postgresql.Driver
POSTGRES_USER=postgres
POSTGRES_PASSWORD=7gn[[G780PH[,JP'HIUG
DATABASE_NAME=organisations
DATABASE_NAME=organisations_schema
DATABASE_HOSTNAME=database
DATABASE_URL=jdbc:postgresql://localhost:5432/
DATABASE_PORT=5432
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ class OrganisationRepository {
if(!valuesValid(organisation)) {
throw UnableToFindCountry(organisation.countryCode)
}
val id: UUID = createContactDetails(organisation.contactDetails)
return createOrganisation(organisation, id)
val contactId: UUID = createContactDetails(organisation.contactDetails)
val addressId: UUID? = organisation.address?.let { createAddress(it) }

return createOrganisation(organisation, contactId, addressId)
}

private fun valuesValid(organisation: OrganisationRequest): Boolean {
Expand All @@ -47,7 +49,7 @@ class OrganisationRepository {
return (reply != null) && (reply > 0)
}

private fun createOrganisation(org: OrganisationRequest, contactDetailsId: UUID): UUID {
private fun createOrganisation(org: OrganisationRequest, contactDetailsId: UUID, addressId: UUID?): UUID {
val keyHolder: KeyHolder = GeneratedKeyHolder()
jdbcTemplate.update(
{ connection ->
Expand All @@ -59,8 +61,9 @@ class OrganisationRepository {
"vat_number, " +
"registration_number, " +
"legal_entity_type, " +
"contact_details_id" +
") VALUES (?, ?, ?, ?, ?, ?, ?)",
"contact_details_id," +
"address_id" +
") VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
arrayOf("id")
)
ps.setString(1, org.name)
Expand All @@ -70,6 +73,7 @@ class OrganisationRepository {
ps.setString(5, org.registrationNumber)
ps.setString(6, org.legalEntityType.toString())
ps.setObject(7, contactDetailsId)
ps.setObject(8, addressId)
ps
}, keyHolder
)
Expand Down Expand Up @@ -99,6 +103,29 @@ class OrganisationRepository {
return keyHolder.getKeyAs(UUID::class.java)!!
}

private fun createAddress(address: AddressRequest): UUID {
val keyHolder: KeyHolder = GeneratedKeyHolder()
jdbcTemplate.update(
{ connection ->
val ps = connection.prepareStatement(
"insert into organisations_schema.address " +
"(" +
"street_name, " +
"home_number, " +
"zip_code" +
") values(?,?,?)",
arrayOf("id")
)
ps.setString(1, address.street_name)
ps.setString(2, address.home_number)
ps.setString(3, address.zip_code)
ps
},
keyHolder
)
return keyHolder.getKeyAs(UUID::class.java)!!
}

private fun organisationQuery() = "select " +
"o.id as id, " +
"o.name as name, " +
Expand All @@ -110,13 +137,18 @@ class OrganisationRepository {
"o.registration_number as registration_number," +
"o.legal_entity_type as legal_entity_type," +
"o.contact_details_id as contact_details_id, " +
"o.address_id as address_id, " +
"cd.phone_number as phone_number, " +
"cd.fax as fax, " +
"cd.email as email " +
"cd.email as email, " +
"ad.street_name as street_name, " +
"ad.home_number as home_number, " +
"ad.zip_code as zip_code " +
"from " +
"organisations_schema.organisations o " +
"INNER JOIN organisations_schema.contact_details cd on o.contact_details_id::uuid = cd.id::uuid " +
"INNER JOIN organisations_schema.countries c on o.country_code = c.country_code "
"INNER JOIN organisations_schema.countries c on o.country_code = c.country_code " +
"LEFT JOIN organisations_schema.address ad ON o.address_id::uuid = ad.id::uuid; "

private fun organisationMapper() = RowMapper<OrganisationResponse> { it: ResultSet, _: Int ->
OrganisationResponse(
Expand All @@ -127,7 +159,8 @@ class OrganisationRepository {
it.getString("vat_number"),
it.getString("registration_number"),
LegalEntityType.valueOf(it.getString("legal_entity_type")),
mapContactDetails(it)
mapContactDetails(it),
mapAddress(it)
)
}

Expand All @@ -140,6 +173,19 @@ class OrganisationRepository {
)
}

private fun mapAddress(it: ResultSet): Address? {
val addressId = it.getString("address_id")
if (!addressId.isNullOrEmpty()) {
return Address(
UUID.fromString(it.getString("address_id")),
it.getString("street_name"),
it.getString("home_number"),
it.getString("zip_code"))
} else {
return null
}
}

private fun mapCountry(it: ResultSet): CountryResponse {
return CountryResponse(
it.getObject("country_id", UUID::class.java),
Expand Down
11 changes: 11 additions & 0 deletions src/main/kotlin/io/billie/organisations/viewmodel/Address.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.billie.organisations.viewmodel

import com.fasterxml.jackson.annotation.JsonProperty
import java.util.*

data class Address(
val id: UUID?,
@JsonProperty("street_name") val street_name: String?,
val home_number: String?,
val zip_code: String?
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.billie.organisations.viewmodel

import com.fasterxml.jackson.annotation.JsonProperty
import java.util.*

data class AddressRequest(
@JsonProperty("street_name") val street_name: String?,
val home_number: String?,
val zip_code: String?
)
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ data class OrganisationRequest(
@JsonProperty("registration_number") val registrationNumber: String?,
@JsonProperty("legal_entity_type") val legalEntityType: LegalEntityType,
@JsonProperty("contact_details") val contactDetails: ContactDetailsRequest,
@JsonProperty("address") val address: AddressRequest?,
)
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ data class OrganisationResponse(
@JsonProperty("registration_number") val registrationNumber: String?,
@JsonProperty("legal_entity_type") val legalEntityType: LegalEntityType,
@JsonProperty("contact_details") val contactDetails: ContactDetails,
@JsonProperty("address") val addressRequest: Address?,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

ALTER TABLE organisations_schema.organisations
ADD address_id VARCHAR(36);
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,4 @@ CREATE TABLE IF NOT EXISTS organisations_schema.organisations
VAT_number VARCHAR(20),
registration_number VARCHAR(20),
legal_entity_type VARCHAR(30) NOT NULL,
contact_details_id VARCHAR(36) NOT NULL
);
contact_details_id VARCHAR(36) NOT NULL);
8 changes: 8 additions & 0 deletions src/main/resources/db/migration/V9__add_address_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

CREATE TABLE IF NOT EXISTS organisations_schema.address
(
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
street_name VARCHAR(20),
home_number VARCHAR(20),
zip_code VARCHAR(6)
);
22 changes: 9 additions & 13 deletions src/test/kotlin/io/billie/functional/CanReadLocationsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.billie.functional

import io.billie.functional.matcher.IsUUID.isUuid
import org.hamcrest.Description
import org.hamcrest.Matchers.*
import org.hamcrest.TypeSafeMatcher
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
Expand Down Expand Up @@ -42,28 +43,23 @@ class CanReadLocationsTest {
.contentType(APPLICATION_JSON)
)
.andExpect(status().isOk)
.andExpect(jsonPath("$.[0].name").value("Harare"))
.andExpect(jsonPath("$.[0].id").value(isUuid()))
.andExpect(jsonPath("$.[0].country_code").value("ZW"))
.andExpect(jsonPath("$.[25].name").value("Mazoe"))
.andExpect(jsonPath("$.[25].id").value(isUuid()))
.andExpect(jsonPath("$.[25].country_code").value("ZW"))
.andExpect(jsonPath("$[*].name", hasItems("Mazoe", "Harare")))
.andExpect(jsonPath("$[*].country_code", everyItem(equalTo("ZW"))))
.andExpect(jsonPath("$[*].id", everyItem(isUuid())))
}

// TODO check the requirements, API doesnt support ordering, thus no ordering
// can be guaranteed, therefore changing tests to check data exists
@Test
fun canViewBECities() {
mockMvc.perform(
get("/countries/be/cities")
.contentType(APPLICATION_JSON)
)
.andExpect(status().isOk)
.andExpect(jsonPath("$.[0].name").value("Brussels"))
.andExpect(jsonPath("$.[0].id").value(isUuid()))
.andExpect(jsonPath("$.[0].country_code").value("BE"))
.andExpect(jsonPath("$.size()").value(468))
.andExpect(jsonPath("$.[467].name").value("Alveringem"))
.andExpect(jsonPath("$.[467].id").value(isUuid()))
.andExpect(jsonPath("$.[467].country_code").value("BE"))
.andExpect(jsonPath("$[*].name", hasItems("Brussels", "Alveringem")))
.andExpect(jsonPath("$[*].country_code", everyItem(equalTo("BE"))))
.andExpect(jsonPath("$[*].id", everyItem(isUuid())))
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
package io.billie.functional

import com.fasterxml.jackson.databind.ObjectMapper
import io.billie.functional.data.Fixtures.bbcAddressFixture
import io.billie.functional.data.Fixtures.bbcContactFixture
import io.billie.functional.data.Fixtures.bbcFixture
import io.billie.functional.data.Fixtures.orgRequestJson
import io.billie.functional.data.Fixtures.orgRequestJsonCountryCodeBlank
import io.billie.functional.data.Fixtures.orgRequestJsonCountryCodeIncorrect
import io.billie.functional.data.Fixtures.orgRequestJsonNoName
import io.billie.functional.data.Fixtures.orgRequestJsonNameBlank
import io.billie.functional.data.Fixtures.orgRequestJsonNoAddress
import io.billie.functional.data.Fixtures.orgRequestJsonNoContactDetails
import io.billie.functional.data.Fixtures.orgRequestJsonNoCountryCode
import io.billie.functional.data.Fixtures.orgRequestJsonNoLegalEntityType
import io.billie.organisations.viewmodel.Entity
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.core.IsEqual.equalTo
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
Expand Down Expand Up @@ -110,6 +113,26 @@ class CanStoreAndReadOrganisationTest {
.andExpect(status().isBadRequest)
}

@Test
fun storeOrgWithoutAddressFails() {
val result = mockMvc.perform(
post("/organisations").contentType(APPLICATION_JSON).content(orgRequestJsonNoAddress())
)
.andExpect(status().isOk)
.andReturn()

val response = mapper.readValue(result.response.contentAsString, Entity::class.java)

val org: Map<String, Any> = orgFromDatabase(response.id)
assertDataMatches(org, bbcFixture(response.id))

val contactDetailsId: UUID = UUID.fromString(org["contact_details_id"] as String)
val contactDetails: Map<String, Any> = contactDetailsFromDatabase(contactDetailsId)
assertDataMatches(contactDetails, bbcContactFixture(contactDetailsId))
val addressId = org["address_id"] as String?
assertNull(addressId)
}

@Test
fun canStoreOrg() {
val result = mockMvc.perform(
Expand All @@ -126,6 +149,10 @@ class CanStoreAndReadOrganisationTest {
val contactDetailsId: UUID = UUID.fromString(org["contact_details_id"] as String)
val contactDetails: Map<String, Any> = contactDetailsFromDatabase(contactDetailsId)
assertDataMatches(contactDetails, bbcContactFixture(contactDetailsId))

val addressId: UUID = UUID.fromString(org["address_id"] as String)
val address: Map<String, Any> = addressFromDatabase(addressId)
assertDataMatches(address, bbcAddressFixture(addressId))
}

fun assertDataMatches(reply: Map<String, Any>, assertions: Map<String, Any>) {
Expand All @@ -143,4 +170,6 @@ class CanStoreAndReadOrganisationTest {
private fun contactDetailsFromDatabase(id: UUID): MutableMap<String, Any> =
queryEntityFromDatabase("select * from organisations_schema.contact_details where id = ?", id)

private fun addressFromDatabase(id: UUID): MutableMap<String, Any> =
queryEntityFromDatabase("select * from organisations_schema.address where id = ?", id)
}
31 changes: 29 additions & 2 deletions src/test/kotlin/io/billie/functional/data/Fixtures.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,22 @@ object Fixtures {
"}"
}

fun orgRequestJsonNoAddress(): String {
return "{\n" +
" \"name\": \"BBC\",\n" +
" \"date_founded\": \"18/10/1922\",\n" +
" \"country_code\": \"GB\",\n" +
" \"vat_number\": \"333289454\",\n" +
" \"registration_number\": \"3686147\",\n" +
" \"legal_entity_type\": \"NONPROFIT_ORGANIZATION\",\n" +
" \"contact_details\": {\n" +
" \"phone_number\": \"+443700100222\",\n" +
" \"fax\": \"\",\n" +
" \"email\": \"yourquestions@bbc.co.uk\"\n" +
" }\n" +
"}"
}

fun orgRequestJson(): String {
return "{\n" +
" \"name\": \"BBC\",\n" +
Expand All @@ -75,6 +91,11 @@ object Fixtures {
" \"phone_number\": \"+443700100222\",\n" +
" \"fax\": \"\",\n" +
" \"email\": \"yourquestions@bbc.co.uk\"\n" +
" },\n" +
" \"address\": {\n" +
" \"street_name\": \"Somestreet\",\n" +
" \"home_number\": \"100\",\n" +
" \"zip_code\": \"10111\"\n" +
" }\n" +
"}"
}
Expand Down Expand Up @@ -147,6 +168,12 @@ object Fixtures {
return data
}



fun bbcAddressFixture(id: UUID): Map<String, Any> {
val data = HashMap<String, Any>()
data["id"] = id
data["street_name"] = "Somestreet"
data["home_number"] = "100"
data["zip_code"] = "10111"
return data
}
}