Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions blogging_api/Controllers/BlogsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using blogging_api.Dtos;
using blogging_api.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.ResponseCaching;

namespace blogging_api.Controllers;

[ApiController]
[Route("api/[controller]")]
public class BlogsController : ControllerBase
{
private readonly BlogService _service;

public BlogsController(BlogService service)
{
_service = service;
}

[HttpPost]
public async Task<ActionResult<BlogResponse>> CreateBlog([FromBody] CreateBlogRequest request)
{
var createdBlog = await _service.CreateBlogAsync(request);

if (createdBlog == null)
{
return NotFound();
}

return createdBlog;
}

[HttpGet]
public async Task<ActionResult<BlogResponse>> GetBlogs([FromQuery] BlogQueryParams query)
{
var blogs = await _service.GetBlogsAsync(query);
return Ok(blogs);
}

[HttpGet("{id:int}")]
public async Task<ActionResult<BlogResponse>> GetBlogById(int id)
{
var response = await _service.GetBlogById(id);

if (response == null)
{
return NotFound();
}

return response;
}

[HttpPut("{id:int}")]
public async Task<ActionResult<BlogResponse>> UpdateBlog(
[FromRoute] int id,
[FromBody] UpdateBlogRequest request
)
{
var result = await _service.UpdateBlogAsync(id, request);

if (result is not null)
{
return result;
}

return NotFound();
}

[HttpDelete("{id:int}")]
public async Task<IActionResult> DeleteBlogById([FromRoute] int id)
{
var success = await _service.DeleteBlogByIdAsync(id);

if (success)
{
return Ok();
}

return Problem();
}
}
15 changes: 15 additions & 0 deletions blogging_api/Data/BlogDbContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Microsoft.EntityFrameworkCore;
using blogging_api.Models;

namespace blogging_api.Data;

public class BlogDbContext : DbContext
{
public BlogDbContext(DbContextOptions<BlogDbContext> options): base(options)
{

}

public DbSet<Blog> BlogPosts => Set<Blog>();
public DbSet<BlogTag> BlogTags => Set<BlogTag>();
}
22 changes: 22 additions & 0 deletions blogging_api/Dtos/Request.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.ComponentModel.DataAnnotations;
using blogging_api.Models;

namespace blogging_api.Dtos;

public record CreateBlogRequest(
[Required(ErrorMessage = "Title is required")]
string Title,
string Content,
List<string> Tags
);

public record UpdateBlogRequest(
string? Title,
string? Content,
List<string>? Tags
);

public record BlogQueryParams(
List<string>? Terms = null,
List<string>? Tags = null
);
10 changes: 10 additions & 0 deletions blogging_api/Dtos/Response.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace blogging_api.Dtos;

public record BlogResponse(
int Id,
string Title,
string Content,
List<string>? Tags,
DateTimeOffset CreatedAt,
DateTimeOffset? EditedAt
);
104 changes: 104 additions & 0 deletions blogging_api/Migrations/20260908225407_InitialCreate.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

86 changes: 86 additions & 0 deletions blogging_api/Migrations/20260908225407_InitialCreate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace blogging_api.Migrations
{
/// <inheritdoc />
public partial class InitialCreate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "BlogPosts",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(max)", nullable: false),
Content = table.Column<string>(type: "nvarchar(max)", nullable: false),
CreatedAt = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false),
EditedAt = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_BlogPosts", x => x.Id);
});

migrationBuilder.CreateTable(
name: "BlogTags",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Tag = table.Column<string>(type: "nvarchar(max)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_BlogTags", x => x.Id);
});

migrationBuilder.CreateTable(
name: "BlogBlogTag",
columns: table => new
{
BlogsId = table.Column<int>(type: "int", nullable: false),
TagsId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_BlogBlogTag", x => new { x.BlogsId, x.TagsId });
table.ForeignKey(
name: "FK_BlogBlogTag_BlogPosts_BlogsId",
column: x => x.BlogsId,
principalTable: "BlogPosts",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_BlogBlogTag_BlogTags_TagsId",
column: x => x.TagsId,
principalTable: "BlogTags",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});

migrationBuilder.CreateIndex(
name: "IX_BlogBlogTag_TagsId",
table: "BlogBlogTag",
column: "TagsId");
}

/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "BlogBlogTag");

migrationBuilder.DropTable(
name: "BlogPosts");

migrationBuilder.DropTable(
name: "BlogTags");
}
}
}
Loading