Esperamos que el código que usted va a crear sea considerado por usted como "Production Ready"; por favor use las buenas prácticas que usted acostumbra aplicar en su rutina de desarrollo de código.
Nota: Para el desarrollo del desafío usted puede utilizar el lenguaje de programación de su preferencia siempre y cuando sea un lenguaje "moderno" y de tipado estático.
Dentro de los criterios que vamos a tener en cuenta a la hora de revisar su código, revisaremos:
- Resuelve el problema propuesto
- Organización y estructura del proyecto
- Mantenibilidad
- Facilidad para hacer tests
- Valoraremos adicionalmente si usa alguna arquitectura limpia.
En Aptuno trabajamos con propriedades de propietarios que están arrendando su inmueble con nosotros. Esas propiedades están localizadas en una o más regiones de la ciudad; el objetivo de este desafío es realizar una aplicación la cual exponga un API que permita realizar algunas operaciones de CRUD para cada una de esas dos entidades con algunas reglas de negocio sobre ellas.
Nota: Para la persistencia, utilice cualquier RDBMS de su preferencia (MySQL, Postgresql, Oracle, etc).
Para este ejercicio vamos a simplificar el concepto de región, para nuestro desafío, va a ser representada como un BoundingBox dentro la ciudad.
Como ejemplos de región, podemos representar algunos barrios, áreas o localidades populares de la ciudad de Bogotá, como por ejemplo La Candelaria, Chapinero, Marly, Zona T, La Floresta, entre otros. Note que en los ejemplos anteriores tenemos diferentes escenarios de regiones; regiones aisladas de las otras regiones, regiones que tienen sobreposición en algunas cuadras con otra región, regiones que están contenidas en otras regiones.
A continuación un ejemplo de la representación JSON de una región, por ejemplo para representar la región de Chapinero:
{
"id": 1,
"name": "Chapinero",
"boundingBox": {
"bottomLeft" : {
"longitude" : -74.069225,
"latitude" : 4.621519
},
"upperRight" : {
"longitude" : -74.024078,
"latitude" : 4.681916
}
}
}- Los ids deben ser numéricos generados automáticamente a partir del número 1.
- El nombre no debe ser vacío y debe ser único.
- Los valores del BoundingBox no pueden ser nulos, y tanto las latitudes como las longitudes deben ser validadas dentro de sus rangos de valores permitidos para este tipo de datos.
- Las propiedades en el BoundingBox son incluyentes de las regiones, por lo tanto si una propiedad está registrada en una de las esquinas del BoundingBox, esa propiedad también hace parte de la región.
Usando las siguientes estructuras de Request y Body permita las siguientes funcionalidades. Para crear o alterar regiones, tenga en cuenta que los campos son obligatorios y deben respetar las reglas descritas para anteriormente.
- Cree nuevas regiones.
Request:
POST /regions
Body:
{
"name": "Chapinero",
"boundingBox": {
"bottomLeft" : {
"longitude" : -74.069,
"latitude" : 4.621
},
"upperRight" : {
"longitude" : -74.024,
"latitude" : 4.681
}
}
}Response:
Usted define la respuesta, hace parte del desafío
- Actualice una región
Request:
PUT /regions/{id}
Body:
{
"id": 1,
"name": "Chapinero",
"boundingBox": {
"bottomLeft" : {
"longitude" : -74.069225,
"latitude" : 4.621519
},
"upperRight" : {
"longitude" : -74.024078,
"latitude" : 4.681916
}
}
}
Response:
Usted define la respuesta, hace parte del desafío
- Retorne una región específica
Request:
GET /regions/{id}
Response:
{
"id": 1,
"name": "Chapinero",
"boundingBox": {
"bottomLeft" : {
"longitude" : -74.069225,
"latitude" : 4.621519
},
"upperRight" : {
"longitude" : -74.024078,
"latitude" : 4.681916
}
}
}- Retorne las propiedades de una región específica
Retorna las propiedades de una región. Los resultados vienen páginados y organizados por fecha de actualización del más reciente al más antiguo. Si no tiene fecha de actualización, debe usar la fecha de creación para ordenar.
Request:
GET /regions/{id}/properties?page={pageNumber}&pageSize={pageSize}
Donde:
page: Es el número de la página; es un parámetro opcional y su valor default es 1pageSize: Es el tamaño solicitado de resultados en la página. Es un parámetro opcional, su valor default es 10, y su valor máximo es 20.
Response: La respuesta debe seguir la siguiente estructura de campos:
page: La página actual de los resultadospageSize: El máximo número de resultados retornado por páginatotal: El número total de propriedades en la regióntotalPages: El número total de páginas que contienen resultados para la búsqueda hecha.data: Un array con los objetos conteniendo las propiedades solicitadas en el request. Para ver en detalle la estructura de unapropiedad, por favor avance a la siguiente sección.
{
"page": 1,
"pageSize": 10,
"totalPages": 1,
"total": 1,
"data": [
{
"id": 10,
"title": "Apartamento cerca a la estación de transmilenio",
"description": "Apartamento con 3 cuartos y 2 baños localizado a 100 metros de la avenida caracas en la zona de Marly",
"location": {
"longitude": -74.0652887,
"latitude": 4.6370493
},
"pricing": {
"rentalPrice": 2000000,
"administrativeFee": 250000
},
"bedrooms": 3,
"bathrooms": 2,
"area": 60,
"photos": [
"https://cdn.pixabay.com/photo/2014/08/11/21/39/wall-416060_960_720.jpg",
"https://cdn.pixabay.com/photo/2016/09/22/11/55/kitchen-1687121_960_720.jpg"
],
"createdAt": "2020-05-10T04:20:33Z",
"updatedAt": "2020-05-10T05:30:00Z",
"regions": ["Chapinero", "Marly"]
}
]
}Una propiedad tiene la información básica para anunciar un inmueble. La propiedad debe estar asociada en una o más regiones.
A continuación un ejemplo de la representación JSON de una propiedad, por ejemplo para representar una propiedad en la región de Marly:
{
"id": 10,
"title": "Apartamento cerca a la estación de transmilenio",
"description": "Apartamento con 3 cuartos y 2 baños localizado a 100 metros de la avenida caracas en la zona de Marly",
"location": {
"longitude": -74.0652887,
"latitude": 4.6370493
},
"pricing": {
"rentalPrice": 2000000,
"administrativeFee": 250000
},
"bedrooms": 3,
"bathrooms": 2,
"area": 60,
"photos": [
"https://cdn.pixabay.com/photo/2014/08/11/21/39/wall-416060_960_720.jpg",
"https://cdn.pixabay.com/photo/2016/09/22/11/55/kitchen-1687121_960_720.jpg"
],
"createdAt": "2020-05-10T04:20:33Z",
"updatedAt": "2020-05-10T05:30:00Z",
"regions": ["Chapinero", "Marly"]
}- Los ids deben ser numéricos generados automáticamente a partir del número 1.
- Los campos
title,location,pricing.rentalPrice,bedrooms,bathrooms,areason obligatórios. - Los campos
createdAt,updatedAtyregionsson automáticamente generados por el sistema. La API no debería permitir modificaciones sobre ellos. - El campo
bedroomstiene como valor mínimo 1 y valor máximo 6. - El campo
bathroomstiene como valor mínimo 1 y valor máximo 4. - El campo
areatiene como valor mínimo 15 y valor máximo 300. - La localización de la propiedad debe corresponder a un punto dentro de por lo menos una
regiónregistrada previamente en la aplicación.
Usando las siguientes estructuras de Request y Body permita las siguientes funcionalidades. Para crear o alterar propiedades, tenga en cuenta las reglas descritas anteriormente.
- Cree nuevas propriedades.
Request:
POST /properties
Body:
{
"title": "Apartamento cerca a la estación de transmilenio",
"location": {
"longitude": -74.0652887,
"latitude": 4.6370493
},
"pricing": {
"rentalPrice": 2000000
},
"bedrooms": 3,
"bathrooms": 2,
"area": 60
}Response:
Usted define la respuesta, hace parte del desafío
- Actualice una propiedad
Request:
PUT /properties/{id}
Body:
{
"id": 10,
"title": "Apartamento cerca a la estación de transmilenio",
"description": "Apartamento con 3 cuartos y 2 baños localizado a 100 metros de la avenida caracas en la zona de Marly",
"location": {
"longitude": -74.0652887,
"latitude": 4.6370493
},
"pricing": {
"rentalPrice": 2000000,
"administrativeFee": 250000
},
"bedrooms": 3,
"bathrooms": 2,
"area": 60,
"photos": [
"https://cdn.pixabay.com/photo/2014/08/11/21/39/wall-416060_960_720.jpg",
"https://cdn.pixabay.com/photo/2016/09/22/11/55/kitchen-1687121_960_720.jpg"
]
}Response:
Usted define la respuesta, hace parte del desafío
- Retorne propiedades
Retorna las propiedades filtradas por un BoundingBox (opcional). Los resultados vienen páginados y organizados por fecha de actualización del más reciente al más antiguo. Si no tiene fecha de actualización, debe usar la fecha de creación para ordenar.
Request:
GET /properties?bbox={bbox}&page={pageNumber}&pageSize={pageSize}
Donde:
bbox: Es un BoundingBox para filtrar propiedades. Es un parámetro opcional, su valor default esnull, es decir debe retornar todas las propiedades. El formato del parámetro esperado en la url esbbox={minLongitude},{minLatitude},{maxLongitude},{maxLatitude}.page: Es el número de la página; es un parámetro opcional y su valor default es 1pageSize: Es el tamaño solicitado de resultados en la página. Es un parámetro opcional, su valor default es 10, y su valor máximo es 20.
Response: La respuesta debe seguir la siguiente estructura de campos:
page: La página actual de los resultadospageSize: El máximo número de resultados retornado por páginatotal: El número total de propriedadestotalPages: El número total de páginas que contienen resultados para la búsqueda hecha.data: Un array con los objetos conteniendo las propiedades solicitadas en el request. Para ver en detalle la estructura de unapropiedad, por favor avance a la siguiente sección.
{
"page": 1,
"pageSize": 10,
"totalPages": 1,
"total": 1,
"data": [
{
"id": 10,
"title": "Apartamento cerca a la estación de transmilenio",
"description": "Apartamento con 3 cuartos y 2 baños localizado a 100 metros de la avenida caracas en la zona de Marly",
"location": {
"longitude": -74.0652887,
"latitude": 4.6370493
},
"pricing": {
"rentalPrice": 2000000,
"administrativeFee": 250000
},
"bedrooms": 3,
"bathrooms": 2,
"area": 60,
"photos": [
"https://cdn.pixabay.com/photo/2014/08/11/21/39/wall-416060_960_720.jpg",
"https://cdn.pixabay.com/photo/2016/09/22/11/55/kitchen-1687121_960_720.jpg"
],
"createdAt": "2020-05-10T04:20:33Z",
"updatedAt": "2020-05-10T05:30:00Z",
"regions": ["Chapinero", "Marly"]
}
]
}Para revisitar los criterios de calificación del desafío, y la descripción de cómo entregar el proyecto, por favor continúe leyendo la documentación principal a partir de aquí.