Florin Bobiș commited on
Commit
d9836ef
0 Parent(s):

Initial commit

Browse files
.gitattributes ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ data/source1.json filter=lfs diff=lfs merge=lfs -text
2
+ data/source2.json filter=lfs diff=lfs merge=lfs -text
3
+ data/source3.csv filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,484 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## Ignore Visual Studio temporary files, build results, and
2
+ ## files generated by popular Visual Studio add-ons.
3
+ ##
4
+ ## Get latest from `dotnet new gitignore`
5
+
6
+ # dotenv files
7
+ .env
8
+
9
+ # User-specific files
10
+ *.rsuser
11
+ *.suo
12
+ *.user
13
+ *.userosscache
14
+ *.sln.docstates
15
+
16
+ # User-specific files (MonoDevelop/Xamarin Studio)
17
+ *.userprefs
18
+
19
+ # Mono auto generated files
20
+ mono_crash.*
21
+
22
+ # Build results
23
+ [Dd]ebug/
24
+ [Dd]ebugPublic/
25
+ [Rr]elease/
26
+ [Rr]eleases/
27
+ x64/
28
+ x86/
29
+ [Ww][Ii][Nn]32/
30
+ [Aa][Rr][Mm]/
31
+ [Aa][Rr][Mm]64/
32
+ bld/
33
+ [Bb]in/
34
+ [Oo]bj/
35
+ [Ll]og/
36
+ [Ll]ogs/
37
+
38
+ # Visual Studio 2015/2017 cache/options directory
39
+ .vs/
40
+ # Uncomment if you have tasks that create the project's static files in wwwroot
41
+ #wwwroot/
42
+
43
+ # Visual Studio 2017 auto generated files
44
+ Generated\ Files/
45
+
46
+ # MSTest test Results
47
+ [Tt]est[Rr]esult*/
48
+ [Bb]uild[Ll]og.*
49
+
50
+ # NUnit
51
+ *.VisualState.xml
52
+ TestResult.xml
53
+ nunit-*.xml
54
+
55
+ # Build Results of an ATL Project
56
+ [Dd]ebugPS/
57
+ [Rr]eleasePS/
58
+ dlldata.c
59
+
60
+ # Benchmark Results
61
+ BenchmarkDotNet.Artifacts/
62
+
63
+ # .NET
64
+ project.lock.json
65
+ project.fragment.lock.json
66
+ artifacts/
67
+
68
+ # Tye
69
+ .tye/
70
+
71
+ # ASP.NET Scaffolding
72
+ ScaffoldingReadMe.txt
73
+
74
+ # StyleCop
75
+ StyleCopReport.xml
76
+
77
+ # Files built by Visual Studio
78
+ *_i.c
79
+ *_p.c
80
+ *_h.h
81
+ *.ilk
82
+ *.meta
83
+ *.obj
84
+ *.iobj
85
+ *.pch
86
+ *.pdb
87
+ *.ipdb
88
+ *.pgc
89
+ *.pgd
90
+ *.rsp
91
+ *.sbr
92
+ *.tlb
93
+ *.tli
94
+ *.tlh
95
+ *.tmp
96
+ *.tmp_proj
97
+ *_wpftmp.csproj
98
+ *.log
99
+ *.tlog
100
+ *.vspscc
101
+ *.vssscc
102
+ .builds
103
+ *.pidb
104
+ *.svclog
105
+ *.scc
106
+
107
+ # Chutzpah Test files
108
+ _Chutzpah*
109
+
110
+ # Visual C++ cache files
111
+ ipch/
112
+ *.aps
113
+ *.ncb
114
+ *.opendb
115
+ *.opensdf
116
+ *.sdf
117
+ *.cachefile
118
+ *.VC.db
119
+ *.VC.VC.opendb
120
+
121
+ # Visual Studio profiler
122
+ *.psess
123
+ *.vsp
124
+ *.vspx
125
+ *.sap
126
+
127
+ # Visual Studio Trace Files
128
+ *.e2e
129
+
130
+ # TFS 2012 Local Workspace
131
+ $tf/
132
+
133
+ # Guidance Automation Toolkit
134
+ *.gpState
135
+
136
+ # ReSharper is a .NET coding add-in
137
+ _ReSharper*/
138
+ *.[Rr]e[Ss]harper
139
+ *.DotSettings.user
140
+
141
+ # TeamCity is a build add-in
142
+ _TeamCity*
143
+
144
+ # DotCover is a Code Coverage Tool
145
+ *.dotCover
146
+
147
+ # AxoCover is a Code Coverage Tool
148
+ .axoCover/*
149
+ !.axoCover/settings.json
150
+
151
+ # Coverlet is a free, cross platform Code Coverage Tool
152
+ coverage*.json
153
+ coverage*.xml
154
+ coverage*.info
155
+
156
+ # Visual Studio code coverage results
157
+ *.coverage
158
+ *.coveragexml
159
+
160
+ # NCrunch
161
+ _NCrunch_*
162
+ .*crunch*.local.xml
163
+ nCrunchTemp_*
164
+
165
+ # MightyMoose
166
+ *.mm.*
167
+ AutoTest.Net/
168
+
169
+ # Web workbench (sass)
170
+ .sass-cache/
171
+
172
+ # Installshield output folder
173
+ [Ee]xpress/
174
+
175
+ # DocProject is a documentation generator add-in
176
+ DocProject/buildhelp/
177
+ DocProject/Help/*.HxT
178
+ DocProject/Help/*.HxC
179
+ DocProject/Help/*.hhc
180
+ DocProject/Help/*.hhk
181
+ DocProject/Help/*.hhp
182
+ DocProject/Help/Html2
183
+ DocProject/Help/html
184
+
185
+ # Click-Once directory
186
+ publish/
187
+
188
+ # Publish Web Output
189
+ *.[Pp]ublish.xml
190
+ *.azurePubxml
191
+ # Note: Comment the next line if you want to checkin your web deploy settings,
192
+ # but database connection strings (with potential passwords) will be unencrypted
193
+ *.pubxml
194
+ *.publishproj
195
+
196
+ # Microsoft Azure Web App publish settings. Comment the next line if you want to
197
+ # checkin your Azure Web App publish settings, but sensitive information contained
198
+ # in these scripts will be unencrypted
199
+ PublishScripts/
200
+
201
+ # NuGet Packages
202
+ *.nupkg
203
+ # NuGet Symbol Packages
204
+ *.snupkg
205
+ # The packages folder can be ignored because of Package Restore
206
+ **/[Pp]ackages/*
207
+ # except build/, which is used as an MSBuild target.
208
+ !**/[Pp]ackages/build/
209
+ # Uncomment if necessary however generally it will be regenerated when needed
210
+ #!**/[Pp]ackages/repositories.config
211
+ # NuGet v3's project.json files produces more ignorable files
212
+ *.nuget.props
213
+ *.nuget.targets
214
+
215
+ # Microsoft Azure Build Output
216
+ csx/
217
+ *.build.csdef
218
+
219
+ # Microsoft Azure Emulator
220
+ ecf/
221
+ rcf/
222
+
223
+ # Windows Store app package directories and files
224
+ AppPackages/
225
+ BundleArtifacts/
226
+ Package.StoreAssociation.xml
227
+ _pkginfo.txt
228
+ *.appx
229
+ *.appxbundle
230
+ *.appxupload
231
+
232
+ # Visual Studio cache files
233
+ # files ending in .cache can be ignored
234
+ *.[Cc]ache
235
+ # but keep track of directories ending in .cache
236
+ !?*.[Cc]ache/
237
+
238
+ # Others
239
+ ClientBin/
240
+ ~$*
241
+ *~
242
+ *.dbmdl
243
+ *.dbproj.schemaview
244
+ *.jfm
245
+ *.pfx
246
+ *.publishsettings
247
+ orleans.codegen.cs
248
+
249
+ # Including strong name files can present a security risk
250
+ # (https://github.com/github/gitignore/pull/2483#issue-259490424)
251
+ #*.snk
252
+
253
+ # Since there are multiple workflows, uncomment next line to ignore bower_components
254
+ # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
255
+ #bower_components/
256
+
257
+ # RIA/Silverlight projects
258
+ Generated_Code/
259
+
260
+ # Backup & report files from converting an old project file
261
+ # to a newer Visual Studio version. Backup files are not needed,
262
+ # because we have git ;-)
263
+ _UpgradeReport_Files/
264
+ Backup*/
265
+ UpgradeLog*.XML
266
+ UpgradeLog*.htm
267
+ ServiceFabricBackup/
268
+ *.rptproj.bak
269
+
270
+ # SQL Server files
271
+ *.mdf
272
+ *.ldf
273
+ *.ndf
274
+
275
+ # Business Intelligence projects
276
+ *.rdl.data
277
+ *.bim.layout
278
+ *.bim_*.settings
279
+ *.rptproj.rsuser
280
+ *- [Bb]ackup.rdl
281
+ *- [Bb]ackup ([0-9]).rdl
282
+ *- [Bb]ackup ([0-9][0-9]).rdl
283
+
284
+ # Microsoft Fakes
285
+ FakesAssemblies/
286
+
287
+ # GhostDoc plugin setting file
288
+ *.GhostDoc.xml
289
+
290
+ # Node.js Tools for Visual Studio
291
+ .ntvs_analysis.dat
292
+ node_modules/
293
+
294
+ # Visual Studio 6 build log
295
+ *.plg
296
+
297
+ # Visual Studio 6 workspace options file
298
+ *.opt
299
+
300
+ # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
301
+ *.vbw
302
+
303
+ # Visual Studio 6 auto-generated project file (contains which files were open etc.)
304
+ *.vbp
305
+
306
+ # Visual Studio 6 workspace and project file (working project files containing files to include in project)
307
+ *.dsw
308
+ *.dsp
309
+
310
+ # Visual Studio 6 technical files
311
+ *.ncb
312
+ *.aps
313
+
314
+ # Visual Studio LightSwitch build output
315
+ **/*.HTMLClient/GeneratedArtifacts
316
+ **/*.DesktopClient/GeneratedArtifacts
317
+ **/*.DesktopClient/ModelManifest.xml
318
+ **/*.Server/GeneratedArtifacts
319
+ **/*.Server/ModelManifest.xml
320
+ _Pvt_Extensions
321
+
322
+ # Paket dependency manager
323
+ .paket/paket.exe
324
+ paket-files/
325
+
326
+ # FAKE - F# Make
327
+ .fake/
328
+
329
+ # CodeRush personal settings
330
+ .cr/personal
331
+
332
+ # Python Tools for Visual Studio (PTVS)
333
+ __pycache__/
334
+ *.pyc
335
+
336
+ # Cake - Uncomment if you are using it
337
+ # tools/**
338
+ # !tools/packages.config
339
+
340
+ # Tabs Studio
341
+ *.tss
342
+
343
+ # Telerik's JustMock configuration file
344
+ *.jmconfig
345
+
346
+ # BizTalk build output
347
+ *.btp.cs
348
+ *.btm.cs
349
+ *.odx.cs
350
+ *.xsd.cs
351
+
352
+ # OpenCover UI analysis results
353
+ OpenCover/
354
+
355
+ # Azure Stream Analytics local run output
356
+ ASALocalRun/
357
+
358
+ # MSBuild Binary and Structured Log
359
+ *.binlog
360
+
361
+ # NVidia Nsight GPU debugger configuration file
362
+ *.nvuser
363
+
364
+ # MFractors (Xamarin productivity tool) working folder
365
+ .mfractor/
366
+
367
+ # Local History for Visual Studio
368
+ .localhistory/
369
+
370
+ # Visual Studio History (VSHistory) files
371
+ .vshistory/
372
+
373
+ # BeatPulse healthcheck temp database
374
+ healthchecksdb
375
+
376
+ # Backup folder for Package Reference Convert tool in Visual Studio 2017
377
+ MigrationBackup/
378
+
379
+ # Ionide (cross platform F# VS Code tools) working folder
380
+ .ionide/
381
+
382
+ # Fody - auto-generated XML schema
383
+ FodyWeavers.xsd
384
+
385
+ # VS Code files for those working on multiple tools
386
+ .vscode/*
387
+ !.vscode/settings.json
388
+ !.vscode/tasks.json
389
+ !.vscode/launch.json
390
+ !.vscode/extensions.json
391
+ *.code-workspace
392
+
393
+ # Local History for Visual Studio Code
394
+ .history/
395
+
396
+ # Windows Installer files from build outputs
397
+ *.cab
398
+ *.msi
399
+ *.msix
400
+ *.msm
401
+ *.msp
402
+
403
+ # JetBrains Rider
404
+ *.sln.iml
405
+ .idea
406
+
407
+ ##
408
+ ## Visual studio for Mac
409
+ ##
410
+
411
+
412
+ # globs
413
+ Makefile.in
414
+ *.userprefs
415
+ *.usertasks
416
+ config.make
417
+ config.status
418
+ aclocal.m4
419
+ install-sh
420
+ autom4te.cache/
421
+ *.tar.gz
422
+ tarballs/
423
+ test-results/
424
+
425
+ # Mac bundle stuff
426
+ *.dmg
427
+ *.app
428
+
429
+ # content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
430
+ # General
431
+ .DS_Store
432
+ .AppleDouble
433
+ .LSOverride
434
+
435
+ # Icon must end with two \r
436
+ Icon
437
+
438
+
439
+ # Thumbnails
440
+ ._*
441
+
442
+ # Files that might appear in the root of a volume
443
+ .DocumentRevisions-V100
444
+ .fseventsd
445
+ .Spotlight-V100
446
+ .TemporaryItems
447
+ .Trashes
448
+ .VolumeIcon.icns
449
+ .com.apple.timemachine.donotpresent
450
+
451
+ # Directories potentially created on remote AFP share
452
+ .AppleDB
453
+ .AppleDesktop
454
+ Network Trash Folder
455
+ Temporary Items
456
+ .apdisk
457
+
458
+ # content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
459
+ # Windows thumbnail cache files
460
+ Thumbs.db
461
+ ehthumbs.db
462
+ ehthumbs_vista.db
463
+
464
+ # Dump file
465
+ *.stackdump
466
+
467
+ # Folder config file
468
+ [Dd]esktop.ini
469
+
470
+ # Recycle Bin used on file shares
471
+ $RECYCLE.BIN/
472
+
473
+ # Windows Installer files
474
+ *.cab
475
+ *.msi
476
+ *.msix
477
+ *.msm
478
+ *.msp
479
+
480
+ # Windows shortcuts
481
+ *.lnk
482
+
483
+ # Vim temporary swap files
484
+ *.swp
.vscode/launch.json ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ // Use IntelliSense to learn about possible attributes.
3
+ // Hover to view descriptions of existing attributes.
4
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5
+ "version": "0.2.0",
6
+ "configurations": [
7
+ {
8
+ "name": ".NET Core Launch (web)",
9
+ "type": "coreclr",
10
+ "request": "launch",
11
+ "preLaunchTask": "build",
12
+ "program": "${workspaceFolder}/bin/Debug/net8.0/QuotesAPI.dll",
13
+ "args": [],
14
+ "cwd": "${workspaceFolder}",
15
+ "stopAtEntry": false,
16
+ "serverReadyAction": {
17
+ "action": "openExternally",
18
+ "pattern": "\\bNow listening on:\\s+(https?://\\S+)"
19
+ },
20
+ "env": {
21
+ "ASPNETCORE_ENVIRONMENT": "Development"
22
+ },
23
+ "sourceFileMap": {
24
+ "/Views": "${workspaceFolder}/Views"
25
+ }
26
+ },
27
+ {
28
+ "name": ".NET Core Attach",
29
+ "type": "coreclr",
30
+ "request": "attach"
31
+ }
32
+ ]
33
+ }
.vscode/tasks.json ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "version": "2.0.0",
3
+ "tasks": [
4
+ {
5
+ "label": "build",
6
+ "command": "dotnet",
7
+ "type": "process",
8
+ "args": [
9
+ "build",
10
+ "${workspaceFolder}/quotes-api-net.generated.sln",
11
+ "/property:GenerateFullPaths=true",
12
+ "/consoleloggerparameters:NoSummary;ForceNoAlign"
13
+ ],
14
+ "problemMatcher": "$msCompile"
15
+ },
16
+ {
17
+ "label": "publish",
18
+ "command": "dotnet",
19
+ "type": "process",
20
+ "args": [
21
+ "publish",
22
+ "${workspaceFolder}/quotes-api-net.generated.sln",
23
+ "/property:GenerateFullPaths=true",
24
+ "/consoleloggerparameters:NoSummary;ForceNoAlign"
25
+ ],
26
+ "problemMatcher": "$msCompile"
27
+ },
28
+ {
29
+ "label": "watch",
30
+ "command": "dotnet",
31
+ "type": "process",
32
+ "args": [
33
+ "watch",
34
+ "run",
35
+ "--project",
36
+ "${workspaceFolder}/quotes-api-net.generated.sln"
37
+ ],
38
+ "problemMatcher": "$msCompile"
39
+ }
40
+ ]
41
+ }
DbContext/QuoteDbContext.cs ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using Microsoft.EntityFrameworkCore;
2
+ using System;
3
+ using System.Collections.Generic;
4
+ using System.ComponentModel.DataAnnotations;
5
+
6
+ public class QuoteDbContext : DbContext
7
+ {
8
+ public DbSet<Quote> Quotes { get; set; }
9
+
10
+ // Constructor
11
+ public QuoteDbContext(DbContextOptions<QuoteDbContext> options) : base(options)
12
+ {
13
+ }
14
+
15
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
16
+ {
17
+ base.OnModelCreating(modelBuilder);
18
+
19
+ // Configure the Quote entity using Fluent API
20
+ modelBuilder.Entity<Quote>(entity =>
21
+ {
22
+ entity.HasKey(q => q.Id); // Primary key
23
+
24
+ // Configure indexed columns
25
+ entity.HasIndex(q => q.Author);
26
+ entity.HasIndex(q => q.QuoteText).IsUnique();
27
+
28
+ // Column length and constraints are defined using Data Annotations,
29
+ // but Fluent API can also be used if needed.
30
+ entity.Property(q => q.Author).IsRequired().HasMaxLength(255);
31
+ entity.Property(q => q.QuoteText).IsRequired().HasMaxLength(1000);
32
+ entity.Property(q => q.Source).HasMaxLength(255);
33
+ entity.Property(q => q.Book).HasMaxLength(255);
34
+ entity.Property(q => q.Categories).HasMaxLength(255);
35
+ entity.Property(q => q.Url).HasMaxLength(500);
36
+ entity.Property(q => q.Isbn).HasMaxLength(13);
37
+ entity.Property(q => q.Language).HasMaxLength(50);
38
+ entity.Property(q => q.OriginalLanguage).HasMaxLength(50);
39
+ });
40
+ }
41
+ }
42
+
43
+ public class Quote
44
+ {
45
+ public int Id { get; set; } // Primary key with auto-increment
46
+
47
+ [Required]
48
+ [MaxLength(255)]
49
+ public string Author { get; set; } // Indexed column
50
+
51
+ [Required]
52
+ [MaxLength(1000)]
53
+ public string QuoteText { get; set; } // Indexed and unique column
54
+
55
+ [MaxLength(255)]
56
+ public string? Source { get; set; } // Nullable column
57
+
58
+ [MaxLength(255)]
59
+ public string? Book { get; set; } // Nullable column
60
+
61
+ [MaxLength(255)]
62
+ public string? Categories { get; set; } // Nullable column
63
+
64
+ [MaxLength(500)]
65
+ public string? Url { get; set; } // Nullable column
66
+
67
+ [MaxLength(13)]
68
+ public string? Isbn { get; set; } // Nullable column
69
+
70
+ [MaxLength(50)]
71
+ public string? Language { get; set; } // Nullable column
72
+
73
+ [MaxLength(50)]
74
+ public string? OriginalLanguage { get; set; } // Nullable column
75
+
76
+ public DateTime DateCreated { get; set; } = DateTime.Now; // Default to current datetime
77
+ }
Program.cs ADDED
@@ -0,0 +1,209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using System.Text.Json;
2
+ using Microsoft.EntityFrameworkCore;
3
+
4
+ var builder = WebApplication.CreateBuilder(args);
5
+
6
+ builder.Services.AddDbContext<QuoteDbContext>(options =>
7
+ options.UseSqlite("Data Source=quotes.db"));
8
+
9
+ builder.Services.AddEndpointsApiExplorer();
10
+ builder.Services.AddSwaggerGen();
11
+
12
+ var app = builder.Build();
13
+
14
+ // Configure the HTTP request pipeline.
15
+ if (app.Environment.IsDevelopment())
16
+ {
17
+ app.UseSwagger();
18
+ app.UseSwaggerUI();
19
+ }
20
+
21
+ const int MaxPageSize = 100;
22
+
23
+ app.UseHttpsRedirection();
24
+
25
+ app.MapGet("/quotes", async (QuoteDbContext db, int pageNumber = 1, int pageSize = 10) =>
26
+ {
27
+ if (pageNumber < 1) pageNumber = 1;
28
+ if (pageSize < 1) pageSize = 10;
29
+ pageSize = Math.Min(pageSize, MaxPageSize); // Limit pageSize to MaxPageSize
30
+
31
+ var quotes = await db.Quotes
32
+ .Skip((pageNumber - 1) * pageSize)
33
+ .Take(pageSize)
34
+ .ToListAsync();
35
+
36
+ return Results.Ok(quotes);
37
+ });
38
+
39
+ app.MapGet("/quotes/{id}", async (int id, QuoteDbContext db) =>
40
+ await db.Quotes.FindAsync(id) is Quote quote
41
+ ? Results.Ok(quote)
42
+ : Results.NotFound("Quote not found"));
43
+
44
+ app.MapPost("/quotes", async (Quote newQuote, QuoteDbContext db) =>
45
+ {
46
+ db.Quotes.Add(newQuote);
47
+ await db.SaveChangesAsync();
48
+ return Results.Created($"/quotes/{newQuote.Id}", newQuote);
49
+ });
50
+
51
+ app.MapPut("/quotes/{id}", async (int id, Quote updatedQuote, QuoteDbContext db) =>
52
+ {
53
+ var quote = await db.Quotes.FindAsync(id);
54
+ if (quote is null) return Results.NotFound("Quote not found");
55
+
56
+ quote.Author = updatedQuote.Author;
57
+ quote.QuoteText = updatedQuote.QuoteText;
58
+ quote.Source = updatedQuote.Source;
59
+ quote.Book = updatedQuote.Book;
60
+ quote.Categories = updatedQuote.Categories;
61
+ quote.Url = updatedQuote.Url;
62
+ quote.Isbn = updatedQuote.Isbn;
63
+ quote.Language = updatedQuote.Language;
64
+ quote.OriginalLanguage = updatedQuote.OriginalLanguage;
65
+
66
+ await db.SaveChangesAsync();
67
+ return Results.NoContent();
68
+ });
69
+
70
+ app.MapDelete("/quotes/{id}", async (int id, QuoteDbContext db) =>
71
+ {
72
+ var quote = await db.Quotes.FindAsync(id);
73
+ if (quote is null) return Results.NotFound("Quote not found");
74
+
75
+ db.Quotes.Remove(quote);
76
+ await db.SaveChangesAsync();
77
+ return Results.NoContent();
78
+ });
79
+
80
+ // Search quotes by author, categories, book, or quoteText with pagination
81
+ app.MapGet("/quotes/search", async (string search, QuoteDbContext db, int pageNumber = 1, int pageSize = 10) =>
82
+ {
83
+ var query = db.Quotes.AsQueryable();
84
+
85
+ if (!string.IsNullOrWhiteSpace(search))
86
+ query = query.Where(q => q.Author.Contains(search));
87
+
88
+ if (!string.IsNullOrWhiteSpace(search))
89
+ query = query.Where(q => q.Categories.Contains(search));
90
+
91
+ if (!string.IsNullOrWhiteSpace(search))
92
+ query = query.Where(q => q.Book.Contains(search));
93
+
94
+ if (!string.IsNullOrWhiteSpace(search))
95
+ query = query.Where(q => q.QuoteText.Contains(search));
96
+
97
+ if (pageNumber < 1) pageNumber = 1;
98
+ if (pageSize < 1) pageSize = 10;
99
+ pageSize = Math.Min(pageSize, MaxPageSize); // Limit pageSize to MaxPageSize
100
+
101
+ var paginatedQuotes = await query
102
+ .Skip((pageNumber - 1) * pageSize)
103
+ .Take(pageSize)
104
+ .ToListAsync();
105
+
106
+ return paginatedQuotes.Any() ? Results.Ok(paginatedQuotes) : Results.NotFound("No matching quotes found.");
107
+ });
108
+
109
+ async Task SaveSource1Async(string jsonFilePath, QuoteDbContext db)
110
+ {
111
+ var path = Path.Combine(Directory.GetCurrentDirectory(), "data", jsonFilePath);
112
+
113
+ if (!File.Exists(path))
114
+ throw new FileNotFoundException("The JSON file for seeding is missing.");
115
+
116
+ // var jsonString = await File.ReadAllTextAsync(path);
117
+
118
+ // // Deserialize JSON to a list of dictionaries
119
+ // var dictionaryList = JsonSerializer.Deserialize<List<Dictionary<string, object>>>(jsonString);
120
+
121
+ // if (dictionaryList == null)
122
+ // throw new InvalidOperationException("Failed to deserialize JSON into dictionary list.");
123
+
124
+ // Fields
125
+ // ==========
126
+ // Quote (string)
127
+ // Author (string)
128
+ // Tags (list<string>)
129
+ // Category (string)
130
+
131
+ // foreach (var dictionary in dictionaryList)
132
+ // {
133
+ // var tags = "";
134
+ // if(dictionary.TryGetValue("Tags", out var categories)) {
135
+ // var list = JsonSerializer.Deserialize<List<string>>(categories.ToString());
136
+ // tags = String.Join(",", list.Select(x=> x.Trim().Trim('"')));
137
+ // }
138
+ // var quote = new Quote
139
+ // {
140
+ // Author = dictionary.TryGetValue("Author", out var author) ? author.ToString() : null,
141
+ // QuoteText = dictionary.TryGetValue("Quote", out var quoteText) ? quoteText.ToString() : null,
142
+ // Categories = tags,
143
+ // };
144
+ // db.Quotes.Add(quote);
145
+ // }
146
+
147
+ // TODO: import data and sanitize relevant fields: Trim and Trim('"')
148
+
149
+ //save for each seed file
150
+ await db.SaveChangesAsync();
151
+ }
152
+
153
+ async Task SaveSource2Async(string jsonFile, QuoteDbContext db)
154
+ {
155
+ var path = Path.Combine(Directory.GetCurrentDirectory(), "data", jsonFile);
156
+
157
+ if (!File.Exists(path))
158
+ throw new FileNotFoundException("The JSON file for seeding is missing.");
159
+
160
+ // Fields
161
+ // ==========
162
+ // content (string)
163
+ // author (string)
164
+ // tags (list<string>)
165
+
166
+ // TODO: import data and sanitize relevant fields: Trim and Trim('"')
167
+
168
+ //save for each seed file
169
+ await db.SaveChangesAsync();
170
+ }
171
+
172
+ async Task SaveSource3Async(string csvFile, QuoteDbContext db)
173
+ {
174
+ var path = Path.Combine(Directory.GetCurrentDirectory(), "data", csvFile);
175
+
176
+ if (!File.Exists(path))
177
+ throw new FileNotFoundException("The CSV file for seeding is missing.");
178
+
179
+ // Fields
180
+ // ==========
181
+ // quote (string)
182
+ // author (string)
183
+ // category (string)
184
+
185
+ // TODO: import data and sanitize relevant fields: Trim and Trim('"')
186
+
187
+ //save for each seed file
188
+ await db.SaveChangesAsync();
189
+ }
190
+
191
+ // Seed database
192
+ async Task SeedDatabase(QuoteDbContext db)
193
+ {
194
+ if (await db.Quotes.AnyAsync())
195
+ return; // Database is already seeded
196
+
197
+ await SaveSource1Async("source1.json", db);
198
+ await SaveSource2Async("source2.json", db);
199
+ await SaveSource3Async("source3.csv", db);
200
+ }
201
+
202
+ using (var scope = app.Services.CreateScope())
203
+ {
204
+ var db = scope.ServiceProvider.GetRequiredService<QuoteDbContext>();
205
+ db.Database.EnsureCreated();
206
+ await SeedDatabase(db);
207
+ }
208
+
209
+ app.Run();
Properties/launchSettings.json ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "$schema": "http://json.schemastore.org/launchsettings.json",
3
+ "profiles": {
4
+ "http": {
5
+ "commandName": "Project",
6
+ "dotnetRunMessages": true,
7
+ "launchBrowser": true,
8
+ "launchUrl": "swagger",
9
+ "applicationUrl": "http://localhost:7001",
10
+ "environmentVariables": {
11
+ "ASPNETCORE_ENVIRONMENT": "Development"
12
+ }
13
+ },
14
+ "https": {
15
+ "commandName": "Project",
16
+ "dotnetRunMessages": true,
17
+ "launchBrowser": true,
18
+ "launchUrl": "swagger",
19
+ "applicationUrl": "https://localhost:7000;http://localhost:7001",
20
+ "environmentVariables": {
21
+ "ASPNETCORE_ENVIRONMENT": "Development"
22
+ }
23
+ },
24
+ }
25
+ }
QuotesAPI.csproj ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <Project Sdk="Microsoft.NET.Sdk.Web">
2
+
3
+ <PropertyGroup>
4
+ <TargetFramework>net8.0</TargetFramework>
5
+ <Nullable>enable</Nullable>
6
+ <ImplicitUsings>enable</ImplicitUsings>
7
+ </PropertyGroup>
8
+
9
+ <ItemGroup>
10
+ <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8" />
11
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.8" />
12
+ <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
13
+ <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
14
+ </ItemGroup>
15
+
16
+ <ItemGroup>
17
+ <!-- Include the 'data' folder and its contents in the build output -->
18
+ <None Update="data\**\*">
19
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
20
+ </None>
21
+ </ItemGroup>
22
+
23
+ </Project>
QuotesAPI.http ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ @QuotesAPI_HostAddress = http://localhost:5058
2
+
3
+ GET {{QuotesAPI_HostAddress}}/weatherforecast/
4
+ Accept: application/json
5
+
6
+ ###
appsettings.Development.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Logging": {
3
+ "LogLevel": {
4
+ "Default": "Information",
5
+ "Microsoft.AspNetCore": "Warning"
6
+ }
7
+ }
8
+ }
appsettings.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Logging": {
3
+ "LogLevel": {
4
+ "Default": "Information",
5
+ "Microsoft.AspNetCore": "Warning"
6
+ }
7
+ },
8
+ "AllowedHosts": "*"
9
+ }
data/source1.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f3fada9a03246edee439a42e4e21ed48e2498d801c76aa56a21889853b41599e
3
+ size 22843448
data/source2.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:647c84cfb730de01677d0067bb34ad899ccd5ec13c9a17c446128c50b14abc27
3
+ size 458066
data/source3.csv ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8fa4c686311730d4df7110ad48d6dd951dce0598af65f6254fabdaee8db69941
3
+ size 144428641
quotes.db ADDED
Binary file (20.5 kB). View file