From 16010b93c9b68ea2157c38802492082213a473e4 Mon Sep 17 00:00:00 2001 From: omerarif-dev Date: Tue, 16 May 2023 10:49:30 +0200 Subject: [PATCH 1/2] Initial Commit --- .gitignore | 454 ++++++++++++++++++++++ .vscode/launch.json | 35 ++ .vscode/tasks.json | 41 ++ Controllers/VocabularyController.cs | 83 ++++ Data/AppDbContext.cs | 15 + Data/IVaultRepo.cs | 14 + Data/VaultRepo.cs | 73 ++++ Dtos/AddFavouriteWordRequestDto.cs | 11 + Dtos/AddMoreWordDetailsRequestDto.cs | 10 + Dtos/GetAllWordsInVaultResponseDto.cs | 28 ++ Models/CustomWordDetails.cs | 28 ++ Models/FavouriteWordDetails.cs | 26 ++ Models/FavouriteWords.cs | 32 ++ Models/WordDefinations.cs | 9 + Models/WordDetails.cs | 13 + Models/WordMeanings.cs | 15 + Program.cs | 37 ++ Properties/launchSettings.json | 31 ++ README.md | 67 ++++ Services/ISeacrhDictionary.cs | 9 + Services/SearchDictionary.cs | 31 ++ Validations/AddFavouriteWordValidation.cs | 17 + VaultManager/IVaultOperation.cs | 12 + VaultManager/VaultOperation.cs | 61 +++ VocabularyVault.csproj | 16 + appsettings.Development.json | 8 + appsettings.json | 10 + 27 files changed, 1186 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 Controllers/VocabularyController.cs create mode 100644 Data/AppDbContext.cs create mode 100644 Data/IVaultRepo.cs create mode 100644 Data/VaultRepo.cs create mode 100644 Dtos/AddFavouriteWordRequestDto.cs create mode 100644 Dtos/AddMoreWordDetailsRequestDto.cs create mode 100644 Dtos/GetAllWordsInVaultResponseDto.cs create mode 100644 Models/CustomWordDetails.cs create mode 100644 Models/FavouriteWordDetails.cs create mode 100644 Models/FavouriteWords.cs create mode 100644 Models/WordDefinations.cs create mode 100644 Models/WordDetails.cs create mode 100644 Models/WordMeanings.cs create mode 100644 Program.cs create mode 100644 Properties/launchSettings.json create mode 100644 Services/ISeacrhDictionary.cs create mode 100644 Services/SearchDictionary.cs create mode 100644 Validations/AddFavouriteWordValidation.cs create mode 100644 VaultManager/IVaultOperation.cs create mode 100644 VaultManager/VaultOperation.cs create mode 100644 VocabularyVault.csproj create mode 100644 appsettings.Development.json create mode 100644 appsettings.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2afa2e2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,454 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# JetBrains Rider +.idea/ +*.sln.iml + +## +## Visual Studio Code +## +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..2687e38 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,35 @@ +{ + "version": "0.2.0", + "configurations": [ + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "name": ".NET Core Launch (web)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/bin/Debug/net6.0/VocabularyVault.dll", + "args": [], + "cwd": "${workspaceFolder}", + "stopAtEntry": false, + // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..c3bf5ee --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/VocabularyVault.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/VocabularyVault.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "--project", + "${workspaceFolder}/VocabularyVault.csproj" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/Controllers/VocabularyController.cs b/Controllers/VocabularyController.cs new file mode 100644 index 0000000..7025078 --- /dev/null +++ b/Controllers/VocabularyController.cs @@ -0,0 +1,83 @@ +using Microsoft.AspNetCore.Mvc; +using VocabularyVault.Data; +using VocabularyVault.Dtos; +using VocabularyVault.Models; +using VocabularyVault.Services; +using VocabularyVault.VaultManager; + +namespace VocabularyVault; + +[Route("api/vault")] +[ApiController] +public class VocabularyController : ControllerBase +{ + private readonly ISeacrhDictionary _searchDictionary; + private readonly IVaultOperation _vaultOperation; + private readonly IVaultRepo _repo; + + public VocabularyController(ISeacrhDictionary seacrhDictionary, IVaultOperation vaultOperation, IVaultRepo repo) + { + _searchDictionary = seacrhDictionary; + _vaultOperation = vaultOperation; + _repo = repo; + } + [HttpGet("search/{word}")] + public async Task> SearchWordInDictionary(string word) + { + if (string.IsNullOrWhiteSpace(word)) + { + return BadRequest("Invalid word."); + } + var data = await _searchDictionary.SearchWordInDictionary(word); + if (data == null) + { + return NotFound("Word not found in the dictionary."); + } + return Ok(data); + } + + [HttpPost("add")] + public async Task> AddVocabulryToVault(AddFavouriteWordRequestDto addFavouriteWordRequestDto) + { + var data = await _vaultOperation.AddFavouriteInVault(addFavouriteWordRequestDto); + if (data == null) + { + return NotFound("Failed to add the word to the vault."); + } + return Ok(data); + } + + [HttpPost("add-detail")] + public async Task> AddMoreWordDetails(AddMoreWordDetailsRequestDto addMoreWordDetailsRequestDto) + { + var data = await _vaultOperation.AddMoreWordDetailsInVault(addMoreWordDetailsRequestDto); + if (data == null) + { + return NotFound("Failed to add details of word to the vault."); + } + return Ok(data); + } + + [HttpGet("get")] + public async Task>> GetVaultData() + { + var favourites = await _vaultOperation.GetAllFromVault(); + return Ok(favourites); + + } + + [HttpDelete("remove/{word}")] + public async Task> RemoveDataFromVault(string word) + { + var removed = await _vaultOperation.RemoveWordFromVault(word); + if (removed) + { + return Ok($"{word} removed !!!"); + } + else + { + return NotFound("Word not found in the vault."); + } + } + +} \ No newline at end of file diff --git a/Data/AppDbContext.cs b/Data/AppDbContext.cs new file mode 100644 index 0000000..81c5bec --- /dev/null +++ b/Data/AppDbContext.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; +using VocabularyVault.Models; + +namespace VocabularyVault.Data; + +public class AppDbContext : DbContext +{ + public AppDbContext(DbContextOptions options) : base(options) + { + + } + + public DbSet FavouriteWords => Set(); + public DbSet FavouriteWordsDetails => Set(); +} \ No newline at end of file diff --git a/Data/IVaultRepo.cs b/Data/IVaultRepo.cs new file mode 100644 index 0000000..2d6fa3a --- /dev/null +++ b/Data/IVaultRepo.cs @@ -0,0 +1,14 @@ +using VocabularyVault.Dtos; +using VocabularyVault.Models; + +namespace VocabularyVault.Data; + +public interface IVaultRepo +{ + Task SaveChangesAysnc(); + Task> GetAllFavouritesInVaultAsync(); + Task RemoveFavouritesInVaultAsync(string word); + Task AddFavouritesInVaultAsync(AddFavouriteWordRequestDto addFavouriteWord); + Task AddMoreWordDetailsInVaultAsync(AddMoreWordDetailsRequestDto addMoreWordDetails); + Task SearchWordAsync(string word); +} \ No newline at end of file diff --git a/Data/VaultRepo.cs b/Data/VaultRepo.cs new file mode 100644 index 0000000..c599bd5 --- /dev/null +++ b/Data/VaultRepo.cs @@ -0,0 +1,73 @@ +using Microsoft.EntityFrameworkCore; +using VocabularyVault.Dtos; +using VocabularyVault.Models; + +namespace VocabularyVault.Data; + +public class VaultRepo : IVaultRepo +{ + private readonly AppDbContext _context; + + public VaultRepo(AppDbContext context) + { + _context = context; + } + public async Task AddFavouritesInVaultAsync(AddFavouriteWordRequestDto addFavouriteWord) + { + var favouriteWordToAdd = FavouriteWords.Parse(addFavouriteWord); + await _context.FavouriteWords.AddAsync(favouriteWordToAdd); + await _context.SaveChangesAsync(); + } + + public async Task AddMoreWordDetailsInVaultAsync(AddMoreWordDetailsRequestDto addMoreWordDetails) + { + var wordToUpdate = await _context.FavouriteWords.SingleOrDefaultAsync(x => x.Word == addMoreWordDetails.Word); + + if (wordToUpdate == null) + { + throw new Exception("The word does not exist in the vault."); + } + + var newDetails = new FavouriteWordDetails + { + Defination = addMoreWordDetails.Defination, + UsageType = addMoreWordDetails.UsageType, + Synonyms = addMoreWordDetails.Synonyms, + Antonyms = addMoreWordDetails.Antonyms + }; + + wordToUpdate.Details.Add(newDetails); + await _context.SaveChangesAsync(); + } + + public async Task> GetAllFavouritesInVaultAsync() + { + return await _context.FavouriteWords.Include(x => x.Details).ToListAsync(); + } + + public async Task RemoveFavouritesInVaultAsync(string word) + { + var wordToRemove = await _context.FavouriteWords.FirstOrDefaultAsync(x => x.Word == word); + if (wordToRemove != null) + { + _context.FavouriteWords.Remove(wordToRemove); + await _context.SaveChangesAsync(); + return true; + } + return false; + } + + public async Task SaveChangesAysnc() + { + await _context.SaveChangesAsync(); + } + + public async Task SearchWordAsync(string word) + { + if (string.IsNullOrEmpty(word)) + { + return null; + } + return await _context.FavouriteWords.FirstOrDefaultAsync(x => x.Word == word); + } +} \ No newline at end of file diff --git a/Dtos/AddFavouriteWordRequestDto.cs b/Dtos/AddFavouriteWordRequestDto.cs new file mode 100644 index 0000000..4ab7507 --- /dev/null +++ b/Dtos/AddFavouriteWordRequestDto.cs @@ -0,0 +1,11 @@ +namespace VocabularyVault.Dtos; + +public class AddFavouriteWordRequestDto +{ + public string? Word { get; set; } + public string? Phonetic { get; set; } + public string? UsageType { get; set; } + public string? Defination { get; set; } + public string? Synonyms { get; set; } + public string? Antonyms { get; set; } +} \ No newline at end of file diff --git a/Dtos/AddMoreWordDetailsRequestDto.cs b/Dtos/AddMoreWordDetailsRequestDto.cs new file mode 100644 index 0000000..bf59a0f --- /dev/null +++ b/Dtos/AddMoreWordDetailsRequestDto.cs @@ -0,0 +1,10 @@ +namespace VocabularyVault.Dtos; + +public class AddMoreWordDetailsRequestDto +{ + public string? Word { get; set; } + public string? UsageType { get; set; } + public string? Defination { get; set; } + public string? Synonyms { get; set; } + public string? Antonyms { get; set; } +} \ No newline at end of file diff --git a/Dtos/GetAllWordsInVaultResponseDto.cs b/Dtos/GetAllWordsInVaultResponseDto.cs new file mode 100644 index 0000000..dabbcea --- /dev/null +++ b/Dtos/GetAllWordsInVaultResponseDto.cs @@ -0,0 +1,28 @@ +using VocabularyVault.Models; + +namespace VocabularyVault.Dtos; + +public class GetAllWordsInVaultResponseDto +{ + public string? word { get; set; } + public string? Phonetic { get; set; } + public List? RealExamples { get; set; } + + public static List Parse(IEnumerable favourites) + { + var dataToReturn = new List(); + + foreach (var favourite in favourites) + { + var temp = new GetAllWordsInVaultResponseDto + { + word = favourite.Word, + Phonetic = favourite.Phonetic, + RealExamples = new List() + }; + temp.RealExamples = CustomWordDetails.Parse(favourite.Details); + dataToReturn.Add(temp); + } + return dataToReturn; + } +} \ No newline at end of file diff --git a/Models/CustomWordDetails.cs b/Models/CustomWordDetails.cs new file mode 100644 index 0000000..07d6ea7 --- /dev/null +++ b/Models/CustomWordDetails.cs @@ -0,0 +1,28 @@ +namespace VocabularyVault.Models +{ + public class CustomWordDetails + { + public string? UsageType { get; set; } + public string? Defination { get; set; } + public string? Synonyms { get; set; } + public string? Antonyms { get; set; } + + public static List Parse(ICollection favouriteWordDetails) + { + var dataToReturn = new List(); + + foreach (var detail in favouriteWordDetails) + { + var temp = new CustomWordDetails + { + UsageType = detail.UsageType, + Defination = detail.Defination, + Synonyms = detail.Synonyms, + Antonyms = detail.Antonyms + }; + dataToReturn.Add(temp); + } + return dataToReturn; + } + } +} \ No newline at end of file diff --git a/Models/FavouriteWordDetails.cs b/Models/FavouriteWordDetails.cs new file mode 100644 index 0000000..b6272de --- /dev/null +++ b/Models/FavouriteWordDetails.cs @@ -0,0 +1,26 @@ +using System.ComponentModel.DataAnnotations; +using VocabularyVault.Dtos; + +namespace VocabularyVault.Models; + +public class FavouriteWordDetails +{ + [Key] + public int Id { get; set; } + public string? UsageType { get; set; } + public string? Defination { get; set; } + public string? Synonyms { get; set; } + public string? Antonyms { get; set; } + public virtual FavouriteWords? FavouriteWords { get; set; } + + public static FavouriteWordDetails Parse(AddFavouriteWordRequestDto addFavouriteWordRequest) + { + return new FavouriteWordDetails + { + UsageType = addFavouriteWordRequest.UsageType, + Defination = addFavouriteWordRequest.Defination, + Antonyms = addFavouriteWordRequest.Antonyms, + Synonyms = addFavouriteWordRequest.Synonyms + }; + } +} \ No newline at end of file diff --git a/Models/FavouriteWords.cs b/Models/FavouriteWords.cs new file mode 100644 index 0000000..a08712a --- /dev/null +++ b/Models/FavouriteWords.cs @@ -0,0 +1,32 @@ +using System.ComponentModel.DataAnnotations; +using VocabularyVault.Dtos; + +namespace VocabularyVault.Models; + +public class FavouriteWords +{ + public FavouriteWords() + { + Details = new HashSet(); + } + [Key] + public int Id { get; set; } + [Required] + public string? Word { get; set; } + public string? Phonetic { get; set; } + public virtual ICollection Details { get; set; } + + public static FavouriteWords Parse(AddFavouriteWordRequestDto addFavouriteWord) + { + var favouriteWordParsed = new FavouriteWords + { + Word = addFavouriteWord.Word, + Phonetic = addFavouriteWord.Phonetic, + Details = new List() + }; + + favouriteWordParsed.Details.Add(FavouriteWordDetails.Parse(addFavouriteWord)); + return favouriteWordParsed; + + } +} \ No newline at end of file diff --git a/Models/WordDefinations.cs b/Models/WordDefinations.cs new file mode 100644 index 0000000..5908729 --- /dev/null +++ b/Models/WordDefinations.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace VocabularyVault.Models; + +public class WordDefinations +{ + [JsonPropertyName("definition")] + public string? Definations { get; set; } +} \ No newline at end of file diff --git a/Models/WordDetails.cs b/Models/WordDetails.cs new file mode 100644 index 0000000..0aeb686 --- /dev/null +++ b/Models/WordDetails.cs @@ -0,0 +1,13 @@ +using System.Text.Json.Serialization; + +namespace VocabularyVault.Models; + +public class WordDetails +{ + [JsonPropertyName("word")] + public string? Word { get; set; } + [JsonPropertyName("phonetic")] + public string? Phonetic { get; set; } + [JsonPropertyName("meanings")] + public List? Meanings { get; set; } +} \ No newline at end of file diff --git a/Models/WordMeanings.cs b/Models/WordMeanings.cs new file mode 100644 index 0000000..d283c73 --- /dev/null +++ b/Models/WordMeanings.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace VocabularyVault.Models; + +public class WordMeanings +{ + [JsonPropertyName("partOfSpeech")] + public string? PartsOfSpeech { get; set; } + [JsonPropertyName("definitions")] + public List? Definitions { get; set; } + [JsonPropertyName("synonyms")] + public List? Synonyms { get; set; } + [JsonPropertyName("antonyms")] + public List? Antonyms { get; set; } +} diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..659fec5 --- /dev/null +++ b/Program.cs @@ -0,0 +1,37 @@ +using FluentValidation; +using Microsoft.EntityFrameworkCore; +using VocabularyVault.Data; +using VocabularyVault.Services; +using VocabularyVault.VaultManager; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.Services.AddControllers(); +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.Configuration.GetValue("BaseUrl")) }); +builder.Services.AddDbContext(opt => opt.UseInMemoryDatabase("VocabularyVaultDb")); +builder.Services.AddValidatorsFromAssemblyContaining(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json new file mode 100644 index 0000000..c5a042f --- /dev/null +++ b/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:44065", + "sslPort": 44302 + } + }, + "profiles": { + "VocabularyVault": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7285;http://localhost:5255", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/README.md b/README.md index 9c678d0..1e8239d 100644 --- a/README.md +++ b/README.md @@ -73,3 +73,70 @@ Once you have completed the task, please follow these steps: ## Conclusion We hope this task gives you an opportunity to demonstrate your skills as a full-stack developer and showcase your creativity, problem-solving abilities, and best practices in your code. We look forward to seeing your completed task and discussing your approach and thought process during the interview. Good luck! + + +# Dotnet version 6.0.402 +# EntityFrameworkCore.InMemoryDatabase used for simplicity +# Standard dotnet build to get essential pacakge before running code + +## ---------========---Product Thought Process------------------------- +# It is vault where use can save word and it's real life usage examples to learn english +# It main source of data is https://api.dictionaryapi.dev/api/v2/entries/en/hello +# User may search for words +# User would like to save word with some of it defination in vault to Reharse +# User would like to add more definations against word in vault to improve English +# User can look at all of the vault data +# User can delete particular word. + + +-------------------- API Endpoints ----------------------- +# Careful when testing as IT IS CASE SENSITIVE + +### Search Words in Dictionary + +Endpoint: GET api/vault/search/{-word-} + +Request Payload: none + + +### Add Favourite Word to Vault + +Endpoint: POST /api/vault/add + +Request Payload: + +{ + "word": "Hello", + "phonetic": "", + "usageType": "nouns", + "defination": "\"Hello!\" or an equivalent greeting.", + "synonyms": "greeting", + "antonyms": "" +} + + +### Add More Detail against existing Favourite Word in Vault + +Endpoint: POST api/vault/add-detail + +Request Payload: + +{ + "word": "Hello", + "usageType": "interjection", + "defination": "Used sarcastically to imply that the person addressed or referred to has done something the speaker or writer considers to be foolish.", + "synonyms": "", + "antonyms": "bye" +} + +### Get All Words in Vault + +Endpoint: GET api/vault/get +Request Payload: none + + +### Remove a Word in Vault + +Endpoint: DELETE api/vault/remove/{-word-} +Request Payload: none + diff --git a/Services/ISeacrhDictionary.cs b/Services/ISeacrhDictionary.cs new file mode 100644 index 0000000..e8292f1 --- /dev/null +++ b/Services/ISeacrhDictionary.cs @@ -0,0 +1,9 @@ +using VocabularyVault.Dtos; +using VocabularyVault.Models; + +namespace VocabularyVault.Services; + +public interface ISeacrhDictionary +{ + Task SearchWordInDictionary(string word); +} \ No newline at end of file diff --git a/Services/SearchDictionary.cs b/Services/SearchDictionary.cs new file mode 100644 index 0000000..1be7e4c --- /dev/null +++ b/Services/SearchDictionary.cs @@ -0,0 +1,31 @@ +using System.Text.Json; +using FluentValidation; +using VocabularyVault.Dtos; +using VocabularyVault.Models; + +namespace VocabularyVault.Services; + +public class SearchDictionary : ISeacrhDictionary +{ + private readonly HttpClient _httpClient; + + public SearchDictionary(HttpClient httpClient) + { + _httpClient = httpClient; + + } + public async Task SearchWordInDictionary(string word) + { + var response = await _httpClient.GetAsync(word); + if (response.IsSuccessStatusCode) + { + var wordDetails = await JsonSerializer.DeserializeAsync>(await response.Content.ReadAsStreamAsync()); + return wordDetails!.First()!; + } + else + { + System.Console.WriteLine("-------- Search Words in Dictionary API Down -----------"); + return null!; + } + } +} \ No newline at end of file diff --git a/Validations/AddFavouriteWordValidation.cs b/Validations/AddFavouriteWordValidation.cs new file mode 100644 index 0000000..3517ad5 --- /dev/null +++ b/Validations/AddFavouriteWordValidation.cs @@ -0,0 +1,17 @@ +using FluentValidation; +using VocabularyVault.Dtos; + +namespace VocabularyVault.Validations; + +public class AddFavouriteWordValidation : AbstractValidator +{ + public AddFavouriteWordValidation() + { + RuleFor(property => property.Word) + .NotEmpty() + .Matches("^[a-zA-Z]+$") + .WithMessage("word cannot be empty and must contains only alphabets"); + RuleFor(property => property.Defination).NotEmpty().WithMessage("Defination cannot be empty"); + RuleFor(property => property.UsageType).NotEmpty().WithMessage("Usage type cannot be empty"); + } +} \ No newline at end of file diff --git a/VaultManager/IVaultOperation.cs b/VaultManager/IVaultOperation.cs new file mode 100644 index 0000000..4f35cb6 --- /dev/null +++ b/VaultManager/IVaultOperation.cs @@ -0,0 +1,12 @@ +using VocabularyVault.Dtos; + +namespace VocabularyVault.VaultManager +{ + public interface IVaultOperation + { + Task AddFavouriteInVault(AddFavouriteWordRequestDto addFavouriteWord); + Task AddMoreWordDetailsInVault(AddMoreWordDetailsRequestDto addMoreWordDetails); + Task> GetAllFromVault(); + Task RemoveWordFromVault(string word); + } +} \ No newline at end of file diff --git a/VaultManager/VaultOperation.cs b/VaultManager/VaultOperation.cs new file mode 100644 index 0000000..9efdddb --- /dev/null +++ b/VaultManager/VaultOperation.cs @@ -0,0 +1,61 @@ +using FluentValidation; +using VocabularyVault.Data; +using VocabularyVault.Dtos; +using VocabularyVault.Validations; + +namespace VocabularyVault.VaultManager; + +public class VaultOperation : IVaultOperation +{ + private readonly IVaultRepo _repo; + private IValidator _addFavouriteWordValidation; + + public VaultOperation(IVaultRepo repo, IValidator addFavouriteWordValidation) + { + _repo = repo; + _addFavouriteWordValidation = addFavouriteWordValidation; + } + + public async Task AddFavouriteInVault(AddFavouriteWordRequestDto addFavouriteWord) + { + var requestCheck = _addFavouriteWordValidation.Validate(addFavouriteWord); + if (!requestCheck.IsValid) + { + throw new ValidationException(requestCheck.Errors.FirstOrDefault()?.ErrorMessage.ToString()); + } + + var favouriteWord = await _repo.SearchWordAsync(addFavouriteWord.Word!); + + if (favouriteWord == null) + { + await _repo.AddFavouritesInVaultAsync(addFavouriteWord); + return "Record Added Successfully"; + } + else + { + return "Word Already Exist in Vault. Maybe Add more details ?"; + } + } + + public async Task AddMoreWordDetailsInVault(AddMoreWordDetailsRequestDto addMoreWordDetails) + { + await _repo.AddMoreWordDetailsInVaultAsync(addMoreWordDetails); + return "Record Update Successfully"; + } + + public async Task> GetAllFromVault() + { + var data = await _repo.GetAllFavouritesInVaultAsync(); + return GetAllWordsInVaultResponseDto.Parse(data); + } + + public async Task RemoveWordFromVault(string word) + { + if (string.IsNullOrEmpty(word)) + { + return false; + } + + return await _repo.RemoveFavouritesInVaultAsync(word); + } +} \ No newline at end of file diff --git a/VocabularyVault.csproj b/VocabularyVault.csproj new file mode 100644 index 0000000..29dd63e --- /dev/null +++ b/VocabularyVault.csproj @@ -0,0 +1,16 @@ + + + + net6.0 + enable + enable + + + + + + + + + + diff --git a/appsettings.Development.json b/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/appsettings.json b/appsettings.json new file mode 100644 index 0000000..8409ed9 --- /dev/null +++ b/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "BaseUrl": "https://api.dictionaryapi.dev/api/v2/entries/en/" +} \ No newline at end of file From 84891c91bc87c01f3283fa4e0a28f8507ee43ef7 Mon Sep 17 00:00:00 2001 From: omerarif-dev Date: Tue, 16 May 2023 10:55:44 +0200 Subject: [PATCH 2/2] Update README.md --- README.md | 85 ++++--------------------------------------------------- 1 file changed, 5 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index 1e8239d..08b942c 100644 --- a/README.md +++ b/README.md @@ -1,85 +1,6 @@ -# FullStack .NET Test -## Purpose -The purpose of this test is to evaluate your ability to design and implement a simple full-stack program that interacts with a public API. During the interview, we will review your code and ask you questions about your implementation, so please be prepared to discuss your design choices, any challenges you faced, and how you overcame them. We will be looking for clean, maintainable code that follows best practices, as well as your ability to explain your choices and think critically about the problem. -For more information on what we will be looking for at each level, please see the following links: -- [Junior Developers](#junior-developers) -- [Mid-Level Developers](#mid-level-developers) -- [Senior Developers](#senior-developers) - -## Task Description -Create a simple full-stack program that allows users to search for and save data from a public API of your choice. The program should have a RESTful API and a web client. - -## API Requirements -The API should be built using .NET and use a public API from the following list: https://github.com/public-apis/public-apis. It should have the following endpoints: - -- `GET /api/{resource}?q={query}`: returns an array of results from the public API that match the search query. -- `POST /api/favorites`: adds a result to the user's favorites list. -- `GET /api/favorites`: returns the user's favorites list. -- `DELETE /api/favorites/{id}`: removes a result from the user's favorites list. -The `POST /api/favorites` endpoint should store the user's favorite results in a database of their choice. You may choose to use a SQL or NoSQL database, or any other database technology you prefer. - -## Web Client Requirements -The web client should be built using .NET and use the API to interact with the server. It should have the following features: - -- A search bar where users can enter a search query and see a list of matching results. -- A way to save results to a "favorites" list. -- A way to view and remove results from the "favorites" list. - - -## Notes for Candidates -The focus of this test is not just on writing functional code, but also on your ability to explain and defend your decisions, think critically about the problem, and adhere to best practices in your code. During the interview, we will be looking for clean, maintainable code that follows best practices and is scalable. We encourage you to discuss your thought process and approach to the problem, as well as any challenges you faced and how you overcame them. - -While the web client should be functional and easy to use, it does not need to have a polished or elaborate design. During the interview, you will be evaluated based on your ability to structure the client-side code to make it modular, maintainable, and testable. You should also consider how to handle state management, routing, and API interactions in a way that maximizes performance and user experience. We encourage you to discuss your approach to testing and debugging your code, as well as any best practices or design patterns you would use to ensure high-quality and scalable code. - -## Assessment - -### Junior Developers - -During the interview, we may discuss the following topics related to your implementation of the project: - -- The reasoning behind your choice of public API and database technology -- How you approached designing and implementing the API endpoints and the web client features. -- How you handled errors and edge cases in your code. -- How you tested your code and ensured its correctness. -- How you would go about adding additional features to the project and scaling it to handle more users. - -### Mid-Level Developers -During the interview, we may discuss the following topics related to your implementation of the project: - -The reasoning behind your choice of public API and database technology, and how you would handle different use cases and performance requirements. -- How you designed and implemented the API endpoints and the web client features in a modular and maintainable way, and how you handled errors and edge cases. -- How you ensured or would ensure the quality and correctness of your code through testing, linting, and code review. -- How you would go about optimizing the performance and scalability of the project, and how you would handle security concerns such as authentication and data validation. - -### Senior Developers -During the interview, we may discuss the following topics related to your implementation of the project: - -- The reasoning behind your choice of public API and database technology, and how you would handle different use cases and performance requirements at scale. -- How you designed and implemented the API endpoints and the web client features in a way that maximizes performance, maintainability, and testability, and how you handled errors and edge cases. -- How you ensured or would ensure the quality and correctness of your code through testing, code review, and other best practices. -- How you would go about designing and implementing highly scalable and performant database systems, and how you would handle security concerns such as authentication, authorization, and data privacy. -- How you would handle more complex scenarios such as real-time updates, caching, and multi-region deployments. - -## Delivery - -Once you have completed the task, please follow these steps: - -1. Fork the original repository. -2. Email the link to your forked repository to tyler@mappingindustries.com. -3. Ensure that your code is properly documented, and include a README.md file with instructions on how to run your code. -4. Create a pull request to the original repository. - -## Conclusion -We hope this task gives you an opportunity to demonstrate your skills as a full-stack developer and showcase your creativity, problem-solving abilities, and best practices in your code. We look forward to seeing your completed task and discussing your approach and thought process during the interview. Good luck! - - -# Dotnet version 6.0.402 -# EntityFrameworkCore.InMemoryDatabase used for simplicity -# Standard dotnet build to get essential pacakge before running code - -## ---------========---Product Thought Process------------------------- +# Product Thought Process- # It is vault where use can save word and it's real life usage examples to learn english # It main source of data is https://api.dictionaryapi.dev/api/v2/entries/en/hello # User may search for words @@ -88,6 +9,10 @@ We hope this task gives you an opportunity to demonstrate your skills as a full- # User can look at all of the vault data # User can delete particular word. +## Dotnet version 6.0.402 +## EntityFrameworkCore.InMemoryDatabase used for simplicity +## Standard dotnet build to get essential pacakge before running code + -------------------- API Endpoints ----------------------- # Careful when testing as IT IS CASE SENSITIVE