diff --git a/build.gradle.kts b/build.gradle.kts index 209c614..61fe1b0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,25 +1,18 @@ -import java.io.File -import java.io.FileInputStream -import java.util.* import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 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("plugin.spring") version "1.5.0" - application - distribution + id("org.springframework.boot") version "3.1.4" + id("io.spring.dependency-management") version "1.1.3" + kotlin("jvm") version "1.8.22" + kotlin("plugin.spring") version "1.8.22" + kotlin("plugin.jpa") version "1.8.22" } group = "io.billie" version = "0.0.1" -java.sourceCompatibility = JavaVersion.VERSION_11 - -val dbConf = Properties().apply { - load(FileInputStream(File(rootProject.rootDir, "database.env"))) +java { + sourceCompatibility = JavaVersion.VERSION_17 } repositories { @@ -27,33 +20,30 @@ repositories { } dependencies { + implementation("org.springframework.boot:spring-boot-starter-actuator") + implementation("org.springframework.boot:spring-boot-starter-data-jpa") + implementation("org.springframework.boot:spring-boot-starter-security") implementation("org.springframework.boot:spring-boot-starter-validation") - implementation("org.springframework.boot:spring-boot-starter-data-jdbc") implementation("org.springframework.boot:spring-boot-starter-web") - - implementation("org.springdoc:springdoc-openapi-data-rest:1.6.11") - implementation("org.springdoc:springdoc-openapi-ui:1.6.11") - implementation("org.springdoc:springdoc-openapi-kotlin:1.6.11") - + implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.flywaydb:flyway-core") implementation("org.jetbrains.kotlin:kotlin-reflect") - implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + developmentOnly("org.springframework.boot:spring-boot-docker-compose") runtimeOnly("org.postgresql:postgresql") - testImplementation("org.springframework.boot:spring-boot-starter-test") -} - -flyway { - url = dbConf.getProperty("DATABASE_URL") - user = dbConf.getProperty("POSTGRES_USER") - password = dbConf.getProperty("POSTGRES_PASSWORD") - locations = arrayOf(dbConf.getProperty("DATABASE_MIGRATION")) + testImplementation("org.springframework.boot:spring-boot-testcontainers") + testImplementation("org.springframework.security:spring-security-test") + testImplementation("org.testcontainers:junit-jupiter") + testImplementation("org.testcontainers:postgresql") + testImplementation("com.willowtreeapps.assertk:assertk:0.27.0") + testImplementation("com.natpryce:hamkrest:1.8.0.1") } tasks.withType { kotlinOptions { - freeCompilerArgs = listOf("-Xjsr305=strict") - jvmTarget = "11" + freeCompilerArgs += "-Xjsr305=strict" + jvmTarget = "17" } } diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..d71f5bb --- /dev/null +++ b/compose.yaml @@ -0,0 +1,9 @@ +services: + postgres: + image: 'postgres:latest' + environment: + - 'POSTGRES_DB=organisations' + - 'POSTGRES_PASSWORD=postgresql' + - 'POSTGRES_USER=postgresql' + ports: + - '5432:5432' diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c..033e24c 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f371643..9f4197d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle.kts b/settings.gradle.kts index 305fb5e..fdd52f3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1 +1 @@ -rootProject.name = "organisations" +rootProject.name = "organization-management" diff --git a/src/main/kotlin/io/billie/countries/data/CityRepository.kt b/src/main/kotlin/io/billie/countries/data/CityRepository.kt deleted file mode 100644 index 6e314e3..0000000 --- a/src/main/kotlin/io/billie/countries/data/CityRepository.kt +++ /dev/null @@ -1,35 +0,0 @@ -package io.billie.countries.data - -import io.billie.countries.model.CityResponse -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.jdbc.core.JdbcTemplate -import org.springframework.jdbc.core.RowMapper -import org.springframework.stereotype.Repository -import org.springframework.transaction.annotation.Transactional -import java.sql.ResultSet -import java.util.* - -@Repository -class CityRepository { - - - @Autowired - lateinit var jdbcTemplate: JdbcTemplate - - @Transactional(readOnly = true) - fun findByCountryCode(countryCode: String): List { - return jdbcTemplate.query( - "select id, name, country_code from organisations_schema.cities where country_code = ?", - cityResponseMapper(), - countryCode - ) - } - - private fun cityResponseMapper() = RowMapper { it: ResultSet, _: Int -> - CityResponse( - it.getObject("id", UUID::class.java), - it.getString("name"), - it.getString("country_code") - ) - } -} diff --git a/src/main/kotlin/io/billie/countries/data/CountryRepository.kt b/src/main/kotlin/io/billie/countries/data/CountryRepository.kt deleted file mode 100644 index e68c6f0..0000000 --- a/src/main/kotlin/io/billie/countries/data/CountryRepository.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.billie.countries.data - -import io.billie.countries.model.CountryResponse -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.jdbc.core.JdbcTemplate -import org.springframework.jdbc.core.RowMapper -import org.springframework.stereotype.Repository -import org.springframework.transaction.annotation.Transactional -import java.sql.ResultSet -import java.util.* - -@Repository -class CountryRepository { - - @Autowired - lateinit var jdbcTemplate: JdbcTemplate - - @Transactional(readOnly=true) - fun findCountries(): List { - val query = jdbcTemplate.query( - "select id, name, country_code from organisations_schema.countries", - countryResponseMapper() - ) - return query - } - - private fun countryResponseMapper() = RowMapper { it: ResultSet, _: Int -> - CountryResponse( - it.getObject("id", UUID::class.java), - it.getString("name"), - it.getString("country_code") - ) - } -} diff --git a/src/main/kotlin/io/billie/countries/model/CityResponse.kt b/src/main/kotlin/io/billie/countries/model/CityResponse.kt deleted file mode 100644 index 5210005..0000000 --- a/src/main/kotlin/io/billie/countries/model/CityResponse.kt +++ /dev/null @@ -1,13 +0,0 @@ -package io.billie.countries.model - -import com.fasterxml.jackson.annotation.JsonProperty -import java.util.* -import javax.validation.constraints.Size - -data class CityResponse( - val id: UUID, - val name: String, - @Size(min = 2, max = 2) - @JsonProperty("country_code") - val countryCode: String -) diff --git a/src/main/kotlin/io/billie/countries/model/CountryResponse.kt b/src/main/kotlin/io/billie/countries/model/CountryResponse.kt deleted file mode 100644 index 42dd1bd..0000000 --- a/src/main/kotlin/io/billie/countries/model/CountryResponse.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.billie.countries.model - -import com.fasterxml.jackson.annotation.JsonProperty -import java.util.* -import javax.validation.constraints.Size - -data class CountryResponse( - val id: UUID, - val name: String, - @JsonProperty("country_code") @Size(min = 2, max = 2) val countryCode: String, -) diff --git a/src/main/kotlin/io/billie/countries/resource/CountryResource.kt b/src/main/kotlin/io/billie/countries/resource/CountryResource.kt deleted file mode 100644 index cca3709..0000000 --- a/src/main/kotlin/io/billie/countries/resource/CountryResource.kt +++ /dev/null @@ -1,59 +0,0 @@ -package io.billie.countries.resource - -import io.billie.countries.model.CityResponse -import io.billie.countries.model.CountryResponse -import io.billie.countries.service.CountryService -import io.swagger.v3.oas.annotations.media.ArraySchema -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.NOT_FOUND -import org.springframework.web.bind.annotation.* -import org.springframework.web.server.ResponseStatusException - -@RestController -@RequestMapping("countries") -class CountryResource(val service: CountryService) { - - @ApiResponses( - value = [ - ApiResponse( - responseCode = "200", - description = "All countries", - content = [ - (Content( - mediaType = "application/json", - array = (ArraySchema(schema = Schema(implementation = CountryResponse::class))) - ))] - )] - ) - @GetMapping - fun index(): List = service.findCountries() - - @ApiResponses( - value = [ - ApiResponse( - responseCode = "200", - description = "Found cities for country", - content = [ - (Content( - mediaType = "application/json", - array = (ArraySchema(schema = Schema(implementation = CityResponse::class))) - ))] - ), - ApiResponse(responseCode = "404", description = "No cities found for country code", content = [Content()])] - ) - @GetMapping("/{countryCode}/cities") - fun cities(@PathVariable("countryCode") countryCode: String): List { - val cities = service.findCities(countryCode.uppercase()) - if (cities.isEmpty()) { - throw ResponseStatusException( - NOT_FOUND, - "No cities found for $countryCode" - ) - } - return cities - } - -} diff --git a/src/main/kotlin/io/billie/countries/service/CountryService.kt b/src/main/kotlin/io/billie/countries/service/CountryService.kt deleted file mode 100644 index 4dd33a5..0000000 --- a/src/main/kotlin/io/billie/countries/service/CountryService.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.billie.countries.service - -import io.billie.countries.data.CityRepository -import io.billie.countries.data.CountryRepository -import io.billie.countries.model.CityResponse -import io.billie.countries.model.CountryResponse -import org.springframework.stereotype.Service - -@Service -class CountryService(val dbCountry: CountryRepository, val dbCity: CityRepository, ) { - - fun findCountries(): List { - return dbCountry.findCountries() - } - fun findCities(countryCode: String): List = dbCity.findByCountryCode(countryCode) - -} diff --git a/src/main/kotlin/io/billie/organisations/data/OrganisationRepository.kt b/src/main/kotlin/io/billie/organisations/data/OrganisationRepository.kt deleted file mode 100644 index 8c0026b..0000000 --- a/src/main/kotlin/io/billie/organisations/data/OrganisationRepository.kt +++ /dev/null @@ -1,151 +0,0 @@ -package io.billie.organisations.data - -import io.billie.countries.model.CountryResponse -import io.billie.organisations.viewmodel.* -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.jdbc.core.JdbcTemplate -import org.springframework.jdbc.core.ResultSetExtractor -import org.springframework.jdbc.core.RowMapper -import org.springframework.jdbc.support.GeneratedKeyHolder -import org.springframework.jdbc.support.KeyHolder -import org.springframework.stereotype.Repository -import org.springframework.transaction.annotation.Transactional -import java.sql.Date -import java.sql.ResultSet -import java.util.* - - -@Repository -class OrganisationRepository { - - @Autowired - lateinit var jdbcTemplate: JdbcTemplate - - @Transactional(readOnly = true) - fun findOrganisations(): List { - return jdbcTemplate.query(organisationQuery(), organisationMapper()) - } - - @Transactional - fun create(organisation: OrganisationRequest): UUID { - if(!valuesValid(organisation)) { - throw UnableToFindCountry(organisation.countryCode) - } - val id: UUID = createContactDetails(organisation.contactDetails) - return createOrganisation(organisation, id) - } - - private fun valuesValid(organisation: OrganisationRequest): Boolean { - val reply: Int? = jdbcTemplate.query( - "select count(country_code) from organisations_schema.countries c WHERE c.country_code = ?", - ResultSetExtractor { - it.next() - it.getInt(1) - }, - organisation.countryCode - ) - return (reply != null) && (reply > 0) - } - - private fun createOrganisation(org: OrganisationRequest, contactDetailsId: UUID): UUID { - val keyHolder: KeyHolder = GeneratedKeyHolder() - jdbcTemplate.update( - { connection -> - val ps = connection.prepareStatement( - "INSERT INTO organisations_schema.organisations (" + - "name, " + - "date_founded, " + - "country_code, " + - "vat_number, " + - "registration_number, " + - "legal_entity_type, " + - "contact_details_id" + - ") VALUES (?, ?, ?, ?, ?, ?, ?)", - arrayOf("id") - ) - ps.setString(1, org.name) - ps.setDate(2, Date.valueOf(org.dateFounded)) - ps.setString(3, org.countryCode) - ps.setString(4, org.VATNumber) - ps.setString(5, org.registrationNumber) - ps.setString(6, org.legalEntityType.toString()) - ps.setObject(7, contactDetailsId) - ps - }, keyHolder - ) - return keyHolder.getKeyAs(UUID::class.java)!! - } - - private fun createContactDetails(contactDetails: ContactDetailsRequest): UUID { - val keyHolder: KeyHolder = GeneratedKeyHolder() - jdbcTemplate.update( - { connection -> - val ps = connection.prepareStatement( - "insert into organisations_schema.contact_details " + - "(" + - "phone_number, " + - "fax, " + - "email" + - ") values(?,?,?)", - arrayOf("id") - ) - ps.setString(1, contactDetails.phoneNumber) - ps.setString(2, contactDetails.fax) - ps.setString(3, contactDetails.email) - ps - }, - keyHolder - ) - return keyHolder.getKeyAs(UUID::class.java)!! - } - - private fun organisationQuery() = "select " + - "o.id as id, " + - "o.name as name, " + - "o.date_founded as date_founded, " + - "o.country_code as country_code, " + - "c.id as country_id, " + - "c.name as country_name, " + - "o.VAT_number as VAT_number, " + - "o.registration_number as registration_number," + - "o.legal_entity_type as legal_entity_type," + - "o.contact_details_id as contact_details_id, " + - "cd.phone_number as phone_number, " + - "cd.fax as fax, " + - "cd.email as email " + - "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 " - - private fun organisationMapper() = RowMapper { it: ResultSet, _: Int -> - OrganisationResponse( - it.getObject("id", UUID::class.java), - it.getString("name"), - Date(it.getDate("date_founded").time).toLocalDate(), - mapCountry(it), - it.getString("vat_number"), - it.getString("registration_number"), - LegalEntityType.valueOf(it.getString("legal_entity_type")), - mapContactDetails(it) - ) - } - - private fun mapContactDetails(it: ResultSet): ContactDetails { - return ContactDetails( - UUID.fromString(it.getString("contact_details_id")), - it.getString("phone_number"), - it.getString("fax"), - it.getString("email") - ) - } - - private fun mapCountry(it: ResultSet): CountryResponse { - return CountryResponse( - it.getObject("country_id", UUID::class.java), - it.getString("country_name"), - it.getString("country_code") - ) - } - -} diff --git a/src/main/kotlin/io/billie/organisations/data/UnableToFindCountry.kt b/src/main/kotlin/io/billie/organisations/data/UnableToFindCountry.kt deleted file mode 100644 index ce9f033..0000000 --- a/src/main/kotlin/io/billie/organisations/data/UnableToFindCountry.kt +++ /dev/null @@ -1,3 +0,0 @@ -package io.billie.organisations.data - -class UnableToFindCountry(val countryCode: String) : RuntimeException() diff --git a/src/main/kotlin/io/billie/organisations/resource/OrganisationResource.kt b/src/main/kotlin/io/billie/organisations/resource/OrganisationResource.kt deleted file mode 100644 index b108a1f..0000000 --- a/src/main/kotlin/io/billie/organisations/resource/OrganisationResource.kt +++ /dev/null @@ -1,49 +0,0 @@ -package io.billie.organisations.resource - -import io.billie.organisations.data.UnableToFindCountry -import io.billie.organisations.service.OrganisationService -import io.billie.organisations.viewmodel.* -import io.swagger.v3.oas.annotations.media.ArraySchema -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.web.bind.annotation.* -import org.springframework.web.server.ResponseStatusException -import java.util.* -import javax.validation.Valid - - -@RestController -@RequestMapping("organisations") -class OrganisationResource(val service: OrganisationService) { - - @GetMapping - fun index(): List = service.findOrganisations() - - @PostMapping - @ApiResponses( - value = [ - ApiResponse( - responseCode = "200", - description = "Accepted the new organisation", - content = [ - (Content( - mediaType = "application/json", - array = (ArraySchema(schema = Schema(implementation = Entity::class))) - ))] - ), - ApiResponse(responseCode = "400", description = "Bad request", content = [Content()])] - ) - fun post(@Valid @RequestBody organisation: OrganisationRequest): Entity { - try { - val id = service.createOrganisation(organisation) - return Entity(id) - } catch (e: UnableToFindCountry) { - throw ResponseStatusException(BAD_REQUEST, e.message) - } - } - -} diff --git a/src/main/kotlin/io/billie/organisations/service/OrganisationService.kt b/src/main/kotlin/io/billie/organisations/service/OrganisationService.kt deleted file mode 100644 index d029521..0000000 --- a/src/main/kotlin/io/billie/organisations/service/OrganisationService.kt +++ /dev/null @@ -1,18 +0,0 @@ -package io.billie.organisations.service - -import io.billie.organisations.data.OrganisationRepository -import io.billie.organisations.viewmodel.OrganisationRequest -import io.billie.organisations.viewmodel.OrganisationResponse -import org.springframework.stereotype.Service -import java.util.* - -@Service -class OrganisationService(val db: OrganisationRepository) { - - fun findOrganisations(): List = db.findOrganisations() - - fun createOrganisation(organisation: OrganisationRequest): UUID { - return db.create(organisation) - } - -} diff --git a/src/main/kotlin/io/billie/organisations/viewmodel/ContactDetails.kt b/src/main/kotlin/io/billie/organisations/viewmodel/ContactDetails.kt deleted file mode 100644 index 03f3b02..0000000 --- a/src/main/kotlin/io/billie/organisations/viewmodel/ContactDetails.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.billie.organisations.viewmodel - -import com.fasterxml.jackson.annotation.JsonProperty -import java.util.* - -data class ContactDetails( - val id: UUID?, - @JsonProperty("phone_number") val phoneNumber: String?, - val fax: String?, - val email: String? -) diff --git a/src/main/kotlin/io/billie/organisations/viewmodel/ContactDetailsRequest.kt b/src/main/kotlin/io/billie/organisations/viewmodel/ContactDetailsRequest.kt deleted file mode 100644 index 6fbd39a..0000000 --- a/src/main/kotlin/io/billie/organisations/viewmodel/ContactDetailsRequest.kt +++ /dev/null @@ -1,10 +0,0 @@ -package io.billie.organisations.viewmodel - -import com.fasterxml.jackson.annotation.JsonProperty -import java.util.* - -data class ContactDetailsRequest( - @JsonProperty("phone_number") val phoneNumber: String?, - val fax: String?, - val email: String? -) diff --git a/src/main/kotlin/io/billie/organisations/viewmodel/Entity.kt b/src/main/kotlin/io/billie/organisations/viewmodel/Entity.kt deleted file mode 100644 index d35b67b..0000000 --- a/src/main/kotlin/io/billie/organisations/viewmodel/Entity.kt +++ /dev/null @@ -1,5 +0,0 @@ -package io.billie.organisations.viewmodel - -import java.util.* - -data class Entity(val id: UUID) diff --git a/src/main/kotlin/io/billie/organisations/viewmodel/OrganisationRequest.kt b/src/main/kotlin/io/billie/organisations/viewmodel/OrganisationRequest.kt deleted file mode 100644 index 2e31f21..0000000 --- a/src/main/kotlin/io/billie/organisations/viewmodel/OrganisationRequest.kt +++ /dev/null @@ -1,19 +0,0 @@ -package io.billie.organisations.viewmodel - -import com.fasterxml.jackson.annotation.JsonFormat -import com.fasterxml.jackson.annotation.JsonProperty -import org.springframework.data.relational.core.mapping.Table -import java.time.LocalDate -import java.util.* -import javax.validation.constraints.NotBlank - -@Table("ORGANISATIONS") -data class OrganisationRequest( - @field:NotBlank val name: String, - @JsonFormat(pattern = "dd/MM/yyyy") @JsonProperty("date_founded") val dateFounded: LocalDate, - @field:NotBlank @JsonProperty("country_code") val countryCode: String, - @JsonProperty("vat_number") val VATNumber: String?, - @JsonProperty("registration_number") val registrationNumber: String?, - @JsonProperty("legal_entity_type") val legalEntityType: LegalEntityType, - @JsonProperty("contact_details") val contactDetails: ContactDetailsRequest, -) diff --git a/src/main/kotlin/io/billie/organisations/viewmodel/OrganisationResponse.kt b/src/main/kotlin/io/billie/organisations/viewmodel/OrganisationResponse.kt deleted file mode 100644 index d0fec75..0000000 --- a/src/main/kotlin/io/billie/organisations/viewmodel/OrganisationResponse.kt +++ /dev/null @@ -1,20 +0,0 @@ -package io.billie.organisations.viewmodel - -import com.fasterxml.jackson.annotation.JsonFormat -import com.fasterxml.jackson.annotation.JsonProperty -import io.billie.countries.model.CountryResponse -import org.springframework.data.relational.core.mapping.Table -import java.time.LocalDate -import java.util.* - -@Table("ORGANISATIONS") -data class OrganisationResponse( - val id: UUID, - val name: String, - @JsonFormat(pattern = "dd/MM/yyyy") @JsonProperty("date_founded") val dateFounded: LocalDate, - val country: CountryResponse, - @JsonProperty("vat_number") val VATNumber: String?, - @JsonProperty("registration_number") val registrationNumber: String?, - @JsonProperty("legal_entity_type") val legalEntityType: LegalEntityType, - @JsonProperty("contact_details") val contactDetails: ContactDetails, -) diff --git a/src/main/kotlin/io/billie/Application.kt b/src/main/kotlin/io/billie/organization_management/Application.kt similarity index 84% rename from src/main/kotlin/io/billie/Application.kt rename to src/main/kotlin/io/billie/organization_management/Application.kt index 16ec860..943ff5d 100644 --- a/src/main/kotlin/io/billie/Application.kt +++ b/src/main/kotlin/io/billie/organization_management/Application.kt @@ -1,4 +1,4 @@ -package io.billie +package io.billie.organization_management import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication diff --git a/src/main/kotlin/io/billie/organization_management/config/SecurityConfig.kt b/src/main/kotlin/io/billie/organization_management/config/SecurityConfig.kt new file mode 100644 index 0000000..751752a --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/config/SecurityConfig.kt @@ -0,0 +1,22 @@ +package io.billie.organization_management.config + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.security.config.annotation.web.builders.HttpSecurity +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity +import org.springframework.security.web.SecurityFilterChain + +@Configuration +@EnableWebSecurity +class SecurityConfig { + + @Bean + fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { + return http + .authorizeHttpRequests { auth -> + auth.anyRequest().permitAll() + } + .csrf { csrfConfigurer -> csrfConfigurer.disable() } + .build() + } +} diff --git a/src/main/kotlin/io/billie/organization_management/config/SwaggerConfig.kt b/src/main/kotlin/io/billie/organization_management/config/SwaggerConfig.kt new file mode 100644 index 0000000..b0fabb8 --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/config/SwaggerConfig.kt @@ -0,0 +1,15 @@ +package io.billie.organization_management.config + +import com.fasterxml.jackson.databind.ObjectMapper +import io.swagger.v3.core.jackson.ModelResolver +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +class SwaggerConfig { + + @Bean + fun modelResolver(objectMapper: ObjectMapper): ModelResolver { + return ModelResolver(objectMapper) + } +} diff --git a/src/main/kotlin/io/billie/organization_management/countries/data/City.kt b/src/main/kotlin/io/billie/organization_management/countries/data/City.kt new file mode 100644 index 0000000..8b33a5d --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/countries/data/City.kt @@ -0,0 +1,21 @@ +package io.billie.organization_management.countries.data + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.Table +import java.util.UUID + +@Entity +@Table(name = "cities") +class City( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: UUID?, + @Column(nullable = false, length = 100) + val name: String, + @Column(nullable = false, length = 2) + val countryCode: String, +) diff --git a/src/main/kotlin/io/billie/organization_management/countries/data/CityRepository.kt b/src/main/kotlin/io/billie/organization_management/countries/data/CityRepository.kt new file mode 100644 index 0000000..320fa5c --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/countries/data/CityRepository.kt @@ -0,0 +1,12 @@ +package io.billie.organization_management.countries.data + +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository +import java.util.* + +@Repository +interface CityRepository : JpaRepository { + + fun findAllByCountryCodeIgnoreCase(countryCode: String): List + fun findByCountryCodeIgnoreCaseAndNameIgnoreCase(countryCode: String, name: String): City? +} diff --git a/src/main/kotlin/io/billie/organization_management/countries/data/Country.kt b/src/main/kotlin/io/billie/organization_management/countries/data/Country.kt new file mode 100644 index 0000000..c057431 --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/countries/data/Country.kt @@ -0,0 +1,21 @@ +package io.billie.organization_management.countries.data + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.Table +import java.util.UUID + +@Entity +@Table(name = "countries") +class Country( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: UUID?, + @Column(nullable = false, length = 100) + val name: String, + @Column(nullable = false, length = 2) + val code: String, +) diff --git a/src/main/kotlin/io/billie/organization_management/countries/data/CountryRepository.kt b/src/main/kotlin/io/billie/organization_management/countries/data/CountryRepository.kt new file mode 100644 index 0000000..19e770f --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/countries/data/CountryRepository.kt @@ -0,0 +1,11 @@ +package io.billie.organization_management.countries.data + +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository +import java.util.* + +@Repository +interface CountryRepository : JpaRepository { + + fun findByCode(code: String): Country? +} diff --git a/src/main/kotlin/io/billie/organization_management/countries/model/CityResponse.kt b/src/main/kotlin/io/billie/organization_management/countries/model/CityResponse.kt new file mode 100644 index 0000000..bf9645a --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/countries/model/CityResponse.kt @@ -0,0 +1,11 @@ +package io.billie.organization_management.countries.model + +import jakarta.validation.constraints.Size +import java.util.* + +data class CityResponse( + val id: UUID, + val name: String, + @Size(min = 2, max = 2) + val countryCode: String +) diff --git a/src/main/kotlin/io/billie/organization_management/countries/model/CountryResponse.kt b/src/main/kotlin/io/billie/organization_management/countries/model/CountryResponse.kt new file mode 100644 index 0000000..3917e5c --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/countries/model/CountryResponse.kt @@ -0,0 +1,11 @@ +package io.billie.organization_management.countries.model + +import jakarta.validation.constraints.Size +import java.util.* + +data class CountryResponse( + val id: UUID, + val name: String, + @Size(min = 2, max = 2) + val countryCode: String, +) diff --git a/src/main/kotlin/io/billie/organization_management/countries/resource/CountryResource.kt b/src/main/kotlin/io/billie/organization_management/countries/resource/CountryResource.kt new file mode 100644 index 0000000..4ebc3c6 --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/countries/resource/CountryResource.kt @@ -0,0 +1,91 @@ +package io.billie.organization_management.countries.resource + +import io.billie.organization_management.countries.model.CityResponse +import io.billie.organization_management.countries.model.CountryResponse +import io.billie.organization_management.countries.service.CountryService +import io.swagger.v3.oas.annotations.media.ArraySchema +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.NOT_FOUND +import org.springframework.web.bind.annotation.* +import org.springframework.web.server.ResponseStatusException + +@RestController +@RequestMapping("countries") +class CountryResource(private val countryService: CountryService) { + + @ApiResponses( + value = [ + ApiResponse( + responseCode = "200", + description = "All countries", + content = [ + ( + Content( + mediaType = "application/json", + array = (ArraySchema(schema = Schema(implementation = CountryResponse::class))), + ) + ), + ], + ), + ], + ) + @GetMapping + fun countries(): List = countryService.findCountries() + + @ApiResponses( + value = [ + ApiResponse( + responseCode = "200", + description = "Found cities for country", + content = [ + ( + Content( + mediaType = "application/json", + array = (ArraySchema(schema = Schema(implementation = CityResponse::class))), + ) + ), + ], + ), + ApiResponse(responseCode = "404", description = "No cities found for country code", content = [Content()]), + ], + ) + @GetMapping("/{countryCode}/cities") + fun cities(@PathVariable("countryCode") countryCode: String): List { + val cities = countryService.findCities(countryCode) + if (cities.isEmpty()) { + throw ResponseStatusException( + NOT_FOUND, + "No cities found for $countryCode", + ) + } + return cities + } + + @ApiResponses( + value = [ + ApiResponse( + responseCode = "200", + description = "Found city for country", + content = [ + ( + Content( + mediaType = "application/json", + array = (ArraySchema(schema = Schema(implementation = CityResponse::class))), + ) + ), + ], + ), + ApiResponse(responseCode = "404", description = "No city found for city name and country code", content = [Content()]), + ], + ) + @GetMapping("/{countryCode}/cities/{cityName}") + fun city( + @PathVariable("countryCode") countryCode: String, + @PathVariable("cityName") cityName: String, + ): CityResponse { + return countryService.findCity(countryCode, cityName) + } +} diff --git a/src/main/kotlin/io/billie/organization_management/countries/service/CountryService.kt b/src/main/kotlin/io/billie/organization_management/countries/service/CountryService.kt new file mode 100644 index 0000000..4f66671 --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/countries/service/CountryService.kt @@ -0,0 +1,48 @@ +package io.billie.organization_management.countries.service + +import io.billie.organization_management.countries.data.CityRepository +import io.billie.organization_management.countries.data.CountryRepository +import io.billie.organization_management.countries.model.CityResponse +import io.billie.organization_management.countries.model.CountryResponse +import io.billie.organization_management.organisations.data.CityNotFoundException +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +class CountryService( + private val countryRepository: CountryRepository, + private val cityRepository: CityRepository, +) { + + @Transactional(readOnly = true) + fun findCountries(): List { + return countryRepository.findAll().map { + CountryResponse( + id = it.id!!, + name = it.name, + countryCode = it.code, + ) + } + } + + @Transactional(readOnly = true) + fun findCities(countryCode: String): List { + return cityRepository.findAllByCountryCodeIgnoreCase(countryCode).map { + CityResponse( + id = it.id!!, + name = it.name, + countryCode = it.countryCode, + ) + } + } + + fun findCity(countryCode: String, cityName: String): CityResponse { + val city = cityRepository.findByCountryCodeIgnoreCaseAndNameIgnoreCase(countryCode, cityName) ?: throw CityNotFoundException(countryCode, cityName) + + return CityResponse( + id = city.id!!, + name = city.name, + countryCode = city.countryCode, + ) + } +} diff --git a/src/main/kotlin/io/billie/organization_management/organisations/data/Address.kt b/src/main/kotlin/io/billie/organization_management/organisations/data/Address.kt new file mode 100644 index 0000000..b7e3a39 --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/organisations/data/Address.kt @@ -0,0 +1,27 @@ +package io.billie.organization_management.organisations.data + +import io.billie.organization_management.countries.data.City +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.ManyToOne +import jakarta.persistence.Table +import java.util.UUID + +@Entity +@Table(name = "addresses") +class Address( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: UUID?, + @Column(length = 50) + val street: String, + @Column(length = 20) + val number: String, + @Column(length = 20) + val postalCode: String, + @ManyToOne + val city: City +) diff --git a/src/main/kotlin/io/billie/organization_management/organisations/data/AddressRepository.kt b/src/main/kotlin/io/billie/organization_management/organisations/data/AddressRepository.kt new file mode 100644 index 0000000..7ced2ff --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/organisations/data/AddressRepository.kt @@ -0,0 +1,8 @@ +package io.billie.organization_management.organisations.data + +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository +import java.util.* + +@Repository +interface AddressRepository : JpaRepository diff --git a/src/main/kotlin/io/billie/organization_management/organisations/data/CityNotFoundException.kt b/src/main/kotlin/io/billie/organization_management/organisations/data/CityNotFoundException.kt new file mode 100644 index 0000000..4a783dc --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/organisations/data/CityNotFoundException.kt @@ -0,0 +1,8 @@ +package io.billie.organization_management.organisations.data + +import java.util.* + +class CityNotFoundException(message: String) : RuntimeException(message) { + constructor(cityId: UUID) : this("City with id $cityId not found") + constructor(countryCode: String, cityName: String) : this("City with name $cityName for country $countryCode not found") +} diff --git a/src/main/kotlin/io/billie/organization_management/organisations/data/ContactDetail.kt b/src/main/kotlin/io/billie/organization_management/organisations/data/ContactDetail.kt new file mode 100644 index 0000000..76ab4c0 --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/organisations/data/ContactDetail.kt @@ -0,0 +1,23 @@ +package io.billie.organization_management.organisations.data + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.Table +import java.util.UUID + +@Entity +@Table(name = "contact_details") +class ContactDetail( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: UUID?, + @Column(length = 20) + val phoneNumber: String?, + @Column(length = 20) + val fax: String?, + @Column(length = 256) + val email: String?, +) diff --git a/src/main/kotlin/io/billie/organization_management/organisations/data/ContactDetailRepository.kt b/src/main/kotlin/io/billie/organization_management/organisations/data/ContactDetailRepository.kt new file mode 100644 index 0000000..fed622e --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/organisations/data/ContactDetailRepository.kt @@ -0,0 +1,8 @@ +package io.billie.organization_management.organisations.data + +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository +import java.util.* + +@Repository +interface ContactDetailRepository : JpaRepository diff --git a/src/main/kotlin/io/billie/organization_management/organisations/data/CountryNotFoundException.kt b/src/main/kotlin/io/billie/organization_management/organisations/data/CountryNotFoundException.kt new file mode 100644 index 0000000..ac41937 --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/organisations/data/CountryNotFoundException.kt @@ -0,0 +1,3 @@ +package io.billie.organization_management.organisations.data + +class CountryNotFoundException(countryCode: String) : RuntimeException("Country with code $countryCode not found") diff --git a/src/main/kotlin/io/billie/organization_management/organisations/data/Organisation.kt b/src/main/kotlin/io/billie/organization_management/organisations/data/Organisation.kt new file mode 100644 index 0000000..95a4ec3 --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/organisations/data/Organisation.kt @@ -0,0 +1,32 @@ +package io.billie.organization_management.organisations.data + +import io.billie.organization_management.countries.data.Country +import io.billie.organization_management.organisations.model.LegalEntityType +import jakarta.persistence.* +import java.time.LocalDate +import java.util.UUID + +@Entity +@Table(name = "organisations") +class Organisation( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: UUID?, + @Column(nullable = false) + val name: String, + @Column(nullable = false) + val dateFounded: LocalDate, + @ManyToOne + val country: Country, + @Column(nullable = false, length = 20) + val vatNumber: String, + @Column(nullable = false, length = 20) + val registrationNumber: String, + @Column(nullable = false, length = 30) + @Enumerated(EnumType.STRING) + val legalEntityType: LegalEntityType, + @OneToOne(cascade = [CascadeType.ALL]) + val contactDetail: ContactDetail, + @OneToOne(cascade = [CascadeType.ALL], orphanRemoval = true) + var address: Address? = null, +) diff --git a/src/main/kotlin/io/billie/organization_management/organisations/data/OrganisationNotFoundException.kt b/src/main/kotlin/io/billie/organization_management/organisations/data/OrganisationNotFoundException.kt new file mode 100644 index 0000000..e227f53 --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/organisations/data/OrganisationNotFoundException.kt @@ -0,0 +1,5 @@ +package io.billie.organization_management.organisations.data + +import java.util.* + +class OrganisationNotFoundException(organisationId: UUID) : RuntimeException("Organisation with id $organisationId not found") diff --git a/src/main/kotlin/io/billie/organization_management/organisations/data/OrganisationRepository.kt b/src/main/kotlin/io/billie/organization_management/organisations/data/OrganisationRepository.kt new file mode 100644 index 0000000..63ebdf9 --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/organisations/data/OrganisationRepository.kt @@ -0,0 +1,8 @@ +package io.billie.organization_management.organisations.data + +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository +import java.util.* + +@Repository +interface OrganisationRepository : JpaRepository diff --git a/src/main/kotlin/io/billie/organization_management/organisations/model/AddressRequest.kt b/src/main/kotlin/io/billie/organization_management/organisations/model/AddressRequest.kt new file mode 100644 index 0000000..c7d91ba --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/organisations/model/AddressRequest.kt @@ -0,0 +1,16 @@ +package io.billie.organization_management.organisations.model + +import jakarta.validation.constraints.NotEmpty +import jakarta.validation.constraints.NotNull +import java.util.UUID + +data class AddressRequest( + @field:NotNull + val cityId: UUID, + @field:NotEmpty + val street: String, + @field:NotEmpty + val number: String, + @field:NotEmpty + val postalCode: String, +) diff --git a/src/main/kotlin/io/billie/organization_management/organisations/model/ContactDetailRequest.kt b/src/main/kotlin/io/billie/organization_management/organisations/model/ContactDetailRequest.kt new file mode 100644 index 0000000..8d6b850 --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/organisations/model/ContactDetailRequest.kt @@ -0,0 +1,7 @@ +package io.billie.organization_management.organisations.model + +data class ContactDetailRequest( + val phoneNumber: String?, + val fax: String?, + val email: String? +) diff --git a/src/main/kotlin/io/billie/organization_management/organisations/model/ContactDetailResponse.kt b/src/main/kotlin/io/billie/organization_management/organisations/model/ContactDetailResponse.kt new file mode 100644 index 0000000..54fc57b --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/organisations/model/ContactDetailResponse.kt @@ -0,0 +1,10 @@ +package io.billie.organization_management.organisations.model + +import java.util.* + +data class ContactDetailResponse( + val id: UUID?, + val phoneNumber: String?, + val fax: String?, + val email: String? +) diff --git a/src/main/kotlin/io/billie/organization_management/organisations/model/Entity.kt b/src/main/kotlin/io/billie/organization_management/organisations/model/Entity.kt new file mode 100644 index 0000000..91a7115 --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/organisations/model/Entity.kt @@ -0,0 +1,5 @@ +package io.billie.organization_management.organisations.model + +import java.util.* + +data class Entity(val id: UUID) diff --git a/src/main/kotlin/io/billie/organisations/viewmodel/LegalEntityType.kt b/src/main/kotlin/io/billie/organization_management/organisations/model/LegalEntityType.kt similarity index 85% rename from src/main/kotlin/io/billie/organisations/viewmodel/LegalEntityType.kt rename to src/main/kotlin/io/billie/organization_management/organisations/model/LegalEntityType.kt index 97680d3..d4428d9 100644 --- a/src/main/kotlin/io/billie/organisations/viewmodel/LegalEntityType.kt +++ b/src/main/kotlin/io/billie/organization_management/organisations/model/LegalEntityType.kt @@ -1,4 +1,4 @@ -package io.billie.organisations.viewmodel +package io.billie.organization_management.organisations.model enum class LegalEntityType { SOLE_PROPRIETORSHIP, diff --git a/src/main/kotlin/io/billie/organization_management/organisations/model/OrganisationRequest.kt b/src/main/kotlin/io/billie/organization_management/organisations/model/OrganisationRequest.kt new file mode 100644 index 0000000..123f2b5 --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/organisations/model/OrganisationRequest.kt @@ -0,0 +1,20 @@ +package io.billie.organization_management.organisations.model + +import com.fasterxml.jackson.annotation.JsonFormat +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import java.time.LocalDate + +data class OrganisationRequest( + @field:NotBlank + val name: String, + @Schema(type = "string", pattern = "dd/MM/yyyy", example = "26/09/2023") + @JsonFormat(pattern = "dd/MM/yyyy") + val dateFounded: LocalDate, + @field:NotBlank + val countryCode: String, + val vatNumber: String, + val registrationNumber: String, + val legalEntityType: LegalEntityType, + val contactDetail: ContactDetailRequest, +) diff --git a/src/main/kotlin/io/billie/organization_management/organisations/model/OrganisationResponse.kt b/src/main/kotlin/io/billie/organization_management/organisations/model/OrganisationResponse.kt new file mode 100644 index 0000000..01f7b5c --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/organisations/model/OrganisationResponse.kt @@ -0,0 +1,18 @@ +package io.billie.organization_management.organisations.model + +import com.fasterxml.jackson.annotation.JsonFormat +import io.billie.organization_management.countries.model.CountryResponse +import java.time.LocalDate +import java.util.* + +data class OrganisationResponse( + val id: UUID, + val name: String, + @JsonFormat(pattern = "dd/MM/yyyy") + val dateFounded: LocalDate, + val country: CountryResponse, + val vatNumber: String?, + val registrationNumber: String?, + val legalEntityType: LegalEntityType, + val contactDetail: ContactDetailResponse, +) diff --git a/src/main/kotlin/io/billie/organization_management/organisations/resource/OrganisationResource.kt b/src/main/kotlin/io/billie/organization_management/organisations/resource/OrganisationResource.kt new file mode 100644 index 0000000..835611f --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/organisations/resource/OrganisationResource.kt @@ -0,0 +1,92 @@ +package io.billie.organization_management.organisations.resource + +import io.billie.organization_management.organisations.data.CityNotFoundException +import io.billie.organization_management.organisations.data.CountryNotFoundException +import io.billie.organization_management.organisations.data.OrganisationNotFoundException +import io.billie.organization_management.organisations.model.AddressRequest +import io.billie.organization_management.organisations.service.OrganisationService +import io.billie.organization_management.organisations.model.Entity +import io.billie.organization_management.organisations.model.OrganisationRequest +import io.billie.organization_management.organisations.model.OrganisationResponse +import io.swagger.v3.oas.annotations.media.ArraySchema +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 jakarta.validation.Valid +import org.springframework.http.HttpStatus.BAD_REQUEST +import org.springframework.web.bind.annotation.* +import org.springframework.web.server.ResponseStatusException +import java.util.UUID + +@RestController +@RequestMapping("organisations") +class OrganisationResource(val service: OrganisationService) { + + @GetMapping + fun organisations(): List = service.findOrganisations() + + @PostMapping + @ApiResponses( + value = [ + ApiResponse( + responseCode = "200", + description = "Accepted the new organisation", + content = [ + ( + Content( + mediaType = "application/json", + array = (ArraySchema(schema = Schema(implementation = Entity::class))), + ) + ), + ], + ), + ApiResponse(responseCode = "400", description = "Bad request", content = [Content()]), + ], + ) + fun addOrganisation( + @Valid + @RequestBody + organisation: OrganisationRequest, + ): Entity { + return try { + Entity(service.createOrganisation(organisation)) + } catch (e: CountryNotFoundException) { + throw ResponseStatusException(BAD_REQUEST, e.message) + } + } + + @PostMapping("{organisationId}/addresses") + @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 addAddress( + @PathVariable + organisationId: UUID, + @Valid + @RequestBody + address: AddressRequest, + ): Entity { + return try { + Entity(service.createAddress(organisationId, address)) + } catch (e: OrganisationNotFoundException) { + throw ResponseStatusException(BAD_REQUEST, e.message) + } catch (e: CityNotFoundException) { + throw ResponseStatusException(BAD_REQUEST, e.message) + } + } +} diff --git a/src/main/kotlin/io/billie/organization_management/organisations/service/OrganisationService.kt b/src/main/kotlin/io/billie/organization_management/organisations/service/OrganisationService.kt new file mode 100644 index 0000000..09bcade --- /dev/null +++ b/src/main/kotlin/io/billie/organization_management/organisations/service/OrganisationService.kt @@ -0,0 +1,106 @@ +package io.billie.organization_management.organisations.service + +import io.billie.organization_management.countries.data.CityRepository +import io.billie.organization_management.countries.data.Country +import io.billie.organization_management.countries.data.CountryRepository +import io.billie.organization_management.countries.model.CountryResponse +import io.billie.organization_management.organisations.data.* +import io.billie.organization_management.organisations.model.* +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import java.util.* +import kotlin.jvm.optionals.getOrElse + +@Service +class OrganisationService( + val organisationRepository: OrganisationRepository, + val countryRepository: CountryRepository, + val cityRepository: CityRepository, +) { + + @Transactional(readOnly = true) + fun findOrganisations(): List { + return organisationRepository.findAll().map { + OrganisationResponse( + id = it.id!!, + name = it.name, + dateFounded = it.dateFounded, + vatNumber = it.vatNumber, + registrationNumber = it.registrationNumber, + legalEntityType = it.legalEntityType, + country = mapToCountryResponse(it.country), + contactDetail = mapToContactDetailResponse(it.contactDetail), + ) + } + } + + private fun mapToCountryResponse(country: Country): CountryResponse { + return country.let { + CountryResponse( + id = it.id!!, + name = it.name, + countryCode = it.code, + ) + } + } + + private fun mapToContactDetailResponse(contactDetail: ContactDetail): ContactDetailResponse { + return contactDetail.let { + ContactDetailResponse( + id = it.id, + phoneNumber = it.phoneNumber, + fax = it.fax, + email = it.email, + ) + } + } + + @Transactional + fun createOrganisation(organisationRequest: OrganisationRequest): UUID { + val country = countryRepository.findByCode(organisationRequest.countryCode) ?: throw CountryNotFoundException(organisationRequest.countryCode) + + val organisation = Organisation( + id = null, + name = organisationRequest.name, + dateFounded = organisationRequest.dateFounded, + country = country, + vatNumber = organisationRequest.vatNumber, + registrationNumber = organisationRequest.registrationNumber, + legalEntityType = organisationRequest.legalEntityType, + contactDetail = mapToContactDetail(organisationRequest.contactDetail), + ) + + return organisationRepository.save(organisation).id!! + } + + private fun mapToContactDetail(contactDetailRequest: ContactDetailRequest): ContactDetail { + return contactDetailRequest.let { + ContactDetail( + id = null, + phoneNumber = it.phoneNumber, + fax = it.fax, + email = it.email, + ) + } + } + + @Transactional + fun createAddress(organisationId: UUID, addressRequest: AddressRequest): UUID { + val organisation = organisationRepository.findById(organisationId).getOrElse { + throw OrganisationNotFoundException(organisationId) + } + val city = cityRepository.findById(addressRequest.cityId).getOrElse { + throw CityNotFoundException(addressRequest.cityId) + } + + organisation.address = Address( + id = null, + street = addressRequest.street, + number = addressRequest.number, + postalCode = addressRequest.postalCode, + city = city, + ) + + return organisationRepository.save(organisation).address!!.id!! + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index 09b6bce..0000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1,7 +0,0 @@ -spring.config.import=optional:file:./database.env[.properties] -spring.datasource.driver-class-name=${DATABASE_DRIVER} -spring.datasource.url=${DATABASE_URL} -spring.datasource.username=${POSTGRES_USER} -spring.datasource.password=${POSTGRES_PASSWORD} -spring.datasource.schema=${DATABASE_SCHEMA} -spring.datasource.initialization-mode=always diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..121f6f6 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,17 @@ +spring: + datasource: + url: jdbc:postgresql://localhost:5432/organisations + username: postgres + password: postgres + hikari: + maximum-pool-size: 20 + jpa: + properties: + hibernate: + default_schema: organisations_schema + jackson: + property-naming-strategy: SNAKE_CASE + +server: + error: + include-message: always \ No newline at end of file diff --git a/src/main/resources/db/migration/V10__add_address_id_toorganisations_table.sql b/src/main/resources/db/migration/V10__add_address_id_toorganisations_table.sql new file mode 100644 index 0000000..3effd9c --- /dev/null +++ b/src/main/resources/db/migration/V10__add_address_id_toorganisations_table.sql @@ -0,0 +1,2 @@ +ALTER TABLE organisations_schema.organisations + ADD address_id UUID references organisations_schema.addresses(id); diff --git a/src/main/resources/db/migration/V3__add_countries_table.sql b/src/main/resources/db/migration/V3__add_countries_table.sql index 2fea932..e790904 100644 --- a/src/main/resources/db/migration/V3__add_countries_table.sql +++ b/src/main/resources/db/migration/V3__add_countries_table.sql @@ -1,6 +1,6 @@ CREATE TABLE IF NOT EXISTS organisations_schema.countries ( - id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, - name VARCHAR(100) NOT NULL, - country_code VARCHAR(2) NOT NULL + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, + name VARCHAR(100) NOT NULL, + code VARCHAR(2) NOT NULL ); diff --git a/src/main/resources/db/migration/V4__add_cities_table.sql b/src/main/resources/db/migration/V4__add_cities_table.sql index f6cb962..cd2a8bf 100644 --- a/src/main/resources/db/migration/V4__add_cities_table.sql +++ b/src/main/resources/db/migration/V4__add_cities_table.sql @@ -1,6 +1,6 @@ CREATE TABLE IF NOT EXISTS organisations_schema.cities ( - id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, - country_code CHAR(2) NOT NULL, - name VARCHAR(100) NOT NULL + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, + country_code VARCHAR(2) NOT NULL, + name VARCHAR(100) NOT NULL ); diff --git a/src/main/resources/db/migration/V5__add_contact_details_table.sql b/src/main/resources/db/migration/V5__add_contact_details_table.sql index 45d8fc6..c306afa 100644 --- a/src/main/resources/db/migration/V5__add_contact_details_table.sql +++ b/src/main/resources/db/migration/V5__add_contact_details_table.sql @@ -1,8 +1,8 @@ CREATE TABLE IF NOT EXISTS organisations_schema.contact_details ( - id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, - phone_number VARCHAR(20), - fax VARCHAR(20), - email VARCHAR(256) + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, + phone_number VARCHAR(20) NOT NULL, + fax VARCHAR(20) NOT NULL, + email VARCHAR(256) NOT NULL ); diff --git a/src/main/resources/db/migration/V6__add_organisations_table.sql b/src/main/resources/db/migration/V6__add_organisations_table.sql index 617637d..eb37b75 100644 --- a/src/main/resources/db/migration/V6__add_organisations_table.sql +++ b/src/main/resources/db/migration/V6__add_organisations_table.sql @@ -1,11 +1,11 @@ CREATE TABLE IF NOT EXISTS organisations_schema.organisations ( - id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, - name VARCHAR NOT NULL, - date_founded DATE NOT NULL, - country_code CHAR(2) NOT NULL, - VAT_number VARCHAR(20), - registration_number VARCHAR(20), - legal_entity_type VARCHAR(30) NOT NULL, - contact_details_id VARCHAR(36) NOT NULL + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, + name VARCHAR NOT NULL, + date_founded DATE NOT NULL, + country_id UUID NOT NULL references organisations_schema.countries(id), + vat_number VARCHAR(20), + registration_number VARCHAR(20), + legal_entity_type VARCHAR(30) NOT NULL, + contact_detail_id UUID NOT NULL references organisations_schema.contact_details(id) ); diff --git a/src/main/resources/db/migration/V7__Add_countries.sql b/src/main/resources/db/migration/V7__Add_countries.sql index db0c9c0..d786a37 100644 --- a/src/main/resources/db/migration/V7__Add_countries.sql +++ b/src/main/resources/db/migration/V7__Add_countries.sql @@ -1,240 +1,240 @@ -insert into organisations_schema.countries(country_code, name) VALUES ('AD','Andorra'); -insert into organisations_schema.countries(country_code, name) VALUES ('AE','United Arab Emirates'); -insert into organisations_schema.countries(country_code, name) VALUES ('AF','Afghanistan'); -insert into organisations_schema.countries(country_code, name) VALUES ('AG','Antigua and/or Barbuda'); -insert into organisations_schema.countries(country_code, name) VALUES ('AI','Anguilla'); -insert into organisations_schema.countries(country_code, name) VALUES ('AL','Albania'); -insert into organisations_schema.countries(country_code, name) VALUES ('AM','Armenia'); -insert into organisations_schema.countries(country_code, name) VALUES ('AN','Netherlands Antilles'); -insert into organisations_schema.countries(country_code, name) VALUES ('AO','Angola'); -insert into organisations_schema.countries(country_code, name) VALUES ('AQ','Antarctica'); -insert into organisations_schema.countries(country_code, name) VALUES ('AR','Argentina'); -insert into organisations_schema.countries(country_code, name) VALUES ('AT','Austria'); -insert into organisations_schema.countries(country_code, name) VALUES ('AU','Australia'); -insert into organisations_schema.countries(country_code, name) VALUES ('AW','Aruba'); -insert into organisations_schema.countries(country_code, name) VALUES ('AZ','Azerbaijan'); -insert into organisations_schema.countries(country_code, name) VALUES ('BA','Bosnia and Herzegovina'); -insert into organisations_schema.countries(country_code, name) VALUES ('BB','Barbados'); -insert into organisations_schema.countries(country_code, name) VALUES ('BD','Bangladesh'); -insert into organisations_schema.countries(country_code, name) VALUES ('BE','Belgium'); -insert into organisations_schema.countries(country_code, name) VALUES ('BF','Burkina Faso'); -insert into organisations_schema.countries(country_code, name) VALUES ('BG','Bulgaria'); -insert into organisations_schema.countries(country_code, name) VALUES ('BH','Bahrain'); -insert into organisations_schema.countries(country_code, name) VALUES ('BI','Burundi'); -insert into organisations_schema.countries(country_code, name) VALUES ('BJ','Benin'); -insert into organisations_schema.countries(country_code, name) VALUES ('BM','Bermuda'); -insert into organisations_schema.countries(country_code, name) VALUES ('BN','Brunei Darussalam'); -insert into organisations_schema.countries(country_code, name) VALUES ('BO','Bolivia'); -insert into organisations_schema.countries(country_code, name) VALUES ('BR','Brazil'); -insert into organisations_schema.countries(country_code, name) VALUES ('BS','Bahamas'); -insert into organisations_schema.countries(country_code, name) VALUES ('BT','Bhutan'); -insert into organisations_schema.countries(country_code, name) VALUES ('BV','Bouvet Island'); -insert into organisations_schema.countries(country_code, name) VALUES ('BW','Botswana'); -insert into organisations_schema.countries(country_code, name) VALUES ('BY','Belarus'); -insert into organisations_schema.countries(country_code, name) VALUES ('BZ','Belize'); -insert into organisations_schema.countries(country_code, name) VALUES ('CA','Canada'); -insert into organisations_schema.countries(country_code, name) VALUES ('CC','Cocos (Keeling) Islands'); -insert into organisations_schema.countries(country_code, name) VALUES ('CF','Central African Republic'); -insert into organisations_schema.countries(country_code, name) VALUES ('CG','Congo'); -insert into organisations_schema.countries(country_code, name) VALUES ('CH','Switzerland'); -insert into organisations_schema.countries(country_code, name) VALUES ('CI','Ivory Coast'); -insert into organisations_schema.countries(country_code, name) VALUES ('CK','Cook Islands'); -insert into organisations_schema.countries(country_code, name) VALUES ('CL','Chile'); -insert into organisations_schema.countries(country_code, name) VALUES ('CM','Cameroon'); -insert into organisations_schema.countries(country_code, name) VALUES ('CN','China'); -insert into organisations_schema.countries(country_code, name) VALUES ('CO','Colombia'); -insert into organisations_schema.countries(country_code, name) VALUES ('CR','Costa Rica'); -insert into organisations_schema.countries(country_code, name) VALUES ('CU','Cuba'); -insert into organisations_schema.countries(country_code, name) VALUES ('CV','Cape Verde'); -insert into organisations_schema.countries(country_code, name) VALUES ('CX','Christmas Island'); -insert into organisations_schema.countries(country_code, name) VALUES ('CY','Cyprus'); -insert into organisations_schema.countries(country_code, name) VALUES ('CZ','Czech Republic'); -insert into organisations_schema.countries(country_code, name) VALUES ('DE','Germany'); -insert into organisations_schema.countries(country_code, name) VALUES ('DJ','Djibouti'); -insert into organisations_schema.countries(country_code, name) VALUES ('DK','Denmark'); -insert into organisations_schema.countries(country_code, name) VALUES ('DM','Dominica'); -insert into organisations_schema.countries(country_code, name) VALUES ('DO','Dominican Republic'); -insert into organisations_schema.countries(country_code, name) VALUES ('DS','American Samoa'); -insert into organisations_schema.countries(country_code, name) VALUES ('DZ','Algeria'); -insert into organisations_schema.countries(country_code, name) VALUES ('EC','Ecuador'); -insert into organisations_schema.countries(country_code, name) VALUES ('EE','Estonia'); -insert into organisations_schema.countries(country_code, name) VALUES ('EG','Egypt'); -insert into organisations_schema.countries(country_code, name) VALUES ('EH','Western Sahara'); -insert into organisations_schema.countries(country_code, name) VALUES ('ER','Eritrea'); -insert into organisations_schema.countries(country_code, name) VALUES ('ES','Spain'); -insert into organisations_schema.countries(country_code, name) VALUES ('ET','Ethiopia'); -insert into organisations_schema.countries(country_code, name) VALUES ('FI','Finland'); -insert into organisations_schema.countries(country_code, name) VALUES ('FJ','Fiji'); -insert into organisations_schema.countries(country_code, name) VALUES ('FK','Falkland Islands (Malvinas)'); -insert into organisations_schema.countries(country_code, name) VALUES ('FM','Micronesia, Federated States of'); -insert into organisations_schema.countries(country_code, name) VALUES ('FO','Faroe Islands'); -insert into organisations_schema.countries(country_code, name) VALUES ('FR','France'); -insert into organisations_schema.countries(country_code, name) VALUES ('FX','France, Metropolitan'); -insert into organisations_schema.countries(country_code, name) VALUES ('GA','Gabon'); -insert into organisations_schema.countries(country_code, name) VALUES ('GB','United Kingdom'); -insert into organisations_schema.countries(country_code, name) VALUES ('GD','Grenada'); -insert into organisations_schema.countries(country_code, name) VALUES ('GE','Georgia'); -insert into organisations_schema.countries(country_code, name) VALUES ('GF','French Guiana'); -insert into organisations_schema.countries(country_code, name) VALUES ('GH','Ghana'); -insert into organisations_schema.countries(country_code, name) VALUES ('GI','Gibraltar'); -insert into organisations_schema.countries(country_code, name) VALUES ('GL','Greenland'); -insert into organisations_schema.countries(country_code, name) VALUES ('GM','Gambia'); -insert into organisations_schema.countries(country_code, name) VALUES ('GN','Guinea'); -insert into organisations_schema.countries(country_code, name) VALUES ('GP','Guadeloupe'); -insert into organisations_schema.countries(country_code, name) VALUES ('GQ','Equatorial Guinea'); -insert into organisations_schema.countries(country_code, name) VALUES ('GR','Greece'); -insert into organisations_schema.countries(country_code, name) VALUES ('GS','South Georgia South Sandwich Islands'); -insert into organisations_schema.countries(country_code, name) VALUES ('GT','Guatemala'); -insert into organisations_schema.countries(country_code, name) VALUES ('GU','Guam'); -insert into organisations_schema.countries(country_code, name) VALUES ('GW','Guinea-Bissau'); -insert into organisations_schema.countries(country_code, name) VALUES ('GY','Guyana'); -insert into organisations_schema.countries(country_code, name) VALUES ('HK','Hong Kong'); -insert into organisations_schema.countries(country_code, name) VALUES ('HM','Heard and Mc Donald Islands'); -insert into organisations_schema.countries(country_code, name) VALUES ('HN','Honduras'); -insert into organisations_schema.countries(country_code, name) VALUES ('HR','Croatia (Hrvatska)'); -insert into organisations_schema.countries(country_code, name) VALUES ('HT','Haiti'); -insert into organisations_schema.countries(country_code, name) VALUES ('HU','Hungary'); -insert into organisations_schema.countries(country_code, name) VALUES ('ID','Indonesia'); -insert into organisations_schema.countries(country_code, name) VALUES ('IE','Ireland'); -insert into organisations_schema.countries(country_code, name) VALUES ('IL','Israel'); -insert into organisations_schema.countries(country_code, name) VALUES ('IN','India'); -insert into organisations_schema.countries(country_code, name) VALUES ('IO','British lndian Ocean Territory'); -insert into organisations_schema.countries(country_code, name) VALUES ('IQ','Iraq'); -insert into organisations_schema.countries(country_code, name) VALUES ('IR','Iran (Islamic Republic of)'); -insert into organisations_schema.countries(country_code, name) VALUES ('IS','Iceland'); -insert into organisations_schema.countries(country_code, name) VALUES ('IT','Italy'); -insert into organisations_schema.countries(country_code, name) VALUES ('JM','Jamaica'); -insert into organisations_schema.countries(country_code, name) VALUES ('JO','Jordan'); -insert into organisations_schema.countries(country_code, name) VALUES ('JP','Japan'); -insert into organisations_schema.countries(country_code, name) VALUES ('KE','Kenya'); -insert into organisations_schema.countries(country_code, name) VALUES ('KG','Kyrgyzstan'); -insert into organisations_schema.countries(country_code, name) VALUES ('KH','Cambodia'); -insert into organisations_schema.countries(country_code, name) VALUES ('KI','Kiribati'); -insert into organisations_schema.countries(country_code, name) VALUES ('KM','Comoros'); -insert into organisations_schema.countries(country_code, name) VALUES ('KN','Saint Kitts and Nevis'); -insert into organisations_schema.countries(country_code, name) VALUES ('KR','Korea, Republic of'); -insert into organisations_schema.countries(country_code, name) VALUES ('KW','Kuwait'); -insert into organisations_schema.countries(country_code, name) VALUES ('KY','Cayman Islands'); -insert into organisations_schema.countries(country_code, name) VALUES ('KZ','Kazakhstan'); -insert into organisations_schema.countries(country_code, name) VALUES ('LB','Lebanon'); -insert into organisations_schema.countries(country_code, name) VALUES ('LC','Saint Lucia'); -insert into organisations_schema.countries(country_code, name) VALUES ('LI','Liechtenstein'); -insert into organisations_schema.countries(country_code, name) VALUES ('LK','Sri Lanka'); -insert into organisations_schema.countries(country_code, name) VALUES ('LR','Liberia'); -insert into organisations_schema.countries(country_code, name) VALUES ('LS','Lesotho'); -insert into organisations_schema.countries(country_code, name) VALUES ('LT','Lithuania'); -insert into organisations_schema.countries(country_code, name) VALUES ('LU','Luxembourg'); -insert into organisations_schema.countries(country_code, name) VALUES ('LV','Latvia'); -insert into organisations_schema.countries(country_code, name) VALUES ('LY','Libyan Arab Jamahiriya'); -insert into organisations_schema.countries(country_code, name) VALUES ('MA','Morocco'); -insert into organisations_schema.countries(country_code, name) VALUES ('MC','Monaco'); -insert into organisations_schema.countries(country_code, name) VALUES ('MD','Moldova, Republic of'); -insert into organisations_schema.countries(country_code, name) VALUES ('ME','Montenegro'); -insert into organisations_schema.countries(country_code, name) VALUES ('MG','Madagascar'); -insert into organisations_schema.countries(country_code, name) VALUES ('MH','Marshall Islands'); -insert into organisations_schema.countries(country_code, name) VALUES ('MK','Macedonia'); -insert into organisations_schema.countries(country_code, name) VALUES ('ML','Mali'); -insert into organisations_schema.countries(country_code, name) VALUES ('MM','Myanmar'); -insert into organisations_schema.countries(country_code, name) VALUES ('MN','Mongolia'); -insert into organisations_schema.countries(country_code, name) VALUES ('MO','Macau'); -insert into organisations_schema.countries(country_code, name) VALUES ('MP','Northern Mariana Islands'); -insert into organisations_schema.countries(country_code, name) VALUES ('MQ','Martinique'); -insert into organisations_schema.countries(country_code, name) VALUES ('MR','Mauritania'); -insert into organisations_schema.countries(country_code, name) VALUES ('MS','Montserrat'); -insert into organisations_schema.countries(country_code, name) VALUES ('MT','Malta'); -insert into organisations_schema.countries(country_code, name) VALUES ('MU','Mauritius'); -insert into organisations_schema.countries(country_code, name) VALUES ('MV','Maldives'); -insert into organisations_schema.countries(country_code, name) VALUES ('MW','Malawi'); -insert into organisations_schema.countries(country_code, name) VALUES ('MX','Mexico'); -insert into organisations_schema.countries(country_code, name) VALUES ('MY','Malaysia'); -insert into organisations_schema.countries(country_code, name) VALUES ('MZ','Mozambique'); -insert into organisations_schema.countries(country_code, name) VALUES ('NA','Namibia'); -insert into organisations_schema.countries(country_code, name) VALUES ('NC','New Caledonia'); -insert into organisations_schema.countries(country_code, name) VALUES ('NE','Niger'); -insert into organisations_schema.countries(country_code, name) VALUES ('NF','Norfork Island'); -insert into organisations_schema.countries(country_code, name) VALUES ('NG','Nigeria'); -insert into organisations_schema.countries(country_code, name) VALUES ('NI','Nicaragua'); -insert into organisations_schema.countries(country_code, name) VALUES ('NL','Netherlands'); -insert into organisations_schema.countries(country_code, name) VALUES ('NO','Norway'); -insert into organisations_schema.countries(country_code, name) VALUES ('NP','Nepal'); -insert into organisations_schema.countries(country_code, name) VALUES ('NR','Nauru'); -insert into organisations_schema.countries(country_code, name) VALUES ('NU','Niue'); -insert into organisations_schema.countries(country_code, name) VALUES ('NZ','New Zealand'); -insert into organisations_schema.countries(country_code, name) VALUES ('OM','Oman'); -insert into organisations_schema.countries(country_code, name) VALUES ('PA','Panama'); -insert into organisations_schema.countries(country_code, name) VALUES ('PE','Peru'); -insert into organisations_schema.countries(country_code, name) VALUES ('PF','French Polynesia'); -insert into organisations_schema.countries(country_code, name) VALUES ('PG','Papua New Guinea'); -insert into organisations_schema.countries(country_code, name) VALUES ('PH','Philippines'); -insert into organisations_schema.countries(country_code, name) VALUES ('PK','Pakistan'); -insert into organisations_schema.countries(country_code, name) VALUES ('PL','Poland'); -insert into organisations_schema.countries(country_code, name) VALUES ('PM','St. Pierre and Miquelon'); -insert into organisations_schema.countries(country_code, name) VALUES ('PN','Pitcairn'); -insert into organisations_schema.countries(country_code, name) VALUES ('PR','Puerto Rico'); -insert into organisations_schema.countries(country_code, name) VALUES ('PT','Portugal'); -insert into organisations_schema.countries(country_code, name) VALUES ('PW','Palau'); -insert into organisations_schema.countries(country_code, name) VALUES ('PY','Paraguay'); -insert into organisations_schema.countries(country_code, name) VALUES ('QA','Qatar'); -insert into organisations_schema.countries(country_code, name) VALUES ('RE','Reunion'); -insert into organisations_schema.countries(country_code, name) VALUES ('RO','Romania'); -insert into organisations_schema.countries(country_code, name) VALUES ('RS','Serbia'); -insert into organisations_schema.countries(country_code, name) VALUES ('RU','Russian Federation'); -insert into organisations_schema.countries(country_code, name) VALUES ('RW','Rwanda'); -insert into organisations_schema.countries(country_code, name) VALUES ('SA','Saudi Arabia'); -insert into organisations_schema.countries(country_code, name) VALUES ('SB','Solomon Islands'); -insert into organisations_schema.countries(country_code, name) VALUES ('SC','Seychelles'); -insert into organisations_schema.countries(country_code, name) VALUES ('SD','Sudan'); -insert into organisations_schema.countries(country_code, name) VALUES ('SE','Sweden'); -insert into organisations_schema.countries(country_code, name) VALUES ('SG','Singapore'); -insert into organisations_schema.countries(country_code, name) VALUES ('SH','St. Helena'); -insert into organisations_schema.countries(country_code, name) VALUES ('SI','Slovenia'); -insert into organisations_schema.countries(country_code, name) VALUES ('SJ','Svalbarn and Jan Mayen Islands'); -insert into organisations_schema.countries(country_code, name) VALUES ('SK','Slovakia'); -insert into organisations_schema.countries(country_code, name) VALUES ('SL','Sierra Leone'); -insert into organisations_schema.countries(country_code, name) VALUES ('SM','San Marino'); -insert into organisations_schema.countries(country_code, name) VALUES ('SN','Senegal'); -insert into organisations_schema.countries(country_code, name) VALUES ('SO','Somalia'); -insert into organisations_schema.countries(country_code, name) VALUES ('SR','Suriname'); -insert into organisations_schema.countries(country_code, name) VALUES ('ST','Sao Tome and Principe'); -insert into organisations_schema.countries(country_code, name) VALUES ('SV','El Salvador'); -insert into organisations_schema.countries(country_code, name) VALUES ('SY','Syrian Arab Republic'); -insert into organisations_schema.countries(country_code, name) VALUES ('SZ','Swaziland'); -insert into organisations_schema.countries(country_code, name) VALUES ('TC','Turks and Caicos Islands'); -insert into organisations_schema.countries(country_code, name) VALUES ('TD','Chad'); -insert into organisations_schema.countries(country_code, name) VALUES ('TF','French Southern Territories'); -insert into organisations_schema.countries(country_code, name) VALUES ('TG','Togo'); -insert into organisations_schema.countries(country_code, name) VALUES ('TH','Thailand'); -insert into organisations_schema.countries(country_code, name) VALUES ('TJ','Tajikistan'); -insert into organisations_schema.countries(country_code, name) VALUES ('TK','Tokelau'); -insert into organisations_schema.countries(country_code, name) VALUES ('TM','Turkmenistan'); -insert into organisations_schema.countries(country_code, name) VALUES ('TN','Tunisia'); -insert into organisations_schema.countries(country_code, name) VALUES ('TO','Tonga'); -insert into organisations_schema.countries(country_code, name) VALUES ('TP','East Timor'); -insert into organisations_schema.countries(country_code, name) VALUES ('TR','Turkey'); -insert into organisations_schema.countries(country_code, name) VALUES ('TT','Trinidad and Tobago'); -insert into organisations_schema.countries(country_code, name) VALUES ('TV','Tuvalu'); -insert into organisations_schema.countries(country_code, name) VALUES ('TW','Taiwan'); -insert into organisations_schema.countries(country_code, name) VALUES ('TY','Mayotte'); -insert into organisations_schema.countries(country_code, name) VALUES ('TZ','Tanzania, United Republic of'); -insert into organisations_schema.countries(country_code, name) VALUES ('UA','Ukraine'); -insert into organisations_schema.countries(country_code, name) VALUES ('UG','Uganda'); -insert into organisations_schema.countries(country_code, name) VALUES ('UM','United States minor outlying islands'); -insert into organisations_schema.countries(country_code, name) VALUES ('US','United States'); -insert into organisations_schema.countries(country_code, name) VALUES ('UY','Uruguay'); -insert into organisations_schema.countries(country_code, name) VALUES ('UZ','Uzbekistan'); -insert into organisations_schema.countries(country_code, name) VALUES ('VA','Vatican City State'); -insert into organisations_schema.countries(country_code, name) VALUES ('VC','Saint Vincent and the Grenadines'); -insert into organisations_schema.countries(country_code, name) VALUES ('VE','Venezuela'); -insert into organisations_schema.countries(country_code, name) VALUES ('VG','Virgin Islands (British)'); -insert into organisations_schema.countries(country_code, name) VALUES ('VI','Virgin Islands (U.S.)'); -insert into organisations_schema.countries(country_code, name) VALUES ('VN','Vietnam'); -insert into organisations_schema.countries(country_code, name) VALUES ('VU','Vanuatu'); -insert into organisations_schema.countries(country_code, name) VALUES ('WF','Wallis and Futuna Islands'); -insert into organisations_schema.countries(country_code, name) VALUES ('WS','Samoa'); -insert into organisations_schema.countries(country_code, name) VALUES ('XK','Kosovo'); -insert into organisations_schema.countries(country_code, name) VALUES ('YE','Yemen'); -insert into organisations_schema.countries(country_code, name) VALUES ('YU','Yugoslavia'); -insert into organisations_schema.countries(country_code, name) VALUES ('ZA','South Africa'); -insert into organisations_schema.countries(country_code, name) VALUES ('ZM','Zambia'); -insert into organisations_schema.countries(country_code, name) VALUES ('ZR','Zaire'); -insert into organisations_schema.countries(country_code, name) VALUES ('ZW','Zimbabwe'); +insert into organisations_schema.countries(code, name) VALUES ('AD','Andorra'); +insert into organisations_schema.countries(code, name) VALUES ('AE','United Arab Emirates'); +insert into organisations_schema.countries(code, name) VALUES ('AF','Afghanistan'); +insert into organisations_schema.countries(code, name) VALUES ('AG','Antigua and/or Barbuda'); +insert into organisations_schema.countries(code, name) VALUES ('AI','Anguilla'); +insert into organisations_schema.countries(code, name) VALUES ('AL','Albania'); +insert into organisations_schema.countries(code, name) VALUES ('AM','Armenia'); +insert into organisations_schema.countries(code, name) VALUES ('AN','Netherlands Antilles'); +insert into organisations_schema.countries(code, name) VALUES ('AO','Angola'); +insert into organisations_schema.countries(code, name) VALUES ('AQ','Antarctica'); +insert into organisations_schema.countries(code, name) VALUES ('AR','Argentina'); +insert into organisations_schema.countries(code, name) VALUES ('AT','Austria'); +insert into organisations_schema.countries(code, name) VALUES ('AU','Australia'); +insert into organisations_schema.countries(code, name) VALUES ('AW','Aruba'); +insert into organisations_schema.countries(code, name) VALUES ('AZ','Azerbaijan'); +insert into organisations_schema.countries(code, name) VALUES ('BA','Bosnia and Herzegovina'); +insert into organisations_schema.countries(code, name) VALUES ('BB','Barbados'); +insert into organisations_schema.countries(code, name) VALUES ('BD','Bangladesh'); +insert into organisations_schema.countries(code, name) VALUES ('BE','Belgium'); +insert into organisations_schema.countries(code, name) VALUES ('BF','Burkina Faso'); +insert into organisations_schema.countries(code, name) VALUES ('BG','Bulgaria'); +insert into organisations_schema.countries(code, name) VALUES ('BH','Bahrain'); +insert into organisations_schema.countries(code, name) VALUES ('BI','Burundi'); +insert into organisations_schema.countries(code, name) VALUES ('BJ','Benin'); +insert into organisations_schema.countries(code, name) VALUES ('BM','Bermuda'); +insert into organisations_schema.countries(code, name) VALUES ('BN','Brunei Darussalam'); +insert into organisations_schema.countries(code, name) VALUES ('BO','Bolivia'); +insert into organisations_schema.countries(code, name) VALUES ('BR','Brazil'); +insert into organisations_schema.countries(code, name) VALUES ('BS','Bahamas'); +insert into organisations_schema.countries(code, name) VALUES ('BT','Bhutan'); +insert into organisations_schema.countries(code, name) VALUES ('BV','Bouvet Island'); +insert into organisations_schema.countries(code, name) VALUES ('BW','Botswana'); +insert into organisations_schema.countries(code, name) VALUES ('BY','Belarus'); +insert into organisations_schema.countries(code, name) VALUES ('BZ','Belize'); +insert into organisations_schema.countries(code, name) VALUES ('CA','Canada'); +insert into organisations_schema.countries(code, name) VALUES ('CC','Cocos (Keeling) Islands'); +insert into organisations_schema.countries(code, name) VALUES ('CF','Central African Republic'); +insert into organisations_schema.countries(code, name) VALUES ('CG','Congo'); +insert into organisations_schema.countries(code, name) VALUES ('CH','Switzerland'); +insert into organisations_schema.countries(code, name) VALUES ('CI','Ivory Coast'); +insert into organisations_schema.countries(code, name) VALUES ('CK','Cook Islands'); +insert into organisations_schema.countries(code, name) VALUES ('CL','Chile'); +insert into organisations_schema.countries(code, name) VALUES ('CM','Cameroon'); +insert into organisations_schema.countries(code, name) VALUES ('CN','China'); +insert into organisations_schema.countries(code, name) VALUES ('CO','Colombia'); +insert into organisations_schema.countries(code, name) VALUES ('CR','Costa Rica'); +insert into organisations_schema.countries(code, name) VALUES ('CU','Cuba'); +insert into organisations_schema.countries(code, name) VALUES ('CV','Cape Verde'); +insert into organisations_schema.countries(code, name) VALUES ('CX','Christmas Island'); +insert into organisations_schema.countries(code, name) VALUES ('CY','Cyprus'); +insert into organisations_schema.countries(code, name) VALUES ('CZ','Czech Republic'); +insert into organisations_schema.countries(code, name) VALUES ('DE','Germany'); +insert into organisations_schema.countries(code, name) VALUES ('DJ','Djibouti'); +insert into organisations_schema.countries(code, name) VALUES ('DK','Denmark'); +insert into organisations_schema.countries(code, name) VALUES ('DM','Dominica'); +insert into organisations_schema.countries(code, name) VALUES ('DO','Dominican Republic'); +insert into organisations_schema.countries(code, name) VALUES ('DS','American Samoa'); +insert into organisations_schema.countries(code, name) VALUES ('DZ','Algeria'); +insert into organisations_schema.countries(code, name) VALUES ('EC','Ecuador'); +insert into organisations_schema.countries(code, name) VALUES ('EE','Estonia'); +insert into organisations_schema.countries(code, name) VALUES ('EG','Egypt'); +insert into organisations_schema.countries(code, name) VALUES ('EH','Western Sahara'); +insert into organisations_schema.countries(code, name) VALUES ('ER','Eritrea'); +insert into organisations_schema.countries(code, name) VALUES ('ES','Spain'); +insert into organisations_schema.countries(code, name) VALUES ('ET','Ethiopia'); +insert into organisations_schema.countries(code, name) VALUES ('FI','Finland'); +insert into organisations_schema.countries(code, name) VALUES ('FJ','Fiji'); +insert into organisations_schema.countries(code, name) VALUES ('FK','Falkland Islands (Malvinas)'); +insert into organisations_schema.countries(code, name) VALUES ('FM','Micronesia, Federated States of'); +insert into organisations_schema.countries(code, name) VALUES ('FO','Faroe Islands'); +insert into organisations_schema.countries(code, name) VALUES ('FR','France'); +insert into organisations_schema.countries(code, name) VALUES ('FX','France, Metropolitan'); +insert into organisations_schema.countries(code, name) VALUES ('GA','Gabon'); +insert into organisations_schema.countries(code, name) VALUES ('GB','United Kingdom'); +insert into organisations_schema.countries(code, name) VALUES ('GD','Grenada'); +insert into organisations_schema.countries(code, name) VALUES ('GE','Georgia'); +insert into organisations_schema.countries(code, name) VALUES ('GF','French Guiana'); +insert into organisations_schema.countries(code, name) VALUES ('GH','Ghana'); +insert into organisations_schema.countries(code, name) VALUES ('GI','Gibraltar'); +insert into organisations_schema.countries(code, name) VALUES ('GL','Greenland'); +insert into organisations_schema.countries(code, name) VALUES ('GM','Gambia'); +insert into organisations_schema.countries(code, name) VALUES ('GN','Guinea'); +insert into organisations_schema.countries(code, name) VALUES ('GP','Guadeloupe'); +insert into organisations_schema.countries(code, name) VALUES ('GQ','Equatorial Guinea'); +insert into organisations_schema.countries(code, name) VALUES ('GR','Greece'); +insert into organisations_schema.countries(code, name) VALUES ('GS','South Georgia South Sandwich Islands'); +insert into organisations_schema.countries(code, name) VALUES ('GT','Guatemala'); +insert into organisations_schema.countries(code, name) VALUES ('GU','Guam'); +insert into organisations_schema.countries(code, name) VALUES ('GW','Guinea-Bissau'); +insert into organisations_schema.countries(code, name) VALUES ('GY','Guyana'); +insert into organisations_schema.countries(code, name) VALUES ('HK','Hong Kong'); +insert into organisations_schema.countries(code, name) VALUES ('HM','Heard and Mc Donald Islands'); +insert into organisations_schema.countries(code, name) VALUES ('HN','Honduras'); +insert into organisations_schema.countries(code, name) VALUES ('HR','Croatia (Hrvatska)'); +insert into organisations_schema.countries(code, name) VALUES ('HT','Haiti'); +insert into organisations_schema.countries(code, name) VALUES ('HU','Hungary'); +insert into organisations_schema.countries(code, name) VALUES ('ID','Indonesia'); +insert into organisations_schema.countries(code, name) VALUES ('IE','Ireland'); +insert into organisations_schema.countries(code, name) VALUES ('IL','Israel'); +insert into organisations_schema.countries(code, name) VALUES ('IN','India'); +insert into organisations_schema.countries(code, name) VALUES ('IO','British lndian Ocean Territory'); +insert into organisations_schema.countries(code, name) VALUES ('IQ','Iraq'); +insert into organisations_schema.countries(code, name) VALUES ('IR','Iran (Islamic Republic of)'); +insert into organisations_schema.countries(code, name) VALUES ('IS','Iceland'); +insert into organisations_schema.countries(code, name) VALUES ('IT','Italy'); +insert into organisations_schema.countries(code, name) VALUES ('JM','Jamaica'); +insert into organisations_schema.countries(code, name) VALUES ('JO','Jordan'); +insert into organisations_schema.countries(code, name) VALUES ('JP','Japan'); +insert into organisations_schema.countries(code, name) VALUES ('KE','Kenya'); +insert into organisations_schema.countries(code, name) VALUES ('KG','Kyrgyzstan'); +insert into organisations_schema.countries(code, name) VALUES ('KH','Cambodia'); +insert into organisations_schema.countries(code, name) VALUES ('KI','Kiribati'); +insert into organisations_schema.countries(code, name) VALUES ('KM','Comoros'); +insert into organisations_schema.countries(code, name) VALUES ('KN','Saint Kitts and Nevis'); +insert into organisations_schema.countries(code, name) VALUES ('KR','Korea, Republic of'); +insert into organisations_schema.countries(code, name) VALUES ('KW','Kuwait'); +insert into organisations_schema.countries(code, name) VALUES ('KY','Cayman Islands'); +insert into organisations_schema.countries(code, name) VALUES ('KZ','Kazakhstan'); +insert into organisations_schema.countries(code, name) VALUES ('LB','Lebanon'); +insert into organisations_schema.countries(code, name) VALUES ('LC','Saint Lucia'); +insert into organisations_schema.countries(code, name) VALUES ('LI','Liechtenstein'); +insert into organisations_schema.countries(code, name) VALUES ('LK','Sri Lanka'); +insert into organisations_schema.countries(code, name) VALUES ('LR','Liberia'); +insert into organisations_schema.countries(code, name) VALUES ('LS','Lesotho'); +insert into organisations_schema.countries(code, name) VALUES ('LT','Lithuania'); +insert into organisations_schema.countries(code, name) VALUES ('LU','Luxembourg'); +insert into organisations_schema.countries(code, name) VALUES ('LV','Latvia'); +insert into organisations_schema.countries(code, name) VALUES ('LY','Libyan Arab Jamahiriya'); +insert into organisations_schema.countries(code, name) VALUES ('MA','Morocco'); +insert into organisations_schema.countries(code, name) VALUES ('MC','Monaco'); +insert into organisations_schema.countries(code, name) VALUES ('MD','Moldova, Republic of'); +insert into organisations_schema.countries(code, name) VALUES ('ME','Montenegro'); +insert into organisations_schema.countries(code, name) VALUES ('MG','Madagascar'); +insert into organisations_schema.countries(code, name) VALUES ('MH','Marshall Islands'); +insert into organisations_schema.countries(code, name) VALUES ('MK','Macedonia'); +insert into organisations_schema.countries(code, name) VALUES ('ML','Mali'); +insert into organisations_schema.countries(code, name) VALUES ('MM','Myanmar'); +insert into organisations_schema.countries(code, name) VALUES ('MN','Mongolia'); +insert into organisations_schema.countries(code, name) VALUES ('MO','Macau'); +insert into organisations_schema.countries(code, name) VALUES ('MP','Northern Mariana Islands'); +insert into organisations_schema.countries(code, name) VALUES ('MQ','Martinique'); +insert into organisations_schema.countries(code, name) VALUES ('MR','Mauritania'); +insert into organisations_schema.countries(code, name) VALUES ('MS','Montserrat'); +insert into organisations_schema.countries(code, name) VALUES ('MT','Malta'); +insert into organisations_schema.countries(code, name) VALUES ('MU','Mauritius'); +insert into organisations_schema.countries(code, name) VALUES ('MV','Maldives'); +insert into organisations_schema.countries(code, name) VALUES ('MW','Malawi'); +insert into organisations_schema.countries(code, name) VALUES ('MX','Mexico'); +insert into organisations_schema.countries(code, name) VALUES ('MY','Malaysia'); +insert into organisations_schema.countries(code, name) VALUES ('MZ','Mozambique'); +insert into organisations_schema.countries(code, name) VALUES ('NA','Namibia'); +insert into organisations_schema.countries(code, name) VALUES ('NC','New Caledonia'); +insert into organisations_schema.countries(code, name) VALUES ('NE','Niger'); +insert into organisations_schema.countries(code, name) VALUES ('NF','Norfork Island'); +insert into organisations_schema.countries(code, name) VALUES ('NG','Nigeria'); +insert into organisations_schema.countries(code, name) VALUES ('NI','Nicaragua'); +insert into organisations_schema.countries(code, name) VALUES ('NL','Netherlands'); +insert into organisations_schema.countries(code, name) VALUES ('NO','Norway'); +insert into organisations_schema.countries(code, name) VALUES ('NP','Nepal'); +insert into organisations_schema.countries(code, name) VALUES ('NR','Nauru'); +insert into organisations_schema.countries(code, name) VALUES ('NU','Niue'); +insert into organisations_schema.countries(code, name) VALUES ('NZ','New Zealand'); +insert into organisations_schema.countries(code, name) VALUES ('OM','Oman'); +insert into organisations_schema.countries(code, name) VALUES ('PA','Panama'); +insert into organisations_schema.countries(code, name) VALUES ('PE','Peru'); +insert into organisations_schema.countries(code, name) VALUES ('PF','French Polynesia'); +insert into organisations_schema.countries(code, name) VALUES ('PG','Papua New Guinea'); +insert into organisations_schema.countries(code, name) VALUES ('PH','Philippines'); +insert into organisations_schema.countries(code, name) VALUES ('PK','Pakistan'); +insert into organisations_schema.countries(code, name) VALUES ('PL','Poland'); +insert into organisations_schema.countries(code, name) VALUES ('PM','St. Pierre and Miquelon'); +insert into organisations_schema.countries(code, name) VALUES ('PN','Pitcairn'); +insert into organisations_schema.countries(code, name) VALUES ('PR','Puerto Rico'); +insert into organisations_schema.countries(code, name) VALUES ('PT','Portugal'); +insert into organisations_schema.countries(code, name) VALUES ('PW','Palau'); +insert into organisations_schema.countries(code, name) VALUES ('PY','Paraguay'); +insert into organisations_schema.countries(code, name) VALUES ('QA','Qatar'); +insert into organisations_schema.countries(code, name) VALUES ('RE','Reunion'); +insert into organisations_schema.countries(code, name) VALUES ('RO','Romania'); +insert into organisations_schema.countries(code, name) VALUES ('RS','Serbia'); +insert into organisations_schema.countries(code, name) VALUES ('RU','Russian Federation'); +insert into organisations_schema.countries(code, name) VALUES ('RW','Rwanda'); +insert into organisations_schema.countries(code, name) VALUES ('SA','Saudi Arabia'); +insert into organisations_schema.countries(code, name) VALUES ('SB','Solomon Islands'); +insert into organisations_schema.countries(code, name) VALUES ('SC','Seychelles'); +insert into organisations_schema.countries(code, name) VALUES ('SD','Sudan'); +insert into organisations_schema.countries(code, name) VALUES ('SE','Sweden'); +insert into organisations_schema.countries(code, name) VALUES ('SG','Singapore'); +insert into organisations_schema.countries(code, name) VALUES ('SH','St. Helena'); +insert into organisations_schema.countries(code, name) VALUES ('SI','Slovenia'); +insert into organisations_schema.countries(code, name) VALUES ('SJ','Svalbarn and Jan Mayen Islands'); +insert into organisations_schema.countries(code, name) VALUES ('SK','Slovakia'); +insert into organisations_schema.countries(code, name) VALUES ('SL','Sierra Leone'); +insert into organisations_schema.countries(code, name) VALUES ('SM','San Marino'); +insert into organisations_schema.countries(code, name) VALUES ('SN','Senegal'); +insert into organisations_schema.countries(code, name) VALUES ('SO','Somalia'); +insert into organisations_schema.countries(code, name) VALUES ('SR','Suriname'); +insert into organisations_schema.countries(code, name) VALUES ('ST','Sao Tome and Principe'); +insert into organisations_schema.countries(code, name) VALUES ('SV','El Salvador'); +insert into organisations_schema.countries(code, name) VALUES ('SY','Syrian Arab Republic'); +insert into organisations_schema.countries(code, name) VALUES ('SZ','Swaziland'); +insert into organisations_schema.countries(code, name) VALUES ('TC','Turks and Caicos Islands'); +insert into organisations_schema.countries(code, name) VALUES ('TD','Chad'); +insert into organisations_schema.countries(code, name) VALUES ('TF','French Southern Territories'); +insert into organisations_schema.countries(code, name) VALUES ('TG','Togo'); +insert into organisations_schema.countries(code, name) VALUES ('TH','Thailand'); +insert into organisations_schema.countries(code, name) VALUES ('TJ','Tajikistan'); +insert into organisations_schema.countries(code, name) VALUES ('TK','Tokelau'); +insert into organisations_schema.countries(code, name) VALUES ('TM','Turkmenistan'); +insert into organisations_schema.countries(code, name) VALUES ('TN','Tunisia'); +insert into organisations_schema.countries(code, name) VALUES ('TO','Tonga'); +insert into organisations_schema.countries(code, name) VALUES ('TP','East Timor'); +insert into organisations_schema.countries(code, name) VALUES ('TR','Turkey'); +insert into organisations_schema.countries(code, name) VALUES ('TT','Trinidad and Tobago'); +insert into organisations_schema.countries(code, name) VALUES ('TV','Tuvalu'); +insert into organisations_schema.countries(code, name) VALUES ('TW','Taiwan'); +insert into organisations_schema.countries(code, name) VALUES ('TY','Mayotte'); +insert into organisations_schema.countries(code, name) VALUES ('TZ','Tanzania, United Republic of'); +insert into organisations_schema.countries(code, name) VALUES ('UA','Ukraine'); +insert into organisations_schema.countries(code, name) VALUES ('UG','Uganda'); +insert into organisations_schema.countries(code, name) VALUES ('UM','United States minor outlying islands'); +insert into organisations_schema.countries(code, name) VALUES ('US','United States'); +insert into organisations_schema.countries(code, name) VALUES ('UY','Uruguay'); +insert into organisations_schema.countries(code, name) VALUES ('UZ','Uzbekistan'); +insert into organisations_schema.countries(code, name) VALUES ('VA','Vatican City State'); +insert into organisations_schema.countries(code, name) VALUES ('VC','Saint Vincent and the Grenadines'); +insert into organisations_schema.countries(code, name) VALUES ('VE','Venezuela'); +insert into organisations_schema.countries(code, name) VALUES ('VG','Virgin Islands (British)'); +insert into organisations_schema.countries(code, name) VALUES ('VI','Virgin Islands (U.S.)'); +insert into organisations_schema.countries(code, name) VALUES ('VN','Vietnam'); +insert into organisations_schema.countries(code, name) VALUES ('VU','Vanuatu'); +insert into organisations_schema.countries(code, name) VALUES ('WF','Wallis and Futuna Islands'); +insert into organisations_schema.countries(code, name) VALUES ('WS','Samoa'); +insert into organisations_schema.countries(code, name) VALUES ('XK','Kosovo'); +insert into organisations_schema.countries(code, name) VALUES ('YE','Yemen'); +insert into organisations_schema.countries(code, name) VALUES ('YU','Yugoslavia'); +insert into organisations_schema.countries(code, name) VALUES ('ZA','South Africa'); +insert into organisations_schema.countries(code, name) VALUES ('ZM','Zambia'); +insert into organisations_schema.countries(code, name) VALUES ('ZR','Zaire'); +insert into organisations_schema.countries(code, name) VALUES ('ZW','Zimbabwe'); diff --git a/src/main/resources/db/migration/V9__add_address_table.sql b/src/main/resources/db/migration/V9__add_address_table.sql new file mode 100644 index 0000000..2dd8913 --- /dev/null +++ b/src/main/resources/db/migration/V9__add_address_table.sql @@ -0,0 +1,9 @@ + +CREATE TABLE IF NOT EXISTS organisations_schema.addresses +( + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, + street VARCHAR(50) NOT NULL, + number VARCHAR(20) NOT NULL, + postal_code VARCHAR(20) NOT NULL, + city_id UUID NOT NULL references organisations_schema.cities(id) +); diff --git a/src/test/kotlin/io/billie/functional/CanStoreAndReadOrganisationTest.kt b/src/test/kotlin/io/billie/functional/CanStoreAndReadOrganisationTest.kt deleted file mode 100644 index 2d57630..0000000 --- a/src/test/kotlin/io/billie/functional/CanStoreAndReadOrganisationTest.kt +++ /dev/null @@ -1,146 +0,0 @@ -package io.billie.functional - -import com.fasterxml.jackson.databind.ObjectMapper -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.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.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT -import org.springframework.boot.test.web.server.LocalServerPort -import org.springframework.http.MediaType.APPLICATION_JSON -import org.springframework.jdbc.core.JdbcTemplate -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post -import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import java.util.* - - -@AutoConfigureMockMvc -@SpringBootTest(webEnvironment = DEFINED_PORT) -class CanStoreAndReadOrganisationTest { - - @LocalServerPort - private val port = 8080 - - @Autowired - private lateinit var mockMvc: MockMvc - - @Autowired - private lateinit var mapper: ObjectMapper - - @Autowired - private lateinit var template: JdbcTemplate - - @Test - fun orgs() { - mockMvc.perform( - get("/organisations") - .contentType(APPLICATION_JSON) - ) - .andExpect(status().isOk()) - } - - @Test - fun cannotStoreOrgWhenNameIsBlank() { - mockMvc.perform( - post("/organisations").contentType(APPLICATION_JSON).content(orgRequestJsonNameBlank()) - ) - .andExpect(status().isBadRequest) - } - - @Test - fun cannotStoreOrgWhenNameIsMissing() { - mockMvc.perform( - post("/organisations").contentType(APPLICATION_JSON).content(orgRequestJsonNoName()) - ) - .andExpect(status().isBadRequest) - } - - @Test - fun cannotStoreOrgWhenCountryCodeIsMissing() { - mockMvc.perform( - post("/organisations").contentType(APPLICATION_JSON).content(orgRequestJsonNoCountryCode()) - ) - .andExpect(status().isBadRequest) - } - - @Test - fun cannotStoreOrgWhenCountryCodeIsBlank() { - mockMvc.perform( - post("/organisations").contentType(APPLICATION_JSON).content(orgRequestJsonCountryCodeBlank()) - ) - .andExpect(status().isBadRequest) - } - - @Test - fun cannotStoreOrgWhenCountryCodeIsNotRecognised() { - mockMvc.perform( - post("/organisations").contentType(APPLICATION_JSON).content(orgRequestJsonCountryCodeIncorrect()) - ) - .andExpect(status().isBadRequest) - } - - @Test - fun cannotStoreOrgWhenNoLegalEntityType() { - mockMvc.perform( - post("/organisations").contentType(APPLICATION_JSON).content(orgRequestJsonNoLegalEntityType()) - ) - .andExpect(status().isBadRequest) - } - - @Test - fun cannotStoreOrgWhenNoContactDetails() { - mockMvc.perform( - post("/organisations").contentType(APPLICATION_JSON).content(orgRequestJsonNoContactDetails()) - ) - .andExpect(status().isBadRequest) - } - - @Test - fun canStoreOrg() { - val result = mockMvc.perform( - post("/organisations").contentType(APPLICATION_JSON).content(orgRequestJson()) - ) - .andExpect(status().isOk) - .andReturn() - - val response = mapper.readValue(result.response.contentAsString, Entity::class.java) - - val org: Map = orgFromDatabase(response.id) - assertDataMatches(org, bbcFixture(response.id)) - - val contactDetailsId: UUID = UUID.fromString(org["contact_details_id"] as String) - val contactDetails: Map = contactDetailsFromDatabase(contactDetailsId) - assertDataMatches(contactDetails, bbcContactFixture(contactDetailsId)) - } - - fun assertDataMatches(reply: Map, assertions: Map) { - for (key in assertions.keys) { - assertThat(reply[key], equalTo(assertions[key])) - } - } - - private fun queryEntityFromDatabase(sql: String, id: UUID): MutableMap = - template.queryForMap(sql, id) - - private fun orgFromDatabase(id: UUID): MutableMap = - queryEntityFromDatabase("select * from organisations_schema.organisations where id = ?", id) - - private fun contactDetailsFromDatabase(id: UUID): MutableMap = - queryEntityFromDatabase("select * from organisations_schema.contact_details where id = ?", id) - -} diff --git a/src/test/kotlin/io/billie/functional/data/Fixtures.kt b/src/test/kotlin/io/billie/functional/data/Fixtures.kt deleted file mode 100644 index 9954801..0000000 --- a/src/test/kotlin/io/billie/functional/data/Fixtures.kt +++ /dev/null @@ -1,152 +0,0 @@ -package io.billie.functional.data - -import java.text.SimpleDateFormat -import java.util.* -import kotlin.collections.HashMap - -object Fixtures { - - fun orgRequestJsonNameBlank(): String { - return "{\n" + - " \"name\": \"\",\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 orgRequestJsonNoName(): String { - return "{\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 orgRequestJsonNoLegalEntityType(): String { - return "{\n" + - " \"name\": \"BBC\",\n" + - " \"date_founded\": \"18/10/1922\",\n" + - " \"country_code\": \"GB\",\n" + - " \"vat_number\": \"333289454\",\n" + - " \"registration_number\": \"3686147\",\n" + - " \"contact_details\": {\n" + - " \"phone_number\": \"+443700100222\",\n" + - " \"fax\": \"\",\n" + - " \"email\": \"yourquestions@bbc.co.uk\"\n" + - " }\n" + - "}" - } - - fun orgRequestJsonNoContactDetails(): 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" + - "}" - } - - fun orgRequestJson(): 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 orgRequestJsonCountryCodeBlank(): String { - return "{\n" + - " \"name\": \"BBC\",\n" + - " \"date_founded\": \"18/10/1922\",\n" + - " \"country_code\": \"\",\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 orgRequestJsonNoCountryCode(): String { - return "{\n" + - " \"name\": \"BBC\",\n" + - " \"date_founded\": \"18/10/1922\",\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 orgRequestJsonCountryCodeIncorrect(): String { - return "{\n" + - " \"name\": \"BBC\",\n" + - " \"date_founded\": \"18/10/1922\",\n" + - " \"country_code\": \"XX\",\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 bbcFixture(id: UUID): Map { - val data = HashMap() - data["id"] = id - data["name"] = "BBC" - data["date_founded"] = SimpleDateFormat("yyyy-MM-dd").parse("1922-10-18") - data["country_code"] = "GB" - data["vat_number"] = "333289454" - data["registration_number"] = "3686147" - data["legal_entity_type"] = "NONPROFIT_ORGANIZATION" - return data - } - - fun bbcContactFixture(id: UUID): Map { - val data = HashMap() - data["id"] = id - data["phone_number"] = "+443700100222" - data["fax"] = "" - data["email"] = "yourquestions@bbc.co.uk" - return data - } - - - -} diff --git a/src/test/kotlin/io/billie/functional/CanReadLocationsTest.kt b/src/test/kotlin/io/billie/organization_management/CanReadLocationsTest.kt similarity index 62% rename from src/test/kotlin/io/billie/functional/CanReadLocationsTest.kt rename to src/test/kotlin/io/billie/organization_management/CanReadLocationsTest.kt index 91782d7..34ad043 100644 --- a/src/test/kotlin/io/billie/functional/CanReadLocationsTest.kt +++ b/src/test/kotlin/io/billie/organization_management/CanReadLocationsTest.kt @@ -1,45 +1,60 @@ -package io.billie.functional +package io.billie.organization_management -import io.billie.functional.matcher.IsUUID.isUuid -import org.hamcrest.Description -import org.hamcrest.TypeSafeMatcher +import io.billie.organization_management.matcher.IsUUID.isUuid import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc import org.springframework.boot.test.context.SpringBootTest -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT -import org.springframework.boot.test.web.server.LocalServerPort +import org.springframework.boot.testcontainers.service.connection.ServiceConnection import org.springframework.http.MediaType.APPLICATION_JSON import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import java.util.* - +import org.testcontainers.containers.PostgreSQLContainer +import org.testcontainers.junit.jupiter.Container +import org.testcontainers.junit.jupiter.Testcontainers @AutoConfigureMockMvc -@SpringBootTest(webEnvironment = DEFINED_PORT) +@SpringBootTest +@Testcontainers class CanReadLocationsTest { - @LocalServerPort - private val port = 8080 + companion object { + + @Container + @ServiceConnection + val postgreSQLContainer = PostgreSQLContainer("postgres:latest") + } @Autowired private lateinit var mockMvc: MockMvc @Test - fun notFoundForUnknownCountry() { + fun `should return 404 when country with given code is not available`() { mockMvc.perform( get("/countries/xx/cities") - .contentType(APPLICATION_JSON) + .contentType(APPLICATION_JSON), ).andExpect(status().isNotFound) } @Test - fun canViewZWCities() { + fun `should return 200 when Kerman city of Iran(ir) is requested`() { + mockMvc.perform( + get("/countries/IR/cities/Kerman") + .contentType(APPLICATION_JSON), + ) + .andExpect(status().isOk) + .andExpect(jsonPath("$.name").value("Kerman")) + .andExpect(jsonPath("$.id").value(isUuid())) + .andExpect(jsonPath("$.country_code").value("IR")) + } + + @Test + fun `should return 200 when cities of Zimbabwe(zw) are requested`() { mockMvc.perform( get("/countries/zw/cities") - .contentType(APPLICATION_JSON) + .contentType(APPLICATION_JSON), ) .andExpect(status().isOk) .andExpect(jsonPath("$.[0].name").value("Harare")) @@ -51,10 +66,10 @@ class CanReadLocationsTest { } @Test - fun canViewBECities() { + fun `should return 200 when cities of Belgium(be) are requested`() { mockMvc.perform( get("/countries/be/cities") - .contentType(APPLICATION_JSON) + .contentType(APPLICATION_JSON), ) .andExpect(status().isOk) .andExpect(jsonPath("$.[0].name").value("Brussels")) @@ -67,10 +82,10 @@ class CanReadLocationsTest { } @Test - fun canViewCountries() { + fun `should return 200 when all countries are requested`() { mockMvc.perform( get("/countries") - .contentType(APPLICATION_JSON) + .contentType(APPLICATION_JSON), ) .andExpect(status().isOk) .andExpect(jsonPath("$.[0].name").value("Andorra")) @@ -80,5 +95,4 @@ class CanReadLocationsTest { .andExpect(jsonPath("$.[239].id").value(isUuid())) .andExpect(jsonPath("$.[239].country_code").value("ZW")) } - } diff --git a/src/test/kotlin/io/billie/organization_management/CanStoreAndReadOrganisationTest.kt b/src/test/kotlin/io/billie/organization_management/CanStoreAndReadOrganisationTest.kt new file mode 100644 index 0000000..d737c32 --- /dev/null +++ b/src/test/kotlin/io/billie/organization_management/CanStoreAndReadOrganisationTest.kt @@ -0,0 +1,261 @@ +package io.billie.organization_management + +import assertk.assertAll +import assertk.assertThat +import assertk.assertions.isEmpty +import assertk.assertions.isEqualTo +import assertk.assertions.isNotNull +import com.fasterxml.jackson.databind.ObjectMapper +import io.billie.organization_management.countries.model.CityResponse +import io.billie.organization_management.data.Fixtures.addressRequestJson +import io.billie.organization_management.data.Fixtures.orgRequestJson +import io.billie.organization_management.data.Fixtures.orgRequestJsonCountryCodeBlank +import io.billie.organization_management.data.Fixtures.orgRequestJsonCountryCodeIncorrect +import io.billie.organization_management.data.Fixtures.orgRequestJsonNameBlank +import io.billie.organization_management.data.Fixtures.orgRequestJsonNoContactDetails +import io.billie.organization_management.data.Fixtures.orgRequestJsonNoCountryCode +import io.billie.organization_management.data.Fixtures.orgRequestJsonNoLegalEntityType +import io.billie.organization_management.data.Fixtures.orgRequestJsonNoName +import io.billie.organization_management.organisations.data.AddressRepository +import io.billie.organization_management.organisations.data.OrganisationRepository +import io.billie.organization_management.organisations.model.AddressRequest +import io.billie.organization_management.organisations.model.Entity +import io.billie.organization_management.organisations.model.OrganisationRequest +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.testcontainers.service.connection.ServiceConnection +import org.springframework.http.MediaType.APPLICATION_JSON +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.testcontainers.containers.PostgreSQLContainer +import org.testcontainers.junit.jupiter.Container +import org.testcontainers.junit.jupiter.Testcontainers +import java.util.UUID + +@AutoConfigureMockMvc +@SpringBootTest +@Testcontainers +class CanStoreAndReadOrganisationTest { + + companion object { + + @Container + @ServiceConnection + val postgreSQLContainer = PostgreSQLContainer("postgres:latest") + } + + @Autowired + private lateinit var mockMvc: MockMvc + + @Autowired + private lateinit var objectMapper: ObjectMapper + + @Autowired + private lateinit var organisationRepository: OrganisationRepository + + @Autowired + private lateinit var addressRepository: AddressRepository + + @Test + fun `should return 200 when getting all organisations`() { + mockMvc.perform( + get("/organisations") + .contentType(APPLICATION_JSON), + ) + .andExpect(status().isOk()) + } + + @Test + fun `should return 400 when name field of request body is blank`() { + mockMvc.perform( + post("/organisations").contentType(APPLICATION_JSON).content(orgRequestJsonNameBlank()), + ) + .andExpect(status().isBadRequest) + } + + @Test + fun `should return 400 when name field of request body is missing`() { + mockMvc.perform( + post("/organisations").contentType(APPLICATION_JSON).content(orgRequestJsonNoName()), + ) + .andExpect(status().isBadRequest) + } + + @Test + fun `should return 400 when country_code field of request body is missing`() { + mockMvc.perform( + post("/organisations").contentType(APPLICATION_JSON).content(orgRequestJsonNoCountryCode()), + ) + .andExpect(status().isBadRequest) + } + + @Test + fun `should return 400 when country_code field of request body is blank`() { + mockMvc.perform( + post("/organisations").contentType(APPLICATION_JSON).content(orgRequestJsonCountryCodeBlank()), + ) + .andExpect(status().isBadRequest) + } + + @Test + fun `should return 400 when country_code field of request body is unrecognizable`() { + mockMvc.perform( + post("/organisations").contentType(APPLICATION_JSON).content(orgRequestJsonCountryCodeIncorrect()), + ) + .andExpect(status().isBadRequest) + } + + @Test + fun `should return 400 when legal_entity_type field of request body is missing`() { + mockMvc.perform( + post("/organisations").contentType(APPLICATION_JSON).content(orgRequestJsonNoLegalEntityType()), + ) + .andExpect(status().isBadRequest) + } + + @Test + fun `should return 400 when contact_detail field of request body is missing`() { + mockMvc.perform( + post("/organisations").contentType(APPLICATION_JSON).content(orgRequestJsonNoContactDetails()), + ) + .andExpect(status().isBadRequest) + } + + @Test + fun `should return 200 when request body is well-defined`() { + val orgRequestJson = orgRequestJson() + val result = mockMvc.perform( + post("/organisations").contentType(APPLICATION_JSON).content(orgRequestJson), + ) + .andExpect(status().isOk) + .andReturn() + + val response = objectMapper.readValue(result.response.contentAsString, Entity::class.java) + val organisation = organisationRepository.findById(response.id).get() + val organisationRequest = objectMapper.readValue(orgRequestJson, OrganisationRequest::class.java) + + assertAll { + assertThat(organisation::id).isNotNull() + assertThat(organisation.id!!::class.java).isEqualTo(UUID::class.java) + assertThat(organisation::name).isEqualTo(organisationRequest.name) + assertThat(organisation::dateFounded).isEqualTo(organisationRequest.dateFounded) + assertThat(organisation.country::code).isEqualTo(organisationRequest.countryCode) + assertThat(organisation::vatNumber).isEqualTo(organisationRequest.vatNumber) + assertThat(organisation::legalEntityType).isEqualTo(organisationRequest.legalEntityType) + assertThat(organisation.contactDetail::id).isNotNull() + assertThat(organisation.contactDetail.id!!::class.java).isEqualTo(UUID::class.java) + assertThat(organisation.contactDetail::phoneNumber).isEqualTo(organisationRequest.contactDetail.phoneNumber) + assertThat(organisation.contactDetail::email).isEqualTo(organisationRequest.contactDetail.email) + assertThat(organisation.contactDetail::fax).isEqualTo(organisationRequest.contactDetail.fax) + } + } + + @Test + fun `should return 400 when organisationId is not available`() { + val addressRequestJson = addressRequestJson() + mockMvc.perform( + post("/organisations/${UUID.randomUUID()}/addresses").contentType(APPLICATION_JSON).content(addressRequestJson), + ) + .andExpect(status().isBadRequest) + } + + @Test + fun `should return 400 when city_id field of request body is unrecognizable`() { + val orgRequestJson = orgRequestJson() + val organisationCreationResult = mockMvc.perform( + post("/organisations").contentType(APPLICATION_JSON).content(orgRequestJson), + ) + .andReturn() + + val response = objectMapper.readValue(organisationCreationResult.response.contentAsString, Entity::class.java) + val addressRequestJson = addressRequestJson() + mockMvc.perform( + post("/organisations/${response.id}/addresses").contentType(APPLICATION_JSON).content(addressRequestJson), + ) + .andExpect(status().isBadRequest) + .andReturn() + } + + @Test + fun `should return 200 when request body for address is well-defined`() { + val orgRequestJson = orgRequestJson() + val organisationCreationResult = mockMvc.perform( + post("/organisations").contentType(APPLICATION_JSON).content(orgRequestJson), + ) + .andReturn() + val organisationCreationResponse = objectMapper.readValue(organisationCreationResult.response.contentAsString, Entity::class.java) + + val kermanCityOfIranResult = mockMvc.perform( + get("/countries/IR/cities/Kerman").contentType(APPLICATION_JSON), + ) + .andReturn() + val kermanCity = objectMapper.readValue(kermanCityOfIranResult.response.contentAsString, CityResponse::class.java) + + val addressRequestJson = addressRequestJson().replace("363a1c53-646a-4bc2-997b-9ce29c2d2f29", "${kermanCity.id}") + mockMvc.perform( + post("/organisations/${organisationCreationResponse.id}/addresses").contentType(APPLICATION_JSON).content(addressRequestJson), + ) + .andExpect(status().isOk) + .andReturn() + + val address = organisationRepository.findById(organisationCreationResponse.id).get().address!! + val addressRequest = objectMapper.readValue(addressRequestJson, AddressRequest::class.java) + + assertAll { + assertThat(address::id).isNotNull() + assertThat(address.id!!::class.java).isEqualTo(UUID::class.java) + assertThat(address::street).isEqualTo(addressRequest.street) + assertThat(address::number).isEqualTo(addressRequest.number) + assertThat(address::postalCode).isEqualTo(addressRequest.postalCode) + assertThat(address.city::id).isEqualTo(addressRequest.cityId) + } + } + + @Test + fun `should discard previous address and assign new one to the organisation`() { + val orgRequestJson = orgRequestJson() + val organisationCreationResult = mockMvc.perform( + post("/organisations").contentType(APPLICATION_JSON).content(orgRequestJson), + ) + .andReturn() + val organisationCreationResponse = objectMapper.readValue(organisationCreationResult.response.contentAsString, Entity::class.java) + + val kermanCityOfIranResult = mockMvc.perform( + get("/countries/IR/cities/Kerman").contentType(APPLICATION_JSON), + ) + .andReturn() + val kermanCity = objectMapper.readValue(kermanCityOfIranResult.response.contentAsString, CityResponse::class.java) + + val addressRequestJson = addressRequestJson().replace("363a1c53-646a-4bc2-997b-9ce29c2d2f29", "${kermanCity.id}") + mockMvc.perform( + post("/organisations/${organisationCreationResponse.id}/addresses").contentType(APPLICATION_JSON).content(addressRequestJson), + ) + .andExpect(status().isOk) + .andReturn() + + val oldAddressId = organisationRepository.findById(organisationCreationResponse.id).get().address!!.id!! + mockMvc.perform( + post("/organisations/${organisationCreationResponse.id}/addresses").contentType(APPLICATION_JSON).content(addressRequestJson), + ) + .andExpect(status().isOk) + .andReturn() + + val address = organisationRepository.findById(organisationCreationResponse.id).get().address!! + val addressRequest = objectMapper.readValue(addressRequestJson, AddressRequest::class.java) + val oldAddress = addressRepository.findById(oldAddressId) + + assertAll { + assertThat(oldAddress).isEmpty() + assertThat(address::id).isNotNull() + assertThat(address.id!!::class.java).isEqualTo(UUID::class.java) + assertThat(address::street).isEqualTo(addressRequest.street) + assertThat(address::number).isEqualTo(addressRequest.number) + assertThat(address::postalCode).isEqualTo(addressRequest.postalCode) + assertThat(address.city::id).isEqualTo(addressRequest.cityId) + } + } +} diff --git a/src/test/kotlin/io/billie/organization_management/data/Fixtures.kt b/src/test/kotlin/io/billie/organization_management/data/Fixtures.kt new file mode 100644 index 0000000..d2be304 --- /dev/null +++ b/src/test/kotlin/io/billie/organization_management/data/Fixtures.kt @@ -0,0 +1,133 @@ +package io.billie.organization_management.data + +object Fixtures { + + fun orgRequestJsonNameBlank(): String { + return """{ + "name": "", + "date_founded": "18/10/1922", + "country_code": "GB", + "vat_number": "333289454", + "registration_number": "3686147", + "legal_entity_type": "NONPROFIT_ORGANIZATION", + "contact_detail": { + "phone_number": "+443700100222", + "fax": "", + "email": "yourquestions@bbc.co.uk" + } + }""" + } + + fun orgRequestJsonNoName(): String { + return """{ + "date_founded": "18/10/1922", + "country_code": "GB", + "vat_number": "333289454", + "registration_number": "3686147", + "legal_entity_type": "NONPROFIT_ORGANIZATION", + "contact_detail": { + "phone_number": "+443700100222", + "fax": "", + "email": "yourquestions@bbc.co.uk" + } + }""" + } + + fun orgRequestJsonNoLegalEntityType(): String { + return """{ + "name": "BBC", + "date_founded": "18/10/1922", + "country_code": "GB", + "vat_number": "333289454", + "registration_number": "3686147", + "contact_detail": { + "phone_number": "+443700100222", + "fax": "", + "email": "yourquestions@bbc.co.uk" + } + }""" + } + + fun orgRequestJsonNoContactDetails(): String { + return """{ + "name": "", + "date_founded": "18/10/1922", + "country_code": "GB", + "vat_number": "333289454", + "registration_number": "3686147", + "legal_entity_type": "NONPROFIT_ORGANIZATION" + }""" + } + + fun orgRequestJson(): String { + return """{ + "name": "BBC", + "date_founded": "18/10/1922", + "country_code": "GB", + "vat_number": "333289454", + "registration_number": "3686147", + "legal_entity_type": "NONPROFIT_ORGANIZATION", + "contact_detail": { + "phone_number": "+443700100222", + "fax": "", + "email": "yourquestions@bbc.co.uk" + } + }""" + } + + fun orgRequestJsonCountryCodeBlank(): String { + return """{ + "name": "BBC", + "date_founded": "18/10/1922", + "country_code": "", + "vat_number": "333289454", + "registration_number": "3686147", + "legal_entity_type": "NONPROFIT_ORGANIZATION", + "contact_detail": { + "phone_number": "+443700100222", + "fax": "", + "email": "yourquestions@bbc.co.uk" + } + }""" + } + + fun orgRequestJsonNoCountryCode(): String { + return """{ + "name": "BBC", + "date_founded": "18/10/1922", + "vat_number": "333289454", + "registration_number": "3686147", + "legal_entity_type": "NONPROFIT_ORGANIZATION", + "contact_detail": { + "phone_number": "+443700100222", + "fax": "", + "email": "yourquestions@bbc.co.uk" + } + }""" + } + + fun orgRequestJsonCountryCodeIncorrect(): String { + return """{ + "name": "BBC", + "date_founded": "18/10/1922", + "country_code": "XX", + "vat_number": "333289454", + "registration_number": "3686147", + "legal_entity_type": "NONPROFIT_ORGANIZATION", + "contact_detail": { + "phone_number": "+443700100222", + "fax": "", + "email": "yourquestions@bbc.co.uk" + } + }""" + } + + fun addressRequestJson(): String { + return """{ + "city_id": "363a1c53-646a-4bc2-997b-9ce29c2d2f29", + "street": "Hafez", + "number": "111-113", + "postal_code": "14560" + }""" + } +} diff --git a/src/test/kotlin/io/billie/functional/matcher/IsUUID.kt b/src/test/kotlin/io/billie/organization_management/matcher/IsUUID.kt similarity index 92% rename from src/test/kotlin/io/billie/functional/matcher/IsUUID.kt rename to src/test/kotlin/io/billie/organization_management/matcher/IsUUID.kt index eb031fe..bea44db 100644 --- a/src/test/kotlin/io/billie/functional/matcher/IsUUID.kt +++ b/src/test/kotlin/io/billie/organization_management/matcher/IsUUID.kt @@ -1,4 +1,4 @@ -package io.billie.functional.matcher +package io.billie.organization_management.matcher import org.hamcrest.Description import org.hamcrest.TypeSafeMatcher