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 build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ plugins {
id("org.flywaydb.flyway") version "9.3.1"
id("org.springframework.boot") version "2.7.3"
id("io.spring.dependency-management") version "1.0.14.RELEASE"
kotlin("jvm") version "1.5.0"
kotlin("jvm") version "1.9.0"
kotlin("plugin.spring") version "1.5.0"
application
distribution
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,71 @@ 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.addresses " +
"(" +
"city, " +
"postcode, "+
"address_line_1, "+
"address_line_2, " +
"organisation_id, "+
"city_id"+
") values(?, ?, ?, ?, ?, ?)",
arrayOf("id")
)
ps.setString(1, address.city)
ps.setString(2, address.postcode)
ps.setString(3, address.addressLine1)
ps.setString(4, address.addressLine2)
ps.setObject(5, address.organisationId)
ps.setObject(6, address.cityId)

ps
},
keyHolder
)
return keyHolder.getKeyAs(UUID::class.java)!!
}

private fun validateAddress(address: AddressRequest): Boolean {

val orgExists: Boolean? = jdbcTemplate.query(
"select exists(select 1 from organisations_schema.organisations o WHERE o.id = ?)",
ResultSetExtractor {
it.next()
it.getBoolean(1)
},
address.organisationId
)

if (!orgExists!!)
throw UnableToFindOrganisation(address.organisationId)

val cityExists: Boolean? = jdbcTemplate.query(
"select exists(select 1 from organisations_schema.cities c WHERE c.id = ?)",
ResultSetExtractor {
it.next()
it.getBoolean(1)
},
address.cityId
)

if (!cityExists!!)
throw UnableToFindCity(address.cityId)

return true
}

@Transactional
fun addAddress(address: AddressRequest): UUID{
validateAddress(address)
return createAddress(address)
}

private fun organisationQuery() = "select " +
"o.id as id, " +
"o.name as name, " +
Expand All @@ -112,11 +177,16 @@ class OrganisationRepository {
"o.contact_details_id as contact_details_id, " +
"cd.phone_number as phone_number, " +
"cd.fax as fax, " +
"cd.email as email " +
"cd.email as email, " +
"a.city as city, "+
"a.postcode as postcode, "+
"a.address_line_1 as address_line_1, "+
"a.address_line_2 as address_line_2 "+
"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.addresses a on o.id = a.organisation_id"

private fun organisationMapper() = RowMapper<OrganisationResponse> { it: ResultSet, _: Int ->
OrganisationResponse(
Expand All @@ -127,7 +197,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 +211,30 @@ class OrganisationRepository {
)
}

private fun mapAddress(it: ResultSet): AddressResponse {
return AddressResponse(
it.getString("city"),
it.getString("postcode"),
it.getString("address_line_1"),
it.getString("address_line_2")
)
}
@Transactional(readOnly = true)
fun findAdressesByOrgCode(orgCode: String): List<AddressResponse> {
return jdbcTemplate.query("SELECT city, postcode, address_line_1, address_line_2 FROM organisations_schema.addresses WHERE organisation_id = ?",
addressesMapper(),
UUID.fromString(orgCode))
}

private fun addressesMapper() = RowMapper<AddressResponse> {it: ResultSet, _: Int ->
AddressResponse(
it.getString("city"),
it.getString("postcode"),
it.getString("address_line_1"),
it.getString("address_line_2")
)
}

private fun mapCountry(it: ResultSet): CountryResponse {
return CountryResponse(
it.getObject("country_id", UUID::class.java),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.billie.organisations.data

import java.util.UUID

class UnableToFindCity(val cityId: UUID): RuntimeException()
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.billie.organisations.data

import java.util.UUID

class UnableToFindOrganisation(val organisationId: UUID): RuntimeException()
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import io.swagger.v3.oas.annotations.media.Content
import io.swagger.v3.oas.annotations.media.Schema
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
import org.springframework.http.HttpStatus
import org.springframework.http.HttpStatus.BAD_REQUEST
import org.springframework.http.HttpStatus.NOT_FOUND
import org.springframework.web.bind.annotation.*
import org.springframework.web.server.ResponseStatusException
import java.util.*
Expand Down Expand Up @@ -46,4 +46,42 @@ class OrganisationResource(val service: OrganisationService) {
}
}

@PostMapping(path = ["/address"])
@ApiResponses(
value = [
ApiResponse(
responseCode = "200",
description = "Accepted the new address",
content = [
(Content(
mediaType = "application/json",
array = (ArraySchema(schema = Schema(implementation = Entity::class)))
))]
),
ApiResponse(responseCode = "400", description = "Bad request", content = [Content()])]
)

fun postAddress(@Valid @RequestBody address: AddressRequest): Entity {
try {
val id = service.addAddress(address)
return Entity(id)
} catch (e: UnableToFindCountry) {
throw ResponseStatusException(BAD_REQUEST, e.message)
}
}

@GetMapping("/{organisationCode}/addresses")
fun addresses(@PathVariable("organisationCode") organisationCode: String): List<AddressResponse> {
val addresses = service.findAdresses(organisationCode)
if(addresses.isEmpty()) {
throw ResponseStatusException(
NOT_FOUND,
"No addresses found for $organisationCode"
)
}
return addresses
}

}


Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package io.billie.organisations.service

import io.billie.organisations.data.OrganisationRepository
import io.billie.organisations.viewmodel.AddressRequest
import io.billie.organisations.viewmodel.AddressResponse
import io.billie.organisations.viewmodel.OrganisationRequest
import io.billie.organisations.viewmodel.OrganisationResponse
import org.springframework.stereotype.Service
Expand All @@ -15,4 +17,10 @@ class OrganisationService(val db: OrganisationRepository) {
return db.create(organisation)
}

fun addAddress(address: AddressRequest): UUID{
return db.addAddress(address)
}

fun findAdresses(orgCode: String): List<AddressResponse> = db.findAdressesByOrgCode(orgCode)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.billie.organisations.viewmodel

import com.fasterxml.jackson.annotation.JsonProperty
import javax.validation.constraints.NotBlank
import java.util.UUID



data class AddressRequest(
@field:NotBlank val city: String,
@field:NotBlank val postcode: String,
@field:NotBlank @JsonProperty("address_line_1") val addressLine1: String,
@JsonProperty("address_line_2") val addressLine2: String?,
@field:JsonProperty("organisation_id") val organisationId: UUID,
@field:JsonProperty("city_id") val cityId: UUID

)
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 AddressResponse(
val city: String?,
val postcode: String?,
@JsonProperty("address_line_1") val addressLine1: String?,
@JsonProperty("address_line_2") val addressLine2: String?
)
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 address: AddressResponse?
)
20 changes: 20 additions & 0 deletions src/main/resources/db/migration/V9__Add_addresses_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
CREATE TABLE IF NOT EXISTS organisations_schema.addresses
(
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
city VARCHAR(100) NOT NULL,
postcode VARCHAR(20) NOT NULL,
address_line_1 VARCHAR(100) NOT NULL,
address_line_2 VARCHAR(100),
organisation_id UUID,
city_id UUID,
timestamp timestamp default current_timestamp
);

ALTER TABLE organisations_schema.addresses
ADD CONSTRAINT constraint_organisation_fk
FOREIGN KEY (organisation_id)
REFERENCES organisations_schema.organisations(id)
ON DELETE CASCADE;

CREATE INDEX idx_address_fk
ON organisations_schema.addresses (organisation_id);
Loading