Merge pull request #7 from StevanFreeborn/master
Deploy master to production
This commit is contained in:
Vendored
+35
@@ -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}/server/bin/Debug/net6.0/server.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/server",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"appService.defaultWebAppToDeploy": "/subscriptions/d031910a-7ca1-44c2-9bb1-0bdb42cd9f43/microsoft.web/sites/subscriptions/d031910a-7ca1-44c2-9bb1-0bdb42cd9f43/resourceGroups/StevanFreeborn/providers/Microsoft.Web/sites/criminalmindsapi",
|
||||
"appService.deploySubpath": "server"
|
||||
}
|
||||
Vendored
+41
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/server/server.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "publish",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"publish",
|
||||
"${workspaceFolder}/server/server.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "watch",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"watch",
|
||||
"run",
|
||||
"--project",
|
||||
"${workspaceFolder}/server/server.csproj"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
||||
+1
-1
@@ -55,7 +55,7 @@ export default class App extends Component {
|
||||
let url = '/api/seasons';
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
url = 'https://criminalminds-server.azurewebsites.net' + url;
|
||||
url = 'https://criminalmindsapi.azurewebsites.net' + url;
|
||||
}
|
||||
|
||||
const response = await fetch(url);
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MongoDB.Bson;
|
||||
using server.Models;
|
||||
using server.Persistence.Repositories;
|
||||
|
||||
namespace server.Controllers.v1
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("/api/characters")]
|
||||
[Produces("application/json")]
|
||||
public class CharactersController : ControllerBase
|
||||
{
|
||||
private readonly ICharacterRepository _characterRepository;
|
||||
|
||||
public CharactersController(ICharacterRepository characterRepository)
|
||||
{
|
||||
_characterRepository = characterRepository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of characters.
|
||||
/// </summary>
|
||||
/// <param name="filter">Filter parameters passed from query string.</param>
|
||||
/// <response code="200">Returns the collection of characters requested.</response>
|
||||
/// <response code="400">Not a valid request.</response>
|
||||
/// <response code="500">Failed to get characters.</response>
|
||||
/// <returns>Returns a collection of characters.</returns>
|
||||
[MapToApiVersion("1.0")]
|
||||
[HttpGet]
|
||||
[ProducesResponseType(typeof(List<Character>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)]
|
||||
public async Task<ActionResult<List<Character>>> GetCharactersAsync([FromQuery] CharacterFilter? filter)
|
||||
{
|
||||
try
|
||||
{
|
||||
var character = await _characterRepository.GetCharactersAsync(filter);
|
||||
return Ok(character);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
return Problem(detail: "Failed to get characters", statusCode: 500);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a character by its id.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the character being requested.</param>
|
||||
/// <response code="200">Returns the character requested.</response>
|
||||
/// <response code="400">Not a valid request.</response>
|
||||
/// <response code="404">Character with the given id not found.</response>
|
||||
/// <response code="500">Failed to get character.</response>
|
||||
/// <returns>Returns a character.</returns>
|
||||
[MapToApiVersion("1.0")]
|
||||
[HttpGet("{id}")]
|
||||
[ProducesResponseType(typeof(Character), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)]
|
||||
public async Task<ActionResult<Character>> GetCharacterByIdAsync(string id)
|
||||
{
|
||||
|
||||
if (!ObjectId.TryParse(id, out _))
|
||||
{
|
||||
ModelState.AddModelError(nameof(id), $"{id} is not a valid id");
|
||||
return ValidationProblem();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var quote = await _characterRepository.GetCharacterByIdAsync(id);
|
||||
|
||||
return quote == null ?
|
||||
Problem(detail: $"Could not find character {id}", statusCode: 404) :
|
||||
Ok(quote);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
return Problem(detail: "Failed to get character", statusCode: 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,15 @@ namespace server.Controllers.v1
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add xml comments
|
||||
/// <summary>
|
||||
/// Gets a quote by its id.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the quote being requested.</param>
|
||||
/// <response code="200">Returns the quote requested.</response>
|
||||
/// <response code="400">Not a valid request.</response>
|
||||
/// <response code="404">Quote with the given id not found.</response>
|
||||
/// <response code="500">Failed to get quote.</response>
|
||||
/// <returns>Returns a quote.</returns>
|
||||
[MapToApiVersion("1.0")]
|
||||
[HttpGet("{id}")]
|
||||
[ProducesResponseType(typeof(Quote), StatusCodes.Status200OK)]
|
||||
|
||||
@@ -12,6 +12,12 @@ namespace server.Models
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The character's full name.
|
||||
/// </summary>
|
||||
[BsonElement("fullName")]
|
||||
public string FullName => $"{FirstName} {LastName}";
|
||||
|
||||
/// <summary>
|
||||
/// The character's first name.
|
||||
/// </summary>
|
||||
@@ -24,6 +30,12 @@ namespace server.Models
|
||||
[BsonElement("lastName")]
|
||||
public string? LastName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The full name of the actor who potrayed the character.
|
||||
/// </summary>
|
||||
[BsonElement("actorFullName")]
|
||||
public string ActorFullName => $"{ActorFirstName} {ActorLastName}";
|
||||
|
||||
/// <summary>
|
||||
/// The first name of the actor who potrayed the character.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace server.Models
|
||||
{
|
||||
public class CharacterFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to filter characters by their name.
|
||||
/// </summary>
|
||||
public string? Name { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Used to filter characters by the actor's name.
|
||||
/// </summary>
|
||||
public string? ActorName { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Used to filter characters by a season.
|
||||
/// </summary>
|
||||
public int? Season { get; set; } = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using server.Models;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Driver.Linq;
|
||||
|
||||
namespace server.Persistence.Repositories
|
||||
{
|
||||
public class CharacterRepository : ICharacterRepository
|
||||
{
|
||||
private readonly IDbContext _context;
|
||||
|
||||
public CharacterRepository(IDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<List<Character>> GetCharactersAsync(CharacterFilter filter)
|
||||
{
|
||||
try
|
||||
{
|
||||
var query = _context.Characters.AsQueryable();
|
||||
|
||||
if (filter?.Name != null)
|
||||
{
|
||||
query = query.Where(character => character.FullName.ToLower().Contains(filter.Name.ToLower()));
|
||||
}
|
||||
|
||||
if (filter?.ActorName != null)
|
||||
{
|
||||
query = query.Where(character => character.ActorFullName.ToLower().Contains(filter.ActorName.ToLower()));
|
||||
}
|
||||
|
||||
if (filter?.Season != null)
|
||||
{
|
||||
query = query.Where(character => character.Seasons.Any(season => season == filter.Season));
|
||||
}
|
||||
|
||||
return await query.ToListAsync();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Character> GetCharacterByIdAsync(string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _context.Characters.Find(character => character.Id == id).SingleOrDefaultAsync();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using server.Models;
|
||||
|
||||
namespace server.Persistence.Repositories
|
||||
{
|
||||
public interface ICharacterRepository
|
||||
{
|
||||
Task<List<Character>> GetCharactersAsync(CharacterFilter filter);
|
||||
Task<Character> GetCharacterByIdAsync(string id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
[
|
||||
{
|
||||
"FirstName": "Jason",
|
||||
"LastName": "Gideon",
|
||||
"ActorFirstName": "Mandy",
|
||||
"ActorLastName": "Patinkin",
|
||||
"Seasons": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
10,
|
||||
15
|
||||
],
|
||||
"FirstEpisode": "Extreme Aggressor",
|
||||
"LastEpisode": "In Name and Blood",
|
||||
"Image": "https://criminalmindsapi.stevanfreeborn.com/characters/jason-gideon.png"
|
||||
},
|
||||
{
|
||||
"FirstName": "Derek",
|
||||
"LastName": "Morgan",
|
||||
"ActorFirstName": "Shemar",
|
||||
"ActorLastName": "Moore",
|
||||
"Seasons": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14
|
||||
],
|
||||
"FirstEpisode": "Extreme Aggressor",
|
||||
"LastEpisode": "A Beautiful Disaster",
|
||||
"Image": "https://criminalmindsapi.stevanfreeborn.com/characters/derek-morgan.png"
|
||||
},
|
||||
{
|
||||
"FirstName": "Aaron",
|
||||
"LastName": "Hotchner",
|
||||
"ActorFirstName": "Thomas",
|
||||
"ActorLastName": "Gibson",
|
||||
"Seasons": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12
|
||||
],
|
||||
"FirstEpisode": "Extreme Aggressor",
|
||||
"LastEpisode": "Sick Day",
|
||||
"Image": "https://criminalmindsapi.stevanfreeborn.com/characters/aaron-hotchner.png"
|
||||
},
|
||||
{
|
||||
"FirstName": "Spencer",
|
||||
"LastName": "Reid",
|
||||
"ActorFirstName": "Matthew",
|
||||
"ActorLastName": "Gubler",
|
||||
"Seasons": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15
|
||||
],
|
||||
"FirstEpisode": "Extreme Aggressor",
|
||||
"LastEpisode": "And in the End",
|
||||
"Image": "https://criminalmindsapi.stevanfreeborn.com/characters/spencer-reid.png"
|
||||
},
|
||||
{
|
||||
"FirstName": "Max",
|
||||
"LastName": "Ryan",
|
||||
"ActorFirstName": "Geoff",
|
||||
"ActorLastName": "Pierson",
|
||||
"Seasons": [
|
||||
1
|
||||
],
|
||||
"FirstEpisode": "Unfinished Business",
|
||||
"LastEpisode": "Unfinished Business",
|
||||
"Image": "https://criminalmindsapi.stevanfreeborn.com/characters/max-ryan.png"
|
||||
},
|
||||
{
|
||||
"FirstName": "Elle",
|
||||
"LastName": "Greenaway",
|
||||
"ActorFirstName": "Lola",
|
||||
"ActorLastName": "Glaudini",
|
||||
"Seasons": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"FirstEpisode": "Extreme Aggressor",
|
||||
"LastEpisode": "The Boogeyman",
|
||||
"Image": "https://criminalmindsapi.stevanfreeborn.com/characters/elle-greenaway.png"
|
||||
},
|
||||
{
|
||||
"FirstName": "Jennifer",
|
||||
"LastName": "Jareau",
|
||||
"ActorFirstName": "A.J.",
|
||||
"ActorLastName": "Cook",
|
||||
"Seasons": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15
|
||||
],
|
||||
"FirstEpisode": "Complusion",
|
||||
"LastEpisode": "And in the End",
|
||||
"Image": "https://criminalmindsapi.stevanfreeborn.com/characters/jennifer-jareau.png"
|
||||
},
|
||||
{
|
||||
"FirstName": "Emily",
|
||||
"LastName": "Prentiss",
|
||||
"ActorFirstName": "Paget",
|
||||
"ActorLastName": "Brewster",
|
||||
"Seasons": [
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
9,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15
|
||||
],
|
||||
"FirstEpisode": "The Last Word",
|
||||
"LastEpisode": "And in the End",
|
||||
"Image": "https://criminalmindsapi.stevanfreeborn.com/characters/emily-prentiss.png"
|
||||
},
|
||||
{
|
||||
"FirstName": "David",
|
||||
"LastName": "Rossi",
|
||||
"ActorFirstName": "Joe",
|
||||
"ActorLastName": "Mantegna",
|
||||
"Seasons": [
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15
|
||||
],
|
||||
"FirstEpisode": "About Face",
|
||||
"LastEpisode": "And in the End",
|
||||
"Image": "https://criminalmindsapi.stevanfreeborn.com/characters/david-rossi.png"
|
||||
},
|
||||
{
|
||||
"FirstName": "Penelope",
|
||||
"LastName": "Garcia",
|
||||
"ActorFirstName": "Kirsten",
|
||||
"ActorLastName": "Vangsness",
|
||||
"Seasons": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15
|
||||
],
|
||||
"FirstEpisode": "Extreme Aggressor",
|
||||
"LastEpisode": "And in the End",
|
||||
"Image": "https://criminalmindsapi.stevanfreeborn.com/characters/penelope-garcia.png"
|
||||
},
|
||||
{
|
||||
"FirstName": "Ian",
|
||||
"LastName": "Doyle",
|
||||
"ActorFirstName": "Timothy",
|
||||
"ActorLastName": "Murphy",
|
||||
"Seasons": [
|
||||
6,
|
||||
7
|
||||
],
|
||||
"FirstEpisode": "The Thirteenth Step",
|
||||
"LastEpisode": "It Takes a Village",
|
||||
"Image": "https://criminalmindsapi.stevanfreeborn.com/characters/ian-doyle.png"
|
||||
},
|
||||
{
|
||||
"FirstName": "Ashley",
|
||||
"LastName": "Seaver",
|
||||
"ActorFirstName": "Rachel",
|
||||
"ActorLastName": "Nichols",
|
||||
"Seasons": [
|
||||
6
|
||||
],
|
||||
"FirstEpisode": "What Happens at Home",
|
||||
"LastEpisode": "Supply & Demand",
|
||||
"Image": "https://criminalmindsapi.stevanfreeborn.com/characters/ashley-seaver.png"
|
||||
},
|
||||
{
|
||||
"FirstName": "Alex",
|
||||
"LastName": "Blake",
|
||||
"ActorFirstName": "Jeanne",
|
||||
"ActorLastName": "Tripplehorn",
|
||||
"Seasons": [
|
||||
8,
|
||||
9
|
||||
],
|
||||
"FirstEpisode": "The Silencer",
|
||||
"LastEpisode": "Demons",
|
||||
"Image": "https://criminalmindsapi.stevanfreeborn.com/characters/alex-blake.png"
|
||||
},
|
||||
{
|
||||
"FirstName": "Kate",
|
||||
"LastName": "Callahan",
|
||||
"ActorFirstName": "Jennifer",
|
||||
"ActorLastName": "Hewitt",
|
||||
"Seasons": [
|
||||
10
|
||||
],
|
||||
"FirstEpisode": "X",
|
||||
"LastEpisode": "The Hunt",
|
||||
"Image": "https://criminalmindsapi.stevanfreeborn.com/characters/kate-callahan.png"
|
||||
},
|
||||
{
|
||||
"FirstName": "Jack",
|
||||
"LastName": "Garrett",
|
||||
"ActorFirstName": "Gary",
|
||||
"ActorLastName": "Sinise",
|
||||
"Seasons": [
|
||||
10
|
||||
],
|
||||
"FirstEpisode": "Beyond Borders",
|
||||
"LastEpisode": "Beyond Borders",
|
||||
"Image": "https://criminalmindsapi.stevanfreeborn.com/characters/jack-garrett.png"
|
||||
},
|
||||
{
|
||||
"FirstName": "Tara",
|
||||
"LastName": "Lewis",
|
||||
"ActorFirstName": "Aisha",
|
||||
"ActorLastName": "Tyler",
|
||||
"Seasons": [
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15
|
||||
],
|
||||
"FirstEpisode": "The Job",
|
||||
"LastEpisode": "And in the End",
|
||||
"Image": "https://criminalmindsapi.stevanfreeborn.com/characters/tara-lewis.png"
|
||||
},
|
||||
{
|
||||
"FirstName": "Luke",
|
||||
"LastName": "Alvez",
|
||||
"ActorFirstName": "Adam",
|
||||
"ActorLastName": "Rodriguez",
|
||||
"Seasons": [
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15
|
||||
],
|
||||
"FirstEpisode": "The Crimson King",
|
||||
"LastEpisode": "And in the End",
|
||||
"Image": "https://criminalmindsapi.stevanfreeborn.com/characters/luke-alvez.png"
|
||||
},
|
||||
{
|
||||
"FirstName": "Stephen",
|
||||
"LastName": "Walker",
|
||||
"ActorFirstName": "Damon",
|
||||
"ActorLastName": "Gupton",
|
||||
"Seasons": [
|
||||
12,
|
||||
13
|
||||
],
|
||||
"FirstEpisode": "Scarecrow",
|
||||
"LastEpisode": "Wheels Up",
|
||||
"Image": "https://criminalmindsapi.stevanfreeborn.com/characters/stephen-walker.png"
|
||||
},
|
||||
{
|
||||
"FirstName": "Matthew",
|
||||
"LastName": "Simmons",
|
||||
"ActorFirstName": "Daniel",
|
||||
"ActorLastName": "Henney",
|
||||
"Seasons": [
|
||||
13,
|
||||
14,
|
||||
15
|
||||
],
|
||||
"FirstEpisode": "Beyond Borders",
|
||||
"LastEpisode": "And in the End",
|
||||
"Image": "https://criminalmindsapi.stevanfreeborn.com/characters/matthew-simmons.png"
|
||||
},
|
||||
{
|
||||
"FirstName": "Krystall",
|
||||
"LastName": "Richards",
|
||||
"ActorFirstName": "Gail",
|
||||
"ActorLastName": "O'Grady",
|
||||
"Seasons": [
|
||||
8,
|
||||
13,
|
||||
14,
|
||||
15
|
||||
],
|
||||
"FirstEpisode": "The Dance of Love",
|
||||
"LastEpisode": "And in the End",
|
||||
"Image": "https://criminalmindsapi.stevanfreeborn.com/characters/krystall-richards.png"
|
||||
}
|
||||
]
|
||||
@@ -9,6 +9,7 @@ namespace server.Persistence.Seed
|
||||
private readonly IMongoCollection<Season> _seasons;
|
||||
private readonly IMongoCollection<Episode> _episodes;
|
||||
private readonly IMongoCollection<Quote> _quotes;
|
||||
private readonly IMongoCollection<Character> _characters;
|
||||
|
||||
public Seeder()
|
||||
{
|
||||
@@ -23,6 +24,7 @@ namespace server.Persistence.Seed
|
||||
_seasons = database.GetCollection<Season>(config.GetSection("MongoDBSettings:SeasonsCollection").Value);
|
||||
_episodes = database.GetCollection<Episode>(config.GetSection("MongoDBSettings:EpisodesCollection").Value);
|
||||
_quotes = database.GetCollection<Quote>(config.GetSection("MongoDBSettings:QuotesCollection").Value);
|
||||
_characters = database.GetCollection<Character>(config.GetSection("MongoDBSettings:CharactersCollection").Value);
|
||||
}
|
||||
|
||||
public async Task SeedSeasonsAsync()
|
||||
@@ -37,7 +39,7 @@ namespace server.Persistence.Seed
|
||||
if(seasons != null)
|
||||
{
|
||||
await _seasons.InsertManyAsync(seasons);
|
||||
Console.WriteLine($"Seeded databases with seasons from {fileName}");
|
||||
Console.WriteLine($"Seeded databases with {nameof(seasons)} from {fileName}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,13 +55,13 @@ namespace server.Persistence.Seed
|
||||
if (episodes != null)
|
||||
{
|
||||
await _episodes.InsertManyAsync(episodes);
|
||||
Console.WriteLine($"Seeded databases with episodes from {fileName}");
|
||||
Console.WriteLine($"Seeded databases with {nameof(episodes)} from {fileName}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SeedQuotesAsync()
|
||||
{
|
||||
await _quotes.DeleteManyAsync(quotes => true);
|
||||
await _quotes.DeleteManyAsync(quote => true);
|
||||
|
||||
const string fileName = "quotes.json";
|
||||
var quotesFilePath = Path.Combine(AppContext.BaseDirectory, $"Persistence/Seed/Data/{fileName}");
|
||||
@@ -69,7 +71,24 @@ namespace server.Persistence.Seed
|
||||
if (quotes != null)
|
||||
{
|
||||
await _quotes.InsertManyAsync(quotes);
|
||||
Console.WriteLine($"Seeded databases with quotes from {fileName}");
|
||||
Console.WriteLine($"Seeded databases with {nameof(quotes)} from {fileName}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task SeedCharactersAsync()
|
||||
{
|
||||
await _characters.DeleteManyAsync(character => true);
|
||||
|
||||
const string fileName = "characters.json";
|
||||
var charactersFilePath = Path.Combine(AppContext.BaseDirectory, $"Persistence/Seed/Data/{fileName}");
|
||||
var charactersJson = await File.ReadAllTextAsync(charactersFilePath);
|
||||
var characters = JsonSerializer.Deserialize<List<Character>>(charactersJson);
|
||||
|
||||
if (characters != null)
|
||||
{
|
||||
await _characters.InsertManyAsync(characters);
|
||||
Console.WriteLine($"Seeded databases with {nameof(characters)} from {fileName}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+18
-115
@@ -1,125 +1,28 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
||||
using Microsoft.AspNetCore.Mvc.Versioning;
|
||||
using Microsoft.Extensions.Options;
|
||||
using server.Filters;
|
||||
using server.Options;
|
||||
using server.Persistence;
|
||||
using server.Persistence.Repositories;
|
||||
using server.Persistence.Seed;
|
||||
using System.Reflection;
|
||||
using AspNetCoreRateLimit;
|
||||
using server.Setup;
|
||||
|
||||
if (args.Length == 2 && args[0].ToLower() == "seed")
|
||||
if (args.Length > 0 && args[0].ToLower() == "seed")
|
||||
{
|
||||
var seeder = new Seeder();
|
||||
|
||||
if (args[1].ToLower() == "seasons")
|
||||
{
|
||||
await seeder.SeedSeasonsAsync();
|
||||
}
|
||||
if (args.Contains("seasons")) await seeder.SeedSeasonsAsync();
|
||||
|
||||
if (args[1].ToLower() == "episodes")
|
||||
{
|
||||
await seeder.SeedEpisodesAsync();
|
||||
}
|
||||
if (args.Contains("episodes")) await seeder.SeedEpisodesAsync();
|
||||
|
||||
if (args[1].ToLower() == "quotes")
|
||||
{
|
||||
await seeder.SeedQuotesAsync();
|
||||
}
|
||||
if (args.Contains("quotes")) await seeder.SeedQuotesAsync();
|
||||
|
||||
if (args.Contains("characters")) await seeder.SeedCharactersAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
var app = WebApplication
|
||||
.CreateBuilder(args)
|
||||
.SetupDb()
|
||||
.SetupMvC()
|
||||
.SetupSwagger()
|
||||
.Build();
|
||||
|
||||
app.SetupMiddleware().Run();
|
||||
}
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddMemoryCache();
|
||||
|
||||
builder.Services.Configure<IpRateLimitOptions>(
|
||||
builder.Configuration.GetSection("IpRateLimiting"));
|
||||
|
||||
builder.Services.Configure<IpRateLimitPolicies>(
|
||||
builder.Configuration.GetSection("IpRateLimitPolicies"));
|
||||
|
||||
builder.Services.AddInMemoryRateLimiting();
|
||||
|
||||
builder.Services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
|
||||
|
||||
builder.Services.Configure<DatabaseSettings>(
|
||||
builder.Configuration.GetSection("MongoDBSettings"));
|
||||
|
||||
builder.Services.AddSingleton<IDatabaseSettings>(sp =>
|
||||
sp.GetRequiredService<IOptions<DatabaseSettings>>().Value);
|
||||
|
||||
builder.Services.AddSingleton<IDbContext, DbContext>();
|
||||
|
||||
builder.Services.AddScoped<ISeasonRepository, SeasonRepository>();
|
||||
|
||||
builder.Services.AddScoped<IEpisodeRepository, EpisodeRepository>();
|
||||
|
||||
builder.Services.AddScoped<IQuoteRepository, QuoteRepository>();
|
||||
|
||||
builder.Services.AddControllers();
|
||||
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
|
||||
builder.Services.AddSwaggerGen(options =>
|
||||
{
|
||||
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
|
||||
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
|
||||
|
||||
options.IncludeXmlComments(xmlPath);
|
||||
options.OperationFilter<ApiVersionOperationFilter>();
|
||||
});
|
||||
|
||||
builder.Services.ConfigureOptions<ConfigureSwaggerOptions>();
|
||||
|
||||
builder.Services.AddApiVersioning(config =>
|
||||
{
|
||||
config.DefaultApiVersion = new ApiVersion(1, 0);
|
||||
config.AssumeDefaultVersionWhenUnspecified = true;
|
||||
config.ReportApiVersions = true;
|
||||
config.ApiVersionReader = new HeaderApiVersionReader("x-api-version");
|
||||
});
|
||||
|
||||
builder.Services.AddVersionedApiExplorer(config =>
|
||||
{
|
||||
config.GroupNameFormat = "'v'VVV";
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.UseSwagger(options =>
|
||||
{
|
||||
options.RouteTemplate = "/{documentName}/docs.json";
|
||||
});
|
||||
|
||||
app.UseSwaggerUI(options =>
|
||||
{
|
||||
var provider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>();
|
||||
|
||||
foreach (var description in provider.ApiVersionDescriptions)
|
||||
{
|
||||
var url = $"/{description.GroupName}/docs.json";
|
||||
var name = $"criminalmindsapi v{description.ApiVersion}";
|
||||
|
||||
options.RoutePrefix = String.Empty;
|
||||
options.SwaggerEndpoint(url, name);
|
||||
options.EnableTryItOutByDefault();
|
||||
options.DisplayRequestDuration();
|
||||
options.DocumentTitle = "criminalmindsapi";
|
||||
}
|
||||
});
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.UseIpRateLimiting();
|
||||
|
||||
app.Run();
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@
|
||||
},
|
||||
"resourceName": {
|
||||
"type": "string",
|
||||
"defaultValue": "criminalminds-server",
|
||||
"defaultValue": "criminalmindsapi",
|
||||
"metadata": {
|
||||
"description": "Name of the main resource to be created by this template."
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using AspNetCoreRateLimit;
|
||||
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
||||
|
||||
namespace server.Setup
|
||||
{
|
||||
public static class Middleware
|
||||
{
|
||||
public static WebApplication SetupMiddleware(this WebApplication app)
|
||||
{
|
||||
app.UseSwagger(options =>
|
||||
{
|
||||
options.RouteTemplate = "/{documentName}/docs.json";
|
||||
});
|
||||
|
||||
app.UseSwaggerUI(options =>
|
||||
{
|
||||
var provider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>();
|
||||
|
||||
foreach (var description in provider.ApiVersionDescriptions)
|
||||
{
|
||||
var url = $"/{description.GroupName}/docs.json";
|
||||
var name = $"criminalmindsapi v{description.ApiVersion}";
|
||||
|
||||
options.RoutePrefix = String.Empty;
|
||||
options.SwaggerEndpoint(url, name);
|
||||
options.EnableTryItOutByDefault();
|
||||
options.DisplayRequestDuration();
|
||||
options.DocumentTitle = "criminalmindsapi";
|
||||
}
|
||||
});
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.UseIpRateLimiting();
|
||||
|
||||
return app;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using AspNetCoreRateLimit;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Versioning;
|
||||
using Microsoft.Extensions.Options;
|
||||
using server.Persistence;
|
||||
using server.Persistence.Repositories;
|
||||
using server.SwaggerOptions;
|
||||
using server.SwaggerOptions.Filters;
|
||||
using System.Reflection;
|
||||
|
||||
namespace server.Setup
|
||||
{
|
||||
public static class Services
|
||||
{
|
||||
public static WebApplicationBuilder SetupDb(this WebApplicationBuilder builder)
|
||||
{
|
||||
builder.Services.Configure<DatabaseSettings>(
|
||||
builder.Configuration.GetSection("MongoDBSettings"));
|
||||
|
||||
builder.Services.AddSingleton<IDatabaseSettings>(sp =>
|
||||
sp.GetRequiredService<IOptions<DatabaseSettings>>().Value);
|
||||
|
||||
builder.Services.AddSingleton<IDbContext, DbContext>();
|
||||
|
||||
builder.Services.AddScoped<ISeasonRepository, SeasonRepository>();
|
||||
|
||||
builder.Services.AddScoped<IEpisodeRepository, EpisodeRepository>();
|
||||
|
||||
builder.Services.AddScoped<IQuoteRepository, QuoteRepository>();
|
||||
|
||||
builder.Services.AddScoped<ICharacterRepository, CharacterRepository>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static WebApplicationBuilder SetupMvC(this WebApplicationBuilder builder)
|
||||
{
|
||||
builder.Services.AddMemoryCache();
|
||||
|
||||
builder.Services.Configure<IpRateLimitOptions>(
|
||||
builder.Configuration.GetSection("IpRateLimiting"));
|
||||
|
||||
builder.Services.Configure<IpRateLimitPolicies>(
|
||||
builder.Configuration.GetSection("IpRateLimitPolicies"));
|
||||
|
||||
builder.Services.AddInMemoryRateLimiting();
|
||||
|
||||
builder.Services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
|
||||
|
||||
builder.Services.AddControllers();
|
||||
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
|
||||
builder.Services.AddApiVersioning(config =>
|
||||
{
|
||||
config.DefaultApiVersion = new ApiVersion(1, 0);
|
||||
config.AssumeDefaultVersionWhenUnspecified = true;
|
||||
config.ReportApiVersions = true;
|
||||
config.ApiVersionReader = new HeaderApiVersionReader("x-api-version");
|
||||
});
|
||||
|
||||
builder.Services.AddVersionedApiExplorer(config =>
|
||||
{
|
||||
config.GroupNameFormat = "'v'VVV";
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static WebApplicationBuilder SetupSwagger(this WebApplicationBuilder builder)
|
||||
{
|
||||
builder.Services.AddSwaggerGen(options =>
|
||||
{
|
||||
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
|
||||
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
|
||||
|
||||
options.IncludeXmlComments(xmlPath);
|
||||
options.OperationFilter<ApiVersionOperationFilter>();
|
||||
});
|
||||
|
||||
builder.Services.ConfigureOptions<ConfigureSwaggerOptions>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -3,7 +3,7 @@ using Microsoft.Extensions.Options;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
|
||||
namespace server.Options
|
||||
namespace server.SwaggerOptions
|
||||
{
|
||||
public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions>
|
||||
{
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
|
||||
namespace server.Filters
|
||||
namespace server.SwaggerOptions.Filters
|
||||
{
|
||||
public class ApiVersionOperationFilter : IOperationFilter
|
||||
{
|
||||
Reference in New Issue
Block a user