From 04d024f1e29d9a80610946c071c0dc8529dcd3ab Mon Sep 17 00:00:00 2001 From: Artur Samoilov Date: Wed, 11 Sep 2024 23:12:53 +0200 Subject: [PATCH 01/10] feat: adds data types --- src/shared/types/city.enum.ts | 8 ++++++++ src/shared/types/comment.type.ts | 6 ++++++ src/shared/types/coordinates.type.ts | 4 ++++ src/shared/types/facility.type.ts | 1 + src/shared/types/housing-type.enum.ts | 6 ++++++ src/shared/types/offer.type.ts | 24 ++++++++++++++++++++++++ src/shared/types/user-type.enum.ts | 4 ++++ src/shared/types/user.type.ts | 9 +++++++++ 8 files changed, 62 insertions(+) create mode 100644 src/shared/types/city.enum.ts create mode 100644 src/shared/types/comment.type.ts create mode 100644 src/shared/types/coordinates.type.ts create mode 100644 src/shared/types/facility.type.ts create mode 100644 src/shared/types/housing-type.enum.ts create mode 100644 src/shared/types/offer.type.ts create mode 100644 src/shared/types/user-type.enum.ts create mode 100644 src/shared/types/user.type.ts diff --git a/src/shared/types/city.enum.ts b/src/shared/types/city.enum.ts new file mode 100644 index 0000000..5be1855 --- /dev/null +++ b/src/shared/types/city.enum.ts @@ -0,0 +1,8 @@ +export enum ECity { + Paris = 'Paris', + Cologne = 'Cologne', + Brussels = 'Brussels', + Amsterdam = 'Amsterdam', + Hamburg = 'Hamburg', + Dusseldorf = 'Dusseldorf' +} diff --git a/src/shared/types/comment.type.ts b/src/shared/types/comment.type.ts new file mode 100644 index 0000000..c133b57 --- /dev/null +++ b/src/shared/types/comment.type.ts @@ -0,0 +1,6 @@ +export type TComment = { + text: string, + date: Date, + rating: number, + author: string, +} diff --git a/src/shared/types/coordinates.type.ts b/src/shared/types/coordinates.type.ts new file mode 100644 index 0000000..2e76940 --- /dev/null +++ b/src/shared/types/coordinates.type.ts @@ -0,0 +1,4 @@ +export type TCoordinates = { + latitude: number, + longitude: number, +} diff --git a/src/shared/types/facility.type.ts b/src/shared/types/facility.type.ts new file mode 100644 index 0000000..e8540ec --- /dev/null +++ b/src/shared/types/facility.type.ts @@ -0,0 +1 @@ +export type TFacility = 'Breakfast' | 'Air conditioning' | 'Laptop friendly workspace' | 'Baby seat' | 'Washer' | 'Towels' | 'Fridge'; diff --git a/src/shared/types/housing-type.enum.ts b/src/shared/types/housing-type.enum.ts new file mode 100644 index 0000000..f888f39 --- /dev/null +++ b/src/shared/types/housing-type.enum.ts @@ -0,0 +1,6 @@ +export enum EHousingType { + Apartment = 'apartment', + House = 'house', + Room = 'room', + Hotel = 'hotel', +} diff --git a/src/shared/types/offer.type.ts b/src/shared/types/offer.type.ts new file mode 100644 index 0000000..752e9ac --- /dev/null +++ b/src/shared/types/offer.type.ts @@ -0,0 +1,24 @@ +import { ECity } from './city.enum.js'; +import { EHousingType } from './housing-type.enum.js'; +import { TFacility } from './facility.type.js'; +import { TCoordinates } from './coordinates.type.js'; + +export type TOffer = { + title: string, + description: string, + date: Date, + city: ECity, + previewImage: string, + images: string[], + isPremium: boolean, + isSelected: boolean, + rating: number, + housingType: EHousingType, + roomsNumber: number, + guestsNumber: number, + price: number, + facilities: TFacility[], + author: string, + commentsNumber: number, + coordinates: TCoordinates, +} diff --git a/src/shared/types/user-type.enum.ts b/src/shared/types/user-type.enum.ts new file mode 100644 index 0000000..a3e0959 --- /dev/null +++ b/src/shared/types/user-type.enum.ts @@ -0,0 +1,4 @@ +export enum EUserType { + Regular = 'regular', + Pro = 'pro', +} diff --git a/src/shared/types/user.type.ts b/src/shared/types/user.type.ts new file mode 100644 index 0000000..44fe043 --- /dev/null +++ b/src/shared/types/user.type.ts @@ -0,0 +1,9 @@ +import { EUserType } from './user-type.enum.js'; + +export type TUser = { + name: string, + email: string, + avatar?: string, + password: string, + userType: EUserType, +} From 4bdcc326281e0e51e9de888a9f99848502441c1f Mon Sep 17 00:00:00 2001 From: Artur Samoilov Date: Wed, 11 Sep 2024 23:13:55 +0200 Subject: [PATCH 02/10] feat: adds mock offers data --- mocks/mock-offers.tsv | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 mocks/mock-offers.tsv diff --git a/mocks/mock-offers.tsv b/mocks/mock-offers.tsv new file mode 100644 index 0000000..75b2210 --- /dev/null +++ b/mocks/mock-offers.tsv @@ -0,0 +1,2 @@ +Champerret Heliopolis The Champerret Heliopolis offers charm and tranquility in the heart of Paris' 17th Arrondissement near Espace Champerret and Porte Maillot conference center. 2024-09-11T20:27:34Z Paris previewParis.jpg paris1.jpg;paris2.jpg;paris3.jpg;paris4.jpg;paris5.jpg;paris6.jpg 1 0 4.5 hotel 1 2 1000 Breakfast;Air conditioning /users/1 13 48.85661;2.351499 +Düsseldorf 1995 Düsseldorf 1995 is located in Düsseldorf, just 2.4 miles from Central Station Düsseldorf and 2.8 miles from Südpark. The property is around 2.9 miles from Theater an der Kö, 3 miles from Königsallee, and 3.1 miles from German Opera on the Rhine. 2024-08-10T20:27:34Z Dusseldorf previewDusseldorf.jpg dusseldorf1.jpg;dusseldorf2.jpg;dusseldorf3.jpg;dusseldorf4.jpg;dusseldorf5.jpg;dusseldorf6.jpg 0 0 3.9 apartment 3 5 74000 Baby seat;Washer;Towels;Fridge /users/2 3 51.225402;6.776314 From b7e95e242195fef0042e36948f14b69f47999b30 Mon Sep 17 00:00:00 2001 From: Artur Samoilov Date: Thu, 12 Sep 2024 02:45:12 +0200 Subject: [PATCH 03/10] feat: adds cli commands --- package.json | 4 +- src/cli/cli-application.ts | 40 +++++++ src/cli/command-parser.ts | 19 ++++ src/cli/commands/command.interface.ts | 4 + src/cli/commands/help.command.ts | 22 ++++ src/cli/commands/import.command.ts | 26 +++++ src/cli/commands/version.command.ts | 48 ++++++++ src/cli/index.ts | 5 + src/main.cli.ts | 14 +++ .../libs/file-reader/file-reader.interface.ts | 3 + src/shared/libs/file-reader/index.ts | 2 + .../libs/file-reader/tsv-file-reader.ts | 104 ++++++++++++++++++ src/shared/types/housing-type.enum.ts | 6 - src/shared/types/housing-type.type.ts | 1 + src/shared/types/index.ts | 3 + src/shared/types/offer.type.ts | 4 +- 16 files changed, 295 insertions(+), 10 deletions(-) create mode 100644 src/cli/cli-application.ts create mode 100644 src/cli/command-parser.ts create mode 100644 src/cli/commands/command.interface.ts create mode 100644 src/cli/commands/help.command.ts create mode 100644 src/cli/commands/import.command.ts create mode 100644 src/cli/commands/version.command.ts create mode 100644 src/cli/index.ts create mode 100644 src/main.cli.ts create mode 100644 src/shared/libs/file-reader/file-reader.interface.ts create mode 100644 src/shared/libs/file-reader/index.ts create mode 100644 src/shared/libs/file-reader/tsv-file-reader.ts delete mode 100644 src/shared/types/housing-type.enum.ts create mode 100644 src/shared/types/housing-type.type.ts create mode 100644 src/shared/types/index.ts diff --git a/package.json b/package.json index 02b8cab..1d84f4f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "six-cities", - "version": "7.0.0", + "version": "0.0.1", "description": "Проект «Шесть городов» от HTML Academy", "keywords": [ "rest", @@ -14,7 +14,7 @@ "lint": "eslint src/ --ext .ts", "compile": "tsc -p tsconfig.json", "clean": "rimraf dist", - "ts": "tsc --noEmit && node --no-warnings=--no-warnings=ExperimentalWarning --loader ts-node/esm" + "ts": "tsc --noEmit && node --no-warnings=ExperimentalWarning --loader ts-node/esm" }, "devDependencies": { "@types/node": "20.12.7", diff --git a/src/cli/cli-application.ts b/src/cli/cli-application.ts new file mode 100644 index 0000000..8d0b032 --- /dev/null +++ b/src/cli/cli-application.ts @@ -0,0 +1,40 @@ +import { ICommand } from './commands/command.interface.js'; +import { CommandParser } from './command-parser.js'; + +type TCommandCollection = Record; + +export class CLIApplication { + private commands: TCommandCollection = {}; + + constructor( + private readonly defaultCommand: string = '--help', + ) {} + + public registerCommands(commandList: ICommand[]): void { + commandList.forEach((command) => { + if (Object.hasOwn(this.commands, command.getName())) { + throw new Error(`Command ${command.getName()} is already registered`); + } + this.commands[command.getName()] = command; + }); + } + + public getDefaultCommand(): ICommand | never { + if (!this.commands[this.defaultCommand]) { + throw new Error(`The default command (${this.defaultCommand}) is not registered.`); + } + return this.commands[this.defaultCommand]; + } + + public getCommand(commandName: string): ICommand { + return this.commands[commandName] ?? this.getDefaultCommand(); + } + + public processCommand(argv: string[]): void { + const parsedCommand = CommandParser.parse(argv); + const [commandName] = Object.keys(parsedCommand); + const command = this.getCommand(commandName); + const commandArguments = parsedCommand[commandName] ?? []; + command.execute(...commandArguments); + } +} diff --git a/src/cli/command-parser.ts b/src/cli/command-parser.ts new file mode 100644 index 0000000..ad54c53 --- /dev/null +++ b/src/cli/command-parser.ts @@ -0,0 +1,19 @@ +type TParsedCommand = Record + +export class CommandParser { + static parse(cliArgs: string[]): TParsedCommand { + const parsedCommand: TParsedCommand = {}; + let currentCommand = ''; + + for (const argument of cliArgs) { + if (argument.startsWith('--')) { + parsedCommand[argument] = []; + currentCommand = argument; + } else if (currentCommand && argument) { + parsedCommand[currentCommand].push(argument); + } + } + + return parsedCommand; + } +} diff --git a/src/cli/commands/command.interface.ts b/src/cli/commands/command.interface.ts new file mode 100644 index 0000000..9423790 --- /dev/null +++ b/src/cli/commands/command.interface.ts @@ -0,0 +1,4 @@ +export interface ICommand { + getName(): string; + execute(...parameters: string[]): void; +} diff --git a/src/cli/commands/help.command.ts b/src/cli/commands/help.command.ts new file mode 100644 index 0000000..eae685e --- /dev/null +++ b/src/cli/commands/help.command.ts @@ -0,0 +1,22 @@ +import { ICommand } from './command.interface.js'; + +export class HelpCommand implements ICommand { + public getName(): string { + return '--help'; + } + + public async execute(..._parameters: string[]): Promise { + console.info(` + Программа для подготовки данных для REST API сервера. + + Пример: cli.js -- [--arguments] + + Команды: + + --version: # выводит номер версии + --help: # печатает этот текст + --import : # импортирует данные из TSV + --generate # генерирует произвольное количество тестовых данных + `); + } +} diff --git a/src/cli/commands/import.command.ts b/src/cli/commands/import.command.ts new file mode 100644 index 0000000..399569f --- /dev/null +++ b/src/cli/commands/import.command.ts @@ -0,0 +1,26 @@ +import { ICommand } from './command.interface.js'; +import { TSVFileReader } from '../../shared/libs/file-reader/index.js'; + +export class ImportCommand implements ICommand { + public getName(): string { + return '--import'; + } + + public execute(...parameters: string[]): void { + const [fileName] = parameters; + const fileReader = new TSVFileReader(fileName.trim()); + + try { + fileReader.read(); + console.log(fileReader.toArray()); + } catch (err) { + + if (!(err instanceof Error)) { + throw err; + } + + console.error(`Can't import data from file: ${fileName}`); + console.error(`Details: ${err.message}`); + } + } +} diff --git a/src/cli/commands/version.command.ts b/src/cli/commands/version.command.ts new file mode 100644 index 0000000..3cfbb63 --- /dev/null +++ b/src/cli/commands/version.command.ts @@ -0,0 +1,48 @@ +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; + +import { ICommand } from './command.interface.js'; + +type TPackageJSONConfig = { + version: string; +} + +const isPackageJSONConfig = (value: unknown): value is TPackageJSONConfig => + typeof value === 'object' && + value !== null && + !Array.isArray(value) && + Object.hasOwn(value, 'version'); + +export class VersionCommand implements ICommand { + constructor( + private readonly filePath: string = 'package.json' + ) {} + + private readVersion(): string { + const jsonContent = readFileSync(resolve(this.filePath), { encoding: 'utf-8' }); + const importedContent: unknown = JSON.parse(jsonContent); + + if (!isPackageJSONConfig(importedContent)) { + throw new Error('Failed to parse json content.'); + } + + return importedContent.version; + } + + public getName(): string { + return '--version'; + } + + public async execute(..._parameters: string[]): Promise { + try { + const version = this.readVersion(); + console.info(version); + } catch (error: unknown) { + console.error(`Failed to read version from ${this.filePath}`); + + if (error instanceof Error) { + console.error(error.message); + } + } + } +} diff --git a/src/cli/index.ts b/src/cli/index.ts new file mode 100644 index 0000000..c8ef6be --- /dev/null +++ b/src/cli/index.ts @@ -0,0 +1,5 @@ +export { CLIApplication } from './cli-application.js'; +export { CommandParser } from './command-parser.js'; +export { HelpCommand } from './commands/help.command.js'; +export { VersionCommand } from './commands/version.command.js'; +export { ImportCommand } from './commands/import.command.js'; diff --git a/src/main.cli.ts b/src/main.cli.ts new file mode 100644 index 0000000..1acb8d6 --- /dev/null +++ b/src/main.cli.ts @@ -0,0 +1,14 @@ +import { CLIApplication, HelpCommand, VersionCommand, ImportCommand } from './cli/index.js'; + +const bootstrap = async () => { + const cliApplication = new CLIApplication(); + cliApplication.registerCommands([ + new HelpCommand(), + new VersionCommand(), + new ImportCommand(), + ]); + + cliApplication.processCommand(process.argv); +}; + +await bootstrap(); diff --git a/src/shared/libs/file-reader/file-reader.interface.ts b/src/shared/libs/file-reader/file-reader.interface.ts new file mode 100644 index 0000000..14c7784 --- /dev/null +++ b/src/shared/libs/file-reader/file-reader.interface.ts @@ -0,0 +1,3 @@ +export interface IFileReader { + read(): void; +} diff --git a/src/shared/libs/file-reader/index.ts b/src/shared/libs/file-reader/index.ts new file mode 100644 index 0000000..28d7df5 --- /dev/null +++ b/src/shared/libs/file-reader/index.ts @@ -0,0 +1,2 @@ +export { IFileReader } from './file-reader.interface.js'; +export { TSVFileReader } from './tsv-file-reader.js'; diff --git a/src/shared/libs/file-reader/tsv-file-reader.ts b/src/shared/libs/file-reader/tsv-file-reader.ts new file mode 100644 index 0000000..77bfb9b --- /dev/null +++ b/src/shared/libs/file-reader/tsv-file-reader.ts @@ -0,0 +1,104 @@ +import { readFileSync } from 'node:fs'; + +import { IFileReader } from './file-reader.interface.js'; +import { TOffer } from '../../types/index.js'; +import { ECity } from '../../types/city.enum.js'; +import { TFacility } from '../../types/facility.type.js'; +import { TCoordinates } from '../../types/coordinates.type.js'; +import { THousingType } from '../../types/housing-type.type.js'; + +export class TSVFileReader implements IFileReader { + private rawData = ''; + + constructor( + private readonly filename: string + ) {} + + private validateRawData(): void { + if (!this.rawData) { + throw new Error('File was not read'); + } + } + + private parseImages(imagesString: string): string[] { + return imagesString.split(';'); + } + + private parseIntNumber(numberString: string): number { + return Number.parseInt(numberString, 10); + } + + private parseNumberWithDot(numberString: string): number { + return Number.parseFloat(numberString); + } + + private parseFacilities(facilitiesString: string): TFacility[] { + return facilitiesString.split(';') as TFacility[]; + } + + private parseCoordinates(coordinatesString: string): TCoordinates { + const coordinatesList = coordinatesString.split(';'); + return { + latitude: this.parseNumberWithDot(coordinatesList[0]), + longitude: this.parseNumberWithDot(coordinatesList[1]), + } as TCoordinates; + } + + private parseLineToOffer(line: string): TOffer { + const [ + title, + description, + createDate, + city, + previewImage, + images, + isPremium, + isSelected, + rating, + housingType, + roomsNumber, + guestsNumber, + price, + facilities, + author, + commentsNumber, + coordinates, + ] = line.split('\t'); + + return { + title, + description, + date: new Date(createDate), + city: ECity[city as keyof typeof ECity], + previewImage, + images: this.parseImages(images), + isPremium: !!this.parseIntNumber(isPremium), + isSelected: !!this.parseIntNumber(isSelected), + rating: this.parseNumberWithDot(rating), + housingType: housingType as THousingType, + roomsNumber: this.parseIntNumber(roomsNumber), + guestsNumber: this.parseIntNumber(guestsNumber), + price: this.parseIntNumber(price), + facilities: this.parseFacilities(facilities), + author, + commentsNumber: this.parseIntNumber(commentsNumber), + coordinates: this.parseCoordinates(coordinates), + }; + } + + private parseRawDataToOffers(): TOffer[] { + return this.rawData + .split('\n') + .filter((row) => row.trim().length > 0) + .map((line) => this.parseLineToOffer(line)); + } + + public read(): void { + this.rawData = readFileSync(this.filename, { encoding: 'utf-8' }); + } + + public toArray(): TOffer[] { + this.validateRawData(); + return this.parseRawDataToOffers(); + } +} diff --git a/src/shared/types/housing-type.enum.ts b/src/shared/types/housing-type.enum.ts deleted file mode 100644 index f888f39..0000000 --- a/src/shared/types/housing-type.enum.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum EHousingType { - Apartment = 'apartment', - House = 'house', - Room = 'room', - Hotel = 'hotel', -} diff --git a/src/shared/types/housing-type.type.ts b/src/shared/types/housing-type.type.ts new file mode 100644 index 0000000..39634bd --- /dev/null +++ b/src/shared/types/housing-type.type.ts @@ -0,0 +1 @@ +export type THousingType = 'apartment' | 'house' | 'room' | 'hotel'; diff --git a/src/shared/types/index.ts b/src/shared/types/index.ts new file mode 100644 index 0000000..2135f65 --- /dev/null +++ b/src/shared/types/index.ts @@ -0,0 +1,3 @@ +export { TOffer } from './offer.type.js'; +export { TUser } from './user.type.js'; +export { TComment } from './comment.type.js'; diff --git a/src/shared/types/offer.type.ts b/src/shared/types/offer.type.ts index 752e9ac..17cf6b0 100644 --- a/src/shared/types/offer.type.ts +++ b/src/shared/types/offer.type.ts @@ -1,5 +1,5 @@ import { ECity } from './city.enum.js'; -import { EHousingType } from './housing-type.enum.js'; +import { THousingType } from './housing-type.type.js'; import { TFacility } from './facility.type.js'; import { TCoordinates } from './coordinates.type.js'; @@ -13,7 +13,7 @@ export type TOffer = { isPremium: boolean, isSelected: boolean, rating: number, - housingType: EHousingType, + housingType: THousingType, roomsNumber: number, guestsNumber: number, price: number, From 02bd3f596ece2a08769c8694dd175cbd8b10b960 Mon Sep 17 00:00:00 2001 From: Artur Samoilov Date: Thu, 12 Sep 2024 02:52:36 +0200 Subject: [PATCH 04/10] feat: adds shebang --- src/main.cli.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.cli.ts b/src/main.cli.ts index 1acb8d6..a7f3ca9 100644 --- a/src/main.cli.ts +++ b/src/main.cli.ts @@ -1,3 +1,4 @@ +#!/usr/bin/env node import { CLIApplication, HelpCommand, VersionCommand, ImportCommand } from './cli/index.js'; const bootstrap = async () => { From 24f99d9b54ec559c430c05d4fb13b30ae4b7fb0d Mon Sep 17 00:00:00 2001 From: Artur Samoilov Date: Thu, 12 Sep 2024 03:26:05 +0200 Subject: [PATCH 05/10] feat: adds colors to help command response text --- package-lock.json | 59 ++++++++++++++++++++++---------- package.json | 3 ++ src/cli/commands/help.command.ts | 15 ++++---- 3 files changed, 51 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index d36d6cc..67affc9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,15 @@ { "name": "six-cities", - "version": "7.0.0", + "version": "0.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "six-cities", - "version": "7.0.0", + "version": "0.0.1", + "dependencies": { + "chalk": "^5.3.0" + }, "devDependencies": { "@types/node": "20.12.7", "@typescript-eslint/eslint-plugin": "6.7.0", @@ -821,16 +824,11 @@ } }, "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" @@ -1401,6 +1399,22 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -4324,14 +4338,9 @@ "dev": true }, "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==" }, "ci-info": { "version": "3.8.0", @@ -4586,6 +4595,18 @@ "optionator": "^0.9.3", "strip-ansi": "^6.0.1", "text-table": "^0.2.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, "eslint-config-htmlacademy": { diff --git a/package.json b/package.json index 1d84f4f..e35a281 100644 --- a/package.json +++ b/package.json @@ -30,5 +30,8 @@ "engines": { "node": "^20.0.0", "npm": ">=10" + }, + "dependencies": { + "chalk": "^5.3.0" } } diff --git a/src/cli/commands/help.command.ts b/src/cli/commands/help.command.ts index eae685e..cd246cc 100644 --- a/src/cli/commands/help.command.ts +++ b/src/cli/commands/help.command.ts @@ -1,4 +1,5 @@ import { ICommand } from './command.interface.js'; +import chalk from 'chalk'; export class HelpCommand implements ICommand { public getName(): string { @@ -7,16 +8,16 @@ export class HelpCommand implements ICommand { public async execute(..._parameters: string[]): Promise { console.info(` - Программа для подготовки данных для REST API сервера. + ${chalk.bold.inverse(' Программа для подготовки данных для REST API сервера. ')} - Пример: cli.js -- [--arguments] + ${chalk.italic('Пример:')} ${chalk.whiteBright('cli.js')} ${chalk.cyanBright('--')} ${chalk.yellowBright('[--arguments]')} - Команды: + ${chalk.italic('Команды:')} + ${chalk.cyanBright('--version:')} ${chalk.italic.dim('# выводит номер версии')} + ${chalk.cyanBright('--help:')} ${chalk.italic.dim('# печатает этот текст')} + ${chalk.cyanBright('--import')} ${chalk.yellowBright('')}: ${chalk.italic.dim('# импортирует данные из TSV')} + ${chalk.cyanBright('--generate')} ${chalk.yellowBright(' ')}: ${chalk.italic.dim('# генерирует произвольное количество тестовых данных')} - --version: # выводит номер версии - --help: # печатает этот текст - --import : # импортирует данные из TSV - --generate # генерирует произвольное количество тестовых данных `); } } From 3e2b6219321a8d3e0ebf07514b2fe577bddbe6c2 Mon Sep 17 00:00:00 2001 From: Artur Samoilov Date: Thu, 12 Sep 2024 03:38:03 +0200 Subject: [PATCH 06/10] fix: help text design --- src/cli/commands/help.command.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cli/commands/help.command.ts b/src/cli/commands/help.command.ts index cd246cc..1ee5a1a 100644 --- a/src/cli/commands/help.command.ts +++ b/src/cli/commands/help.command.ts @@ -8,7 +8,7 @@ export class HelpCommand implements ICommand { public async execute(..._parameters: string[]): Promise { console.info(` - ${chalk.bold.inverse(' Программа для подготовки данных для REST API сервера. ')} + ${chalk.whiteBright('Программа для подготовки данных для REST API сервера.')} ${chalk.italic('Пример:')} ${chalk.whiteBright('cli.js')} ${chalk.cyanBright('--')} ${chalk.yellowBright('[--arguments]')} @@ -17,7 +17,6 @@ export class HelpCommand implements ICommand { ${chalk.cyanBright('--help:')} ${chalk.italic.dim('# печатает этот текст')} ${chalk.cyanBright('--import')} ${chalk.yellowBright('')}: ${chalk.italic.dim('# импортирует данные из TSV')} ${chalk.cyanBright('--generate')} ${chalk.yellowBright(' ')}: ${chalk.italic.dim('# генерирует произвольное количество тестовых данных')} - `); } } From 0359acf717971ee366121a4eff9f9ab83c8bb9c2 Mon Sep 17 00:00:00 2001 From: Artur Samoilov Date: Thu, 12 Sep 2024 18:05:12 +0200 Subject: [PATCH 07/10] fix: sets command execute function async --- src/cli/cli-application.ts | 6 +++--- src/cli/commands/command.interface.ts | 2 +- src/cli/commands/import.command.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cli/cli-application.ts b/src/cli/cli-application.ts index 8d0b032..97155b3 100644 --- a/src/cli/cli-application.ts +++ b/src/cli/cli-application.ts @@ -19,7 +19,7 @@ export class CLIApplication { }); } - public getDefaultCommand(): ICommand | never { + public getDefaultCommand(): ICommand { if (!this.commands[this.defaultCommand]) { throw new Error(`The default command (${this.defaultCommand}) is not registered.`); } @@ -30,11 +30,11 @@ export class CLIApplication { return this.commands[commandName] ?? this.getDefaultCommand(); } - public processCommand(argv: string[]): void { + public async processCommand(argv: string[]): Promise { const parsedCommand = CommandParser.parse(argv); const [commandName] = Object.keys(parsedCommand); const command = this.getCommand(commandName); const commandArguments = parsedCommand[commandName] ?? []; - command.execute(...commandArguments); + await command.execute(...commandArguments); } } diff --git a/src/cli/commands/command.interface.ts b/src/cli/commands/command.interface.ts index 9423790..a238ead 100644 --- a/src/cli/commands/command.interface.ts +++ b/src/cli/commands/command.interface.ts @@ -1,4 +1,4 @@ export interface ICommand { getName(): string; - execute(...parameters: string[]): void; + execute(...parameters: string[]): Promise; } diff --git a/src/cli/commands/import.command.ts b/src/cli/commands/import.command.ts index 399569f..793a134 100644 --- a/src/cli/commands/import.command.ts +++ b/src/cli/commands/import.command.ts @@ -6,7 +6,7 @@ export class ImportCommand implements ICommand { return '--import'; } - public execute(...parameters: string[]): void { + public async execute(...parameters: string[]): Promise { const [fileName] = parameters; const fileReader = new TSVFileReader(fileName.trim()); From 920b0817a1bcdfceee1dabc829462409e710fd08 Mon Sep 17 00:00:00 2001 From: Artur Samoilov Date: Sun, 15 Sep 2024 17:05:34 +0200 Subject: [PATCH 08/10] fix: small bugs after review --- mocks/mock-offers.tsv | 4 +- package.json | 2 +- src/cli/cli-application.ts | 25 ++++++------ src/cli/command-parser.ts | 19 ---------- src/cli/commands/help.command.ts | 8 ++-- src/cli/commands/import.command.ts | 7 +++- .../commands/{ => types}/command.interface.ts | 0 .../types/package-json-config.interface.ts | 3 ++ src/cli/commands/utils/parse-command.util.ts | 15 ++++++++ src/cli/commands/version.command.ts | 31 ++++++++------- src/cli/index.ts | 1 - src/main.cli.ts | 2 +- src/shared/libs/file-reader/index.ts | 2 +- .../libs/file-reader/tsv-file-reader.ts | 38 ++++++++----------- .../{ => types}/file-reader.interface.ts | 0 .../{comment.type.ts => comment.interface.ts} | 2 +- ...nates.type.ts => coordinates.interface.ts} | 2 +- src/shared/types/index.ts | 3 -- .../{offer.type.ts => offer.interface.ts} | 7 ++-- src/shared/types/user-type.enum.ts | 2 +- .../types/{user.type.ts => user.interface.ts} | 4 +- 21 files changed, 84 insertions(+), 93 deletions(-) delete mode 100644 src/cli/command-parser.ts rename src/cli/commands/{ => types}/command.interface.ts (100%) create mode 100644 src/cli/commands/types/package-json-config.interface.ts create mode 100644 src/cli/commands/utils/parse-command.util.ts rename src/shared/libs/file-reader/{ => types}/file-reader.interface.ts (100%) rename src/shared/types/{comment.type.ts => comment.interface.ts} (70%) rename src/shared/types/{coordinates.type.ts => coordinates.interface.ts} (57%) delete mode 100644 src/shared/types/index.ts rename src/shared/types/{offer.type.ts => offer.interface.ts} (78%) rename src/shared/types/{user.type.ts => user.interface.ts} (74%) diff --git a/mocks/mock-offers.tsv b/mocks/mock-offers.tsv index 75b2210..674d5f8 100644 --- a/mocks/mock-offers.tsv +++ b/mocks/mock-offers.tsv @@ -1,2 +1,2 @@ -Champerret Heliopolis The Champerret Heliopolis offers charm and tranquility in the heart of Paris' 17th Arrondissement near Espace Champerret and Porte Maillot conference center. 2024-09-11T20:27:34Z Paris previewParis.jpg paris1.jpg;paris2.jpg;paris3.jpg;paris4.jpg;paris5.jpg;paris6.jpg 1 0 4.5 hotel 1 2 1000 Breakfast;Air conditioning /users/1 13 48.85661;2.351499 -Düsseldorf 1995 Düsseldorf 1995 is located in Düsseldorf, just 2.4 miles from Central Station Düsseldorf and 2.8 miles from Südpark. The property is around 2.9 miles from Theater an der Kö, 3 miles from Königsallee, and 3.1 miles from German Opera on the Rhine. 2024-08-10T20:27:34Z Dusseldorf previewDusseldorf.jpg dusseldorf1.jpg;dusseldorf2.jpg;dusseldorf3.jpg;dusseldorf4.jpg;dusseldorf5.jpg;dusseldorf6.jpg 0 0 3.9 apartment 3 5 74000 Baby seat;Washer;Towels;Fridge /users/2 3 51.225402;6.776314 +Champerret Heliopolis The Champerret Heliopolis offers charm and tranquility in the heart of Paris' 17th Arrondissement near Espace Champerret and Porte Maillot conference center. 2024-09-11T20:27:34Z Paris previewParis.jpg paris1.jpg;paris2.jpg;paris3.jpg;paris4.jpg;paris5.jpg;paris6.jpg 1 4.5 hotel 1 2 1000 Breakfast;Air conditioning /users/1 13 48.85661;2.351499 +Düsseldorf 1995 Düsseldorf 1995 is located in Düsseldorf, just 2.4 miles from Central Station Düsseldorf and 2.8 miles from Südpark. The property is around 2.9 miles from Theater an der Kö, 3 miles from Königsallee, and 3.1 miles from German Opera on the Rhine. 2024-08-10T20:27:34Z Dusseldorf previewDusseldorf.jpg dusseldorf1.jpg;dusseldorf2.jpg;dusseldorf3.jpg;dusseldorf4.jpg;dusseldorf5.jpg;dusseldorf6.jpg 0 3.9 apartment 3 5 74000 Baby seat;Washer;Towels;Fridge /users/2 3 51.225402;6.776314 diff --git a/package.json b/package.json index e35a281..ae77f66 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,6 @@ "npm": ">=10" }, "dependencies": { - "chalk": "^5.3.0" + "chalk": "5.3.0" } } diff --git a/src/cli/cli-application.ts b/src/cli/cli-application.ts index 97155b3..883eea2 100644 --- a/src/cli/cli-application.ts +++ b/src/cli/cli-application.ts @@ -1,22 +1,22 @@ -import { ICommand } from './commands/command.interface.js'; -import { CommandParser } from './command-parser.js'; - -type TCommandCollection = Record; +import { ICommand } from './commands/types/command.interface.js'; +import { parseCommand } from './commands/utils/parse-command.util.js'; export class CLIApplication { - private commands: TCommandCollection = {}; + private readonly commands: Record = {}; constructor( private readonly defaultCommand: string = '--help', ) {} public registerCommands(commandList: ICommand[]): void { - commandList.forEach((command) => { - if (Object.hasOwn(this.commands, command.getName())) { - throw new Error(`Command ${command.getName()} is already registered`); + commandList.reduce((acc, command) => { + const commandName = command.getName(); + if (acc[commandName]) { + throw new Error(`Command ${commandName} is already registered`); } - this.commands[command.getName()] = command; - }); + acc[commandName] = command; + return acc; + }, this.commands); } public getDefaultCommand(): ICommand { @@ -31,10 +31,9 @@ export class CLIApplication { } public async processCommand(argv: string[]): Promise { - const parsedCommand = CommandParser.parse(argv); + const parsedCommand = parseCommand(argv); const [commandName] = Object.keys(parsedCommand); - const command = this.getCommand(commandName); const commandArguments = parsedCommand[commandName] ?? []; - await command.execute(...commandArguments); + await this.getCommand(commandName).execute(...commandArguments); } } diff --git a/src/cli/command-parser.ts b/src/cli/command-parser.ts deleted file mode 100644 index ad54c53..0000000 --- a/src/cli/command-parser.ts +++ /dev/null @@ -1,19 +0,0 @@ -type TParsedCommand = Record - -export class CommandParser { - static parse(cliArgs: string[]): TParsedCommand { - const parsedCommand: TParsedCommand = {}; - let currentCommand = ''; - - for (const argument of cliArgs) { - if (argument.startsWith('--')) { - parsedCommand[argument] = []; - currentCommand = argument; - } else if (currentCommand && argument) { - parsedCommand[currentCommand].push(argument); - } - } - - return parsedCommand; - } -} diff --git a/src/cli/commands/help.command.ts b/src/cli/commands/help.command.ts index 1ee5a1a..81f85e6 100644 --- a/src/cli/commands/help.command.ts +++ b/src/cli/commands/help.command.ts @@ -1,4 +1,4 @@ -import { ICommand } from './command.interface.js'; +import { ICommand } from './types/command.interface.js'; import chalk from 'chalk'; export class HelpCommand implements ICommand { @@ -13,9 +13,9 @@ export class HelpCommand implements ICommand { ${chalk.italic('Пример:')} ${chalk.whiteBright('cli.js')} ${chalk.cyanBright('--')} ${chalk.yellowBright('[--arguments]')} ${chalk.italic('Команды:')} - ${chalk.cyanBright('--version:')} ${chalk.italic.dim('# выводит номер версии')} - ${chalk.cyanBright('--help:')} ${chalk.italic.dim('# печатает этот текст')} - ${chalk.cyanBright('--import')} ${chalk.yellowBright('')}: ${chalk.italic.dim('# импортирует данные из TSV')} + ${chalk.cyanBright('--version:')} ${chalk.italic.dim('# выводит номер версии приложения')} + ${chalk.cyanBright('--help:')} ${chalk.italic.dim('# печатает справку по командам')} + ${chalk.cyanBright('--import')} ${chalk.yellowBright('')}: ${chalk.italic.dim('# импортирует данные из TSV файла')} ${chalk.cyanBright('--generate')} ${chalk.yellowBright(' ')}: ${chalk.italic.dim('# генерирует произвольное количество тестовых данных')} `); } diff --git a/src/cli/commands/import.command.ts b/src/cli/commands/import.command.ts index 793a134..3cb8fe8 100644 --- a/src/cli/commands/import.command.ts +++ b/src/cli/commands/import.command.ts @@ -1,4 +1,4 @@ -import { ICommand } from './command.interface.js'; +import { ICommand } from './types/command.interface.js'; import { TSVFileReader } from '../../shared/libs/file-reader/index.js'; export class ImportCommand implements ICommand { @@ -8,6 +8,11 @@ export class ImportCommand implements ICommand { public async execute(...parameters: string[]): Promise { const [fileName] = parameters; + + if (!fileName || fileName.trim() === '') { + throw new Error('File name is missing.'); + } + const fileReader = new TSVFileReader(fileName.trim()); try { diff --git a/src/cli/commands/command.interface.ts b/src/cli/commands/types/command.interface.ts similarity index 100% rename from src/cli/commands/command.interface.ts rename to src/cli/commands/types/command.interface.ts diff --git a/src/cli/commands/types/package-json-config.interface.ts b/src/cli/commands/types/package-json-config.interface.ts new file mode 100644 index 0000000..3a2e936 --- /dev/null +++ b/src/cli/commands/types/package-json-config.interface.ts @@ -0,0 +1,3 @@ +export interface IPackageJSONConfig { + version: string; +} diff --git a/src/cli/commands/utils/parse-command.util.ts b/src/cli/commands/utils/parse-command.util.ts new file mode 100644 index 0000000..fe1f83b --- /dev/null +++ b/src/cli/commands/utils/parse-command.util.ts @@ -0,0 +1,15 @@ +export const parseCommand = (cliArgs: string[]): Record => { + const parsedCommand: Record = {}; + let currentCommand = ''; + + for (const argument of cliArgs) { + if (argument.startsWith('--')) { + parsedCommand[argument] = []; + currentCommand = argument; + } else if (currentCommand && argument) { + parsedCommand[currentCommand].push(argument); + } + } + + return parsedCommand; +}; diff --git a/src/cli/commands/version.command.ts b/src/cli/commands/version.command.ts index 3cfbb63..5b9c769 100644 --- a/src/cli/commands/version.command.ts +++ b/src/cli/commands/version.command.ts @@ -1,30 +1,29 @@ import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; -import { ICommand } from './command.interface.js'; - -type TPackageJSONConfig = { - version: string; -} - -const isPackageJSONConfig = (value: unknown): value is TPackageJSONConfig => - typeof value === 'object' && - value !== null && - !Array.isArray(value) && - Object.hasOwn(value, 'version'); +import { ICommand } from './types/command.interface.js'; +import { IPackageJSONConfig } from './types/package-json-config.interface.js'; export class VersionCommand implements ICommand { constructor( private readonly filePath: string = 'package.json' ) {} + private isPackageJSONConfig(value: unknown): asserts value is IPackageJSONConfig { + if (!( + typeof value === 'object' && + value !== null && + !Array.isArray(value) && + Object.hasOwn(value, 'version') + )) { + throw new Error(); + }; + } + private readVersion(): string { const jsonContent = readFileSync(resolve(this.filePath), { encoding: 'utf-8' }); const importedContent: unknown = JSON.parse(jsonContent); - - if (!isPackageJSONConfig(importedContent)) { - throw new Error('Failed to parse json content.'); - } + this.isPackageJSONConfig(importedContent); return importedContent.version; } @@ -37,7 +36,7 @@ export class VersionCommand implements ICommand { try { const version = this.readVersion(); console.info(version); - } catch (error: unknown) { + } catch (error) { console.error(`Failed to read version from ${this.filePath}`); if (error instanceof Error) { diff --git a/src/cli/index.ts b/src/cli/index.ts index c8ef6be..13c4d8f 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -1,5 +1,4 @@ export { CLIApplication } from './cli-application.js'; -export { CommandParser } from './command-parser.js'; export { HelpCommand } from './commands/help.command.js'; export { VersionCommand } from './commands/version.command.js'; export { ImportCommand } from './commands/import.command.js'; diff --git a/src/main.cli.ts b/src/main.cli.ts index a7f3ca9..f6cbb8e 100644 --- a/src/main.cli.ts +++ b/src/main.cli.ts @@ -9,7 +9,7 @@ const bootstrap = async () => { new ImportCommand(), ]); - cliApplication.processCommand(process.argv); + await cliApplication.processCommand(process.argv); }; await bootstrap(); diff --git a/src/shared/libs/file-reader/index.ts b/src/shared/libs/file-reader/index.ts index 28d7df5..2ef4f35 100644 --- a/src/shared/libs/file-reader/index.ts +++ b/src/shared/libs/file-reader/index.ts @@ -1,2 +1,2 @@ -export { IFileReader } from './file-reader.interface.js'; +export { IFileReader } from './types/file-reader.interface.js'; export { TSVFileReader } from './tsv-file-reader.js'; diff --git a/src/shared/libs/file-reader/tsv-file-reader.ts b/src/shared/libs/file-reader/tsv-file-reader.ts index 77bfb9b..881f3a2 100644 --- a/src/shared/libs/file-reader/tsv-file-reader.ts +++ b/src/shared/libs/file-reader/tsv-file-reader.ts @@ -1,10 +1,10 @@ import { readFileSync } from 'node:fs'; -import { IFileReader } from './file-reader.interface.js'; -import { TOffer } from '../../types/index.js'; +import { IFileReader } from './types/file-reader.interface.js'; +import { IOffer } from '../../types/offer.interface.js'; import { ECity } from '../../types/city.enum.js'; import { TFacility } from '../../types/facility.type.js'; -import { TCoordinates } from '../../types/coordinates.type.js'; +import { ICoordinates } from '../../types/coordinates.interface.js'; import { THousingType } from '../../types/housing-type.type.js'; export class TSVFileReader implements IFileReader { @@ -20,31 +20,27 @@ export class TSVFileReader implements IFileReader { } } - private parseImages(imagesString: string): string[] { - return imagesString.split(';'); + private parseSemicolonSeparatedValues(valuesString: string): T { + return valuesString.split(';') as T; } private parseIntNumber(numberString: string): number { - return Number.parseInt(numberString, 10); + return Number.parseInt(numberString); } private parseNumberWithDot(numberString: string): number { return Number.parseFloat(numberString); } - private parseFacilities(facilitiesString: string): TFacility[] { - return facilitiesString.split(';') as TFacility[]; - } - - private parseCoordinates(coordinatesString: string): TCoordinates { + private parseCoordinates(coordinatesString: string): ICoordinates { const coordinatesList = coordinatesString.split(';'); return { latitude: this.parseNumberWithDot(coordinatesList[0]), longitude: this.parseNumberWithDot(coordinatesList[1]), - } as TCoordinates; + } as ICoordinates; } - private parseLineToOffer(line: string): TOffer { + private parseLineToOffer(line: string): IOffer { const [ title, description, @@ -53,7 +49,6 @@ export class TSVFileReader implements IFileReader { previewImage, images, isPremium, - isSelected, rating, housingType, roomsNumber, @@ -69,27 +64,26 @@ export class TSVFileReader implements IFileReader { title, description, date: new Date(createDate), - city: ECity[city as keyof typeof ECity], + city: ECity[city as ECity], previewImage, - images: this.parseImages(images), + images: this.parseSemicolonSeparatedValues(images), isPremium: !!this.parseIntNumber(isPremium), - isSelected: !!this.parseIntNumber(isSelected), rating: this.parseNumberWithDot(rating), - housingType: housingType as THousingType, + housingType: housingType as THousingType, // enum roomsNumber: this.parseIntNumber(roomsNumber), guestsNumber: this.parseIntNumber(guestsNumber), price: this.parseIntNumber(price), - facilities: this.parseFacilities(facilities), + facilities: this.parseSemicolonSeparatedValues(facilities), // enum list author, commentsNumber: this.parseIntNumber(commentsNumber), coordinates: this.parseCoordinates(coordinates), }; } - private parseRawDataToOffers(): TOffer[] { + private parseRawDataToOffers(): IOffer[] { return this.rawData .split('\n') - .filter((row) => row.trim().length > 0) + .filter((row) => row.trim().length) .map((line) => this.parseLineToOffer(line)); } @@ -97,7 +91,7 @@ export class TSVFileReader implements IFileReader { this.rawData = readFileSync(this.filename, { encoding: 'utf-8' }); } - public toArray(): TOffer[] { + public toArray(): IOffer[] { this.validateRawData(); return this.parseRawDataToOffers(); } diff --git a/src/shared/libs/file-reader/file-reader.interface.ts b/src/shared/libs/file-reader/types/file-reader.interface.ts similarity index 100% rename from src/shared/libs/file-reader/file-reader.interface.ts rename to src/shared/libs/file-reader/types/file-reader.interface.ts diff --git a/src/shared/types/comment.type.ts b/src/shared/types/comment.interface.ts similarity index 70% rename from src/shared/types/comment.type.ts rename to src/shared/types/comment.interface.ts index c133b57..b14aa61 100644 --- a/src/shared/types/comment.type.ts +++ b/src/shared/types/comment.interface.ts @@ -1,4 +1,4 @@ -export type TComment = { +export interface IComment { text: string, date: Date, rating: number, diff --git a/src/shared/types/coordinates.type.ts b/src/shared/types/coordinates.interface.ts similarity index 57% rename from src/shared/types/coordinates.type.ts rename to src/shared/types/coordinates.interface.ts index 2e76940..7ef0b12 100644 --- a/src/shared/types/coordinates.type.ts +++ b/src/shared/types/coordinates.interface.ts @@ -1,4 +1,4 @@ -export type TCoordinates = { +export interface ICoordinates { latitude: number, longitude: number, } diff --git a/src/shared/types/index.ts b/src/shared/types/index.ts deleted file mode 100644 index 2135f65..0000000 --- a/src/shared/types/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { TOffer } from './offer.type.js'; -export { TUser } from './user.type.js'; -export { TComment } from './comment.type.js'; diff --git a/src/shared/types/offer.type.ts b/src/shared/types/offer.interface.ts similarity index 78% rename from src/shared/types/offer.type.ts rename to src/shared/types/offer.interface.ts index 17cf6b0..36d3991 100644 --- a/src/shared/types/offer.type.ts +++ b/src/shared/types/offer.interface.ts @@ -1,9 +1,9 @@ import { ECity } from './city.enum.js'; import { THousingType } from './housing-type.type.js'; import { TFacility } from './facility.type.js'; -import { TCoordinates } from './coordinates.type.js'; +import { ICoordinates } from './coordinates.interface.js'; -export type TOffer = { +export interface IOffer { title: string, description: string, date: Date, @@ -11,7 +11,6 @@ export type TOffer = { previewImage: string, images: string[], isPremium: boolean, - isSelected: boolean, rating: number, housingType: THousingType, roomsNumber: number, @@ -20,5 +19,5 @@ export type TOffer = { facilities: TFacility[], author: string, commentsNumber: number, - coordinates: TCoordinates, + coordinates: ICoordinates, } diff --git a/src/shared/types/user-type.enum.ts b/src/shared/types/user-type.enum.ts index a3e0959..32468d2 100644 --- a/src/shared/types/user-type.enum.ts +++ b/src/shared/types/user-type.enum.ts @@ -1,4 +1,4 @@ export enum EUserType { - Regular = 'regular', + Regular = 'обычный', Pro = 'pro', } diff --git a/src/shared/types/user.type.ts b/src/shared/types/user.interface.ts similarity index 74% rename from src/shared/types/user.type.ts rename to src/shared/types/user.interface.ts index 44fe043..5a0f024 100644 --- a/src/shared/types/user.type.ts +++ b/src/shared/types/user.interface.ts @@ -1,9 +1,9 @@ import { EUserType } from './user-type.enum.js'; -export type TUser = { +export interface IUser { name: string, email: string, - avatar?: string, + avatar: string, password: string, userType: EUserType, } From eb235344c07a85c07f955503d45e58f877f6b2f9 Mon Sep 17 00:00:00 2001 From: Artur Samoilov Date: Sun, 15 Sep 2024 17:24:48 +0200 Subject: [PATCH 09/10] fix: types --- src/shared/libs/file-reader/index.ts | 1 - src/shared/libs/file-reader/tsv-file-reader.ts | 10 +++++----- src/shared/types/facility.enum.ts | 9 +++++++++ src/shared/types/facility.type.ts | 1 - src/shared/types/housing-type.enum.ts | 6 ++++++ src/shared/types/housing-type.type.ts | 1 - src/shared/types/offer.interface.ts | 8 ++++---- 7 files changed, 24 insertions(+), 12 deletions(-) create mode 100644 src/shared/types/facility.enum.ts delete mode 100644 src/shared/types/facility.type.ts create mode 100644 src/shared/types/housing-type.enum.ts delete mode 100644 src/shared/types/housing-type.type.ts diff --git a/src/shared/libs/file-reader/index.ts b/src/shared/libs/file-reader/index.ts index 2ef4f35..47b4308 100644 --- a/src/shared/libs/file-reader/index.ts +++ b/src/shared/libs/file-reader/index.ts @@ -1,2 +1 @@ -export { IFileReader } from './types/file-reader.interface.js'; export { TSVFileReader } from './tsv-file-reader.js'; diff --git a/src/shared/libs/file-reader/tsv-file-reader.ts b/src/shared/libs/file-reader/tsv-file-reader.ts index 881f3a2..38cda46 100644 --- a/src/shared/libs/file-reader/tsv-file-reader.ts +++ b/src/shared/libs/file-reader/tsv-file-reader.ts @@ -3,9 +3,9 @@ import { readFileSync } from 'node:fs'; import { IFileReader } from './types/file-reader.interface.js'; import { IOffer } from '../../types/offer.interface.js'; import { ECity } from '../../types/city.enum.js'; -import { TFacility } from '../../types/facility.type.js'; +import { EFacility } from '../../types/facility.enum.js'; import { ICoordinates } from '../../types/coordinates.interface.js'; -import { THousingType } from '../../types/housing-type.type.js'; +import { EHousingType } from '../../types/housing-type.enum.js'; export class TSVFileReader implements IFileReader { private rawData = ''; @@ -64,16 +64,16 @@ export class TSVFileReader implements IFileReader { title, description, date: new Date(createDate), - city: ECity[city as ECity], + city: city as ECity, previewImage, images: this.parseSemicolonSeparatedValues(images), isPremium: !!this.parseIntNumber(isPremium), rating: this.parseNumberWithDot(rating), - housingType: housingType as THousingType, // enum + housingType: housingType as EHousingType, roomsNumber: this.parseIntNumber(roomsNumber), guestsNumber: this.parseIntNumber(guestsNumber), price: this.parseIntNumber(price), - facilities: this.parseSemicolonSeparatedValues(facilities), // enum list + facilities: this.parseSemicolonSeparatedValues(facilities), author, commentsNumber: this.parseIntNumber(commentsNumber), coordinates: this.parseCoordinates(coordinates), diff --git a/src/shared/types/facility.enum.ts b/src/shared/types/facility.enum.ts new file mode 100644 index 0000000..d3808f6 --- /dev/null +++ b/src/shared/types/facility.enum.ts @@ -0,0 +1,9 @@ +export enum EFacility { + Breakfast = 'Breakfast', + AirConditioning = 'Air conditioning', + LaptopFriendly = 'Laptop friendly workspace', + BabySeat = 'Baby seat', + Washer = 'Washer', + Towels = 'Towels', + Fridge = 'Fridge' +} diff --git a/src/shared/types/facility.type.ts b/src/shared/types/facility.type.ts deleted file mode 100644 index e8540ec..0000000 --- a/src/shared/types/facility.type.ts +++ /dev/null @@ -1 +0,0 @@ -export type TFacility = 'Breakfast' | 'Air conditioning' | 'Laptop friendly workspace' | 'Baby seat' | 'Washer' | 'Towels' | 'Fridge'; diff --git a/src/shared/types/housing-type.enum.ts b/src/shared/types/housing-type.enum.ts new file mode 100644 index 0000000..df4e577 --- /dev/null +++ b/src/shared/types/housing-type.enum.ts @@ -0,0 +1,6 @@ +export enum EHousingType { + Apartment = 'apartment', + House = 'house', + Room = 'room', + Hotel = 'hotel' +} diff --git a/src/shared/types/housing-type.type.ts b/src/shared/types/housing-type.type.ts deleted file mode 100644 index 39634bd..0000000 --- a/src/shared/types/housing-type.type.ts +++ /dev/null @@ -1 +0,0 @@ -export type THousingType = 'apartment' | 'house' | 'room' | 'hotel'; diff --git a/src/shared/types/offer.interface.ts b/src/shared/types/offer.interface.ts index 36d3991..05c09e4 100644 --- a/src/shared/types/offer.interface.ts +++ b/src/shared/types/offer.interface.ts @@ -1,6 +1,6 @@ import { ECity } from './city.enum.js'; -import { THousingType } from './housing-type.type.js'; -import { TFacility } from './facility.type.js'; +import { EHousingType } from './housing-type.enum.js'; +import { EFacility } from './facility.enum.js'; import { ICoordinates } from './coordinates.interface.js'; export interface IOffer { @@ -12,11 +12,11 @@ export interface IOffer { images: string[], isPremium: boolean, rating: number, - housingType: THousingType, + housingType: EHousingType, roomsNumber: number, guestsNumber: number, price: number, - facilities: TFacility[], + facilities: EFacility[], author: string, commentsNumber: number, coordinates: ICoordinates, From 0636d4f8c88e4e73eb89d485c0ac9fd6e7ebb596 Mon Sep 17 00:00:00 2001 From: Artur Samoilov Date: Sun, 15 Sep 2024 17:28:31 +0200 Subject: [PATCH 10/10] fix: eslint errors --- src/cli/commands/version.command.ts | 2 +- src/shared/libs/file-reader/constants/decimal-radix.const.ts | 1 + src/shared/libs/file-reader/tsv-file-reader.ts | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 src/shared/libs/file-reader/constants/decimal-radix.const.ts diff --git a/src/cli/commands/version.command.ts b/src/cli/commands/version.command.ts index 5b9c769..b9cb67f 100644 --- a/src/cli/commands/version.command.ts +++ b/src/cli/commands/version.command.ts @@ -17,7 +17,7 @@ export class VersionCommand implements ICommand { Object.hasOwn(value, 'version') )) { throw new Error(); - }; + } } private readVersion(): string { diff --git a/src/shared/libs/file-reader/constants/decimal-radix.const.ts b/src/shared/libs/file-reader/constants/decimal-radix.const.ts new file mode 100644 index 0000000..1311ccd --- /dev/null +++ b/src/shared/libs/file-reader/constants/decimal-radix.const.ts @@ -0,0 +1 @@ +export const DECIMAL_RADIX = 10; diff --git a/src/shared/libs/file-reader/tsv-file-reader.ts b/src/shared/libs/file-reader/tsv-file-reader.ts index 38cda46..038ff20 100644 --- a/src/shared/libs/file-reader/tsv-file-reader.ts +++ b/src/shared/libs/file-reader/tsv-file-reader.ts @@ -6,6 +6,7 @@ import { ECity } from '../../types/city.enum.js'; import { EFacility } from '../../types/facility.enum.js'; import { ICoordinates } from '../../types/coordinates.interface.js'; import { EHousingType } from '../../types/housing-type.enum.js'; +import { DECIMAL_RADIX } from './constants/decimal-radix.const.js'; export class TSVFileReader implements IFileReader { private rawData = ''; @@ -25,7 +26,7 @@ export class TSVFileReader implements IFileReader { } private parseIntNumber(numberString: string): number { - return Number.parseInt(numberString); + return Number.parseInt(numberString, DECIMAL_RADIX); } private parseNumberWithDot(numberString: string): number {