diff --git a/CHANGELOG.MD b/CHANGELOG.MD index bc58234..ae9f604 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -32,3 +32,15 @@ All notable changes to this project will be documented in this file. - Route Neighborhoods By State - Route Seed Neighborhoods By State - logSeed Collection + +## [0.0.5] - 2022-06-27 + +### Added + +- Route Cities By State +- Route Cities By Country +- Route States By Country +- Route Countries +- Swagger Openapi automation +- Optmize Neighborhoods by State +- Sort Collections result diff --git a/README.md b/README.md index 95d27af..dc938d5 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,10 @@ API to get Places by Local Names. -- Only Neighborhoods for now +- Neighborhoods +- Cities +- States +- Countries ## Installation @@ -81,8 +84,16 @@ After run the server, make a get in browser, postman or similar http://localhost:3000/neighborhoods/city/brazil/sc/orleans ## Neighborhoods By State example http://localhost:3000/neighborhoods/city/brazil/sc +## Cities By State example +http://localhost:3000/cities/brazil/sc +## Cities By Country example +http://localhost:3000/cities/brazil +## States By Country example +http://localhost:3000/states/brazil +## Countries example +http://localhost:3000/countries/ ``` -[Swagger](https://app.swaggerhub.com/apis/dev-seeder/Places/1.0.0) +[Swagger](https://app.swaggerhub.com/apis/dev-seeder/Places/) -At the moment, it's working only for Brazilians cities and states. +At the moment, it's working only for Brazilians places. diff --git a/config/yaml/values.yaml b/config/yaml/values.yaml index 16aa11c..4afe32e 100644 --- a/config/yaml/values.yaml +++ b/config/yaml/values.yaml @@ -7,3 +7,5 @@ database: connection: 'mongodb+srv://dev-seeder-root:hpYpxyjrKExNQviu@devseeder.v6xtv.mongodb.net/places?retryWrites=true&w=majority&ssl=true&wtimeoutMS=0' api: port: 3000 +doc: + version: 0.0.5 diff --git a/package-lock.json b/package-lock.json index db1940f..13a91a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@nestjs/core": "^8.0.0", "@nestjs/mongoose": "^9.1.0", "@nestjs/platform-express": "^8.0.0", + "@nestjs/swagger": "^5.2.1", "chai": "^4.3.6", "cheerio": "^1.0.0-rc.10", "class-transformer": "^0.5.1", @@ -30,7 +31,8 @@ "rimraf": "^3.0.2", "rxjs": "^7.2.0", "sinon": "^14.0.0", - "superagent": "^7.1.3" + "superagent": "^7.1.3", + "swagger-ui-express": "^4.4.0" }, "devDependencies": { "@nestjs/cli": "^8.0.0", @@ -1468,6 +1470,25 @@ } } }, + "node_modules/@nestjs/mapped-types": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-1.0.1.tgz", + "integrity": "sha512-NFvofzSinp00j5rzUd4tf+xi9od6383iY0JP7o0Bnu1fuItAUkWBgc4EKuIQ3D+c2QI3i9pG1kDWAeY27EMGtg==", + "peerDependencies": { + "@nestjs/common": "^7.0.8 || ^8.0.0", + "class-transformer": "^0.2.0 || ^0.3.0 || ^0.4.0 || ^0.5.0", + "class-validator": "^0.11.1 || ^0.12.0 || ^0.13.0", + "reflect-metadata": "^0.1.12" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, "node_modules/@nestjs/mongoose": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/@nestjs/mongoose/-/mongoose-9.1.0.tgz", @@ -1516,6 +1537,31 @@ "typescript": "^3.4.5 || ^4.3.5" } }, + "node_modules/@nestjs/swagger": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-5.2.1.tgz", + "integrity": "sha512-7dNa08WCnTsW/oAk3Ujde+z64JMfNm19DhpXasFR8oJp/9pggYAbYU927HpA+GJsSFJX6adjIRZsCKUqaGWznw==", + "dependencies": { + "@nestjs/mapped-types": "1.0.1", + "lodash": "4.17.21", + "path-to-regexp": "3.2.0" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0", + "@nestjs/core": "^8.0.0", + "fastify-swagger": "*", + "reflect-metadata": "^0.1.12", + "swagger-ui-express": "*" + }, + "peerDependenciesMeta": { + "fastify-swagger": { + "optional": true + }, + "swagger-ui-express": { + "optional": true + } + } + }, "node_modules/@nestjs/testing": { "version": "8.4.5", "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-8.4.5.tgz", @@ -8427,6 +8473,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-ui-dist": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.12.0.tgz", + "integrity": "sha512-B0Iy2ueXtbByE6OOyHTi3lFQkpPi/L7kFOKFeKTr44za7dJIELa9kzaca6GkndCgpK1QTjArnoXG+aUy0XQp1w==" + }, + "node_modules/swagger-ui-express": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.4.0.tgz", + "integrity": "sha512-1CzRkHG386VQMVZK406jcpgnW2a9A5A/NiAjKhsFTQqUBWRF+uGbXTU/mA7WSV3mTzyOQDvjBdWP/c2qd5lqKw==", + "dependencies": { + "swagger-ui-dist": ">=4.11.0" + }, + "engines": { + "node": ">= v0.10.32" + }, + "peerDependencies": { + "express": ">=4.0.0" + } + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -10588,6 +10653,12 @@ "uuid": "8.3.2" } }, + "@nestjs/mapped-types": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-1.0.1.tgz", + "integrity": "sha512-NFvofzSinp00j5rzUd4tf+xi9od6383iY0JP7o0Bnu1fuItAUkWBgc4EKuIQ3D+c2QI3i9pG1kDWAeY27EMGtg==", + "requires": {} + }, "@nestjs/mongoose": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/@nestjs/mongoose/-/mongoose-9.1.0.tgz", @@ -10619,6 +10690,16 @@ "pluralize": "8.0.0" } }, + "@nestjs/swagger": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-5.2.1.tgz", + "integrity": "sha512-7dNa08WCnTsW/oAk3Ujde+z64JMfNm19DhpXasFR8oJp/9pggYAbYU927HpA+GJsSFJX6adjIRZsCKUqaGWznw==", + "requires": { + "@nestjs/mapped-types": "1.0.1", + "lodash": "4.17.21", + "path-to-regexp": "3.2.0" + } + }, "@nestjs/testing": { "version": "8.4.5", "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-8.4.5.tgz", @@ -15890,6 +15971,19 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, + "swagger-ui-dist": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.12.0.tgz", + "integrity": "sha512-B0Iy2ueXtbByE6OOyHTi3lFQkpPi/L7kFOKFeKTr44za7dJIELa9kzaca6GkndCgpK1QTjArnoXG+aUy0XQp1w==" + }, + "swagger-ui-express": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.4.0.tgz", + "integrity": "sha512-1CzRkHG386VQMVZK406jcpgnW2a9A5A/NiAjKhsFTQqUBWRF+uGbXTU/mA7WSV3mTzyOQDvjBdWP/c2qd5lqKw==", + "requires": { + "swagger-ui-dist": ">=4.11.0" + } + }, "symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", diff --git a/package.json b/package.json index 7743384..f85dc81 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@nestjs/core": "^8.0.0", "@nestjs/mongoose": "^9.1.0", "@nestjs/platform-express": "^8.0.0", + "@nestjs/swagger": "^5.2.1", "chai": "^4.3.6", "cheerio": "^1.0.0-rc.10", "class-transformer": "^0.5.1", @@ -45,7 +46,8 @@ "rimraf": "^3.0.2", "rxjs": "^7.2.0", "sinon": "^14.0.0", - "superagent": "^7.1.3" + "superagent": "^7.1.3", + "swagger-ui-express": "^4.4.0" }, "devDependencies": { "@nestjs/cli": "^8.0.0", diff --git a/src/app.module.ts b/src/app.module.ts index cfd0b29..8cafec9 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -4,9 +4,12 @@ import { APP_INTERCEPTOR } from '@nestjs/core'; import { MongooseModule } from '@nestjs/mongoose'; import { FiltersModule } from './core/error-handling/filters.module'; import { TransformResponseInterceptor } from './core/http/transform-response.interceptor'; +import { CitiesModule } from './microservice/adapter/cities.module'; +import { CountriesModule } from './microservice/adapter/countries.module'; import { ExtensionsModule } from './microservice/adapter/helper/extensions/exensions.module'; import { CustomPuppeteerModule } from './microservice/adapter/helper/modules/custom-puppeteer.module'; import { NeighborhoodsModule } from './microservice/adapter/neighborhoods.module'; +import { StatesModule } from './microservice/adapter/states.module'; @Module({ imports: [ @@ -23,7 +26,10 @@ import { NeighborhoodsModule } from './microservice/adapter/neighborhoods.module uri: config.get('database.mongodb.connection') }) }), - NeighborhoodsModule + NeighborhoodsModule, + CitiesModule, + StatesModule, + CountriesModule ], controllers: [], providers: [ diff --git a/src/main.ts b/src/main.ts index 7e3f12a..53c08a0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,6 +3,8 @@ import { ConfigService } from '@nestjs/config'; import { NestFactory } from '@nestjs/core'; import { useContainer } from 'class-validator'; import { AppModule } from './app.module'; +import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; +import * as fs from 'fs'; async function bootstrap() { const app = await NestFactory.create(AppModule); @@ -12,7 +14,19 @@ async function bootstrap() { }) ); useContainer(app.select(AppModule), { fallbackOnErrors: true }); + const configService = app.get(ConfigService); + + const docApi = new DocumentBuilder() + .setTitle('Places') + .setDescription('Places API') + .setVersion(configService.get('doc.version')) + .build(); + + const document = SwaggerModule.createDocument(app, docApi); + fs.writeFileSync('./swagger-spec.json', JSON.stringify(document)); + SwaggerModule.setup('api', app, document); + return await app.listen(configService.get('api.port')); } diff --git a/src/microservice/adapter/cities.module.ts b/src/microservice/adapter/cities.module.ts new file mode 100644 index 0000000..df43649 --- /dev/null +++ b/src/microservice/adapter/cities.module.ts @@ -0,0 +1,33 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import configuration from '../../config/configuration'; +import { MongooseModule } from '@nestjs/mongoose'; +import { City, CitySchema } from '../domain/schemas/city.schema'; +import { ValidateCityByNameOrAliasService } from '../domain/service/cities/validate-city-by-name-or-alias.service'; +import { CitiesMongoose } from './repository/cities/cities-mongoose.repository'; +import { ValidateInputParamsService } from '../domain/service/validate/validate-input-params.service'; +import { GetCitiesByStateService } from '../domain/service/cities/get/get-cities-by-state.service'; +import { CitiesController } from './controller/cities.controller'; +import { GetCitiesByCountryService } from '../domain/service/cities/get/get-cities-by-country.service'; +import { StatesModule } from './states.module'; + +@Module({ + imports: [ + ConfigModule.forRoot({ + isGlobal: true, + load: [configuration] + }), + MongooseModule.forFeature([{ name: City.name, schema: CitySchema }]), + StatesModule + ], + controllers: [CitiesController], + providers: [ + CitiesMongoose, + ValidateCityByNameOrAliasService, + ValidateInputParamsService, + GetCitiesByStateService, + GetCitiesByCountryService + ], + exports: [CitiesMongoose, ValidateInputParamsService, GetCitiesByStateService] +}) +export class CitiesModule {} diff --git a/src/microservice/adapter/controller/cities.controller.ts b/src/microservice/adapter/controller/cities.controller.ts new file mode 100644 index 0000000..2c84dea --- /dev/null +++ b/src/microservice/adapter/controller/cities.controller.ts @@ -0,0 +1,78 @@ +import { Controller, Get, HttpStatus, Param } from '@nestjs/common'; +import { NestResponse } from '../../../core/http/nest-response'; +import { AbstractController } from '../../domain/controller/abstract-controller'; +import { GetCitiesByStateService } from '../../domain/service/cities/get/get-cities-by-state.service'; +import { SearchCitiesDTO } from '../../domain/model/search/cities/search-cities-dto.model'; +import { GetCitiesByCountryService } from '../../domain/service/cities/get/get-cities-by-country.service'; +import { + ApiOkResponse, + ApiOperation, + ApiParam, + ApiTags +} from '@nestjs/swagger'; +import { CityResponse } from '../../domain/model/cities/city-response.model'; +import { CitiesByCountry } from '../../domain/model/cities/cities-by-country.model'; + +@ApiTags('cities') +@Controller('cities') +export class CitiesController extends AbstractController { + constructor( + private readonly getCitiesByStateService: GetCitiesByStateService, + private readonly getCitiesByCountryService: GetCitiesByCountryService + ) { + super(); + } + + @ApiOperation({ + description: 'Get Cities By State Reference' + }) + @ApiOkResponse({ + description: 'Cities found.', + isArray: true, + type: CityResponse + }) + @ApiParam({ + name: 'country', + required: true, + description: 'The Country name of the city', + type: String + }) + @ApiParam({ + name: 'state', + required: true, + description: 'The State name of the city', + type: String + }) + @Get('/state/:country/:state') + async getCitiesByState( + @Param() params: SearchCitiesDTO + ): Promise { + return this.buildResponse( + HttpStatus.OK, + await this.getCitiesByStateService.getCitiesByState(params) + ); + } + + @ApiOperation({ + description: 'Get Cities By Country Reference' + }) + @ApiOkResponse({ + description: 'Cities found.', + type: CitiesByCountry + }) + @ApiParam({ + name: 'country', + required: true, + description: 'The Country name of the city', + type: String + }) + @Get('/country/:country') + async getCitiesByCountry( + @Param('country') country: string + ): Promise { + return this.buildResponse( + HttpStatus.OK, + await this.getCitiesByCountryService.getCitiesByCountry(country) + ); + } +} diff --git a/src/microservice/adapter/controller/countries.controller.ts b/src/microservice/adapter/controller/countries.controller.ts new file mode 100644 index 0000000..ecfde22 --- /dev/null +++ b/src/microservice/adapter/controller/countries.controller.ts @@ -0,0 +1,30 @@ +import { Controller, Get, HttpStatus } from '@nestjs/common'; +import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { CountryResponse } from '../../domain/model/countries/country-response.model'; +import { NestResponse } from '../../../core/http/nest-response'; +import { AbstractController } from '../../domain/controller/abstract-controller'; +import { GetCountriesService } from '../../domain/service/countries/get-countries.service'; + +@ApiTags('countries') +@Controller('countries') +export class CountriesController extends AbstractController { + constructor(private readonly getCountriesService: GetCountriesService) { + super(); + } + + @ApiOperation({ + description: 'Get All Countries' + }) + @ApiOkResponse({ + description: 'Countries found.', + isArray: true, + type: CountryResponse + }) + @Get('/') + async getAllCountries(): Promise { + return this.buildResponse( + HttpStatus.OK, + await this.getCountriesService.getAll() + ); + } +} diff --git a/src/microservice/adapter/controller/neighborhoods.controller.ts b/src/microservice/adapter/controller/neighborhoods.controller.ts index 611c533..7763cbd 100644 --- a/src/microservice/adapter/controller/neighborhoods.controller.ts +++ b/src/microservice/adapter/controller/neighborhoods.controller.ts @@ -3,9 +3,19 @@ import { GetNeighborhoodsByStateService } from '../../domain/service/neighborhoo import { NestResponse } from '../../../core/http/nest-response'; import { AbstractController } from '../../domain/controller/abstract-controller'; import { GetNeighborhoodsByCityService } from '../../domain/service/neighborhoods/get/get-neighborhoods-by-city.service'; -import { SearchNeighborhoodsInput } from '../../domain/model/search/search-neighborhoods-input.model'; +import { SearchNeighborhoodsDTO } from '../../domain/model/search/neighborhoods/search-neighborhoods-dto.model'; import { SeedNeighborhoodsByStateService } from '../../domain/service/neighborhoods/seed/seed-neighborhoods-by-state.service'; +import { + ApiExcludeEndpoint, + ApiOkResponse, + ApiOperation, + ApiParam, + ApiTags +} from '@nestjs/swagger'; +import { NeighborhoodByCity } from '../../domain/model/neighborhoods/neighborhood-by-city.model'; +import { NeighborhoodsByState } from '../../domain/model/neighborhoods/neighborhoods-by-state.model'; +@ApiTags('neighborhoods') @Controller('neighborhoods') export class NeighborhoodsController extends AbstractController { constructor( @@ -16,9 +26,35 @@ export class NeighborhoodsController extends AbstractController { super(); } + @ApiOperation({ + description: 'Get Neighborhoods By City Reference' + }) + @ApiOkResponse({ + description: 'Neighborhoods found.', + isArray: true, + type: NeighborhoodByCity + }) + @ApiParam({ + name: 'country', + required: true, + description: 'The Country name of the neighborhood', + type: String + }) + @ApiParam({ + name: 'state', + required: true, + description: 'The State name of the neighborhood', + type: String + }) + @ApiParam({ + name: 'city', + required: true, + description: 'The City name of the neighborhood', + type: String + }) @Get('/city/:country/:state/:city') async getNeighborhoodsByCity( - @Param() params: SearchNeighborhoodsInput + @Param() params: SearchNeighborhoodsDTO ): Promise { return this.buildResponse( HttpStatus.OK, @@ -26,9 +62,28 @@ export class NeighborhoodsController extends AbstractController { ); } + @ApiOperation({ + description: 'Get Neighborhoods By State Reference' + }) + @ApiOkResponse({ + description: 'Neighborhoods found.', + type: NeighborhoodsByState + }) + @ApiParam({ + name: 'country', + required: true, + description: 'The Country name of the neighborhood', + type: String + }) + @ApiParam({ + name: 'state', + required: true, + description: 'The State name of the neighborhood', + type: String + }) @Get('/state/:country/:state') async getNeighborhoodsByState( - @Param() params: SearchNeighborhoodsInput + @Param() params: SearchNeighborhoodsDTO ): Promise { return this.buildResponse( HttpStatus.OK, @@ -36,9 +91,10 @@ export class NeighborhoodsController extends AbstractController { ); } + @ApiExcludeEndpoint() @Get('/seed/state/:country/:state') async seedNeighborhoodsByState( - @Param() params: SearchNeighborhoodsInput + @Param() params: SearchNeighborhoodsDTO ): Promise { return this.buildResponse( HttpStatus.OK, diff --git a/src/microservice/adapter/controller/states.controller.ts b/src/microservice/adapter/controller/states.controller.ts new file mode 100644 index 0000000..9e43944 --- /dev/null +++ b/src/microservice/adapter/controller/states.controller.ts @@ -0,0 +1,37 @@ +import { Controller, Get, HttpStatus, Param } from '@nestjs/common'; +import { GetStatesByCountryService } from '../../domain/service/states/get-states-by-country.service'; +import { NestResponse } from '../../../core/http/nest-response'; +import { AbstractController } from '../../domain/controller/abstract-controller'; +import { ApiOkResponse, ApiParam, ApiTags } from '@nestjs/swagger'; +import { StatesByCountry } from '../../domain/model/states/states-by-country.model'; + +@ApiTags('states') +@Controller('states') +export class StatesController extends AbstractController { + constructor( + private readonly getStatesByCountryService: GetStatesByCountryService + ) { + super(); + } + + @ApiOkResponse({ + description: 'States found.', + isArray: true, + type: StatesByCountry + }) + @ApiParam({ + name: 'country', + required: true, + description: 'The Country name of the state', + type: String + }) + @Get('/country/:country') + async getStatesByCountry( + @Param('country') country: string + ): Promise { + return this.buildResponse( + HttpStatus.OK, + await this.getStatesByCountryService.getStatesByCountry(country) + ); + } +} diff --git a/src/microservice/adapter/countries.module.ts b/src/microservice/adapter/countries.module.ts new file mode 100644 index 0000000..6c90ea0 --- /dev/null +++ b/src/microservice/adapter/countries.module.ts @@ -0,0 +1,22 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import configuration from '../../config/configuration'; +import { MongooseModule } from '@nestjs/mongoose'; +import { Country, CountrySchema } from '../domain/schemas/country.schema'; +import { CountriesMongoose } from './repository/countries/countries-mongoose.repository'; +import { CountriesController } from './controller/countries.controller'; +import { GetCountriesService } from '../domain/service/countries/get-countries.service'; + +@Module({ + imports: [ + ConfigModule.forRoot({ + isGlobal: true, + load: [configuration] + }), + MongooseModule.forFeature([{ name: Country.name, schema: CountrySchema }]) + ], + controllers: [CountriesController], + providers: [CountriesMongoose, GetCountriesService], + exports: [CountriesMongoose] +}) +export class CountriesModule {} diff --git a/src/microservice/adapter/helper/builder/cities/cities-by-country.builder.ts b/src/microservice/adapter/helper/builder/cities/cities-by-country.builder.ts new file mode 100644 index 0000000..538501c --- /dev/null +++ b/src/microservice/adapter/helper/builder/cities/cities-by-country.builder.ts @@ -0,0 +1,23 @@ +import { Builder } from '../../../../domain/helper/builder/builder.builder'; +import { CitiesByCountry } from '../../../../domain/model/cities/cities-by-country.model'; +import { City } from '../../../../domain/schemas/city.schema'; + +export class CitiesByCountryBuilder extends Builder { + constructor(inputElement: City[]) { + super(inputElement); + } + + build(): CitiesByCountry { + const builtElement = new CitiesByCountry(); + for (const item of this.inputElement) { + const keyState = `${item.stateName.capitalize()} - ${item.stateCode.toUpperCase()}`; + if (!Object.keys(builtElement).includes(keyState)) + builtElement[keyState] = []; + + delete item.stateName; + builtElement[keyState].push(item); + } + + return builtElement; + } +} diff --git a/src/microservice/adapter/helper/builder/neighborhoods/neighborhoods-by-state.builder.ts b/src/microservice/adapter/helper/builder/neighborhoods/neighborhoods-by-state.builder.ts new file mode 100644 index 0000000..58c522a --- /dev/null +++ b/src/microservice/adapter/helper/builder/neighborhoods/neighborhoods-by-state.builder.ts @@ -0,0 +1,32 @@ +import { + NeighborhoodsByState, + NeighborhooodAggregatedByCity +} from '../../../../domain/model/neighborhoods/neighborhoods-by-state.model'; +import { Neighborhood } from '../../../../domain/schemas/neighborhood.schema'; +import { Builder } from '../../../../domain/helper/builder/builder.builder'; +import { ValidOutputSearchByState } from '../../../../domain/interface/valid-output-search/valid-outpu-search.interface'; + +export class NeighborhoodsByStateBuilder extends Builder< + Neighborhood[], + NeighborhoodsByState +> { + constructor(inputElement: Neighborhood[]) { + super(inputElement); + } + + build(convertedSearch: ValidOutputSearchByState): NeighborhoodsByState { + const builtElement = new NeighborhoodsByState(); + for (const item of this.inputElement) { + const keyCity = item.city.capitalize(); + if (!Object.keys(builtElement).includes(keyCity)) + builtElement[keyCity] = []; + + const obj = new NeighborhooodAggregatedByCity(); + obj.name = item.name; + obj.cityId = item.cityId; + obj.state = `${convertedSearch.state.name} - ${convertedSearch.country.iso3}`; + builtElement[keyCity].push(obj); + } + return builtElement; + } +} diff --git a/src/microservice/adapter/helper/builder/neighborhoods/neighborhoods-mongo.builder.ts b/src/microservice/adapter/helper/builder/neighborhoods/neighborhoods-mongo.builder.ts index 4db3e1a..c0a05cd 100644 --- a/src/microservice/adapter/helper/builder/neighborhoods/neighborhoods-mongo.builder.ts +++ b/src/microservice/adapter/helper/builder/neighborhoods/neighborhoods-mongo.builder.ts @@ -1,14 +1,20 @@ -import { ValidOutputSearchNeighborhood } from 'src/microservice/domain/interface/valid-output-search/valid-outpu-search-neighborhood.interface'; +import { Builder } from '../../../../domain/helper/builder/builder.builder'; +import { ValidOutputSearchByCity } from '../../../../domain/interface/valid-output-search/valid-outpu-search.interface'; import { NeighborhoodByCity } from '../../../../domain/model/neighborhoods/neighborhood-by-city.model'; import { Neighborhood } from '../../../../domain/schemas/neighborhood.schema'; -export class NeighborhoodsMongoBuilder { - constructor(private readonly puppeteerResponse: NeighborhoodByCity[]) {} +export class NeighborhoodsMongoBuilder extends Builder< + NeighborhoodByCity[], + Neighborhood[] +> { + constructor(inputElement: NeighborhoodByCity[]) { + super(inputElement); + } - build(searchParams: ValidOutputSearchNeighborhood): Neighborhood[] { + build(searchParams: ValidOutputSearchByCity): Neighborhood[] { const arr = []; - this.puppeteerResponse.forEach((item) => { + this.inputElement.forEach((item) => { const obj = new Neighborhood(); obj.countryId = searchParams.country.id; obj.country = searchParams.country.name; diff --git a/src/microservice/adapter/helper/builder/neighborhoods/neighborhoods-puppeteer.builder.ts b/src/microservice/adapter/helper/builder/neighborhoods/neighborhoods-puppeteer.builder.ts index 73533c8..ddb0dbb 100644 --- a/src/microservice/adapter/helper/builder/neighborhoods/neighborhoods-puppeteer.builder.ts +++ b/src/microservice/adapter/helper/builder/neighborhoods/neighborhoods-puppeteer.builder.ts @@ -1,15 +1,24 @@ +import { Builder } from '../../../../domain/helper/builder/builder.builder'; import { NeighborhoodByCity } from '../../../../domain/model/neighborhoods/neighborhood-by-city.model'; import { Neighborhood } from '../../../../domain/schemas/neighborhood.schema'; -export class NeighborhoodsPuppeteerBuilder { - constructor(private readonly mongoResponse: Neighborhood[]) {} +export class NeighborhoodsPuppeteerBuilder extends Builder< + Neighborhood[], + NeighborhoodByCity[] +> { + constructor(inputElement: Neighborhood[]) { + super(inputElement); + } build(): NeighborhoodByCity[] { const arr = []; - this.mongoResponse.forEach((document) => { + this.inputElement.forEach((document) => { const obj = new NeighborhoodByCity(); - obj.city = `${document.city.capitalize()} - ${document.state.toUpperCase()}`; obj.name = document.name; + obj.cityId = document.cityId; + obj.city = `${document.city.capitalize()} - ${document.state.toUpperCase()}`; + obj.stateId = document.stateId; + obj.countryId = document.countryId; arr.push(obj); }); return arr; diff --git a/src/microservice/adapter/helper/builder/neighborhoods/search-neighborhoods-db.builder.ts b/src/microservice/adapter/helper/builder/neighborhoods/search-neighborhoods-db.builder.ts index 24f9d66..8f9afbd 100644 --- a/src/microservice/adapter/helper/builder/neighborhoods/search-neighborhoods-db.builder.ts +++ b/src/microservice/adapter/helper/builder/neighborhoods/search-neighborhoods-db.builder.ts @@ -1,16 +1,20 @@ -import { ValidOutputSearchNeighborhood } from '../../../../domain/interface/valid-output-search/valid-outpu-search-neighborhood.interface'; -import { SearchNeighborhoodsDB } from '../../../../domain/model/search/search-neighborhoods-db.model'; +import { Builder } from '../../../../domain/helper/builder/builder.builder'; +import { ValidOutputSearchByCity } from '../../../../domain/interface/valid-output-search/valid-outpu-search.interface'; +import { SearchNeighborhoodsDB } from '../../../../domain/model/search/neighborhoods/search-neighborhoods-db.model'; -export class SearchNeighborhoodsDBBuilder { - constructor( - private readonly convertedSearch: ValidOutputSearchNeighborhood - ) {} +export class SearchNeighborhoodsDBBuilder extends Builder< + ValidOutputSearchByCity, + SearchNeighborhoodsDB +> { + constructor(inputElement: ValidOutputSearchByCity) { + super(inputElement); + } build(): SearchNeighborhoodsDB { return new SearchNeighborhoodsDB( - this.convertedSearch.country.id, - this.convertedSearch.state.id, - this.convertedSearch.city.id + this.inputElement.country.id, + this.inputElement.state.id, + this.inputElement.city.id ); } } diff --git a/src/microservice/adapter/helper/extensions/exensions.module.ts b/src/microservice/adapter/helper/extensions/exensions.module.ts index acfc2c1..0489ae4 100644 --- a/src/microservice/adapter/helper/extensions/exensions.module.ts +++ b/src/microservice/adapter/helper/extensions/exensions.module.ts @@ -1,5 +1,4 @@ import './string.extension'; -import './object.extension'; import { Module } from '@nestjs/common'; @Module({ diff --git a/src/microservice/adapter/helper/extensions/object.extension.ts b/src/microservice/adapter/helper/extensions/object.extension.ts deleted file mode 100644 index 3662288..0000000 --- a/src/microservice/adapter/helper/extensions/object.extension.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* eslint-disable @typescript-eslint/no-this-alias */ -import { EmptyPropException } from '../../../../core/error-handling/exception/empty-prop.exception'; - -declare global { - interface Object { - validateIsAnyEmptyKey(): void; - getMethods(): string[]; - } -} - -Object.prototype.validateIsAnyEmptyKey = function () { - const context = this; - Object.keys(this).forEach(function (key) { - if (context[key].length === 0) throw new EmptyPropException(key); - }); -}; - -Object.prototype.getMethods = (): any[] => { - const properties = new Set(); - let currentObj = this; - const obj = this; - do { - Object.getOwnPropertyNames(currentObj).forEach((item) => - properties.add(item) - ); - } while ((currentObj = Object.getPrototypeOf(currentObj))); - return [...properties.keys()].filter( - (item: string) => - typeof obj !== 'undefined' && typeof obj[item] === 'function' - ); -}; - -export {}; diff --git a/src/microservice/adapter/neighborhoods.module.ts b/src/microservice/adapter/neighborhoods.module.ts index ae0586b..5926fa9 100644 --- a/src/microservice/adapter/neighborhoods.module.ts +++ b/src/microservice/adapter/neighborhoods.module.ts @@ -12,22 +12,12 @@ import { } from '../domain/schemas/neighborhood.schema'; import { MongooseModule } from '@nestjs/mongoose'; import { SaveNeighborhoodsByCityService } from '../domain/service/neighborhoods/save-neighborhoods-by-city.service'; -import { Country, CountrySchema } from '../domain/schemas/country.schema'; -import { ValidateCountryByNameOrAliasService } from '../domain/service/countries/validate-country-by-name-or-alias.service'; -import { CountriesMongoose } from './repository/countries/countries-mongoose.repository'; -import { State, StateSchema } from '../domain/schemas/state.schema'; -import { ValidateStateByNameOrAliasService } from '../domain/service/states/validate-state-by-name-or-alias.service'; -import { StatesMongoose } from './repository/states/states-mongoose.repository'; -import { City, CitySchema } from '../domain/schemas/city.schema'; -import { ValidateCityByNameOrAliasService } from '../domain/service/cities/validate-city-by-name-or-alias.service'; -import { CitiesMongoose } from './repository/cities/cities-mongoose.repository'; import { GetNeighborhoodsByStateService } from '../domain/service/neighborhoods/get/get-neighborhoods-by-state.service'; -import { ValidateInputParamsService } from '../domain/service/validate-input-params.service'; import { SeedNeighborhoodsByStateService } from '../domain/service/neighborhoods/seed/seed-neighborhoods-by-state.service'; -import { GetCitiesByStateService } from '../domain/service/cities/get-cities-by-state.service'; import { LogSeed, LogSeedSchema } from '../domain/schemas/logseed.schema'; import { LogSeedMongoose } from './repository/logseed/logseed-mongoose.repository'; import { LogSeedJobService } from '../domain/service/logseed/log-seed-job.service'; +import { CitiesModule } from './cities.module'; @Module({ imports: [ @@ -38,11 +28,9 @@ import { LogSeedJobService } from '../domain/service/logseed/log-seed-job.servic }), MongooseModule.forFeature([ { name: Neighborhood.name, schema: NeighborhoodSchema }, - { name: Country.name, schema: CountrySchema }, - { name: State.name, schema: StateSchema }, - { name: City.name, schema: CitySchema }, { name: LogSeed.name, schema: LogSeedSchema } - ]) + ]), + CitiesModule ], controllers: [NeighborhoodsController], providers: [ @@ -51,20 +39,12 @@ import { LogSeedJobService } from '../domain/service/logseed/log-seed-job.servic useClass: GuiaMaisRepository }, NeighborhoodsMongoose, - CountriesMongoose, - StatesMongoose, - CitiesMongoose, LogSeedMongoose, GetNeighborhoodsByCityService, GetNeighborhoodsByStateService, SaveNeighborhoodsByCityService, - ValidateCountryByNameOrAliasService, - ValidateStateByNameOrAliasService, - ValidateCityByNameOrAliasService, - ValidateInputParamsService, SeedNeighborhoodsByStateService, - LogSeedJobService, - GetCitiesByStateService + LogSeedJobService ] }) export class NeighborhoodsModule {} diff --git a/src/microservice/adapter/repository/neighborhoods/puppeteer/guia-mais.repository.ts b/src/microservice/adapter/repository/neighborhoods/puppeteer/guia-mais.repository.ts index 6aeb5fb..ac52c37 100644 --- a/src/microservice/adapter/repository/neighborhoods/puppeteer/guia-mais.repository.ts +++ b/src/microservice/adapter/repository/neighborhoods/puppeteer/guia-mais.repository.ts @@ -3,11 +3,12 @@ import { ConfigService } from '@nestjs/config'; import { CheerioAPI } from 'cheerio'; import { InjectPage } from 'nest-puppeteer'; import { NeighborhoodByCity } from '../../../../domain/model/neighborhoods/neighborhood-by-city.model'; -import { SearchNeighborhoodsInput } from '../../../../domain/model/search/search-neighborhoods-input.model'; +import { SearchNeighborhoodsDTO } from '../../../../domain/model/search/neighborhoods/search-neighborhoods-dto.model'; import { IPuppeteerNeighborhoodRepository } from '../../../../domain/interface/puppeteer/repository/puppeteer-neighborhood-repository.interface'; import { PuppeteerNeighborhoodRepository } from '../../../../domain/repository/puppeteer/neighborhood/puppeteer-neighborhood.repository'; import { Page } from '../../../../domain/interface/puppeteer/page.interface'; import { EnumTranslations } from '../../../../domain/enumerators/enum-translations.enumerator'; +import { ValidOutputSearchByCity } from '../../../../domain/interface/valid-output-search/valid-outpu-search.interface'; @Injectable() export class GuiaMaisRepository @@ -25,7 +26,11 @@ export class GuiaMaisRepository ); } - buildElementsFromDocument(searchParams, $: CheerioAPI): NeighborhoodByCity[] { + buildElementsFromDocument( + searchParams, + convertedSearch: ValidOutputSearchByCity, + $: CheerioAPI + ): NeighborhoodByCity[] { const arrNeighborhoods = []; $('.cities.centerContent') .find('a') @@ -33,7 +38,10 @@ export class GuiaMaisRepository const neighborhood = new NeighborhoodByCity(); neighborhood.name = $(this).text(); + neighborhood.cityId = convertedSearch.city.id; neighborhood.city = `${searchParams.city.capitalize()} - ${searchParams.state.toUpperCase()}`; + neighborhood.stateId = convertedSearch.state.id; + neighborhood.countryId = convertedSearch.country.id; arrNeighborhoods.push(neighborhood); }); @@ -42,7 +50,7 @@ export class GuiaMaisRepository } async callEndpoint( - searchParams: SearchNeighborhoodsInput + searchParams: SearchNeighborhoodsDTO ): Promise { const url = `${this.url}/${searchParams.city}-${searchParams.state}`; return this.getDocumentHtml(url); diff --git a/src/microservice/adapter/states.module.ts b/src/microservice/adapter/states.module.ts new file mode 100644 index 0000000..9c44cad --- /dev/null +++ b/src/microservice/adapter/states.module.ts @@ -0,0 +1,35 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import configuration from '../../config/configuration'; +import { MongooseModule } from '@nestjs/mongoose'; +import { State, StateSchema } from '../domain/schemas/state.schema'; +import { ValidateStateByNameOrAliasService } from '../domain/service/states/validate-state-by-name-or-alias.service'; +import { StatesMongoose } from './repository/states/states-mongoose.repository'; +import { GetStatesByCountryService } from '../domain/service/states/get-states-by-country.service'; +import { StatesController } from './controller/states.controller'; +import { CountriesModule } from './countries.module'; +import { ValidateCountryByNameOrAliasService } from '../domain/service/countries/validate-country-by-name-or-alias.service'; + +@Module({ + imports: [ + ConfigModule.forRoot({ + isGlobal: true, + load: [configuration] + }), + MongooseModule.forFeature([{ name: State.name, schema: StateSchema }]), + CountriesModule + ], + controllers: [StatesController], + providers: [ + StatesMongoose, + ValidateStateByNameOrAliasService, + ValidateCountryByNameOrAliasService, + GetStatesByCountryService + ], + exports: [ + StatesMongoose, + ValidateCountryByNameOrAliasService, + ValidateStateByNameOrAliasService + ] +}) +export class StatesModule {} diff --git a/src/microservice/domain/helper/builder/builder.builder.ts b/src/microservice/domain/helper/builder/builder.builder.ts new file mode 100644 index 0000000..eba252d --- /dev/null +++ b/src/microservice/domain/helper/builder/builder.builder.ts @@ -0,0 +1,9 @@ +import { IBuilder } from '../../interface/helper/builder.interface'; + +export abstract class Builder + implements IBuilder +{ + constructor(protected readonly inputElement: InputElementConvert) {} + + abstract build(buildParams?: any): BuiltElement; +} diff --git a/src/microservice/domain/interface/aggregated/aggregated-neighborhoods-city.interface.ts b/src/microservice/domain/interface/aggregated/aggregated-neighborhoods-city.interface.ts index 8f6ccdd..195690e 100644 --- a/src/microservice/domain/interface/aggregated/aggregated-neighborhoods-city.interface.ts +++ b/src/microservice/domain/interface/aggregated/aggregated-neighborhoods-city.interface.ts @@ -1,4 +1,4 @@ -export interface AggregatedNeighborhoodsCity { +export interface AggregatedNeighborhoodsByCity { _id: { cityId: number }; count: number; city: string; diff --git a/src/microservice/domain/interface/helper/builder.interface.ts b/src/microservice/domain/interface/helper/builder.interface.ts new file mode 100644 index 0000000..380b8a6 --- /dev/null +++ b/src/microservice/domain/interface/helper/builder.interface.ts @@ -0,0 +1,3 @@ +export interface IBuilder { + build(buildParams): BuiltElement; +} diff --git a/src/microservice/domain/interface/puppeteer/repository/puppeteer-neighborhood-repository.interface.ts b/src/microservice/domain/interface/puppeteer/repository/puppeteer-neighborhood-repository.interface.ts index 3c59397..38bfcd1 100644 --- a/src/microservice/domain/interface/puppeteer/repository/puppeteer-neighborhood-repository.interface.ts +++ b/src/microservice/domain/interface/puppeteer/repository/puppeteer-neighborhood-repository.interface.ts @@ -1,12 +1,18 @@ -import { SearchNeighborhoodsInput } from '../../../model/search/search-neighborhoods-input.model'; +import { SearchNeighborhoodsDTO } from '../../../model/search/neighborhoods/search-neighborhoods-dto.model'; import { NeighborhoodByCity } from '../../../model/neighborhoods/neighborhood-by-city.model'; import { IPuppeteerRepository } from './puppeteer-repository.interface'; -import { EnumTranslations } from 'src/microservice/domain/enumerators/enum-translations.enumerator'; +import { EnumTranslations } from '../../../enumerators/enum-translations.enumerator'; +import { ValidOutputSearchByCity } from '../../valid-output-search/valid-outpu-search.interface'; export interface IPuppeteerNeighborhoodRepository - extends IPuppeteerRepository { + extends IPuppeteerRepository< + NeighborhoodByCity, + SearchNeighborhoodsDTO, + ValidOutputSearchByCity + > { language: EnumTranslations; getNeighborhoodsByCity( - searchParams: SearchNeighborhoodsInput + searchParams: SearchNeighborhoodsDTO, + convertedSearch: ValidOutputSearchByCity ): Promise; } diff --git a/src/microservice/domain/interface/puppeteer/repository/puppeteer-repository.interface.ts b/src/microservice/domain/interface/puppeteer/repository/puppeteer-repository.interface.ts index 49a40bd..9ac7645 100644 --- a/src/microservice/domain/interface/puppeteer/repository/puppeteer-repository.interface.ts +++ b/src/microservice/domain/interface/puppeteer/repository/puppeteer-repository.interface.ts @@ -1,6 +1,10 @@ import { CheerioAPI } from 'cheerio'; -export interface IPuppeteerRepository { +export interface IPuppeteerRepository< + ElementPlace, + SearchElement, + ValidOutputSearch +> { callEndpoint(searchParams: SearchElement): Promise; getDocumentHtml(url: string): Promise; @@ -11,6 +15,7 @@ export interface IPuppeteerRepository { buildElementsFromDocument( searchParams: SearchElement, + convertedSearch: ValidOutputSearch, $: CheerioAPI ): ElementPlace[]; diff --git a/src/microservice/domain/interface/valid-output-search/valid-outpu-search-neighborhood.interface.ts b/src/microservice/domain/interface/valid-output-search/valid-outpu-search.interface.ts similarity index 60% rename from src/microservice/domain/interface/valid-output-search/valid-outpu-search-neighborhood.interface.ts rename to src/microservice/domain/interface/valid-output-search/valid-outpu-search.interface.ts index 435c65c..ebd6da1 100644 --- a/src/microservice/domain/interface/valid-output-search/valid-outpu-search-neighborhood.interface.ts +++ b/src/microservice/domain/interface/valid-output-search/valid-outpu-search.interface.ts @@ -2,8 +2,13 @@ import { City } from '../../schemas/city.schema'; import { Country } from '../../schemas/country.schema'; import { State } from '../../schemas/state.schema'; -export interface ValidOutputSearchNeighborhood { +export interface ValidOutputSearchByCity { country: Country; state: State; city: City; } + +export interface ValidOutputSearchByState { + country: Country; + state: State; +} diff --git a/src/microservice/domain/model/cities/cities-by-country.model.ts b/src/microservice/domain/model/cities/cities-by-country.model.ts new file mode 100644 index 0000000..0837c4f --- /dev/null +++ b/src/microservice/domain/model/cities/cities-by-country.model.ts @@ -0,0 +1,5 @@ +import { CityResponse } from './city-response.model'; + +export class CitiesByCountry { + [key: string]: CityResponse[]; +} diff --git a/src/microservice/domain/model/cities/city-response.model.ts b/src/microservice/domain/model/cities/city-response.model.ts new file mode 100644 index 0000000..8b66dec --- /dev/null +++ b/src/microservice/domain/model/cities/city-response.model.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { PlacesReponse } from '../places-response.model'; + +export class CityResponse extends PlacesReponse { + @ApiProperty({ + type: String, + description: 'The name of the City', + example: 'Orleans' + }) + name: string; +} diff --git a/src/microservice/domain/model/countries/country-response.model.ts b/src/microservice/domain/model/countries/country-response.model.ts new file mode 100644 index 0000000..0177739 --- /dev/null +++ b/src/microservice/domain/model/countries/country-response.model.ts @@ -0,0 +1,74 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class CountryResponse { + @ApiProperty({ + type: Number, + description: 'Country Id reference in database', + example: 1 + }) + id: number; + + @ApiProperty({ + type: String, + description: 'Country Name', + example: 'Brasil' + }) + name: string; + + @ApiProperty({ + type: String, + description: `The code(ISO3) of the Country`, + example: 'BRA' + }) + iso3: string; + + @ApiProperty({ + type: String, + description: `The code(ISO2) of the Country`, + example: 'BR' + }) + iso2: string; + + @ApiProperty({ + type: String, + description: 'Capital', + example: 'Brasília' + }) + capital: string; + + @ApiProperty({ + type: String, + description: 'Currency Symbol', + example: 'R$' + }) + currency: string; + + @ApiProperty({ + type: String, + description: 'Continent Region of the country', + example: 'South America' + }) + region: string; + + @ApiProperty({ + type: String, + description: 'Continent SubRegion of the country', + example: 'South' + }) + subregion: string; + + @ApiProperty({ + type: String, + isArray: true, + description: 'Alias names', + example: ['BR', 'BRA', 'Brazil', 'Brasil'] + }) + alias: string; + + @ApiProperty({ + type: Number, + description: 'Phone number code DDI', + example: 55 + }) + phoneCode: number; +} diff --git a/src/microservice/domain/model/dto.model.ts b/src/microservice/domain/model/dto.model.ts new file mode 100644 index 0000000..64fd512 --- /dev/null +++ b/src/microservice/domain/model/dto.model.ts @@ -0,0 +1,11 @@ +import { EmptyPropException } from '../../../core/error-handling/exception/empty-prop.exception'; + +export abstract class DTO { + validateIsAnyEmptyKey() { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const context = this; + Object.keys(this).forEach(function (key) { + if (context[key].length === 0) throw new EmptyPropException(key); + }); + } +} diff --git a/src/microservice/domain/model/neighborhoods/neighborhood-by-city.model.ts b/src/microservice/domain/model/neighborhoods/neighborhood-by-city.model.ts index 260bde8..3703473 100644 --- a/src/microservice/domain/model/neighborhoods/neighborhood-by-city.model.ts +++ b/src/microservice/domain/model/neighborhoods/neighborhood-by-city.model.ts @@ -1,4 +1,38 @@ +import { ApiProperty } from '@nestjs/swagger'; + export class NeighborhoodByCity { + @ApiProperty({ + type: String, + description: 'The name of the Neighborhood', + example: 'Alto Paraná' + }) name: string; + + @ApiProperty({ + type: Number, + description: 'The id reference of the City in database', + example: 1 + }) + cityId: number; + + @ApiProperty({ + type: String, + description: `The name of the City's Neighborhood`, + example: 'Orleans' + }) city: string; + + @ApiProperty({ + type: Number, + description: 'The id reference of the state in database', + example: 2014 + }) + stateId: number; + + @ApiProperty({ + type: Number, + description: 'The id reference of the country in database', + example: 31 + }) + countryId: number; } diff --git a/src/microservice/domain/model/neighborhoods/neighborhoods-by-state.model.ts b/src/microservice/domain/model/neighborhoods/neighborhoods-by-state.model.ts index 46ae889..a3a6907 100644 --- a/src/microservice/domain/model/neighborhoods/neighborhoods-by-state.model.ts +++ b/src/microservice/domain/model/neighborhoods/neighborhoods-by-state.model.ts @@ -1,9 +1,28 @@ +import { ApiProperty } from '@nestjs/swagger'; + export class NeighborhoodsByState { [key: string]: NeighborhooodAggregatedByCity[]; } export class NeighborhooodAggregatedByCity { + @ApiProperty({ + type: String, + description: 'The name of the Neighborhood', + example: 'Alto Paraná' + }) name: string; + + @ApiProperty({ + type: Number, + description: 'The id reference of the City in database', + example: 1 + }) cityId: number; + + @ApiProperty({ + type: String, + description: 'The name of the state', + example: 'Santa Catarina - BRA' + }) state: string; } diff --git a/src/microservice/domain/model/places-response.model.ts b/src/microservice/domain/model/places-response.model.ts new file mode 100644 index 0000000..f6e09e2 --- /dev/null +++ b/src/microservice/domain/model/places-response.model.ts @@ -0,0 +1,31 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export abstract class PlacesReponse { + @ApiProperty({ + type: Number, + description: `Id reference in database`, + example: 1 + }) + id: number; + + @ApiProperty({ + type: String, + description: `The code(ISO3) of the Country's State`, + example: 'BRA' + }) + countryCode: string; + + @ApiProperty({ + type: Number, + description: 'Country Id reference in database', + example: 31 + }) + countryId: number; + + @ApiProperty({ + type: String, + description: `The code(ISO2) of the State`, + example: 'SC' + }) + stateCode: string; +} diff --git a/src/microservice/domain/model/search/cities/search-cities-db.model.ts b/src/microservice/domain/model/search/cities/search-cities-db.model.ts new file mode 100644 index 0000000..97558f7 --- /dev/null +++ b/src/microservice/domain/model/search/cities/search-cities-db.model.ts @@ -0,0 +1,5 @@ +export class SearchCitiesDB { + public id: any; + + constructor(public countryId: number, public stateId: number = null) {} +} diff --git a/src/microservice/domain/model/search/cities/search-cities-dto.model.ts b/src/microservice/domain/model/search/cities/search-cities-dto.model.ts new file mode 100644 index 0000000..29d6068 --- /dev/null +++ b/src/microservice/domain/model/search/cities/search-cities-dto.model.ts @@ -0,0 +1,22 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { DTO } from '../../dto.model'; + +export class SearchCitiesDTO extends DTO { + @ApiProperty({ + type: String, + description: 'The country of the City' + }) + public country: string; + + @ApiProperty({ + type: String, + description: 'The state of the City' + }) + public state: string; + + constructor(country: string, state: string) { + super(); + this.country = country; + this.state = state; + } +} diff --git a/src/microservice/domain/model/search/search-neighborhoods-db.model.ts b/src/microservice/domain/model/search/neighborhoods/search-neighborhoods-db.model.ts similarity index 76% rename from src/microservice/domain/model/search/search-neighborhoods-db.model.ts rename to src/microservice/domain/model/search/neighborhoods/search-neighborhoods-db.model.ts index 1ecd08c..965ebdf 100644 --- a/src/microservice/domain/model/search/search-neighborhoods-db.model.ts +++ b/src/microservice/domain/model/search/neighborhoods/search-neighborhoods-db.model.ts @@ -4,6 +4,6 @@ export class SearchNeighborhoodsDB { constructor( public countryId: number, public stateId: number, - public cityId: number + public cityId: number = null ) {} } diff --git a/src/microservice/domain/model/search/neighborhoods/search-neighborhoods-dto.model.ts b/src/microservice/domain/model/search/neighborhoods/search-neighborhoods-dto.model.ts new file mode 100644 index 0000000..5b20553 --- /dev/null +++ b/src/microservice/domain/model/search/neighborhoods/search-neighborhoods-dto.model.ts @@ -0,0 +1,35 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { DTO } from '../../dto.model'; + +export class SearchNeighborhoodsDTO extends DTO { + public name: string; + + @ApiProperty({ + type: String, + description: 'The country of the Neighborhood' + }) + public country: string; + + @ApiProperty({ + type: String, + description: 'The state of the Neighborhood' + }) + public state: string; + + @ApiProperty({ + type: String, + description: 'The city of the Neighborhood' + }) + public city: string; + + constructor(country: string, state: string, city: string = null) { + super(); + this.country = country; + this.state = state; + this.city = city; + } + + setName(name: string) { + this.name = name; + } +} diff --git a/src/microservice/domain/model/search/search-cities-db.model.ts b/src/microservice/domain/model/search/search-cities-db.model.ts deleted file mode 100644 index 84ab2ed..0000000 --- a/src/microservice/domain/model/search/search-cities-db.model.ts +++ /dev/null @@ -1,5 +0,0 @@ -export class SearchCitiesDB { - public id: any; - - constructor(public countryId: number, public stateId: number) {} -} diff --git a/src/microservice/domain/model/search/search-neighborhoods-input.model.ts b/src/microservice/domain/model/search/search-neighborhoods-input.model.ts deleted file mode 100644 index e0e4ec0..0000000 --- a/src/microservice/domain/model/search/search-neighborhoods-input.model.ts +++ /dev/null @@ -1,13 +0,0 @@ -export class SearchNeighborhoodsInput { - public name: string; - - constructor( - public country: string, - public state: string, - public city: string = null - ) {} - - setName(name: string) { - this.name = name; - } -} diff --git a/src/microservice/domain/model/states/states-by-country.model.ts b/src/microservice/domain/model/states/states-by-country.model.ts new file mode 100644 index 0000000..801f094 --- /dev/null +++ b/src/microservice/domain/model/states/states-by-country.model.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { PlacesReponse } from '../places-response.model'; + +export class StatesByCountry extends PlacesReponse { + @ApiProperty({ + type: String, + description: 'The name of the State', + example: 'Santa Catarina' + }) + name: string; +} diff --git a/src/microservice/domain/repository/mongoose/mongoose.repository.ts b/src/microservice/domain/repository/mongoose/mongoose.repository.ts index e907849..39bc53e 100644 --- a/src/microservice/domain/repository/mongoose/mongoose.repository.ts +++ b/src/microservice/domain/repository/mongoose/mongoose.repository.ts @@ -61,10 +61,16 @@ export abstract class MongooseRepository { buildRegexFilterQuery(objSearch: object = {}) { const objSearchRegex = {}; Object.keys(objSearch).forEach(function (key) { + if (objSearch[key] == null) return; objSearchRegex[key] = objSearch[key]; if (typeof objSearch[key] === 'string') objSearchRegex[key] = new RegExp(objSearch[key], 'i'); }); return objSearchRegex; } + + async findAll(select: object = {}): Promise { + if (Object.keys(select).length === 0) select = { _id: 0 }; + return this.model.find({}).select(select).lean().exec(); + } } diff --git a/src/microservice/domain/repository/mongoose/places-mongoose.repository.ts b/src/microservice/domain/repository/mongoose/places-mongoose.repository.ts index 914f1af..6afdfbc 100644 --- a/src/microservice/domain/repository/mongoose/places-mongoose.repository.ts +++ b/src/microservice/domain/repository/mongoose/places-mongoose.repository.ts @@ -10,22 +10,31 @@ export abstract class PlacesMongooseRepository< super(model); } - async findByNameOrAlias(name: string, extraSearch = {}): Promise { - const nameRegex = new RegExp(name, 'i'); + async findByNameOrAliasOrId(ref: string, extraSearch = {}): Promise { + const nameRegex = new RegExp(ref, 'i'); return this.model .find({ ...extraSearch, - $or: [{ name: nameRegex }, { alias: { $in: [nameRegex] } }] + $or: [ + { alias: { $in: [nameRegex] } }, + isNaN(parseInt(ref)) ? { name: nameRegex } : { id: ref } + ] }) .lean() .exec(); } - async findBySearchParams(searchParams: object): Promise { - return this.model - .find(this.buildRegexFilterQuery(searchParams)) - .select({ _id: 0 }) - .lean() - .exec(); + async findBySearchParams( + searchParams: object, + select: object = {}, + sort: any = { name: 1 } + ): Promise { + if (Object.keys(select).length === 0) select = { _id: 0 }; + let res = this.model.find(this.buildRegexFilterQuery(searchParams)); + + if (typeof sort === 'object' && Object.keys(sort).length > 0) + res = res.sort(sort); + + return res.select(select).lean().exec(); } } diff --git a/src/microservice/domain/repository/puppeteer/neighborhood/puppeteer-neighborhood.repository.ts b/src/microservice/domain/repository/puppeteer/neighborhood/puppeteer-neighborhood.repository.ts index b955114..93ff235 100644 --- a/src/microservice/domain/repository/puppeteer/neighborhood/puppeteer-neighborhood.repository.ts +++ b/src/microservice/domain/repository/puppeteer/neighborhood/puppeteer-neighborhood.repository.ts @@ -1,10 +1,11 @@ import { CheerioAPI } from 'cheerio'; import { NeighborhoodByCity } from '../../../model/neighborhoods/neighborhood-by-city.model'; -import { SearchNeighborhoodsInput } from '../../../model/search/search-neighborhoods-input.model'; +import { SearchNeighborhoodsDTO } from '../../../model/search/neighborhoods/search-neighborhoods-dto.model'; import { PuppeteerRepository } from '../puppeteer.repository'; import { IPuppeteerNeighborhoodRepository } from '../../../interface/puppeteer/repository/puppeteer-neighborhood-repository.interface'; import { NotFoundException } from '../../../../../core/error-handling/exception/not-found.exception'; -import { EnumTranslations } from 'src/microservice/domain/enumerators/enum-translations.enumerator'; +import { EnumTranslations } from '../../../enumerators/enum-translations.enumerator'; +import { ValidOutputSearchByCity } from '../../../interface/valid-output-search/valid-outpu-search.interface'; export abstract class PuppeteerNeighborhoodRepository extends PuppeteerRepository @@ -12,12 +13,17 @@ export abstract class PuppeteerNeighborhoodRepository { language: EnumTranslations; async getNeighborhoodsByCity( - searchParams: SearchNeighborhoodsInput + searchParams: SearchNeighborhoodsDTO, + convertedSearch: ValidOutputSearchByCity ): Promise { this.validateInput(searchParams); const $ = await this.callEndpoint(searchParams); - const elements = this.buildElementsFromDocument(searchParams, $); + const elements = this.buildElementsFromDocument( + searchParams, + convertedSearch, + $ + ); this.validateOutput(elements); @@ -29,11 +35,12 @@ export abstract class PuppeteerNeighborhoodRepository } abstract buildElementsFromDocument( - _searchParams: SearchNeighborhoodsInput, + _searchParams: SearchNeighborhoodsDTO, + convertedSearch: ValidOutputSearchByCity, _$: CheerioAPI ); abstract callEndpoint( - _searchParams: SearchNeighborhoodsInput + _searchParams: SearchNeighborhoodsDTO ): Promise; } diff --git a/src/microservice/domain/repository/puppeteer/puppeteer.repository.ts b/src/microservice/domain/repository/puppeteer/puppeteer.repository.ts index dcd55d9..0473e62 100644 --- a/src/microservice/domain/repository/puppeteer/puppeteer.repository.ts +++ b/src/microservice/domain/repository/puppeteer/puppeteer.repository.ts @@ -2,7 +2,7 @@ import { Logger } from '@nestjs/common'; import * as cheerio from 'cheerio'; import { CheerioAPI } from 'cheerio'; import { Page } from '../../interface/puppeteer/page.interface'; -import { SearchNeighborhoodsInput } from '../../model/search/search-neighborhoods-input.model'; +import { SearchNeighborhoodsDTO } from '../../model/search/neighborhoods/search-neighborhoods-dto.model'; export abstract class PuppeteerRepository { protected readonly logger: Logger = new Logger(this.constructor.name); @@ -27,7 +27,7 @@ export abstract class PuppeteerRepository { return this.page.evaluate(() => document.querySelector('*').outerHTML); } - validateInput(searchParams: SearchNeighborhoodsInput) { + validateInput(searchParams: SearchNeighborhoodsDTO) { searchParams.validateIsAnyEmptyKey(); } } diff --git a/src/microservice/domain/service/cities/get-cities-by-state.service.ts b/src/microservice/domain/service/cities/get-cities-by-state.service.ts deleted file mode 100644 index 36ca970..0000000 --- a/src/microservice/domain/service/cities/get-cities-by-state.service.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { NeighborhoodsMongoose } from '../../../adapter/repository/neighborhoods/neighborhoods-mongoose.repository'; -import { CitiesMongoose } from '../../../adapter/repository/cities/cities-mongoose.repository'; -import { AggregatedNeighborhoodsCity } from '../../interface/aggregated/aggregated-neighborhoods-city.interface'; -import { SearchCitiesDB } from '../../model/search/search-cities-db.model'; -import { City } from '../../schemas/city.schema'; -import { CitiesService } from './cities.service'; - -@Injectable() -export class GetCitiesByStateService extends CitiesService { - constructor( - mongoRepository: CitiesMongoose, - private readonly mongoNeighborhoodsRepository: NeighborhoodsMongoose - ) { - super(mongoRepository); - } - - async getCitiesByState( - searchParams: SearchCitiesDB, - arrIgnore = [] - ): Promise { - if (arrIgnore.length > 0) searchParams.id = { $nin: arrIgnore }; - return this.mongoRepository.findBySearchParams(searchParams); - } - - async groupByCity(stateId: number): Promise { - return this.mongoNeighborhoodsRepository.groupBy( - { cityId: '$cityId' }, - { stateId }, - { city: 'city' } - ); - } -} diff --git a/src/microservice/domain/service/cities/get/get-cities-by-country.service.ts b/src/microservice/domain/service/cities/get/get-cities-by-country.service.ts new file mode 100644 index 0000000..86f6639 --- /dev/null +++ b/src/microservice/domain/service/cities/get/get-cities-by-country.service.ts @@ -0,0 +1,49 @@ +import { Injectable } from '@nestjs/common'; +import { CitiesByCountry } from '../../../model/cities/cities-by-country.model'; +import { CitiesMongoose } from '../../../../adapter/repository/cities/cities-mongoose.repository'; +import { City } from '../../../schemas/city.schema'; +import { ValidateCountryByNameOrAliasService } from '../../countries/validate-country-by-name-or-alias.service'; +import { CitiesService } from '../cities.service'; +import { CitiesByCountryBuilder } from '../../../../adapter/helper/builder/cities/cities-by-country.builder'; + +@Injectable() +export class GetCitiesByCountryService extends CitiesService { + constructor( + mongoRepository: CitiesMongoose, + protected readonly validateCountryService: ValidateCountryByNameOrAliasService + ) { + super(mongoRepository); + } + + async getCitiesByCountry(countryRef: string): Promise { + const country = await this.validateCountryService.validateCountry( + countryRef + ); + + this.logger.log('Searching cities in database...'); + + const arrCities = await this.findCitiesByCountry(country.id); + return new CitiesByCountryBuilder(arrCities).build(); + } + + async findCitiesByCountry(countryId: number): Promise { + const select = { + _id: 0, + id: 1, + name: 1, + countryId: 1, + countryCode: 1, + stateId: 1, + stateName: 1, + state: 1, + stateCode: 1, + city: 1, + cityId: 1 + }; + + return this.mongoRepository.findBySearchParams({ countryId }, select, { + stateName: 1, + name: 1 + }); + } +} diff --git a/src/microservice/domain/service/cities/get/get-cities-by-state.service.ts b/src/microservice/domain/service/cities/get/get-cities-by-state.service.ts new file mode 100644 index 0000000..c2f564c --- /dev/null +++ b/src/microservice/domain/service/cities/get/get-cities-by-state.service.ts @@ -0,0 +1,51 @@ +import { Injectable } from '@nestjs/common'; +import { CitiesMongoose } from '../../../../adapter/repository/cities/cities-mongoose.repository'; +import { CityResponse } from '../../../model/cities/city-response.model'; +import { SearchCitiesDB } from '../../../model/search/cities/search-cities-db.model'; +import { SearchCitiesDTO } from '../../../model/search/cities/search-cities-dto.model'; +import { City } from '../../../schemas/city.schema'; +import { ValidateInputParamsService } from '../../validate/validate-input-params.service'; +import { CitiesService } from '../cities.service'; + +@Injectable() +export class GetCitiesByStateService extends CitiesService { + constructor( + mongoRepository: CitiesMongoose, + protected readonly validateService: ValidateInputParamsService + ) { + super(mongoRepository); + } + + async getCitiesByState( + searchParams: SearchCitiesDTO + ): Promise { + const convertedSearch = + await this.validateService.validateAndConvertSearchByState(searchParams); + + const searchDB = new SearchCitiesDB( + convertedSearch.country.id, + convertedSearch.state.id + ); + + this.logger.log('Searching cities in database...'); + + return this.findCitiesByState(searchDB); + } + + async findCitiesByState( + searchParams: SearchCitiesDB, + arrIgnore = [] + ): Promise { + if (arrIgnore.length > 0) searchParams.id = { $nin: arrIgnore }; + const select = { + _id: 0, + id: 1, + name: 1, + countryId: 1, + countryCode: 1, + stateId: 1, + stateCode: 1 + }; + return this.mongoRepository.findBySearchParams(searchParams, select); + } +} diff --git a/src/microservice/domain/service/cities/validate-city-by-name-or-alias.service.ts b/src/microservice/domain/service/cities/validate-city-by-name-or-alias.service.ts index cf4083a..7c86331 100644 --- a/src/microservice/domain/service/cities/validate-city-by-name-or-alias.service.ts +++ b/src/microservice/domain/service/cities/validate-city-by-name-or-alias.service.ts @@ -15,7 +15,10 @@ export class ValidateCityByNameOrAliasService extends CitiesService { countryId: number, stateId: number ): Promise { - return this.mongoRepository.findByNameOrAlias(name, { countryId, stateId }); + return this.mongoRepository.findByNameOrAliasOrId(name, { + countryId, + stateId + }); } async validateCity( diff --git a/src/microservice/domain/service/countries/countries.service.ts b/src/microservice/domain/service/countries/countries.service.ts index 3bdcb71..ea5abbd 100644 --- a/src/microservice/domain/service/countries/countries.service.ts +++ b/src/microservice/domain/service/countries/countries.service.ts @@ -1,4 +1,4 @@ -import { CountriesMongoose } from 'src/microservice/adapter/repository/countries/countries-mongoose.repository'; +import { CountriesMongoose } from '../../../adapter/repository/countries/countries-mongoose.repository'; import { AbstractService } from '../abstract-service.service'; export abstract class CountriesService extends AbstractService { diff --git a/src/microservice/domain/service/countries/get-countries.service.ts b/src/microservice/domain/service/countries/get-countries.service.ts new file mode 100644 index 0000000..0217045 --- /dev/null +++ b/src/microservice/domain/service/countries/get-countries.service.ts @@ -0,0 +1,28 @@ +import { Injectable } from '@nestjs/common'; +import { CountriesMongoose } from '../../../adapter/repository/countries/countries-mongoose.repository'; +import { CountryResponse } from '../../model/countries/country-response.model'; +import { CountriesService } from './countries.service'; + +@Injectable() +export class GetCountriesService extends CountriesService { + constructor(mongoRepository: CountriesMongoose) { + super(mongoRepository); + } + + async getAll(): Promise { + const select = { + _id: 0, + id: 1, + name: 1, + iso3: 1, + iso2: 1, + capital: 1, + currency: 1, + region: 1, + subregion: 1, + alias: 1, + phoneCode: 1 + }; + return this.mongoRepository.findAll(select); + } +} diff --git a/src/microservice/domain/service/countries/validate-country-by-name-or-alias.service.ts b/src/microservice/domain/service/countries/validate-country-by-name-or-alias.service.ts index 2c0acf7..3d2b30f 100644 --- a/src/microservice/domain/service/countries/validate-country-by-name-or-alias.service.ts +++ b/src/microservice/domain/service/countries/validate-country-by-name-or-alias.service.ts @@ -11,7 +11,7 @@ export class ValidateCountryByNameOrAliasService extends CountriesService { } async getCountryByNameOrAlias(name: string): Promise { - return this.mongoRepository.findByNameOrAlias(name); + return this.mongoRepository.findByNameOrAliasOrId(name); } async validateCountry(country: string): Promise { diff --git a/src/microservice/domain/service/neighborhoods/get/get-neighborhoods-by-city.service.ts b/src/microservice/domain/service/neighborhoods/get/get-neighborhoods-by-city.service.ts index b09a0ed..9eef8ed 100644 --- a/src/microservice/domain/service/neighborhoods/get/get-neighborhoods-by-city.service.ts +++ b/src/microservice/domain/service/neighborhoods/get/get-neighborhoods-by-city.service.ts @@ -1,13 +1,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { NeighborhoodByCity } from '../../../model/neighborhoods/neighborhood-by-city.model'; -import { SearchNeighborhoodsInput } from '../../../model/search/search-neighborhoods-input.model'; +import { SearchNeighborhoodsDTO } from '../../../model/search/neighborhoods/search-neighborhoods-dto.model'; import { NeighborhoodsMongoose } from '../../../../adapter/repository/neighborhoods/neighborhoods-mongoose.repository'; import { GuiaMaisRepository } from '../../../../adapter/repository/neighborhoods/puppeteer/guia-mais.repository'; import { SaveNeighborhoodsByCityService } from '../save-neighborhoods-by-city.service'; -import { ValidOutputSearchNeighborhood } from '../../../interface/valid-output-search/valid-outpu-search-neighborhood.interface'; -import { SearchNeighborhoodsDB } from '../../../model/search/search-neighborhoods-db.model'; +import { ValidOutputSearchByCity } from '../../../interface/valid-output-search/valid-outpu-search.interface'; +import { SearchNeighborhoodsDB } from '../../../model/search/neighborhoods/search-neighborhoods-db.model'; import { NeighborhoodsService } from '../neighborhoods.service'; -import { ValidateInputParamsService } from '../../validate-input-params.service'; +import { ValidateInputParamsService } from '../../validate/validate-input-params.service'; @Injectable() export class GetNeighborhoodsByCityService extends NeighborhoodsService { @@ -22,7 +22,7 @@ export class GetNeighborhoodsByCityService extends NeighborhoodsService { } async getNeighborhoodsByCity( - searchParams: SearchNeighborhoodsInput + searchParams: SearchNeighborhoodsDTO ): Promise { const convertedSearch = await this.validateService.validateAndConvertSearchByCity(searchParams); @@ -48,11 +48,12 @@ export class GetNeighborhoodsByCityService extends NeighborhoodsService { } async searchByPuppeterAndSave( - searchParams: SearchNeighborhoodsInput, - convertedSearch: ValidOutputSearchNeighborhood + searchParams: SearchNeighborhoodsDTO, + convertedSearch: ValidOutputSearchByCity ): Promise { const resPuppeteer = await this.guiaMaisRepository.getNeighborhoodsByCity( - searchParams + searchParams, + convertedSearch ); await this.saveNeighborhoodsService.saveNeighborhoodsByCity( @@ -65,7 +66,7 @@ export class GetNeighborhoodsByCityService extends NeighborhoodsService { } async findNeighborhoodsByCityInDatabase( - convertedSearch: ValidOutputSearchNeighborhood + convertedSearch: ValidOutputSearchByCity ): Promise { const searchDB = new SearchNeighborhoodsDB( convertedSearch.country.id, diff --git a/src/microservice/domain/service/neighborhoods/get/get-neighborhoods-by-state.service.ts b/src/microservice/domain/service/neighborhoods/get/get-neighborhoods-by-state.service.ts index bcb1859..6338403 100644 --- a/src/microservice/domain/service/neighborhoods/get/get-neighborhoods-by-state.service.ts +++ b/src/microservice/domain/service/neighborhoods/get/get-neighborhoods-by-state.service.ts @@ -1,77 +1,64 @@ import { Injectable } from '@nestjs/common'; -import { SearchNeighborhoodsInput } from '../../../model/search/search-neighborhoods-input.model'; +import { SearchNeighborhoodsDTO } from '../../../model/search/neighborhoods/search-neighborhoods-dto.model'; import { NeighborhoodsMongoose } from '../../../../adapter/repository/neighborhoods/neighborhoods-mongoose.repository'; -import { ValidOutputSearchNeighborhood } from '../../../interface/valid-output-search/valid-outpu-search-neighborhood.interface'; -import { SearchNeighborhoodsDB } from '../../../model/search/search-neighborhoods-db.model'; -import { - NeighborhoodsByState, - NeighborhooodAggregatedByCity -} from '../../../model/neighborhoods/neighborhoods-by-state.model'; +import { ValidOutputSearchByState } from '../../../interface/valid-output-search/valid-outpu-search.interface'; +import { SearchNeighborhoodsDB } from '../../../model/search/neighborhoods/search-neighborhoods-db.model'; +import { NeighborhoodsByState } from '../../../model/neighborhoods/neighborhoods-by-state.model'; import { NeighborhoodsService } from '../neighborhoods.service'; -import { ValidateInputParamsService } from '../../validate-input-params.service'; -import { GetCitiesByStateService } from '../../cities/get-cities-by-state.service'; +import { ValidateInputParamsService } from '../../validate/validate-input-params.service'; +import { AggregatedNeighborhoodsByCity } from '../../../interface/aggregated/aggregated-neighborhoods-city.interface'; +import { NeighborhoodsByStateBuilder } from '../../../../adapter/helper/builder/neighborhoods/neighborhoods-by-state.builder'; +import { Neighborhood } from '../../../schemas/neighborhood.schema'; @Injectable() export class GetNeighborhoodsByStateService extends NeighborhoodsService { constructor( protected readonly validateService: ValidateInputParamsService, - protected readonly getCitiesByStateService: GetCitiesByStateService, mongoRepository: NeighborhoodsMongoose ) { super(mongoRepository); } async getNeighborhoodsByState( - searchParams: SearchNeighborhoodsInput + searchParams: SearchNeighborhoodsDTO ): Promise { const convertedSearch = await this.validateService.validateAndConvertSearchByState(searchParams); return this.findNeighborhoodsByStateInDatabase(convertedSearch); } + async findNeighborhoodsByStateInDatabase( - convertedSearch: ValidOutputSearchNeighborhood + convertedSearch: ValidOutputSearchByState ): Promise { - const arrResponse: NeighborhoodsByState = {}; - this.logger.log( - `Searching cities for state '${convertedSearch.state.stateCode}'...` - ); - const aggregatedByCity = await this.getCitiesByStateService.groupByCity( - convertedSearch.state.id + `Searching Neighborhoods for state '${convertedSearch.state.stateCode}'...` ); - this.logger.log(`Founded cities: ${aggregatedByCity.length}`); - for await (const item of aggregatedByCity) { - const cityId = item._id.cityId; - const arrNeighborhoods = await this.findByCityAndStateInDatabase( - convertedSearch, - cityId - ); - - this.logger.log( - `City "${item.city}": ${arrNeighborhoods.length} Neighborhoods` - ); + const arrByState = await this.findByStateInDatabase(convertedSearch); + this.logger.log(`Founded Neighborhoods: ${arrByState.length}`); - arrResponse[item.city] = arrNeighborhoods.map((neighborhood) => { - const obj = new NeighborhooodAggregatedByCity(); - obj.name = neighborhood.name; - obj.cityId = cityId; - obj.state = `${convertedSearch.state.name} - ${convertedSearch.country.iso3}`; - return obj; - }); - } - return arrResponse; + return new NeighborhoodsByStateBuilder(arrByState).build(convertedSearch); } - async findByCityAndStateInDatabase( - convertedSearch: ValidOutputSearchNeighborhood, - cityId: number - ) { + async findByStateInDatabase( + convertedSearch: ValidOutputSearchByState + ): Promise { const searchDB = new SearchNeighborhoodsDB( convertedSearch.country.id, - convertedSearch.state.id, - cityId + convertedSearch.state.id + ); + return this.mongoRepository.findBySearchParams( + searchDB, + { _id: 0, name: 1, cityId: 1, state: 1, city: 1 }, + { city: 1, name: 1 } + ); + } + + async groupByCity(stateId: number): Promise { + return this.mongoRepository.groupBy( + { cityId: '$cityId' }, + { stateId }, + { city: 'city' } ); - return this.findInDatabase(searchDB); } } diff --git a/src/microservice/domain/service/neighborhoods/neighborhoods.service.ts b/src/microservice/domain/service/neighborhoods/neighborhoods.service.ts index be4a66a..fa2f97d 100644 --- a/src/microservice/domain/service/neighborhoods/neighborhoods.service.ts +++ b/src/microservice/domain/service/neighborhoods/neighborhoods.service.ts @@ -1,7 +1,7 @@ import { NeighborhoodsPuppeteerBuilder } from '../../../adapter/helper/builder/neighborhoods/neighborhoods-puppeteer.builder'; import { NeighborhoodsMongoose } from '../../../adapter/repository/neighborhoods/neighborhoods-mongoose.repository'; import { NeighborhoodByCity } from '../../model/neighborhoods/neighborhood-by-city.model'; -import { SearchNeighborhoodsDB } from '../../model/search/search-neighborhoods-db.model'; +import { SearchNeighborhoodsDB } from '../../model/search/neighborhoods/search-neighborhoods-db.model'; import { AbstractService } from '../abstract-service.service'; export abstract class NeighborhoodsService extends AbstractService { diff --git a/src/microservice/domain/service/neighborhoods/save-neighborhoods-by-city.service.ts b/src/microservice/domain/service/neighborhoods/save-neighborhoods-by-city.service.ts index 7d146f1..ff7bd2a 100644 --- a/src/microservice/domain/service/neighborhoods/save-neighborhoods-by-city.service.ts +++ b/src/microservice/domain/service/neighborhoods/save-neighborhoods-by-city.service.ts @@ -2,9 +2,9 @@ import { Injectable } from '@nestjs/common'; import { NeighborhoodByCity } from '../../model/neighborhoods/neighborhood-by-city.model'; import { NeighborhoodsMongoose } from '../../../adapter/repository/neighborhoods/neighborhoods-mongoose.repository'; import { NeighborhoodsMongoBuilder } from '../../../adapter/helper/builder/neighborhoods/neighborhoods-mongo.builder'; -import { SearchNeighborhoodsInput } from '../../model/search/search-neighborhoods-input.model'; +import { SearchNeighborhoodsDTO } from '../../model/search/neighborhoods/search-neighborhoods-dto.model'; import { NeighborhoodsService } from './neighborhoods.service'; -import { ValidOutputSearchNeighborhood } from '../../interface/valid-output-search/valid-outpu-search-neighborhood.interface'; +import { ValidOutputSearchByCity } from '../../interface/valid-output-search/valid-outpu-search.interface'; import { Neighborhood } from '../../schemas/neighborhood.schema'; import { SearchNeighborhoodsDBBuilder } from '../../../adapter/helper/builder/neighborhoods/search-neighborhoods-db.builder'; @@ -16,8 +16,8 @@ export class SaveNeighborhoodsByCityService extends NeighborhoodsService { async saveNeighborhoodsByCity( neighborhoodsPuppeteer: NeighborhoodByCity[], - searchParams: SearchNeighborhoodsInput, - convertedSearch: ValidOutputSearchNeighborhood + searchParams: SearchNeighborhoodsDTO, + convertedSearch: ValidOutputSearchByCity ): Promise { const arrDocument = new NeighborhoodsMongoBuilder( neighborhoodsPuppeteer @@ -36,7 +36,7 @@ export class SaveNeighborhoodsByCityService extends NeighborhoodsService { async createNeighborhood( item: Neighborhood, - convertedSearch: ValidOutputSearchNeighborhood + convertedSearch: ValidOutputSearchByCity ) { item.countryId = convertedSearch.country.id; item.country = convertedSearch.country.name.capitalize(); @@ -51,7 +51,7 @@ export class SaveNeighborhoodsByCityService extends NeighborhoodsService { } async findNeighborhoodInDatabase( - convertedSearch: ValidOutputSearchNeighborhood, + convertedSearch: ValidOutputSearchByCity, name: string ) { const searchParams = new SearchNeighborhoodsDBBuilder( diff --git a/src/microservice/domain/service/neighborhoods/seed/seed-neighborhoods-by-state.service.ts b/src/microservice/domain/service/neighborhoods/seed/seed-neighborhoods-by-state.service.ts index 0923b3c..00f98c0 100644 --- a/src/microservice/domain/service/neighborhoods/seed/seed-neighborhoods-by-state.service.ts +++ b/src/microservice/domain/service/neighborhoods/seed/seed-neighborhoods-by-state.service.ts @@ -1,23 +1,25 @@ import { Injectable } from '@nestjs/common'; -import { SearchNeighborhoodsInput } from '../../../model/search/search-neighborhoods-input.model'; +import { SearchNeighborhoodsDTO } from '../../../model/search/neighborhoods/search-neighborhoods-dto.model'; import { NeighborhoodsService } from '../neighborhoods.service'; -import { ValidateInputParamsService } from '../../validate-input-params.service'; -import { GetCitiesByStateService } from '../../cities/get-cities-by-state.service'; -import { SearchCitiesDB } from '../../../model/search/search-cities-db.model'; +import { ValidateInputParamsService } from '../../validate/validate-input-params.service'; +import { GetCitiesByStateService } from '../../cities/get/get-cities-by-state.service'; +import { SearchCitiesDB } from '../../../model/search/cities/search-cities-db.model'; import { GetNeighborhoodsByCityService } from '../get/get-neighborhoods-by-city.service'; -import { ValidOutputSearchNeighborhood } from '../../../interface/valid-output-search/valid-outpu-search-neighborhood.interface'; +import { ValidOutputSearchByState } from '../../../interface/valid-output-search/valid-outpu-search.interface'; import { EnumTranslations } from '../../../enumerators/enum-translations.enumerator'; import { City } from '../../../schemas/city.schema'; import { NeighborhoodsMongoose } from '../../../../adapter/repository/neighborhoods/neighborhoods-mongoose.repository'; import { LogSeedJobService } from '../../logseed/log-seed-job.service'; -import { CustomResponse } from 'src/core/interface/custom-response.interface'; +import { CustomResponse } from '../../../../../core/interface/custom-response.interface'; +import { GetNeighborhoodsByStateService } from '../get/get-neighborhoods-by-state.service'; @Injectable() export class SeedNeighborhoodsByStateService extends NeighborhoodsService { constructor( mongoRepository: NeighborhoodsMongoose, private readonly validateService: ValidateInputParamsService, - private readonly getNeighborhoodsService: GetNeighborhoodsByCityService, + private readonly getNeighborhoodsByCityService: GetNeighborhoodsByCityService, + private readonly getNeighborhoodsByStateService: GetNeighborhoodsByStateService, private readonly getCitiesByStateService: GetCitiesByStateService, private readonly logSeedService: LogSeedJobService ) { @@ -25,7 +27,7 @@ export class SeedNeighborhoodsByStateService extends NeighborhoodsService { } async seedNeighborhoodsByState( - searchParams: SearchNeighborhoodsInput + searchParams: SearchNeighborhoodsDTO ): Promise { const convertedSearch = await this.validateService.validateAndConvertSearchByState(searchParams); @@ -40,7 +42,7 @@ export class SeedNeighborhoodsByStateService extends NeighborhoodsService { ); this.logger.log(`Getting cities for state '${searchParams.state}'...`); - const cities = await this.getCitiesByStateService.getCitiesByState( + const cities = await this.getCitiesByStateService.findCitiesByState( searchCities, arrSeededCities ); @@ -71,7 +73,7 @@ export class SeedNeighborhoodsByStateService extends NeighborhoodsService { } async logErrorSeedJob( - convertedSearch: ValidOutputSearchNeighborhood, + convertedSearch: ValidOutputSearchByState, city: City, err: Error ): Promise { @@ -83,25 +85,27 @@ export class SeedNeighborhoodsByStateService extends NeighborhoodsService { ); } - async seedByCity(convertedSearch: ValidOutputSearchNeighborhood, city: City) { - const searchParamsByCity = new SearchNeighborhoodsInput( + async seedByCity(convertedSearch: ValidOutputSearchByState, city: City) { + const searchParamsByCity = new SearchNeighborhoodsDTO( convertedSearch.country.translations[EnumTranslations.BR], convertedSearch.state.stateCode, city.name ); - convertedSearch.city = city; this.logger.log(`Seeding city[${city.id}] ${city.name}...`); - await this.getNeighborhoodsService.searchByPuppeterAndSave( + await this.getNeighborhoodsByCityService.searchByPuppeterAndSave( searchParamsByCity, - convertedSearch + { + country: convertedSearch.country, + state: convertedSearch.state, + city + } ); } async getSeededCities(stateId: number): Promise { this.logger.log('Getting seeded cities...'); - const aggregatedCities = await this.getCitiesByStateService.groupByCity( - stateId - ); + const aggregatedCities = + await this.getNeighborhoodsByStateService.groupByCity(stateId); return aggregatedCities.map((item) => { return item._id.cityId; }); diff --git a/src/microservice/domain/service/states/get-states-by-country.service.ts b/src/microservice/domain/service/states/get-states-by-country.service.ts new file mode 100644 index 0000000..1d17b9f --- /dev/null +++ b/src/microservice/domain/service/states/get-states-by-country.service.ts @@ -0,0 +1,40 @@ +import { Injectable } from '@nestjs/common'; +import { StatesMongoose } from '../../../adapter/repository/states/states-mongoose.repository'; +import { StatesByCountry } from '../../model/states/states-by-country.model'; +import { State } from '../../schemas/state.schema'; +import { ValidateCountryByNameOrAliasService } from '../countries/validate-country-by-name-or-alias.service'; +import { StatesService } from './states.service'; + +@Injectable() +export class GetStatesByCountryService extends StatesService { + constructor( + mongoRepository: StatesMongoose, + protected readonly validateCountryService: ValidateCountryByNameOrAliasService + ) { + super(mongoRepository); + } + + async getStatesByCountry(countryRef: string): Promise { + const country = await this.validateCountryService.validateCountry( + countryRef + ); + + this.logger.log('Searching states in database...'); + + return this.findStatesByCountry(country.id); + } + + async findStatesByCountry(countryId: number): Promise { + const select = { + _id: 0, + id: 1, + name: 1, + countryId: 1, + countryCode: 1, + stateId: 1, + stateCode: 1 + }; + + return this.mongoRepository.findBySearchParams({ countryId }, select); + } +} diff --git a/src/microservice/domain/service/states/validate-state-by-name-or-alias.service.ts b/src/microservice/domain/service/states/validate-state-by-name-or-alias.service.ts index 7260675..be12556 100644 --- a/src/microservice/domain/service/states/validate-state-by-name-or-alias.service.ts +++ b/src/microservice/domain/service/states/validate-state-by-name-or-alias.service.ts @@ -14,7 +14,7 @@ export class ValidateStateByNameOrAliasService extends StatesService { name: string, countryId: number ): Promise { - return this.mongoRepository.findByNameOrAlias(name, { countryId }); + return this.mongoRepository.findByNameOrAliasOrId(name, { countryId }); } async validateState(state: string, countryId: number): Promise { diff --git a/src/microservice/domain/service/validate-input-params.service.ts b/src/microservice/domain/service/validate/validate-input-params.service.ts similarity index 53% rename from src/microservice/domain/service/validate-input-params.service.ts rename to src/microservice/domain/service/validate/validate-input-params.service.ts index 9633e19..deeebaa 100644 --- a/src/microservice/domain/service/validate-input-params.service.ts +++ b/src/microservice/domain/service/validate/validate-input-params.service.ts @@ -1,10 +1,14 @@ import { Injectable } from '@nestjs/common'; -import { SearchNeighborhoodsInput } from '../model/search/search-neighborhoods-input.model'; -import { ValidOutputSearchNeighborhood } from '../interface/valid-output-search/valid-outpu-search-neighborhood.interface'; -import { AbstractService } from './abstract-service.service'; -import { ValidateCountryByNameOrAliasService } from './countries/validate-country-by-name-or-alias.service'; -import { ValidateStateByNameOrAliasService } from './states/validate-state-by-name-or-alias.service'; -import { ValidateCityByNameOrAliasService } from './cities/validate-city-by-name-or-alias.service'; +import { SearchNeighborhoodsDTO } from '../../model/search/neighborhoods/search-neighborhoods-dto.model'; +import { + ValidOutputSearchByCity, + ValidOutputSearchByState +} from '../../interface/valid-output-search/valid-outpu-search.interface'; +import { AbstractService } from '../abstract-service.service'; +import { ValidateCountryByNameOrAliasService } from '../countries/validate-country-by-name-or-alias.service'; +import { ValidateStateByNameOrAliasService } from '../states/validate-state-by-name-or-alias.service'; +import { ValidateCityByNameOrAliasService } from '../cities/validate-city-by-name-or-alias.service'; +import { SearchCitiesDTO } from '../../model/search/cities/search-cities-dto.model'; @Injectable() export class ValidateInputParamsService extends AbstractService { @@ -17,8 +21,8 @@ export class ValidateInputParamsService extends AbstractService { } async validateAndConvertSearchByState( - searchParams: SearchNeighborhoodsInput - ): Promise { + searchParams: SearchNeighborhoodsDTO | SearchCitiesDTO + ): Promise { const country = await this.getCountryService.validateCountry( searchParams.country ); @@ -26,12 +30,12 @@ export class ValidateInputParamsService extends AbstractService { searchParams.state, country.id ); - return { country, state, city: null }; + return { country, state }; } async validateAndConvertSearchByCity( - searchParams: SearchNeighborhoodsInput - ): Promise { + searchParams: SearchNeighborhoodsDTO + ): Promise { const country = await this.getCountryService.validateCountry( searchParams.country ); diff --git a/swagger-spec.json b/swagger-spec.json new file mode 100644 index 0000000..7ef527c --- /dev/null +++ b/swagger-spec.json @@ -0,0 +1 @@ +{"openapi":"3.0.0","paths":{"/neighborhoods/city/{country}/{state}/{city}":{"get":{"operationId":"NeighborhoodsController_getNeighborhoodsByCity","summary":"","description":"Get Neighborhoods By City Reference","parameters":[{"name":"country","required":true,"in":"path","description":"The Country name of the neighborhood","schema":{"type":"string"}},{"name":"state","required":true,"in":"path","description":"The State name of the neighborhood","schema":{"type":"string"}},{"name":"city","required":true,"in":"path","description":"The City name of the neighborhood","schema":{"type":"string"}}],"responses":{"200":{"description":"Neighborhoods found.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/NeighborhoodByCity"}}}}}},"tags":["neighborhoods"]}},"/neighborhoods/state/{country}/{state}":{"get":{"operationId":"NeighborhoodsController_getNeighborhoodsByState","summary":"","description":"Get Neighborhoods By State Reference","parameters":[{"name":"country","required":true,"in":"path","description":"The Country name of the neighborhood","schema":{"type":"string"}},{"name":"state","required":true,"in":"path","description":"The State name of the neighborhood","schema":{"type":"string"}},{"name":"city","required":true,"in":"path","description":"The city of the Neighborhood","schema":{"type":"string"}}],"responses":{"200":{"description":"Neighborhoods found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NeighborhoodsByState"}}}}},"tags":["neighborhoods"]}},"/cities/state/{country}/{state}":{"get":{"operationId":"CitiesController_getCitiesByState","summary":"","description":"Get Cities By State Reference","parameters":[{"name":"country","required":true,"in":"path","description":"The Country name of the city","schema":{"type":"string"}},{"name":"state","required":true,"in":"path","description":"The State name of the city","schema":{"type":"string"}}],"responses":{"200":{"description":"Cities found.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CityResponse"}}}}}},"tags":["cities"]}},"/cities/country/{country}":{"get":{"operationId":"CitiesController_getCitiesByCountry","summary":"","description":"Get Cities By Country Reference","parameters":[{"name":"country","required":true,"in":"path","description":"The Country name of the city","schema":{"type":"string"}}],"responses":{"200":{"description":"Cities found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CitiesByCountry"}}}}},"tags":["cities"]}},"/states/country/{country}":{"get":{"operationId":"StatesController_getStatesByCountry","parameters":[{"name":"country","required":true,"in":"path","description":"The Country name of the state","schema":{"type":"string"}}],"responses":{"200":{"description":"States found.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/StatesByCountry"}}}}}},"tags":["states"]}},"/countries":{"get":{"operationId":"CountriesController_getAllCountries","summary":"","description":"Get All Countries","parameters":[],"responses":{"200":{"description":"Countries found.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CountryResponse"}}}}}},"tags":["countries"]}}},"info":{"title":"Places","description":"Places API","version":"0.0.5","contact":{}},"tags":[],"servers":[],"components":{"schemas":{"NeighborhoodByCity":{"type":"object","properties":{"name":{"type":"string","description":"The name of the Neighborhood","example":"Alto Paraná"},"cityId":{"type":"number","description":"The id reference of the City in database","example":1},"city":{"type":"string","description":"The name of the City's Neighborhood","example":"Orleans"},"stateId":{"type":"number","description":"The id reference of the state in database","example":2014},"countryId":{"type":"number","description":"The id reference of the country in database","example":31}},"required":["name","cityId","city","stateId","countryId"]},"NeighborhoodsByState":{"type":"object","properties":{}},"CityResponse":{"type":"object","properties":{"id":{"type":"number","description":"City Id reference in database","example":1},"name":{"type":"string","description":"The name of the City","example":"Orleans"},"countryCode":{"type":"string","description":"The code(ISO3) of the Country's City","example":"BRA"},"countryId":{"type":"number","description":"Country Id reference in database","example":31},"stateCode":{"type":"string","description":"The code(ISO2) of the State's City","example":"SC"},"stateId":{"type":"number","description":"State Id reference in database","example":2014}},"required":["id","name","countryCode","countryId","stateCode","stateId"]},"CitiesByCountry":{"type":"object","properties":{}},"StatesByCountry":{"type":"object","properties":{"id":{"type":"number","description":"State Id reference in database","example":1},"name":{"type":"string","description":"The name of the State","example":"Orleans"},"countryCode":{"type":"string","description":"The code(ISO3) of the Country's State","example":"BRA"},"countryId":{"type":"number","description":"Country Id reference in database","example":31},"stateCode":{"type":"string","description":"The code(ISO2) of the State","example":"SC"}},"required":["id","name","countryCode","countryId","stateCode"]},"CountryResponse":{"type":"object","properties":{"id":{"type":"number","description":"Country Id reference in database","example":1},"name":{"type":"string","description":"Country Name","example":"Brasil"},"iso3":{"type":"string","description":"The code(ISO3) of the Country","example":"BRA"},"iso2":{"type":"string","description":"The code(ISO2) of the Country","example":"BR"},"capital":{"type":"string","description":"Capital","example":"Brasília"},"currency":{"type":"string","description":"Currency Symbol","example":"R$"},"region":{"type":"string","description":"Continent Region of the country","example":"South America"},"subregion":{"type":"string","description":"Continent SubRegion of the country","example":"South"},"alias":{"description":"Alias names","example":["BR","BRA","Brazil","Brasil"],"type":"array","items":{"type":"string"}},"phoneCode":{"type":"number","description":"Phone number code DDI","example":55}},"required":["id","name","iso3","iso2","capital","currency","region","subregion","alias","phoneCode"]}}}} \ No newline at end of file diff --git a/test/e2e/app.e2e-spec.ts b/test/e2e/app.e2e-spec.ts index b98ad97..084085a 100644 --- a/test/e2e/app.e2e-spec.ts +++ b/test/e2e/app.e2e-spec.ts @@ -5,7 +5,7 @@ import { AppModule } from '../../src/app.module'; import { NestFactory } from '@nestjs/core'; import '../../src/microservice/adapter/helper/extensions/exensions.module'; -jest.setTimeout(50000); +jest.setTimeout(400000); describe('App (e2e) ', () => { let app: INestApplication; @@ -41,4 +41,42 @@ describe('App (e2e) ', () => { expect(actual.body.Orleans).to.be.an('array').that.is.not.empty; }); }); + + describe('City (e2e) ', () => { + it('/cities/state/brasil/sc (GET)', async () => { + const actual = await request(app.getHttpServer()).get( + '/cities/state/brasil/sc' + ); + + expect(actual.body).to.be.an('array').that.is.not.empty; + }); + + it('/cities/country/brasil (GET)', async () => { + const actual = await request(app.getHttpServer()).get( + '/cities/country/brasil' + ); + + expect(actual.body).to.be.an('object').that.is.not.empty; + expect(actual.body['Santa Catarina - SC']).to.be.an('array').that.is.not + .empty; + }); + }); + + describe('State (e2e) ', () => { + it('/states/country/brasil (GET)', async () => { + const actual = await request(app.getHttpServer()).get( + '/states/country/brasil' + ); + + expect(actual.body).to.be.an('array').that.is.not.empty; + }); + }); + + describe('Country (e2e) ', () => { + it('/countries/ (GET)', async () => { + const actual = await request(app.getHttpServer()).get('/countries/'); + + expect(actual.body).to.be.an('array').that.is.not.empty; + }); + }); }); diff --git a/test/mock/mongoose/mock-mongoose.ts b/test/mock/mongoose/mock-mongoose.ts index a0e567c..9c2ad39 100644 --- a/test/mock/mongoose/mock-mongoose.ts +++ b/test/mock/mongoose/mock-mongoose.ts @@ -1,7 +1,15 @@ export const mockModelMongoose = { find: () => { return { - exec: jest.fn(() => null) + select: jest.fn(() => { + return { + lean: jest.fn(() => { + return { + exec: jest.fn(() => null) + }; + }) + }; + }) }; }, aggregate: () => { diff --git a/test/unit/microservice/adapter/controller/cities.controller.spec.ts b/test/unit/microservice/adapter/controller/cities.controller.spec.ts new file mode 100644 index 0000000..a64b105 --- /dev/null +++ b/test/unit/microservice/adapter/controller/cities.controller.spec.ts @@ -0,0 +1,107 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ConfigModule } from '@nestjs/config'; +import configuration from '../../../../../src/config/configuration'; +import { expect } from 'chai'; +import * as sinon from 'sinon'; +import { ExtensionsModule } from '../../../../../src/microservice/adapter/helper/extensions/exensions.module'; +import { ValidateInputParamsService } from '../../../../../src/microservice/domain/service/validate/validate-input-params.service'; +import { CitiesController } from '../../../../../src/microservice/adapter/controller/cities.controller'; +import { GetCitiesByStateService } from '../../../../../src/microservice/domain/service/cities/get/get-cities-by-state.service'; +import { GetCitiesByCountryService } from '../../../../../src/microservice/domain/service/cities/get/get-cities-by-country.service'; +import { SearchCitiesDTO } from '../../../../../src/microservice/domain/model/search/cities/search-cities-dto.model'; +import { City } from '../../../../../src/microservice/domain/schemas/city.schema'; + +describe('CitiesController', () => { + let citiesController: CitiesController; + + const mockGetCitiesByStateService = { + getCitiesByState: () => { + return; + } + }; + + const mockGetCitiesByCountryService = { + getCitiesByCountry: () => { + return; + } + }; + + const mockValidateService = { + validateAndConvertSearchByState: () => { + return {}; + }, + validateAndConvertSearchByCity: () => { + return {}; + } + }; + + const mockCities = () => { + const city1 = new City(); + city1.name = 'any'; + + const city2 = new City(); + city2.name = 'any'; + return [city1, city2]; + }; + + beforeEach(async () => { + const app: TestingModule = await Test.createTestingModule({ + imports: [ + ConfigModule.forRoot({ + isGlobal: true, + load: [configuration] + }), + ExtensionsModule + ], + controllers: [CitiesController], + providers: [ + { + provide: GetCitiesByStateService, + useFactory: () => mockGetCitiesByStateService + }, + { + provide: GetCitiesByCountryService, + useFactory: () => mockGetCitiesByCountryService + }, + { + provide: ValidateInputParamsService, + useFactory: () => mockValidateService + } + ] + }).compile(); + + citiesController = app.get(CitiesController); + }); + + describe('getCitiesByState', () => { + it('should call getCitiesByState and return an array', async () => { + const getServiceStub = sinon + .stub(mockGetCitiesByStateService, 'getCitiesByState') + .returns(mockCities()); + + const searchParams = new SearchCitiesDTO('brasil', 'sc'); + + const actual = await citiesController.getCitiesByState(searchParams); + + expect(actual.body).to.be.an('array').that.contains; + expect(actual.body).to.have.lengthOf(2); + + getServiceStub.restore(); + }); + }); + + describe('getCitiesByCountry', () => { + it('should call getCitiesByCountry and return an array', async () => { + const getServiceStub = sinon + .stub(mockGetCitiesByCountryService, 'getCitiesByCountry') + .returns(mockCities()); + + const actual = await citiesController.getCitiesByCountry('brasil'); + + expect(actual.body).to.be.an('array').that.contains; + expect(actual.body).to.have.lengthOf(2); + + getServiceStub.restore(); + }); + }); +}); diff --git a/test/unit/microservice/adapter/controller/countries.controller.spec.ts b/test/unit/microservice/adapter/controller/countries.controller.spec.ts new file mode 100644 index 0000000..ac6d4e6 --- /dev/null +++ b/test/unit/microservice/adapter/controller/countries.controller.spec.ts @@ -0,0 +1,64 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ConfigModule } from '@nestjs/config'; +import configuration from '../../../../../src/config/configuration'; +import { expect } from 'chai'; +import * as sinon from 'sinon'; +import { ExtensionsModule } from '../../../../../src/microservice/adapter/helper/extensions/exensions.module'; +import { CountriesController } from '../../../../../src/microservice/adapter/controller/countries.controller'; +import { GetCountriesService } from '../../../../../src/microservice/domain/service/countries/get-countries.service'; +import { Country } from '../../../../../src/microservice/domain/schemas/country.schema'; + +describe('CountriesController', () => { + let countriesController: CountriesController; + + const mockGetCountriesService = { + getAll: () => { + return; + } + }; + + const mockCountries = () => { + const country1 = new Country(); + country1.name = 'any'; + + const country2 = new Country(); + country2.name = 'any'; + return [country1, country2]; + }; + + beforeEach(async () => { + const app: TestingModule = await Test.createTestingModule({ + imports: [ + ConfigModule.forRoot({ + isGlobal: true, + load: [configuration] + }), + ExtensionsModule + ], + controllers: [CountriesController], + providers: [ + { + provide: GetCountriesService, + useFactory: () => mockGetCountriesService + } + ] + }).compile(); + + countriesController = app.get(CountriesController); + }); + + describe('getAllCountries', () => { + it('should call getAllCountries and return an array', async () => { + const getServiceStub = sinon + .stub(mockGetCountriesService, 'getAll') + .returns(mockCountries()); + + const actual = await countriesController.getAllCountries(); + + expect(actual.body).to.be.an('array').that.contains; + expect(actual.body).to.have.lengthOf(2); + + getServiceStub.restore(); + }); + }); +}); diff --git a/test/unit/microservice/adapter/controller/neighborhoods.controller.spec.ts b/test/unit/microservice/adapter/controller/neighborhoods.controller.spec.ts index 3b93dce..0cddbd9 100644 --- a/test/unit/microservice/adapter/controller/neighborhoods.controller.spec.ts +++ b/test/unit/microservice/adapter/controller/neighborhoods.controller.spec.ts @@ -8,10 +8,10 @@ import { NeighborhoodByCity } from '../../../../../src/microservice/domain/model import { ExtensionsModule } from '../../../../../src/microservice/adapter/helper/extensions/exensions.module'; import { GetNeighborhoodsByCityService } from '../../../../../src/microservice/domain/service/neighborhoods/get/get-neighborhoods-by-city.service'; import { SaveNeighborhoodsByCityService } from '../../../../../src/microservice/domain/service/neighborhoods/save-neighborhoods-by-city.service'; -import { SearchNeighborhoodsInput } from '../../../../../src/microservice/domain/model/search/search-neighborhoods-input.model'; +import { SearchNeighborhoodsDTO } from '../../../../../src/microservice/domain/model/search/neighborhoods/search-neighborhoods-dto.model'; import { GetNeighborhoodsByStateService } from '../../../../../src/microservice/domain/service/neighborhoods/get/get-neighborhoods-by-state.service'; import { SeedNeighborhoodsByStateService } from '../../../../../src/microservice/domain/service/neighborhoods/seed/seed-neighborhoods-by-state.service'; -import { ValidateInputParamsService } from '../../../../../src/microservice/domain/service/validate-input-params.service'; +import { ValidateInputParamsService } from '../../../../../src/microservice/domain/service/validate/validate-input-params.service'; describe('NeighborhoodsController', () => { let neighborhoodsController: NeighborhoodsController; @@ -55,11 +55,17 @@ describe('NeighborhoodsController', () => { const mockNeighborhoods: NeighborhoodByCity[] = [ { name: 'Aires Rodrigues', - city: 'Orleans-SC' + city: 'Orleans - SC', + countryId: 31, + stateId: 2014, + cityId: 1 }, { name: 'Alto Paraná', - city: 'Orleans-SC' + city: 'Orleans - SC', + countryId: 31, + stateId: 2014, + cityId: 2 } ]; @@ -108,7 +114,7 @@ describe('NeighborhoodsController', () => { .stub(mockGetNeighborhoodsService, 'getNeighborhoodsByCity') .returns(mockNeighborhoods); - const searchParams = new SearchNeighborhoodsInput( + const searchParams = new SearchNeighborhoodsDTO( 'brasil', 'sc', 'orleans' @@ -131,7 +137,7 @@ describe('NeighborhoodsController', () => { .stub(mockGetNeighborhoodsByStateService, 'getNeighborhoodsByState') .returns(mockNeighborhoods); - const searchParams = new SearchNeighborhoodsInput('brasil', 'sc'); + const searchParams = new SearchNeighborhoodsDTO('brasil', 'sc'); const actual = await neighborhoodsController.getNeighborhoodsByState( searchParams @@ -155,7 +161,7 @@ describe('NeighborhoodsController', () => { .stub(mockSeedNeighborhoodsByStateService, 'seedNeighborhoodsByState') .returns(mockResponseSeed); - const searchParams = new SearchNeighborhoodsInput('brasil', 'sc'); + const searchParams = new SearchNeighborhoodsDTO('brasil', 'sc'); const actual = await neighborhoodsController.seedNeighborhoodsByState( searchParams diff --git a/test/unit/microservice/adapter/controller/states.controller.spec.ts b/test/unit/microservice/adapter/controller/states.controller.spec.ts new file mode 100644 index 0000000..193523a --- /dev/null +++ b/test/unit/microservice/adapter/controller/states.controller.spec.ts @@ -0,0 +1,78 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ConfigModule } from '@nestjs/config'; +import configuration from '../../../../../src/config/configuration'; +import { expect } from 'chai'; +import * as sinon from 'sinon'; +import { ExtensionsModule } from '../../../../../src/microservice/adapter/helper/extensions/exensions.module'; +import { ValidateInputParamsService } from '../../../../../src/microservice/domain/service/validate/validate-input-params.service'; +import { StatesController } from '../../../../../src/microservice/adapter/controller/states.controller'; +import { GetStatesByCountryService } from '../../../../../src/microservice/domain/service/states/get-states-by-country.service'; +import { State } from '../../../../../src/microservice/domain/schemas/state.schema'; + +describe('StatesController', () => { + let statesController: StatesController; + + const mockGetStatesByCountryService = { + getStatesByCountry: () => { + return; + } + }; + + const mockValidateService = { + validateAndConvertSearchByState: () => { + return {}; + }, + validateAndConvertSearchByCity: () => { + return {}; + } + }; + + const mockStates = () => { + const state1 = new State(); + state1.name = 'any'; + + const state2 = new State(); + state2.name = 'any'; + return [state1, state2]; + }; + + beforeEach(async () => { + const app: TestingModule = await Test.createTestingModule({ + imports: [ + ConfigModule.forRoot({ + isGlobal: true, + load: [configuration] + }), + ExtensionsModule + ], + controllers: [StatesController], + providers: [ + { + provide: GetStatesByCountryService, + useFactory: () => mockGetStatesByCountryService + }, + { + provide: ValidateInputParamsService, + useFactory: () => mockValidateService + } + ] + }).compile(); + + statesController = app.get(StatesController); + }); + + describe('getStatesByCountry', () => { + it('should call getStatesByCountry and return an array', async () => { + const getServiceStub = sinon + .stub(mockGetStatesByCountryService, 'getStatesByCountry') + .returns(mockStates()); + + const actual = await statesController.getStatesByCountry('brasil'); + + expect(actual.body).to.be.an('array').that.contains; + expect(actual.body).to.have.lengthOf(2); + + getServiceStub.restore(); + }); + }); +}); diff --git a/test/unit/microservice/adapter/helper/builder/neighborhoods/neighborhoods-mongo.builder.spec.ts b/test/unit/microservice/adapter/helper/builder/neighborhoods/neighborhoods-mongo.builder.spec.ts index ed03916..140320e 100644 --- a/test/unit/microservice/adapter/helper/builder/neighborhoods/neighborhoods-mongo.builder.spec.ts +++ b/test/unit/microservice/adapter/helper/builder/neighborhoods/neighborhoods-mongo.builder.spec.ts @@ -5,15 +5,22 @@ import { Neighborhood } from '../../../../../../../src/microservice/domain/schem import { Country } from '../../../../../../../src/microservice/domain/schemas/country.schema'; import { State } from '../../../../../../../src/microservice/domain/schemas/state.schema'; import { City } from '../../../../../../../src/microservice/domain/schemas/city.schema'; +import { NeighborhoodByCity } from '../../../../../../../src/microservice/domain/model/neighborhoods/neighborhood-by-city.model'; -const mockNeighborhoodsByCity = [ +const mockNeighborhoods: NeighborhoodByCity[] = [ { name: 'Harlem', - city: 'New York City - NY' + city: 'New York City - NY', + countryId: 31, + stateId: 41, + cityId: 3 }, { name: `Hell's Kitchen`, - city: 'New York City - NY' + city: 'New York City - NY', + countryId: 31, + stateId: 41, + cityId: 4 } ]; @@ -53,7 +60,7 @@ const mockConvertedSearch = () => { describe('NeighborhoodsMongoBuilder ', () => { it('Should instanciate NeighborhoodsMongoBuilder and build correctly', function () { - const nestBuilder = new NeighborhoodsMongoBuilder(mockNeighborhoodsByCity); + const nestBuilder = new NeighborhoodsMongoBuilder(mockNeighborhoods); const actual = nestBuilder.build(mockConvertedSearch()); expect(JSON.stringify(actual)).to.be.equal( JSON.stringify(mockMongoNeighborhoods()) diff --git a/test/unit/microservice/adapter/helper/builder/neighborhoods/neighborhoods-puppeteer.builder.spec.ts b/test/unit/microservice/adapter/helper/builder/neighborhoods/neighborhoods-puppeteer.builder.spec.ts index 4461785..87716d4 100644 --- a/test/unit/microservice/adapter/helper/builder/neighborhoods/neighborhoods-puppeteer.builder.spec.ts +++ b/test/unit/microservice/adapter/helper/builder/neighborhoods/neighborhoods-puppeteer.builder.spec.ts @@ -2,15 +2,22 @@ import '../../../../../../../src/microservice/adapter/helper/extensions/exension import { expect } from 'chai'; import { Neighborhood } from '../../../../../../../src/microservice/domain/schemas/neighborhood.schema'; import { NeighborhoodsPuppeteerBuilder } from '../../../../../../../src/microservice/adapter/helper/builder/neighborhoods/neighborhoods-puppeteer.builder'; +import { NeighborhoodByCity } from '../../../../../../../src/microservice/domain/model/neighborhoods/neighborhood-by-city.model'; -const mockNeighborhoodsByCity = [ +const mockNeighborhoods: NeighborhoodByCity[] = [ { + name: 'Harlem', + cityId: 3, city: 'New York City - NY', - name: 'Harlem' + stateId: 41, + countryId: 1 }, { + name: `Hell's Kitchen`, + cityId: 4, city: 'New York City - NY', - name: `Hell's Kitchen` + stateId: 41, + countryId: 1 } ]; @@ -20,6 +27,9 @@ const mockMongoNeighborhoods = () => { item1.country = 'USA'; item1.state = 'NY'; item1.city = 'New York City'; + item1.countryId = 1; + item1.stateId = 41; + item1.cityId = 3; item1.name = 'Harlem'; arr.push(item1); @@ -27,6 +37,9 @@ const mockMongoNeighborhoods = () => { item2.country = 'USA'; item2.state = 'NY'; item2.city = 'New York City'; + item2.countryId = 1; + item2.stateId = 41; + item2.cityId = 4; item2.name = `Hell's Kitchen`; arr.push(item2); @@ -35,12 +48,10 @@ const mockMongoNeighborhoods = () => { describe('NeighborhoodsPuppeteerBuilder ', () => { it('Should instanciate NeighborhoodsPuppeteerBuilder and build correctly', function () { - const nestBuilder = new NeighborhoodsPuppeteerBuilder( - mockMongoNeighborhoods() - ); - const actual = nestBuilder.build(); + const builder = new NeighborhoodsPuppeteerBuilder(mockMongoNeighborhoods()); + const actual = builder.build(); expect(JSON.stringify(actual)).to.be.equal( - JSON.stringify(mockNeighborhoodsByCity) + JSON.stringify(mockNeighborhoods) ); }); }); diff --git a/test/unit/microservice/adapter/helper/extensions/object.extension.spec.ts b/test/unit/microservice/adapter/helper/extensions/object.extension.spec.ts deleted file mode 100644 index 101ca95..0000000 --- a/test/unit/microservice/adapter/helper/extensions/object.extension.spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -import '../../../../../../src/microservice/adapter/helper/extensions/exensions.module'; -import { expect } from 'chai'; -import { SearchNeighborhoodsInput } from '../../../../../../src/microservice/domain/model/search/search-neighborhoods-input.model'; - -describe('object.extension', () => { - describe('getMethods', function () { - it('Should call getMethods and contain toString', function () { - const obj = new Object(); - expect(obj.getMethods()).to.include.members(['toString']); - }); - }); - - describe('validateIsAnyEmptyKey', function () { - it('Should call validateIsAnyEmptyKey and throw exccption', function () { - const validation = () => { - const obj = new SearchNeighborhoodsInput('brasil', 'sc', 'orleans'); - obj.city = ''; - obj.validateIsAnyEmptyKey(); - }; - - try { - validation(); - } catch (err) { - expect(err.message).to.be.equal("The property 'City' cannot be empty"); - } - }); - }); -}); diff --git a/test/unit/microservice/adapter/repository/countries/countries-mongoose.repository.spec.ts b/test/unit/microservice/adapter/repository/countries/countries-mongoose.repository.spec.ts index e2e199f..86d3201 100644 --- a/test/unit/microservice/adapter/repository/countries/countries-mongoose.repository.spec.ts +++ b/test/unit/microservice/adapter/repository/countries/countries-mongoose.repository.spec.ts @@ -32,6 +32,15 @@ describe('CountriesMongoose', () => { return { exec: jest.fn(() => mockCountries()) }; + }), + select: jest.fn(() => { + return { + lean: jest.fn(() => { + return { + exec: jest.fn(() => mockCountries()) + }; + }) + }; }) }; @@ -55,13 +64,39 @@ describe('CountriesMongoose', () => { await app.close(); }); - describe('findBySearchParams', () => { - it('should call findBySearchParams and return an array', async () => { + describe('findByNameOrAliasOrId', () => { + it('should call findByNameOrAliasOrId and return an array', async () => { + const findManyStub = sinon + .stub(mockModelMongoose, 'find') + .returns(mockFindCountries); + + const actual = await sut.findByNameOrAliasOrId('any'); + + expect(actual).to.be.an('array').that.is.not.empty; + + findManyStub.restore(); + }); + + it('should call findByNameOrAliasOrId with number param and return an array', async () => { + const findManyStub = sinon + .stub(mockModelMongoose, 'find') + .returns(mockFindCountries); + + const actual = await sut.findByNameOrAliasOrId('1'); + + expect(actual).to.be.an('array').that.is.not.empty; + + findManyStub.restore(); + }); + }); + + describe('findAll', () => { + it('should call findAll and return an array', async () => { const findManyStub = sinon .stub(mockModelMongoose, 'find') .returns(mockFindCountries); - const actual = await sut.findByNameOrAlias('any'); + const actual = await sut.findAll(); expect(actual).to.be.an('array').that.is.not.empty; diff --git a/test/unit/microservice/adapter/repository/neighborhoods/neighborhoods-mongoose.repository.spec.ts b/test/unit/microservice/adapter/repository/neighborhoods/neighborhoods-mongoose.repository.spec.ts index 7888d00..54c218c 100644 --- a/test/unit/microservice/adapter/repository/neighborhoods/neighborhoods-mongoose.repository.spec.ts +++ b/test/unit/microservice/adapter/repository/neighborhoods/neighborhoods-mongoose.repository.spec.ts @@ -7,7 +7,7 @@ import { getModelToken } from '@nestjs/mongoose'; import { Neighborhood } from '../../../../../../src/microservice/domain/schemas/neighborhood.schema'; import { mockModelMongoose } from '../../../../../mock/mongoose/mock-mongoose'; import { MongoDBException } from '../../../../../../src/core/error-handling/exception/mongodb-.exception'; -import { SearchNeighborhoodsDB } from '../../../../../../src/microservice/domain/model/search/search-neighborhoods-db.model'; +import { SearchNeighborhoodsDB } from '../../../../../../src/microservice/domain/model/search/neighborhoods/search-neighborhoods-db.model'; jest.useFakeTimers(); jest.setTimeout(20000); @@ -60,14 +60,18 @@ describe('NeighborhoodsMongoose', () => { ]; const mockFindNeighborhoods = { - select: jest.fn(() => { + sort: jest.fn(() => { return { - lean: jest.fn(() => { + select: jest.fn(() => { return { + lean: jest.fn(() => { + return { + exec: jest.fn(() => mockNeighborhoods()) + }; + }), exec: jest.fn(() => mockNeighborhoods()) }; - }), - exec: jest.fn(() => mockNeighborhoods()) + }) }; }) }; @@ -133,6 +137,13 @@ describe('NeighborhoodsMongoose', () => { const actual = await sut.buildRegexFilterQuery(); expect(JSON.stringify(actual)).to.be.equal(JSON.stringify({})); }); + + it('should call buildRegexFilterQuery with null param and return regex filter with empty obj', async () => { + const actual = await sut.buildRegexFilterQuery({ + name: null + }); + expect(JSON.stringify(actual)).to.be.equal(JSON.stringify({})); + }); }); describe('insertOne', () => { diff --git a/test/unit/microservice/adapter/repository/neighborhoods/puppeteer/guia-mais.repository.spec.ts b/test/unit/microservice/adapter/repository/neighborhoods/puppeteer/guia-mais.repository.spec.ts index aca4ca6..9f70335 100644 --- a/test/unit/microservice/adapter/repository/neighborhoods/puppeteer/guia-mais.repository.spec.ts +++ b/test/unit/microservice/adapter/repository/neighborhoods/puppeteer/guia-mais.repository.spec.ts @@ -4,10 +4,13 @@ import { expect } from 'chai'; import * as sinon from 'sinon'; import { GuiaMaisRepository } from '../../../../../../../src/microservice/adapter/repository/neighborhoods/puppeteer/guia-mais.repository'; import { PuppeteerModule } from 'nest-puppeteer'; -import { SearchNeighborhoodsInput } from '../../../../../../../src/microservice/domain/model/search/search-neighborhoods-input.model'; +import { SearchNeighborhoodsDTO } from '../../../../../../../src/microservice/domain/model/search/neighborhoods/search-neighborhoods-dto.model'; import * as fs from 'fs'; import { ExtensionsModule } from '../../../../../../../src/microservice/adapter/helper/extensions/exensions.module'; import * as cheerio from 'cheerio'; +import { Country } from '../../../../../../../src/microservice/domain/schemas/country.schema'; +import { State } from '../../../../../../../src/microservice/domain/schemas/state.schema'; +import { City } from '../../../../../../../src/microservice/domain/schemas/city.schema'; jest.useFakeTimers(); jest.setTimeout(50000); @@ -24,6 +27,21 @@ describe('GuiaMaisRepository', () => { } ); + const mockConvertedSearch = () => { + const mockCountry = new Country(); + mockCountry.name = 'USA'; + const mockState = new State(); + mockState.name = 'New York'; + mockState.stateCode = 'NY'; + const mockCity = new City(); + mockCity.name = 'New York City'; + return { + country: mockCountry, + state: mockState, + city: mockCity + }; + }; + beforeEach(async () => { app = await Test.createTestingModule({ imports: [ @@ -56,7 +74,7 @@ describe('GuiaMaisRepository', () => { describe('getNeighborhoodsByCity', () => { it('should call getNeighborhoodsByCity and return an array', async () => { - const mockSearchParams = new SearchNeighborhoodsInput( + const mockSearchParams = new SearchNeighborhoodsDTO( 'brasil', 'sc', 'orleans' @@ -64,7 +82,10 @@ describe('GuiaMaisRepository', () => { const getDataHtmlStub = sinon.stub(sut, 'getDataHtml').returns(mockHTML); const goToUrlStub = sinon.stub(sut, 'goToUrl').returns(); - const actual = await sut.getNeighborhoodsByCity(mockSearchParams); + const actual = await sut.getNeighborhoodsByCity( + mockSearchParams, + mockConvertedSearch() + ); expect(actual).to.be.an('array').that.is.not.empty; expect(actual[0].city).to.be.equal('Orleans - SC'); @@ -77,7 +98,7 @@ describe('GuiaMaisRepository', () => { describe('callEndpoint', () => { it('should call callEndpoint and call getDocumentHtml with the correct params', async () => { - const mockSearchParams = new SearchNeighborhoodsInput( + const mockSearchParams = new SearchNeighborhoodsDTO( 'brasil', 'sc', 'orleans' diff --git a/test/unit/microservice/domain/model/cities/city-response.model.spec.ts b/test/unit/microservice/domain/model/cities/city-response.model.spec.ts new file mode 100644 index 0000000..b85336f --- /dev/null +++ b/test/unit/microservice/domain/model/cities/city-response.model.spec.ts @@ -0,0 +1,13 @@ +import { expect } from 'chai'; +import { CityResponse } from '../../../../../../src/microservice/domain/model/cities/city-response.model'; + +describe('CityResponse', () => { + it('should instance CityResponse and return the object with the correct properties', async () => { + const model = new CityResponse(); + model.name = 'Torres'; + model.stateCode = 'RS'; + + expect(model.name).to.be.equal('Torres'); + expect(model.stateCode).to.be.equal('RS'); + }); +}); diff --git a/test/unit/microservice/domain/model/countries/country-response.model.spec.ts b/test/unit/microservice/domain/model/countries/country-response.model.spec.ts new file mode 100644 index 0000000..dc4a615 --- /dev/null +++ b/test/unit/microservice/domain/model/countries/country-response.model.spec.ts @@ -0,0 +1,15 @@ +import { expect } from 'chai'; +import { CountryResponse } from '../../../../../../src/microservice/domain/model/countries/country-response.model'; + +describe('CountryResponse', () => { + it('should instance CountryResponse and return the object with the correct properties', async () => { + const model = new CountryResponse(); + model.name = 'Brazil'; + model.iso2 = 'BR'; + model.iso3 = 'BRA'; + + expect(model.name).to.be.equal('Brazil'); + expect(model.iso3).to.be.equal('BRA'); + expect(model.iso2).to.be.equal('BR'); + }); +}); diff --git a/test/unit/microservice/domain/model/search/neighborhoods/search-neighborhoods-dto.model.spec.ts b/test/unit/microservice/domain/model/search/neighborhoods/search-neighborhoods-dto.model.spec.ts new file mode 100644 index 0000000..99fbd72 --- /dev/null +++ b/test/unit/microservice/domain/model/search/neighborhoods/search-neighborhoods-dto.model.spec.ts @@ -0,0 +1,33 @@ +import { expect } from 'chai'; +import { SearchNeighborhoodsDTO } from '../../../../../../../src/microservice/domain/model/search/neighborhoods/search-neighborhoods-dto.model'; +import '../../../../../../../src/microservice/adapter/helper/extensions/exensions.module'; + +describe('SearchNeighborhoodsDTO', () => { + it('should instance SearchNeighborhoodsDTO and return the object with the correct properties', async () => { + const model = new SearchNeighborhoodsDTO('brasil', 'sc', 'praia grande'); + + expect(model.country).to.be.equal('brasil'); + expect(model.state).to.be.equal('sc'); + expect(model.city).to.be.equal('praia grande'); + }); + + it('should instance SearchNeighborhoodsDTO, setName and return the object with the correct properties', async () => { + const model = new SearchNeighborhoodsDTO('brasil', 'sc', 'praia grande'); + model.setName('Vila Rosa'); + + expect(model.country).to.be.equal('brasil'); + expect(model.state).to.be.equal('sc'); + expect(model.city).to.be.equal('praia grande'); + expect(model.name).to.be.equal('Vila Rosa'); + }); + + it('should instance SearchNeighborhoodsDTOand throws invalid data exception', async () => { + const model = new SearchNeighborhoodsDTO('', 'sc', 'praia grande'); + + try { + model.validateIsAnyEmptyKey(); + } catch (err) { + expect(err.message).to.be.equal(`The property 'Country' cannot be empty`); + } + }); +}); diff --git a/test/unit/microservice/domain/model/search/search-neighborhoods-input.model.spec.ts b/test/unit/microservice/domain/model/search/search-neighborhoods-input.model.spec.ts deleted file mode 100644 index f95fb4a..0000000 --- a/test/unit/microservice/domain/model/search/search-neighborhoods-input.model.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { expect } from 'chai'; -import { SearchNeighborhoodsInput } from '../../../../../../src/microservice/domain/model/search/search-neighborhoods-input.model'; - -describe('SearchNeighborhoodsInput', () => { - it('should instance SearchNeighborhoodsInput and return the object with the correct properties', async () => { - const model = new SearchNeighborhoodsInput('brasil', 'sc', 'praia grande'); - - expect(model.country).to.be.equal('brasil'); - expect(model.state).to.be.equal('sc'); - expect(model.city).to.be.equal('praia grande'); - }); - - it('should instance SearchNeighborhoodsInput, setName and return the object with the correct properties', async () => { - const model = new SearchNeighborhoodsInput('brasil', 'sc', 'praia grande'); - model.setName('Vila Rosa'); - - expect(model.country).to.be.equal('brasil'); - expect(model.state).to.be.equal('sc'); - expect(model.city).to.be.equal('praia grande'); - expect(model.name).to.be.equal('Vila Rosa'); - }); -}); diff --git a/test/unit/microservice/domain/model/states/states-by-country.model.spec.ts b/test/unit/microservice/domain/model/states/states-by-country.model.spec.ts new file mode 100644 index 0000000..20ae5de --- /dev/null +++ b/test/unit/microservice/domain/model/states/states-by-country.model.spec.ts @@ -0,0 +1,13 @@ +import { expect } from 'chai'; +import { StatesByCountry } from '../../../../../../src/microservice/domain/model/states/states-by-country.model'; + +describe('StatesByCountry', () => { + it('should instance StatesByCountry and return the object with the correct properties', async () => { + const model = new StatesByCountry(); + model.name = 'Rio Grande do Sul'; + model.stateCode = 'RS'; + + expect(model.name).to.be.equal('Rio Grande do Sul'); + expect(model.stateCode).to.be.equal('RS'); + }); +}); diff --git a/test/unit/microservice/domain/service/cities/get-cities-by-state.service.spec.ts b/test/unit/microservice/domain/service/cities/get-cities-by-state.service.spec.ts deleted file mode 100644 index 5cafc91..0000000 --- a/test/unit/microservice/domain/service/cities/get-cities-by-state.service.spec.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { NeighborhoodsMongoose } from '../../../../../../src/microservice/adapter/repository/neighborhoods/neighborhoods-mongoose.repository'; -import '../../../../../../src/microservice/adapter/helper/extensions/exensions.module'; -import { ValidateInputParamsService } from '../../../../../../src/microservice/domain/service/validate-input-params.service'; -import { City } from '../../../../../../src/microservice/domain/schemas/city.schema'; -import { GetCitiesByStateService } from '../../../../../../src/microservice/domain/service/cities/get-cities-by-state.service'; -import { SearchCitiesDB } from '../../../../../../src/microservice/domain/model/search/search-cities-db.model'; -import { CitiesMongoose } from '../../../../../../src/microservice/adapter/repository/cities/cities-mongoose.repository'; - -describe('GetCitiesByStateService', () => { - let sut: GetCitiesByStateService; - - const mockNeighborhoodsMongooseRepository = { - findBySearchParams: () => { - return []; - }, - - groupBy: () => { - return []; - } - }; - - const mockCitiesMongooseRepository = { - findBySearchParams: () => { - return []; - }, - - groupBy: () => { - return []; - } - }; - - const mockValidateService = { - validateAndConvertSearchByState: () => { - return {}; - }, - validateAndConvertSearchByCity: () => { - return {}; - } - }; - - const mockAggregatedCities = [ - { - _id: { cityId: 1 }, - count: 60, - city: 'Orleans' - }, - { - _id: { cityId: 2 }, - count: 13, - city: 'Braço do Norte' - } - ]; - - const mockCities = () => { - const city1 = new City(); - city1.name = 'any'; - - const city2 = new City(); - city2.name = 'any'; - return [city1, city2]; - }; - - beforeEach(async () => { - const app: TestingModule = await Test.createTestingModule({ - imports: [], - controllers: [], - providers: [ - { - provide: NeighborhoodsMongoose, - useValue: mockNeighborhoodsMongooseRepository - }, - { - provide: CitiesMongoose, - useValue: mockCitiesMongooseRepository - }, - { - provide: ValidateInputParamsService, - useFactory: () => mockValidateService - }, - GetCitiesByStateService - ] - }).compile(); - - sut = app.get(GetCitiesByStateService); - }); - - describe('getCitiesByState', () => { - it('should call getCitiesByState and return an array by puppeteer', async () => { - const arrMockCities = mockCities(); - const groupByStub = sinon - .stub(mockCitiesMongooseRepository, 'findBySearchParams') - .returns(arrMockCities); - - const searchParams = new SearchCitiesDB(1, 2); - - const arrIgnore = [3, 4, 5]; - - const actual = await sut.getCitiesByState(searchParams, arrIgnore); - - expect(actual).to.be.equal(arrMockCities); - - groupByStub.restore(); - }); - }); - - describe('getCitiesByState', () => { - it('should call getCitiesByState and return an array by puppeteer', async () => { - const arrMockCities = mockCities(); - const groupByStub = sinon - .stub(mockCitiesMongooseRepository, 'findBySearchParams') - .returns(arrMockCities); - - const searchParams = new SearchCitiesDB(1, 2); - - const actual = await sut.getCitiesByState(searchParams); - - expect(actual).to.be.equal(arrMockCities); - - groupByStub.restore(); - }); - }); - - describe('groupByCity', () => { - it('should call groupByCity and return an array by puppeteer', async () => { - const groupByStub = sinon - .stub(mockNeighborhoodsMongooseRepository, 'groupBy') - .returns(mockAggregatedCities); - - const actual = await sut.groupByCity(1); - - expect(actual).to.be.equal(mockAggregatedCities); - - groupByStub.restore(); - }); - }); -}); diff --git a/test/unit/microservice/domain/service/cities/get/get-cities-by-country.service.spec.ts b/test/unit/microservice/domain/service/cities/get/get-cities-by-country.service.spec.ts new file mode 100644 index 0000000..87772b0 --- /dev/null +++ b/test/unit/microservice/domain/service/cities/get/get-cities-by-country.service.spec.ts @@ -0,0 +1,89 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { expect } from 'chai'; +import * as sinon from 'sinon'; +import '../../../../../../../src/microservice/adapter/helper/extensions/exensions.module'; +import { ValidateInputParamsService } from '../../../../../../../src/microservice/domain/service/validate/validate-input-params.service'; +import { City } from '../../../../../../../src/microservice/domain/schemas/city.schema'; +import { CitiesMongoose } from '../../../../../../../src/microservice/adapter/repository/cities/cities-mongoose.repository'; +import { GetCitiesByCountryService } from '../../../../../../../src/microservice/domain/service/cities/get/get-cities-by-country.service'; +import { ValidateCountryByNameOrAliasService } from '../../../../../../../src/microservice/domain/service/countries/validate-country-by-name-or-alias.service'; + +describe('GetCitiesByCountryService', () => { + let sut: GetCitiesByCountryService; + + const mockCitiesMongooseRepository = { + findBySearchParams: () => { + return []; + } + }; + + const mockValidateService = { + validateAndConvertSearchByState: () => { + return {}; + }, + validateAndConvertSearchByCity: () => { + return {}; + } + }; + + const mockValidateCountryService = { + validateCountry: () => { + return {}; + } + }; + + const mockCities = () => { + const city1 = new City(); + city1.name = 'any'; + city1.stateName = 'anystate'; + city1.stateCode = 'A2'; + + const city2 = new City(); + city2.name = 'any2'; + city2.stateName = 'anystate2'; + city2.stateCode = 'A1'; + return [city1, city2]; + }; + + beforeEach(async () => { + const app: TestingModule = await Test.createTestingModule({ + imports: [], + controllers: [], + providers: [ + { + provide: CitiesMongoose, + useValue: mockCitiesMongooseRepository + }, + { + provide: ValidateInputParamsService, + useFactory: () => mockValidateService + }, + { + provide: ValidateCountryByNameOrAliasService, + useFactory: () => mockValidateCountryService + }, + GetCitiesByCountryService + ] + }).compile(); + + sut = app.get(GetCitiesByCountryService); + }); + + describe('getCitiesByCountry', () => { + it('should call getCitiesByCountry and return an array', async () => { + const arrMockCities = mockCities(); + const getCitiesStub = sinon + .stub(mockCitiesMongooseRepository, 'findBySearchParams') + .returns(arrMockCities); + + const actual = await sut.getCitiesByCountry('any'); + + expect(actual['Anystate - A2']).to.be.an('array'); + expect(actual['Anystate - A2'].length).to.be.equal(1); + expect(actual['Anystate2 - A1']).to.be.an('array'); + expect(actual['Anystate2 - A1'].length).to.be.equal(1); + + getCitiesStub.restore(); + }); + }); +}); diff --git a/test/unit/microservice/domain/service/cities/get/get-cities-by-state.service.spec.ts b/test/unit/microservice/domain/service/cities/get/get-cities-by-state.service.spec.ts new file mode 100644 index 0000000..b3c2f2a --- /dev/null +++ b/test/unit/microservice/domain/service/cities/get/get-cities-by-state.service.spec.ts @@ -0,0 +1,147 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { expect } from 'chai'; +import * as sinon from 'sinon'; +import { NeighborhoodsMongoose } from '../../../../../../../src/microservice/adapter/repository/neighborhoods/neighborhoods-mongoose.repository'; +import '../../../../../../../src/microservice/adapter/helper/extensions/exensions.module'; +import { ValidateInputParamsService } from '../../../../../../../src/microservice/domain/service/validate/validate-input-params.service'; +import { City } from '../../../../../../../src/microservice/domain/schemas/city.schema'; +import { GetCitiesByStateService } from '../../../../../../../src/microservice/domain/service/cities/get/get-cities-by-state.service'; +import { SearchCitiesDB } from '../../../../../../../src/microservice/domain/model/search/cities/search-cities-db.model'; +import { CitiesMongoose } from '../../../../../../../src/microservice/adapter/repository/cities/cities-mongoose.repository'; +import { SearchCitiesDTO } from '../../../../../../../src/microservice/domain/model/search/cities/search-cities-dto.model'; +import { State } from '../../../../../../../src/microservice/domain/schemas/state.schema'; +import { Country } from '../../../../../../../src/microservice/domain/schemas/country.schema'; + +describe('GetCitiesByStateService', () => { + let sut: GetCitiesByStateService; + + const mockNeighborhoodsMongooseRepository = { + findBySearchParams: () => { + return []; + }, + + groupBy: () => { + return []; + } + }; + + const mockCitiesMongooseRepository = { + findBySearchParams: () => { + return []; + }, + + groupBy: () => { + return []; + } + }; + + const mockValidateService = { + validateAndConvertSearchByState: () => { + return {}; + }, + validateAndConvertSearchByCity: () => { + return {}; + } + }; + + const mockConvertedSearch = () => { + const mockCountry = new Country(); + mockCountry.name = 'USA'; + const mockState = new State(); + mockState.name = 'New York'; + mockState.stateCode = 'NY'; + return { + country: mockCountry, + state: mockState + }; + }; + + const mockCities = () => { + const city1 = new City(); + city1.name = 'any'; + + const city2 = new City(); + city2.name = 'any'; + return [city1, city2]; + }; + + beforeEach(async () => { + const app: TestingModule = await Test.createTestingModule({ + imports: [], + controllers: [], + providers: [ + { + provide: NeighborhoodsMongoose, + useValue: mockNeighborhoodsMongooseRepository + }, + { + provide: CitiesMongoose, + useValue: mockCitiesMongooseRepository + }, + { + provide: ValidateInputParamsService, + useFactory: () => mockValidateService + }, + GetCitiesByStateService + ] + }).compile(); + + sut = app.get(GetCitiesByStateService); + }); + + describe('findCitiesByState', () => { + it('should call findCitiesByState and return an array by puppeteer', async () => { + const arrMockCities = mockCities(); + const groupByStub = sinon + .stub(mockCitiesMongooseRepository, 'findBySearchParams') + .returns(arrMockCities); + + const searchParams = new SearchCitiesDB(1, 2); + + const arrIgnore = [3, 4, 5]; + + const actual = await sut.findCitiesByState(searchParams, arrIgnore); + + expect(actual).to.be.equal(arrMockCities); + + groupByStub.restore(); + }); + + it('should call findCitiesByState and return an array by puppeteer', async () => { + const arrMockCities = mockCities(); + const groupByStub = sinon + .stub(mockCitiesMongooseRepository, 'findBySearchParams') + .returns(arrMockCities); + + const searchParams = new SearchCitiesDB(1, 2); + + const actual = await sut.findCitiesByState(searchParams); + + expect(actual).to.be.equal(arrMockCities); + + groupByStub.restore(); + }); + }); + + describe('getCitiesByState', () => { + it('should call getCitiesByState and return an array', async () => { + const arrMockCities = mockCities(); + const groupByStub = sinon + .stub(mockCitiesMongooseRepository, 'findBySearchParams') + .returns(arrMockCities); + + const convertedSearchStub = sinon + .stub(mockValidateService, 'validateAndConvertSearchByState') + .returns(mockConvertedSearch()); + + const searchParams = new SearchCitiesDTO('Brazil', 'SC'); + + const actual = await sut.getCitiesByState(searchParams); + + expect(actual).to.be.equal(arrMockCities); + + groupByStub.restore(); + convertedSearchStub.restore(); + }); + }); +}); diff --git a/test/unit/microservice/domain/service/cities/validate-city-by-name-or-alias.service.spec.ts b/test/unit/microservice/domain/service/cities/validate-city-by-name-or-alias.service.spec.ts index 8453a1c..41f142c 100644 --- a/test/unit/microservice/domain/service/cities/validate-city-by-name-or-alias.service.spec.ts +++ b/test/unit/microservice/domain/service/cities/validate-city-by-name-or-alias.service.spec.ts @@ -10,7 +10,7 @@ describe('ValidateCityByNameOrAliasService', () => { let sut: ValidateCityByNameOrAliasService; const mockCitiesMongooseRepository = { - findByNameOrAlias: () => { + findByNameOrAliasOrId: () => { return [new City()]; } }; @@ -43,7 +43,7 @@ describe('ValidateCityByNameOrAliasService', () => { describe('getCityByNameOrAlias', () => { it('should call getCityByCity and return an array by mongodb', async () => { const mongoFindStub = sinon - .stub(mockCitiesMongooseRepository, 'findByNameOrAlias') + .stub(mockCitiesMongooseRepository, 'findByNameOrAliasOrId') .returns(mockMongoCities()); const actual = await sut.getCityByNameOrAlias('orleans', 1, 2); @@ -59,7 +59,7 @@ describe('ValidateCityByNameOrAliasService', () => { describe('validateCity', () => { it('should call validateCity and throws invalid data exception', async () => { const getCityStub = sinon - .stub(mockCitiesMongooseRepository, 'findByNameOrAlias') + .stub(mockCitiesMongooseRepository, 'findByNameOrAliasOrId') .returns([]); try { @@ -76,7 +76,7 @@ describe('ValidateCityByNameOrAliasService', () => { city.name = 'any'; const getCityStub = sinon - .stub(mockCitiesMongooseRepository, 'findByNameOrAlias') + .stub(mockCitiesMongooseRepository, 'findByNameOrAliasOrId') .returns([city]); const actual = await sut.validateCity('orleans', 1, 2); diff --git a/test/unit/microservice/domain/service/countries/get-coutnries.service.spec.ts b/test/unit/microservice/domain/service/countries/get-coutnries.service.spec.ts new file mode 100644 index 0000000..7fe2d04 --- /dev/null +++ b/test/unit/microservice/domain/service/countries/get-coutnries.service.spec.ts @@ -0,0 +1,58 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { expect } from 'chai'; +import * as sinon from 'sinon'; +import '../../../../../../src/microservice/adapter/helper/extensions/exensions.module'; +import { CountriesMongoose } from '../../../../../../src/microservice/adapter/repository/countries/countries-mongoose.repository'; +import { GetCountriesService } from '../../../../../../src/microservice/domain/service/countries/get-countries.service'; +import { CountryResponse } from '../../../../../../src/microservice/domain/model/countries/country-response.model'; + +describe('GetCountriesService', () => { + let sut: GetCountriesService; + + const mockMongoose = { + findAll: () => { + return []; + } + }; + + beforeEach(async () => { + const app: TestingModule = await Test.createTestingModule({ + imports: [], + controllers: [], + providers: [ + { + provide: CountriesMongoose, + useValue: mockMongoose + }, + GetCountriesService + ] + }).compile(); + + sut = app.get(GetCountriesService); + }); + + const mockCountries = () => { + const country1 = new CountryResponse(); + country1.id = 1; + country1.name = 'any'; + + const country2 = new CountryResponse(); + country1.id = 2; + country1.name = 'any'; + + return [country1, country2]; + }; + + describe('getAll', () => { + it('should call getAll and return an array', async () => { + const arr = mockCountries(); + const getCitiesStub = sinon.stub(mockMongoose, 'findAll').returns(arr); + + const actual = await sut.getAll(); + + expect(actual).to.be.equal(arr); + + getCitiesStub.restore(); + }); + }); +}); diff --git a/test/unit/microservice/domain/service/countries/validate-country-by-name-or-alias.service.spec.ts b/test/unit/microservice/domain/service/countries/validate-country-by-name-or-alias.service.spec.ts index e77333e..88be8d4 100644 --- a/test/unit/microservice/domain/service/countries/validate-country-by-name-or-alias.service.spec.ts +++ b/test/unit/microservice/domain/service/countries/validate-country-by-name-or-alias.service.spec.ts @@ -10,7 +10,7 @@ describe('ValidateCountryByNameOrAliasService', () => { let sut: ValidateCountryByNameOrAliasService; const mockCountriesMongooseRepository = { - findByNameOrAlias: () => { + findByNameOrAliasOrId: () => { return [new Country()]; } }; @@ -43,7 +43,7 @@ describe('ValidateCountryByNameOrAliasService', () => { describe('validateCountry', () => { it('should call validateCountry and throws invalid data exception', async () => { const getCountryStub = sinon - .stub(mockCountriesMongooseRepository, 'findByNameOrAlias') + .stub(mockCountriesMongooseRepository, 'findByNameOrAliasOrId') .returns([]); try { @@ -59,7 +59,7 @@ describe('ValidateCountryByNameOrAliasService', () => { describe('getCountryByCity', () => { it('should call getCountryByCity and return an array by mongodb', async () => { const mongoFindStub = sinon - .stub(mockCountriesMongooseRepository, 'findByNameOrAlias') + .stub(mockCountriesMongooseRepository, 'findByNameOrAliasOrId') .returns(mockMongoCountries()); const actual = await sut.getCountryByNameOrAlias('brasil'); @@ -76,7 +76,7 @@ describe('ValidateCountryByNameOrAliasService', () => { country.name = 'any'; const getCountryStub = sinon - .stub(mockCountriesMongooseRepository, 'findByNameOrAlias') + .stub(mockCountriesMongooseRepository, 'findByNameOrAliasOrId') .returns([country]); const actual = await sut.validateCountry('orleans'); diff --git a/test/unit/microservice/domain/service/neighborhoods/get/get-neighborhoods-by-city.service.spec.ts b/test/unit/microservice/domain/service/neighborhoods/get/get-neighborhoods-by-city.service.spec.ts index 58bd3d4..f9b414f 100644 --- a/test/unit/microservice/domain/service/neighborhoods/get/get-neighborhoods-by-city.service.spec.ts +++ b/test/unit/microservice/domain/service/neighborhoods/get/get-neighborhoods-by-city.service.spec.ts @@ -10,8 +10,8 @@ import '../../../../../../../src/microservice/adapter/helper/extensions/exension import { CountriesMongoose } from '../../../../../../../src/microservice/adapter/repository/countries/countries-mongoose.repository'; import { CitiesMongoose } from '../../../../../../../src/microservice/adapter/repository/cities/cities-mongoose.repository'; import { StatesMongoose } from '../../../../../../../src/microservice/adapter/repository/states/states-mongoose.repository'; -import { SearchNeighborhoodsInput } from '../../../../../../../src/microservice/domain/model/search/search-neighborhoods-input.model'; -import { ValidateInputParamsService } from '../../../../../../../src/microservice/domain/service/validate-input-params.service'; +import { SearchNeighborhoodsDTO } from '../../../../../../../src/microservice/domain/model/search/neighborhoods/search-neighborhoods-dto.model'; +import { ValidateInputParamsService } from '../../../../../../../src/microservice/domain/service/validate/validate-input-params.service'; import { Country } from '../../../../../../../src/microservice/domain/schemas/country.schema'; import { State } from '../../../../../../../src/microservice/domain/schemas/state.schema'; import { City } from '../../../../../../../src/microservice/domain/schemas/city.schema'; @@ -65,11 +65,17 @@ describe('GetNeighborhoodsByCityService', () => { const mockNeighborhoods: NeighborhoodByCity[] = [ { name: 'Aires Rodrigues', - city: 'Orleans-SC' + city: 'Orleans - SC', + countryId: 31, + stateId: 2014, + cityId: 1 }, { name: 'Alto Paraná', - city: 'Orleans-SC' + city: 'Orleans - SC', + countryId: 31, + stateId: 2014, + cityId: 2 } ]; @@ -137,7 +143,7 @@ describe('GetNeighborhoodsByCityService', () => { .stub(mockValidateService, 'validateAndConvertSearchByCity') .returns(mockConvertedSearch()); - const searchParams = new SearchNeighborhoodsInput( + const searchParams = new SearchNeighborhoodsDTO( 'brasil', 'sc', 'orleans' @@ -161,7 +167,7 @@ describe('GetNeighborhoodsByCityService', () => { .stub(mockValidateService, 'validateAndConvertSearchByCity') .returns(mockConvertedSearch()); - const searchParams = new SearchNeighborhoodsInput( + const searchParams = new SearchNeighborhoodsDTO( 'Brazil', 'SC', 'Orleans' diff --git a/test/unit/microservice/domain/service/neighborhoods/get/get-neighborhoods-by-state.service.spec.ts b/test/unit/microservice/domain/service/neighborhoods/get/get-neighborhoods-by-state.service.spec.ts index e492e20..1e32e08 100644 --- a/test/unit/microservice/domain/service/neighborhoods/get/get-neighborhoods-by-state.service.spec.ts +++ b/test/unit/microservice/domain/service/neighborhoods/get/get-neighborhoods-by-state.service.spec.ts @@ -4,12 +4,10 @@ import * as sinon from 'sinon'; import { NeighborhoodsMongoose } from '../../../../../../../src/microservice/adapter/repository/neighborhoods/neighborhoods-mongoose.repository'; import { Neighborhood } from '../../../../../../../src/microservice/domain/schemas/neighborhood.schema'; import '../../../../../../../src/microservice/adapter/helper/extensions/exensions.module'; -import { SearchNeighborhoodsInput } from '../../../../../../../src/microservice/domain/model/search/search-neighborhoods-input.model'; -import { ValidateInputParamsService } from '../../../../../../../src/microservice/domain/service/validate-input-params.service'; +import { SearchNeighborhoodsDTO } from '../../../../../../../src/microservice/domain/model/search/neighborhoods/search-neighborhoods-dto.model'; +import { ValidateInputParamsService } from '../../../../../../../src/microservice/domain/service/validate/validate-input-params.service'; import { Country } from '../../../../../../../src/microservice/domain/schemas/country.schema'; import { State } from '../../../../../../../src/microservice/domain/schemas/state.schema'; -import { City } from '../../../../../../../src/microservice/domain/schemas/city.schema'; -import { GetCitiesByStateService } from '../../../../../../../src/microservice/domain/service/cities/get-cities-by-state.service'; import { GetNeighborhoodsByStateService } from '../../../../../../../src/microservice/domain/service/neighborhoods/get/get-neighborhoods-by-state.service'; describe('GetNeighborhoodsByStateService', () => { @@ -19,7 +17,6 @@ describe('GetNeighborhoodsByStateService', () => { findBySearchParams: () => { return []; }, - groupBy: () => { return []; } @@ -34,18 +31,20 @@ describe('GetNeighborhoodsByStateService', () => { } }; - const mockGetCitiesByStateService = { - groupByCity: () => { - return []; - } - }; - const mockMongoNeighborhoods = () => { const arr = []; const item1 = new Neighborhood(); item1.id = 1; item1.name = 'any'; + item1.city = 'Orleans'; + item1.cityId = 1; arr.push(item1); + const item2 = new Neighborhood(); + item2.id = 2; + item2.name = 'any1'; + item2.city = 'Braço do Norte'; + item2.cityId = 2; + arr.push(item2); return arr; }; @@ -53,12 +52,12 @@ describe('GetNeighborhoodsByStateService', () => { const country = new Country(); country.id = 31; country.name = 'Brazil'; + country.iso3 = 'BRA'; const state = new State(); state.id = 2014; state.name = 'Santa Catarina'; state.stateCode = 'SC'; - const city = new City(); - return { country, city, state }; + return { country, state }; }; const mockAggregatedCities = [ @@ -83,10 +82,6 @@ describe('GetNeighborhoodsByStateService', () => { provide: NeighborhoodsMongoose, useValue: mockNeighborhoodsMongooseRepository }, - { - provide: GetCitiesByStateService, - useValue: mockGetCitiesByStateService - }, { provide: ValidateInputParamsService, useFactory: () => mockValidateService @@ -101,30 +96,39 @@ describe('GetNeighborhoodsByStateService', () => { }); describe('getNeighborhoodsByState', () => { - it('should call getNeighborhoodsByState and return an array by puppeteer', async () => { + it('should call getNeighborhoodsByState and return an array', async () => { const validateStub = sinon .stub(mockValidateService, 'validateAndConvertSearchByState') .returns(mockConvertedSearch()); const findInDatabaseStub = sinon - .stub(sut, 'findInDatabase') + .stub(mockNeighborhoodsMongooseRepository, 'findBySearchParams') .returns(mockMongoNeighborhoods()); - const groupByStub = sinon - .stub(mockGetCitiesByStateService, 'groupByCity') - .returns(mockAggregatedCities); - - const searchParams = new SearchNeighborhoodsInput('brasil', 'sc'); + const searchParams = new SearchNeighborhoodsDTO('brasil', 'sc'); const actual = await sut.getNeighborhoodsByState(searchParams); expect(actual.Orleans).to.be.an('array'); expect(actual.Orleans.length).to.be.equal(1); - expect(actual['Braço do Norte']).to.be.an('array'); - expect(actual['Braço do Norte'].length).to.be.equal(1); + expect(actual['Braço Do Norte']).to.be.an('array'); + expect(actual['Braço Do Norte'].length).to.be.equal(1); findInDatabaseStub.restore(); validateStub.restore(); + }); + }); + + describe('groupByCity', () => { + it('should call groupByCity and return an array', async () => { + const groupByStub = sinon + .stub(mockNeighborhoodsMongooseRepository, 'groupBy') + .returns(mockAggregatedCities); + + const actual = await sut.groupByCity(1); + + expect(actual).to.be.equal(mockAggregatedCities); + groupByStub.restore(); }); }); diff --git a/test/unit/microservice/domain/service/neighborhoods/save-neighborhoods-by-city.service.spec.ts b/test/unit/microservice/domain/service/neighborhoods/save-neighborhoods-by-city.service.spec.ts index a640f80..f722574 100644 --- a/test/unit/microservice/domain/service/neighborhoods/save-neighborhoods-by-city.service.spec.ts +++ b/test/unit/microservice/domain/service/neighborhoods/save-neighborhoods-by-city.service.spec.ts @@ -3,13 +3,13 @@ import * as sinon from 'sinon'; import { SaveNeighborhoodsByCityService } from '../../../../../../src/microservice/domain/service/neighborhoods/save-neighborhoods-by-city.service'; import { NeighborhoodsMongoose } from '../../../../../../src/microservice/adapter/repository/neighborhoods/neighborhoods-mongoose.repository'; import { ExtensionsModule } from '../../../../../../src/microservice/adapter/helper/extensions/exensions.module'; -import { SearchNeighborhoodsInput } from '../../../../../../src/microservice/domain/model/search/search-neighborhoods-input.model'; +import { SearchNeighborhoodsDTO } from '../../../../../../src/microservice/domain/model/search/neighborhoods/search-neighborhoods-dto.model'; import { NeighborhoodByCity } from '../../../../../../src/microservice/domain/model/neighborhoods/neighborhood-by-city.model'; import { Neighborhood } from '../../../../../../src/microservice/domain/schemas/neighborhood.schema'; import { Country } from '../../../../../../src/microservice/domain/schemas/country.schema'; import { State } from '../../../../../../src/microservice/domain/schemas/state.schema'; import { City } from '../../../../../../src/microservice/domain/schemas/city.schema'; -import { SearchNeighborhoodsDB } from '../../../../../../src/microservice/domain/model/search/search-neighborhoods-db.model'; +import { SearchNeighborhoodsDB } from '../../../../../../src/microservice/domain/model/search/neighborhoods/search-neighborhoods-db.model'; describe('SaveNeighborhoodsByCityService', () => { let sut: SaveNeighborhoodsByCityService; @@ -17,11 +17,17 @@ describe('SaveNeighborhoodsByCityService', () => { const mockNeighborhoods: NeighborhoodByCity[] = [ { name: 'Aires Rodrigues', - city: 'Orleans-SC' + city: 'Orleans - SC', + countryId: 31, + stateId: 2014, + cityId: 1 }, { name: 'Alto Paraná', - city: 'Orleans-SC' + city: 'Orleans - SC', + countryId: 31, + stateId: 2014, + cityId: 2 } ]; @@ -97,7 +103,7 @@ describe('SaveNeighborhoodsByCityService', () => { 'insertOne' ); - const searchParams = new SearchNeighborhoodsInput( + const searchParams = new SearchNeighborhoodsDTO( 'brasil', 'sc', 'orleans' @@ -125,7 +131,7 @@ describe('SaveNeighborhoodsByCityService', () => { 'insertOne' ); - const searchParams = new SearchNeighborhoodsInput( + const searchParams = new SearchNeighborhoodsDTO( 'brasil', 'sc', 'orleans' diff --git a/test/unit/microservice/domain/service/neighborhoods/seed/seed-neighborhoods-by-city.service.spec.ts b/test/unit/microservice/domain/service/neighborhoods/seed/seed-neighborhoods-by-state.service.spec.ts similarity index 81% rename from test/unit/microservice/domain/service/neighborhoods/seed/seed-neighborhoods-by-city.service.spec.ts rename to test/unit/microservice/domain/service/neighborhoods/seed/seed-neighborhoods-by-state.service.spec.ts index 483d9f6..83158a2 100644 --- a/test/unit/microservice/domain/service/neighborhoods/seed/seed-neighborhoods-by-city.service.spec.ts +++ b/test/unit/microservice/domain/service/neighborhoods/seed/seed-neighborhoods-by-state.service.spec.ts @@ -3,26 +3,30 @@ import { SeedNeighborhoodsByStateService } from '../../../../../../../src/micros import { expect } from 'chai'; import * as sinon from 'sinon'; import '../../../../../../../src/microservice/adapter/helper/extensions/exensions.module'; -import { SearchNeighborhoodsInput } from '../../../../../../../src/microservice/domain/model/search/search-neighborhoods-input.model'; +import { SearchNeighborhoodsDTO } from '../../../../../../../src/microservice/domain/model/search/neighborhoods/search-neighborhoods-dto.model'; import { Country } from '../../../../../../../src/microservice/domain/schemas/country.schema'; import { City } from '../../../../../../../src/microservice/domain/schemas/city.schema'; -import { GetCitiesByStateService } from '../../../../../../../src/microservice/domain/service/cities/get-cities-by-state.service'; +import { GetCitiesByStateService } from '../../../../../../../src/microservice/domain/service/cities/get/get-cities-by-state.service'; import { GetNeighborhoodsByCityService } from '../../../../../../../src/microservice/domain/service/neighborhoods/get/get-neighborhoods-by-city.service'; import { LogSeedJobService } from '../../../../../../../src/microservice/domain/service/logseed/log-seed-job.service'; import { State } from '../../../../../../../src/microservice/domain/schemas/state.schema'; -import { ValidateInputParamsService } from '../../../../../../../src/microservice/domain/service/validate-input-params.service'; +import { ValidateInputParamsService } from '../../../../../../../src/microservice/domain/service/validate/validate-input-params.service'; import { NeighborhoodsMongoose } from '../../../../../../../src/microservice/adapter/repository/neighborhoods/neighborhoods-mongoose.repository'; import { Translations } from '../../../../../../../src/microservice/domain/model/translations.model'; import { EnumTranslations } from '../../../../../../../src/microservice/domain/enumerators/enum-translations.enumerator'; import { NotFoundException } from '../../../../../../../src/core/error-handling/exception/not-found.exception'; +import { GetNeighborhoodsByStateService } from '../../../../../../../src/microservice/domain/service/neighborhoods/get/get-neighborhoods-by-state.service'; describe('SeedNeighborhoodsByStateService', () => { let sut: SeedNeighborhoodsByStateService; const mockGetCitiesByStateService = { - getCitiesByState: () => { + findCitiesByState: () => { return; - }, + } + }; + + const mockGetNeighborhoodsByStateService = { groupByCity: () => { return; } @@ -122,6 +126,10 @@ describe('SeedNeighborhoodsByStateService', () => { provide: LogSeedJobService, useValue: mockLogSeedService }, + { + provide: GetNeighborhoodsByStateService, + useValue: mockGetNeighborhoodsByStateService + }, SeedNeighborhoodsByStateService ] }).compile(); @@ -134,7 +142,7 @@ describe('SeedNeighborhoodsByStateService', () => { describe('seedNeighborhoodsByStateService', () => { it('should call seedNeighborhoodsByStateService and return "Seeded"', async () => { const groupByCityStub = sinon - .stub(mockGetCitiesByStateService, 'groupByCity') + .stub(mockGetNeighborhoodsByStateService, 'groupByCity') .returns([]); const validateStub = sinon @@ -142,10 +150,10 @@ describe('SeedNeighborhoodsByStateService', () => { .returns(mockConvertedSearch()); const getCitiesByStateStub = sinon - .stub(mockGetCitiesByStateService, 'getCitiesByState') + .stub(mockGetCitiesByStateService, 'findCitiesByState') .returns(mockCities()); - const searchParams = new SearchNeighborhoodsInput('brasil', 'sc'); + const searchParams = new SearchNeighborhoodsDTO('brasil', 'sc'); const actual = await sut.seedNeighborhoodsByState(searchParams); @@ -159,7 +167,7 @@ describe('SeedNeighborhoodsByStateService', () => { it('should call seedNeighborhoodsByStateService and return "Nothing to seed"', async () => { const groupByCityStub = sinon - .stub(mockGetCitiesByStateService, 'groupByCity') + .stub(mockGetNeighborhoodsByStateService, 'groupByCity') .returns([]); const validateStub = sinon @@ -167,10 +175,10 @@ describe('SeedNeighborhoodsByStateService', () => { .returns(mockConvertedSearch()); const getCitiesByStateStub = sinon - .stub(mockGetCitiesByStateService, 'getCitiesByState') + .stub(mockGetCitiesByStateService, 'findCitiesByState') .returns([]); - const searchParams = new SearchNeighborhoodsInput('brasil', 'sc'); + const searchParams = new SearchNeighborhoodsDTO('brasil', 'sc'); const actual = await sut.seedNeighborhoodsByState(searchParams); @@ -184,7 +192,7 @@ describe('SeedNeighborhoodsByStateService', () => { it('should call seedNeighborhoodsByStateService with error and call logSeedService', async () => { const groupByCityStub = sinon - .stub(mockGetCitiesByStateService, 'groupByCity') + .stub(mockGetNeighborhoodsByStateService, 'groupByCity') .returns([]); const validateStub = sinon @@ -192,7 +200,7 @@ describe('SeedNeighborhoodsByStateService', () => { .returns(mockConvertedSearch()); const getCitiesByStateStub = sinon - .stub(mockGetCitiesByStateService, 'getCitiesByState') + .stub(mockGetCitiesByStateService, 'findCitiesByState') .returns(mockCities()); const mockError = new NotFoundException('Neighborhoods'); @@ -206,7 +214,7 @@ describe('SeedNeighborhoodsByStateService', () => { 'logSeedByState' ); - const searchParams = new SearchNeighborhoodsInput('brasil', 'sc'); + const searchParams = new SearchNeighborhoodsDTO('brasil', 'sc'); await sut.seedNeighborhoodsByState(searchParams); @@ -246,7 +254,7 @@ describe('SeedNeighborhoodsByStateService', () => { await sut.seedByCity(mockConvertedSearch(), mockCity); - const spySearch = new SearchNeighborhoodsInput('brasil', 'SC', 'Orleans'); + const spySearch = new SearchNeighborhoodsDTO('brasil', 'SC', 'Orleans'); const spyConvertedSearch = mockConvertedSearch(); spyConvertedSearch.city = mockCity; @@ -263,7 +271,7 @@ describe('SeedNeighborhoodsByStateService', () => { describe('getSeededCities', () => { it('should call getSeededCities and return the correct values', async () => { const groupByCityStub = sinon - .stub(mockGetCitiesByStateService, 'groupByCity') + .stub(mockGetNeighborhoodsByStateService, 'groupByCity') .resolves(mockAggregatedCities); const actual = await sut.getSeededCities(1); diff --git a/test/unit/microservice/domain/service/states/get-states-by-country.service.spec.ts b/test/unit/microservice/domain/service/states/get-states-by-country.service.spec.ts new file mode 100644 index 0000000..297be78 --- /dev/null +++ b/test/unit/microservice/domain/service/states/get-states-by-country.service.spec.ts @@ -0,0 +1,87 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { expect } from 'chai'; +import * as sinon from 'sinon'; +import '../../../../../../src/microservice/adapter/helper/extensions/exensions.module'; +import { ValidateInputParamsService } from '../../../../../../src/microservice/domain/service/validate/validate-input-params.service'; +import { State } from '../../../../../../src/microservice/domain/schemas/state.schema'; +import { StatesMongoose } from '../../../../../../src/microservice/adapter/repository/states/states-mongoose.repository'; +import { ValidateCountryByNameOrAliasService } from '../../../../../../src/microservice/domain/service/countries/validate-country-by-name-or-alias.service'; +import { GetStatesByCountryService } from '../../../../../../src/microservice/domain/service/states/get-states-by-country.service'; + +describe('GetStatesByCountryService', () => { + let sut: GetStatesByCountryService; + + const mockCitiesMongooseRepository = { + findBySearchParams: () => { + return []; + } + }; + + const mockValidateService = { + validateAndConvertSearchByState: () => { + return {}; + }, + validateAndConvertSearchByCity: () => { + return {}; + } + }; + + const mockValidateCountryService = { + validateCountry: () => { + return {}; + } + }; + + beforeEach(async () => { + const app: TestingModule = await Test.createTestingModule({ + imports: [], + controllers: [], + providers: [ + { + provide: StatesMongoose, + useValue: mockCitiesMongooseRepository + }, + { + provide: ValidateInputParamsService, + useFactory: () => mockValidateService + }, + { + provide: ValidateCountryByNameOrAliasService, + useFactory: () => mockValidateCountryService + }, + GetStatesByCountryService + ] + }).compile(); + + sut = app.get(GetStatesByCountryService); + }); + + const mockStates = () => { + const state1 = new State(); + state1.id = 1; + state1.name = 'any'; + state1.stateCode = 'SC'; + + const state2 = new State(); + state1.id = 2; + state1.name = 'any'; + state1.stateCode = 'RS'; + + return [state1, state2]; + }; + + describe('getStatesByCountry', () => { + it('should call getStatesByCountry and return an array', async () => { + const arrStates = mockStates(); + const getCitiesStub = sinon + .stub(mockCitiesMongooseRepository, 'findBySearchParams') + .returns(arrStates); + + const actual = await sut.getStatesByCountry('any'); + + expect(actual).to.be.equal(arrStates); + + getCitiesStub.restore(); + }); + }); +}); diff --git a/test/unit/microservice/domain/service/states/validate-state-by-name-or-alias.service.spec.ts b/test/unit/microservice/domain/service/states/validate-state-by-name-or-alias.service.spec.ts index c491812..fd11532 100644 --- a/test/unit/microservice/domain/service/states/validate-state-by-name-or-alias.service.spec.ts +++ b/test/unit/microservice/domain/service/states/validate-state-by-name-or-alias.service.spec.ts @@ -10,7 +10,7 @@ describe('ValidateStateByNameOrAliasService', () => { let sut: ValidateStateByNameOrAliasService; const mockStatesMongooseRepository = { - findByNameOrAlias: () => { + findByNameOrAliasOrId: () => { return [new State()]; } }; @@ -43,7 +43,7 @@ describe('ValidateStateByNameOrAliasService', () => { describe('getStateByNameOrAlias', () => { it('should call getStateByCity and return an array by mongodb', async () => { const mongoFindStub = sinon - .stub(mockStatesMongooseRepository, 'findByNameOrAlias') + .stub(mockStatesMongooseRepository, 'findByNameOrAliasOrId') .returns(mockMongoStates()); const actual = await sut.getStateByNameOrAlias('sc', 1); @@ -59,7 +59,7 @@ describe('ValidateStateByNameOrAliasService', () => { describe('validateState', () => { it('should call validateState and throws invalid data exception', async () => { const getStateStub = sinon - .stub(mockStatesMongooseRepository, 'findByNameOrAlias') + .stub(mockStatesMongooseRepository, 'findByNameOrAliasOrId') .returns([]); try { @@ -75,7 +75,7 @@ describe('ValidateStateByNameOrAliasService', () => { const state = new State(); state.name = 'any'; const getStateStub = sinon - .stub(mockStatesMongooseRepository, 'findByNameOrAlias') + .stub(mockStatesMongooseRepository, 'findByNameOrAliasOrId') .returns([state]); const actual = await sut.validateState('orleans', 1); diff --git a/test/unit/microservice/domain/service/validate/validate-input-params.service.spec.ts b/test/unit/microservice/domain/service/validate/validate-input-params.service.spec.ts index ac259c1..ca5e7d2 100644 --- a/test/unit/microservice/domain/service/validate/validate-input-params.service.spec.ts +++ b/test/unit/microservice/domain/service/validate/validate-input-params.service.spec.ts @@ -4,12 +4,12 @@ import * as sinon from 'sinon'; import '../../../../../../src/microservice/adapter/helper/extensions/exensions.module'; import { ValidateStateByNameOrAliasService } from '../../../../../../src/microservice/domain/service/states/validate-state-by-name-or-alias.service'; import { State } from '../../../../../../src/microservice/domain/schemas/state.schema'; -import { ValidateInputParamsService } from '../../../../../../src/microservice/domain/service/validate-input-params.service'; +import { ValidateInputParamsService } from '../../../../../../src/microservice/domain/service/validate/validate-input-params.service'; import { Country } from '../../../../../../src/microservice/domain/schemas/country.schema'; import { City } from '../../../../../../src/microservice/domain/schemas/city.schema'; import { ValidateCountryByNameOrAliasService } from '../../../../../../src/microservice/domain/service/countries/validate-country-by-name-or-alias.service'; import { ValidateCityByNameOrAliasService } from '../../../../../../src/microservice/domain/service/cities/validate-city-by-name-or-alias.service'; -import { SearchNeighborhoodsInput } from '../../../../../../src/microservice/domain/model/search/search-neighborhoods-input.model'; +import { SearchNeighborhoodsDTO } from '../../../../../../src/microservice/domain/model/search/neighborhoods/search-neighborhoods-dto.model'; describe('ValidateInputParamsService', () => { let sut: ValidateInputParamsService; @@ -87,7 +87,7 @@ describe('ValidateInputParamsService', () => { return { country, state, city }; }; - const searchParams = new SearchNeighborhoodsInput( + const searchParams = new SearchNeighborhoodsDTO( 'brasil', 'sc', 'orleans' @@ -116,10 +116,10 @@ describe('ValidateInputParamsService', () => { .returns(state); const mockConvertedSearch = () => { - return { country, state, city: null }; + return { country, state }; }; - const searchParams = new SearchNeighborhoodsInput('brasil', 'sc'); + const searchParams = new SearchNeighborhoodsDTO('brasil', 'sc'); const actual = await sut.validateAndConvertSearchByState(searchParams);