diff --git a/.gitignore b/.gitignore
index 3a868930..5cf459c5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,4 @@ riderModule.iml
/_ReSharper.Caches/
.fake
.vscode
+.vs/
diff --git a/.vs/MyFirstBlog/DesignTimeBuild/.dtbcache.v2 b/.vs/MyFirstBlog/DesignTimeBuild/.dtbcache.v2
deleted file mode 100644
index e3a42a61..00000000
Binary files a/.vs/MyFirstBlog/DesignTimeBuild/.dtbcache.v2 and /dev/null differ
diff --git a/.vs/MyFirstBlog/FileContentIndex/1451f00c-8a3c-4b67-9988-24ae06d88ff9.vsidx b/.vs/MyFirstBlog/FileContentIndex/1451f00c-8a3c-4b67-9988-24ae06d88ff9.vsidx
deleted file mode 100644
index ae1b509b..00000000
Binary files a/.vs/MyFirstBlog/FileContentIndex/1451f00c-8a3c-4b67-9988-24ae06d88ff9.vsidx and /dev/null differ
diff --git a/.vs/MyFirstBlog/FileContentIndex/3671b2ea-91c1-4fa3-8590-3e73598d97ea.vsidx b/.vs/MyFirstBlog/FileContentIndex/3671b2ea-91c1-4fa3-8590-3e73598d97ea.vsidx
deleted file mode 100644
index 78b55c80..00000000
Binary files a/.vs/MyFirstBlog/FileContentIndex/3671b2ea-91c1-4fa3-8590-3e73598d97ea.vsidx and /dev/null differ
diff --git a/.vs/MyFirstBlog/config/applicationhost.config b/.vs/MyFirstBlog/config/applicationhost.config
deleted file mode 100644
index 0d88f0db..00000000
--- a/.vs/MyFirstBlog/config/applicationhost.config
+++ /dev/null
@@ -1,1016 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/.vs/MyFirstBlog/v17/.futdcache.v2 b/.vs/MyFirstBlog/v17/.futdcache.v2
deleted file mode 100644
index 128982d8..00000000
Binary files a/.vs/MyFirstBlog/v17/.futdcache.v2 and /dev/null differ
diff --git a/.vs/MyFirstBlog/v17/.suo b/.vs/MyFirstBlog/v17/.suo
deleted file mode 100644
index 74e1a08b..00000000
Binary files a/.vs/MyFirstBlog/v17/.suo and /dev/null differ
diff --git a/.vs/MyFirstBlog/v17/DocumentLayout.json b/.vs/MyFirstBlog/v17/DocumentLayout.json
deleted file mode 100644
index 653eb6b4..00000000
--- a/.vs/MyFirstBlog/v17/DocumentLayout.json
+++ /dev/null
@@ -1,149 +0,0 @@
-{
- "Version": 1,
- "WorkspaceRootPath": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\",
- "Documents": [
- {
- "AbsoluteMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|c:\\00code\\fullstack\\myfirstblogbackend\\myfirstblog\\properties\\launchsettings.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}",
- "RelativeMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|solutionrelative:myfirstblog\\properties\\launchsettings.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}"
- },
- {
- "AbsoluteMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|c:\\00code\\fullstack\\myfirstblogbackend\\myfirstblog\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|solutionrelative:myfirstblog\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
- },
- {
- "AbsoluteMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|c:\\00code\\fullstack\\myfirstblogbackend\\myfirstblog\\entities\\post.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|solutionrelative:myfirstblog\\entities\\post.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
- },
- {
- "AbsoluteMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|c:\\00code\\fullstack\\myfirstblogbackend\\myfirstblog\\services\\postservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|solutionrelative:myfirstblog\\services\\postservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
- },
- {
- "AbsoluteMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|c:\\00code\\fullstack\\myfirstblogbackend\\myfirstblog\\appsettings.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}",
- "RelativeMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|solutionrelative:myfirstblog\\appsettings.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}"
- },
- {
- "AbsoluteMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|c:\\00code\\fullstack\\myfirstblogbackend\\myfirstblog\\extensions.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|solutionrelative:myfirstblog\\extensions.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
- },
- {
- "AbsoluteMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|c:\\00code\\fullstack\\myfirstblogbackend\\myfirstblog\\dtos\\postdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|solutionrelative:myfirstblog\\dtos\\postdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
- },
- {
- "AbsoluteMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|c:\\00code\\fullstack\\myfirstblogbackend\\myfirstblog\\controllers\\postscontroller.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{BB64A02C-C5D9-4D64-A357-7322C54B1419}|MyFirstBlog\\MyFirstBlog.csproj|solutionrelative:myfirstblog\\controllers\\postscontroller.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
- }
- ],
- "DocumentGroupContainers": [
- {
- "Orientation": 0,
- "VerticalTabListWidth": 256,
- "DocumentGroups": [
- {
- "DockedWidth": 200,
- "SelectedChildIndex": 6,
- "Children": [
- {
- "$type": "Document",
- "DocumentIndex": 1,
- "Title": "Program.cs",
- "DocumentMoniker": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Program.cs",
- "RelativeDocumentMoniker": "MyFirstBlog\\Program.cs",
- "ToolTip": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Program.cs",
- "RelativeToolTip": "MyFirstBlog\\Program.cs",
- "ViewState": "AQIAAAAAAAAAAAAAAAAAAA4AAAAjAAAA",
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2024-05-15T14:44:39.864Z"
- },
- {
- "$type": "Document",
- "DocumentIndex": 2,
- "Title": "Post.cs",
- "DocumentMoniker": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Entities\\Post.cs",
- "RelativeDocumentMoniker": "MyFirstBlog\\Entities\\Post.cs",
- "ToolTip": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Entities\\Post.cs",
- "RelativeToolTip": "MyFirstBlog\\Entities\\Post.cs",
- "ViewState": "AQIAAAAAAAAAAAAAAAAAAAEAAAAAAAAA",
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2024-05-15T14:43:03.248Z"
- },
- {
- "$type": "Document",
- "DocumentIndex": 6,
- "Title": "PostDto.cs",
- "DocumentMoniker": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Dtos\\PostDto.cs",
- "RelativeDocumentMoniker": "MyFirstBlog\\Dtos\\PostDto.cs",
- "ToolTip": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Dtos\\PostDto.cs",
- "RelativeToolTip": "MyFirstBlog\\Dtos\\PostDto.cs",
- "ViewState": "AQIAAAAAAAAAAAAAAAAAAAgAAAABAAAA",
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2024-05-15T14:42:47.081Z"
- },
- {
- "$type": "Document",
- "DocumentIndex": 4,
- "Title": "appsettings.json",
- "DocumentMoniker": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\appsettings.json",
- "RelativeDocumentMoniker": "MyFirstBlog\\appsettings.json",
- "ToolTip": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\appsettings.json",
- "RelativeToolTip": "MyFirstBlog\\appsettings.json",
- "ViewState": "AQIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|",
- "WhenOpened": "2024-05-15T14:49:58.169Z"
- },
- {
- "$type": "Document",
- "DocumentIndex": 3,
- "Title": "PostService.cs",
- "DocumentMoniker": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Services\\PostService.cs",
- "RelativeDocumentMoniker": "MyFirstBlog\\Services\\PostService.cs",
- "ToolTip": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Services\\PostService.cs",
- "RelativeToolTip": "MyFirstBlog\\Services\\PostService.cs",
- "ViewState": "AQIAAAAAAAAAAAAAAAAAAAkAAAAkAAAA",
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2024-05-15T14:45:00.15Z"
- },
- {
- "$type": "Document",
- "DocumentIndex": 5,
- "Title": "Extensions.cs",
- "DocumentMoniker": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Extensions.cs",
- "RelativeDocumentMoniker": "MyFirstBlog\\Extensions.cs",
- "ToolTip": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Extensions.cs",
- "RelativeToolTip": "MyFirstBlog\\Extensions.cs",
- "ViewState": "AQIAAAAAAAAAAAAAAAAAAAwAAAAuAAAA",
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2024-05-15T14:44:53.417Z"
- },
- {
- "$type": "Document",
- "DocumentIndex": 0,
- "Title": "launchSettings.json",
- "DocumentMoniker": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Properties\\launchSettings.json",
- "RelativeDocumentMoniker": "MyFirstBlog\\Properties\\launchSettings.json",
- "ToolTip": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Properties\\launchSettings.json",
- "RelativeToolTip": "MyFirstBlog\\Properties\\launchSettings.json",
- "ViewState": "AQIAAA8AAAAAAAAAAAAAABkAAAAfAAAA",
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|",
- "WhenOpened": "2024-05-15T14:50:05.724Z",
- "EditorCaption": ""
- },
- {
- "$type": "Document",
- "DocumentIndex": 7,
- "Title": "PostsController.cs",
- "DocumentMoniker": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Controllers\\PostsController.cs",
- "RelativeDocumentMoniker": "MyFirstBlog\\Controllers\\PostsController.cs",
- "ToolTip": "C:\\00code\\FullStack\\MyFirstBlogBackEnd\\MyFirstBlog\\Controllers\\PostsController.cs",
- "RelativeToolTip": "MyFirstBlog\\Controllers\\PostsController.cs",
- "ViewState": "AQIAAAYAAAAAAAAAAAAAAAsAAAAAAAAA",
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2024-05-15T14:42:54.111Z"
- }
- ]
- }
- ]
- }
- ]
-}
\ No newline at end of file
diff --git a/.vs/ProjectEvaluation/myfirstblog.metadata.v7.bin b/.vs/ProjectEvaluation/myfirstblog.metadata.v7.bin
deleted file mode 100644
index a3a97897..00000000
Binary files a/.vs/ProjectEvaluation/myfirstblog.metadata.v7.bin and /dev/null differ
diff --git a/.vs/ProjectEvaluation/myfirstblog.projects.v7.bin b/.vs/ProjectEvaluation/myfirstblog.projects.v7.bin
deleted file mode 100644
index cfc3e6a4..00000000
Binary files a/.vs/ProjectEvaluation/myfirstblog.projects.v7.bin and /dev/null differ
diff --git a/MyFirstBlog/Controllers/PostsController.cs b/MyFirstBlog/Controllers/PostsController.cs
index 8fa6bf2c..59412de5 100644
--- a/MyFirstBlog/Controllers/PostsController.cs
+++ b/MyFirstBlog/Controllers/PostsController.cs
@@ -1,34 +1,66 @@
-namespace MyFirstBlog.Controllers;
-
using Microsoft.AspNetCore.Mvc;
using MyFirstBlog.Dtos;
using MyFirstBlog.Services;
+using System.Collections.Generic;
-[ApiController]
-[Route("posts")]
+namespace MyFirstBlog.Controllers
+{
+ [ApiController]
+ [Route("posts")]
+ public class PostsController : ControllerBase
+ {
+ private readonly IPostService _postService;
-public class PostsController : ControllerBase {
- private IPostService _postService;
+ public PostsController(IPostService postService)
+ {
+ _postService = postService;
+ }
- public PostsController(IPostService postService) {
- _postService = postService;
- }
+ // GET /posts
+ [HttpGet]
+ public IEnumerable GetPosts()
+ {
+ return _postService.GetPosts();
+ }
- // Get /posts
- [HttpGet]
- public IEnumerable GetPosts() {
- return _postService.GetPosts();
- }
+ // GET /posts/{slug}
+
+ [HttpGet("{slug}")]
+ public ActionResult GetPost(string slug)
+ {
+ var post = _postService.GetPost(slug);
+ if (post == null)
+ {
+ return NotFound();
+ }
+ return Ok(post);
+ }
+
+
+
+ [HttpPost]
+ public IActionResult CreatePost([FromBody] PostCreateRequest post)
+ {
+ var errors = new List();
+
+ if (string.IsNullOrWhiteSpace(post.Title))
+ errors.Add("Title cannot be blank");
+
+ if (string.IsNullOrWhiteSpace(post.Description))
+ errors.Add("You cannot send a message with empty description");
+
+ if (errors.Any())
+ return BadRequest(new { errors });
- // Get /posts/:slug
- [HttpGet("{slug}")]
- public ActionResult GetPost(string slug) {
- var post = _postService.GetPost(slug);
+ var newPost = _postService.CreatePost(post);
- if (post is null) {
- return NotFound();
+ return CreatedAtAction(nameof(GetPost), new { slug = newPost.Slug }, new
+ {
+ title = newPost.Title,
+ description = newPost.Description
+ });
+ //return CreatedAtAction(nameof(GetPost), new { slug = newPost.Slug }, newPost);
}
- return post;
}
}
diff --git a/MyFirstBlog/Dtos/PostCreateRequest.cs b/MyFirstBlog/Dtos/PostCreateRequest.cs
new file mode 100644
index 00000000..e3a3b8b5
--- /dev/null
+++ b/MyFirstBlog/Dtos/PostCreateRequest.cs
@@ -0,0 +1,8 @@
+namespace MyFirstBlog.Dtos
+{
+ public class PostCreateRequest
+ {
+ public string Title { get; set; }
+ public string Description { get; set; }
+ }
+}
diff --git a/MyFirstBlog/Dtos/PostDto.cs b/MyFirstBlog/Dtos/PostDto.cs
index 12ca4818..a3ea024f 100644
--- a/MyFirstBlog/Dtos/PostDto.cs
+++ b/MyFirstBlog/Dtos/PostDto.cs
@@ -5,5 +5,6 @@ public record PostDto {
public string Title { get; init; } = default!;
public string Slug { get; init; } = default!;
public string Body { get; init; } = default!;
+ public string Description { get; set; } = default!;
public DateTime CreatedDate { get; init; }
}
diff --git a/MyFirstBlog/Entities/Post.cs b/MyFirstBlog/Entities/Post.cs
index 9347e859..3e1dca5a 100644
--- a/MyFirstBlog/Entities/Post.cs
+++ b/MyFirstBlog/Entities/Post.cs
@@ -4,5 +4,6 @@ public record Post {
public string Title { get; init; } = default!;
public string Slug { get; init; } = default!;
public string Body { get; init; } = default!;
+ public string Description { get; set; }
public DateTime CreatedDate { get; init; }
}
diff --git a/MyFirstBlog/Extensions.cs b/MyFirstBlog/Extensions.cs
index a5326018..bd1d11da 100644
--- a/MyFirstBlog/Extensions.cs
+++ b/MyFirstBlog/Extensions.cs
@@ -10,6 +10,7 @@ public static PostDto AsDto(this Post post) {
Title = post.Title,
Slug = post.Slug,
Body = post.Body,
+ Description = post.Description,
CreatedDate = post.CreatedDate
};
diff --git a/MyFirstBlog/Migrations/20250807001933_AddDescriptionToPost.Designer.cs b/MyFirstBlog/Migrations/20250807001933_AddDescriptionToPost.Designer.cs
new file mode 100644
index 00000000..8bedcef9
--- /dev/null
+++ b/MyFirstBlog/Migrations/20250807001933_AddDescriptionToPost.Designer.cs
@@ -0,0 +1,56 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using MyFirstBlog.Helpers;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace MyFirstBlog.Migrations
+{
+ [DbContext(typeof(DataContext))]
+ [Migration("20250807001933_AddDescriptionToPost")]
+ partial class AddDescriptionToPost
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "7.0.0")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("MyFirstBlog.Entities.Post", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("Body")
+ .HasColumnType("text");
+
+ b.Property("CreatedDate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .HasColumnType("text");
+
+ b.Property("Slug")
+ .HasColumnType("text");
+
+ b.Property("Title")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("Posts");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/MyFirstBlog/Migrations/20250807001933_AddDescriptionToPost.cs b/MyFirstBlog/Migrations/20250807001933_AddDescriptionToPost.cs
new file mode 100644
index 00000000..becd03e2
--- /dev/null
+++ b/MyFirstBlog/Migrations/20250807001933_AddDescriptionToPost.cs
@@ -0,0 +1,28 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace MyFirstBlog.Migrations
+{
+ ///
+ public partial class AddDescriptionToPost : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "Description",
+ table: "Posts",
+ type: "text",
+ nullable: true);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "Description",
+ table: "Posts");
+ }
+ }
+}
diff --git a/MyFirstBlog/Migrations/20250807042432_AddDescriptionToPosts.Designer.cs b/MyFirstBlog/Migrations/20250807042432_AddDescriptionToPosts.Designer.cs
new file mode 100644
index 00000000..9bd2300a
--- /dev/null
+++ b/MyFirstBlog/Migrations/20250807042432_AddDescriptionToPosts.Designer.cs
@@ -0,0 +1,56 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using MyFirstBlog.Helpers;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace MyFirstBlog.Migrations
+{
+ [DbContext(typeof(DataContext))]
+ [Migration("20250807042432_AddDescriptionToPosts")]
+ partial class AddDescriptionToPosts
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "7.0.0")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("MyFirstBlog.Entities.Post", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("Body")
+ .HasColumnType("text");
+
+ b.Property("CreatedDate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .HasColumnType("text");
+
+ b.Property("Slug")
+ .HasColumnType("text");
+
+ b.Property("Title")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("Posts");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/MyFirstBlog/Migrations/20250807042432_AddDescriptionToPosts.cs b/MyFirstBlog/Migrations/20250807042432_AddDescriptionToPosts.cs
new file mode 100644
index 00000000..a2fb9035
--- /dev/null
+++ b/MyFirstBlog/Migrations/20250807042432_AddDescriptionToPosts.cs
@@ -0,0 +1,22 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace MyFirstBlog.Migrations
+{
+ ///
+ public partial class AddDescriptionToPosts : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+
+ }
+ }
+}
diff --git a/MyFirstBlog/Migrations/20250807050512_UpdatePostSchema.Designer.cs b/MyFirstBlog/Migrations/20250807050512_UpdatePostSchema.Designer.cs
new file mode 100644
index 00000000..6b5b62e1
--- /dev/null
+++ b/MyFirstBlog/Migrations/20250807050512_UpdatePostSchema.Designer.cs
@@ -0,0 +1,56 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using MyFirstBlog.Helpers;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace MyFirstBlog.Migrations
+{
+ [DbContext(typeof(DataContext))]
+ [Migration("20250807050512_UpdatePostSchema")]
+ partial class UpdatePostSchema
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "7.0.0")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("MyFirstBlog.Entities.Post", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("Body")
+ .HasColumnType("text");
+
+ b.Property("CreatedDate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .HasColumnType("text");
+
+ b.Property("Slug")
+ .HasColumnType("text");
+
+ b.Property("Title")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("Posts");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/MyFirstBlog/Migrations/20250807050512_UpdatePostSchema.cs b/MyFirstBlog/Migrations/20250807050512_UpdatePostSchema.cs
new file mode 100644
index 00000000..4437f6d4
--- /dev/null
+++ b/MyFirstBlog/Migrations/20250807050512_UpdatePostSchema.cs
@@ -0,0 +1,22 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace MyFirstBlog.Migrations
+{
+ ///
+ public partial class UpdatePostSchema : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+
+ }
+ }
+}
diff --git a/MyFirstBlog/Migrations/DataContextModelSnapshot.cs b/MyFirstBlog/Migrations/DataContextModelSnapshot.cs
index 2965cd9a..679ec616 100644
--- a/MyFirstBlog/Migrations/DataContextModelSnapshot.cs
+++ b/MyFirstBlog/Migrations/DataContextModelSnapshot.cs
@@ -1,4 +1,5 @@
//
+using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
@@ -21,7 +22,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
- modelBuilder.Entity("MyFirstBlog.Models.Post", b =>
+ modelBuilder.Entity("MyFirstBlog.Entities.Post", b =>
{
b.Property("Id")
.ValueGeneratedOnAdd()
@@ -30,9 +31,12 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Property("Body")
.HasColumnType("text");
- b.Property("CreatedDate")
+ b.Property("CreatedDate")
.HasColumnType("timestamp with time zone");
+ b.Property("Description")
+ .HasColumnType("text");
+
b.Property("Slug")
.HasColumnType("text");
diff --git a/MyFirstBlog/Services/PostService.cs b/MyFirstBlog/Services/PostService.cs
index 6bac099f..5d540adf 100644
--- a/MyFirstBlog/Services/PostService.cs
+++ b/MyFirstBlog/Services/PostService.cs
@@ -1,37 +1,53 @@
-namespace MyFirstBlog.Services;
-
-using MyFirstBlog.Helpers;
+using MyFirstBlog.Dtos;
using MyFirstBlog.Entities;
+using MyFirstBlog.Helpers;
+using System.Collections.Generic;
+using System.Linq;
using System.Text.RegularExpressions;
-using MyFirstBlog.Dtos;
public interface IPostService
{
IEnumerable GetPosts();
- PostDto GetPost(String slug);
+ PostDto GetPost(string slug);
+ PostDto CreatePost(PostCreateRequest post);
}
-public class PostService : IPostService
-{
- private DataContext _context;
- public PostService(DataContext context)
+namespace MyFirstBlog.Services
+{
+ public class PostService : IPostService
{
- _context = context;
- }
+ private readonly DataContext _context;
- public IEnumerable GetPosts()
- {
- return _context.Posts.Select(post => post.AsDto());
- }
+ public PostService(DataContext context)
+ {
+ _context = context;
+ }
- public PostDto GetPost(string slug)
- {
- return getPost(slug).AsDto();
- }
+ public IEnumerable GetPosts()
+ {
+ return _context.Posts.Select(post => post.AsDto());
+ }
- private Post getPost(string slug)
- {
- return _context.Posts.Where(a=>a.Slug==slug.ToString()).SingleOrDefault();
+ public PostDto GetPost(string slug)
+ {
+ return _context.Posts.FirstOrDefault(p => p.Slug == slug)?.AsDto();
+ }
+
+ public PostDto CreatePost(PostCreateRequest post)
+ {
+ var entity = new Post
+ {
+ Title = post.Title,
+ Description = post.Description,
+ Slug = Regex.Replace(post.Title.ToLower(), @"\s+", "-"),
+ CreatedDate = DateTime.UtcNow
+ };
+
+ _context.Posts.Add(entity);
+ _context.SaveChanges(); // ✅ Save to DB
+
+ return entity.AsDto();
+ }
}
}
diff --git a/MyFirstBlog/appsettings.Development.json b/MyFirstBlog/appsettings.Development.json
index 68bd7dc3..5bf58494 100644
--- a/MyFirstBlog/appsettings.Development.json
+++ b/MyFirstBlog/appsettings.Development.json
@@ -1,6 +1,6 @@
{
"ConnectionStrings": {
- "DefaultConnection": "Host=localhost; Database=bvc-blog; Username=postgres; Password=postgres"
+ "DefaultConnection": "Host=localhost; Database=bvc-blog; Username=postgres; Password=admin123"
},
"Logging": {
"LogLevel": {