@@ -5,39 +5,10 @@
|
|||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<meta
|
|
||||||
name="description"
|
|
||||||
content="Web site created using create-react-app"
|
|
||||||
/>
|
|
||||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
|
||||||
<!--
|
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
|
||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
|
||||||
-->
|
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
|
||||||
<!--
|
|
||||||
Notice the use of %PUBLIC_URL% in the tags above.
|
|
||||||
It will be replaced with the URL of the `public` folder during the build.
|
|
||||||
Only files inside the `public` folder can be referenced from the HTML.
|
|
||||||
|
|
||||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
|
||||||
work correctly both with client-side routing and a non-root public URL.
|
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
|
||||||
-->
|
|
||||||
<title>React App</title>
|
<title>React App</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<!--
|
|
||||||
This HTML file is a template.
|
|
||||||
If you open it directly in the browser, you will see an empty page.
|
|
||||||
|
|
||||||
You can add webfonts, meta tags, or analytics to this file.
|
|
||||||
The build step will place the bundled scripts into the <body> tag.
|
|
||||||
|
|
||||||
To begin the development, run `npm start` or `yarn start`.
|
|
||||||
To create a production bundle, use `npm run build` or `yarn build`.
|
|
||||||
-->
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+8
-1
@@ -51,7 +51,14 @@ export default class App extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async populateWeatherData() {
|
async populateWeatherData() {
|
||||||
const response = await fetch('https://criminalminds-server.azurewebsites.net/weatherforecast');
|
|
||||||
|
const url = '/api/seasons';
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV == 'production') {
|
||||||
|
url = 'https://criminalminds-server.azurewebsites.net' + url;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(url);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
this.setState({ forecasts: data, loading: false });
|
this.setState({ forecasts: data, loading: false });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const { createProxyMiddleware } = require('http-proxy-middleware');
|
const { createProxyMiddleware } = require('http-proxy-middleware');
|
||||||
|
|
||||||
const context = [
|
const context = [
|
||||||
"/weatherforecast",
|
'/api/seasons'
|
||||||
];
|
];
|
||||||
|
|
||||||
module.exports = function (app) {
|
module.exports = function (app) {
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using server.Models;
|
||||||
|
using server.Persistence.Repositories;
|
||||||
|
|
||||||
|
namespace server.Controllers
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/seasons")]
|
||||||
|
[Produces("application/json")]
|
||||||
|
public class SeasonsController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ISeasonRepository _seasonRepository;
|
||||||
|
|
||||||
|
public SeasonsController(ISeasonRepository seasonRepository)
|
||||||
|
{
|
||||||
|
_seasonRepository = seasonRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[ProducesResponseType(typeof(List<Season>), 200)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<ActionResult<List<Season>>> GetSeasonsAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var seasons = await _seasonRepository.GetSeasonsAsync();
|
||||||
|
return Ok(seasons);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to get seasons");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{number:int}")]
|
||||||
|
[ProducesResponseType(typeof(Season), 200)]
|
||||||
|
[ProducesResponseType(typeof(ErrorResponse), 404)]
|
||||||
|
[ProducesResponseType(500)]
|
||||||
|
public async Task<ActionResult<Season>> GetSeasonByNumberAsync(int number)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var season = await _seasonRepository.GetSeasonByNumberAsync(number);
|
||||||
|
|
||||||
|
if (season == null) return NotFound(new ErrorResponse($"Could not find season number {number}"));
|
||||||
|
|
||||||
|
return Ok(season);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to get season");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace server.Controllers
|
|
||||||
{
|
|
||||||
[ApiController]
|
|
||||||
[Route("[controller]")]
|
|
||||||
public class WeatherForecastController : ControllerBase
|
|
||||||
{
|
|
||||||
private static readonly string[] Summaries = new[]
|
|
||||||
{
|
|
||||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly ILogger<WeatherForecastController> _logger;
|
|
||||||
|
|
||||||
public WeatherForecastController(ILogger<WeatherForecastController> logger)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet(Name = "GetWeatherForecast")]
|
|
||||||
public IEnumerable<WeatherForecast> Get()
|
|
||||||
{
|
|
||||||
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
|
|
||||||
{
|
|
||||||
Date = DateTime.Now.AddDays(index),
|
|
||||||
TemperatureC = Random.Shared.Next(-20, 55),
|
|
||||||
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
|
|
||||||
})
|
|
||||||
.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using MongoDB.Bson;
|
||||||
|
using MongoDB.Bson.Serialization.Attributes;
|
||||||
|
|
||||||
|
namespace server.Models
|
||||||
|
{
|
||||||
|
public class Character
|
||||||
|
{
|
||||||
|
[BsonId]
|
||||||
|
[BsonRepresentation(BsonType.ObjectId)]
|
||||||
|
public string? Id { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("firstName")]
|
||||||
|
public string? FirstName { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("lastName")]
|
||||||
|
public string? LastName { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("actorFirstName")]
|
||||||
|
public string? ActorFirstName { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("actorLastName")]
|
||||||
|
public string? ActorLastName { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("seasons")]
|
||||||
|
public int[]? Seasons { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("firstEpisode")]
|
||||||
|
public string? FirstEpisode { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("lastEpisode")]
|
||||||
|
public string? LastEpisode { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("image")]
|
||||||
|
public string? Image { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using MongoDB.Bson;
|
||||||
|
using MongoDB.Bson.Serialization.Attributes;
|
||||||
|
|
||||||
|
namespace server.Models
|
||||||
|
{
|
||||||
|
public class Episode
|
||||||
|
{
|
||||||
|
[BsonId]
|
||||||
|
[BsonRepresentation(BsonType.ObjectId)]
|
||||||
|
public string? Id { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("season")]
|
||||||
|
public int? Season { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("numberInSeries")]
|
||||||
|
public int? NumberInSeries { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("numberInSeason")]
|
||||||
|
public int? NumberInSeason { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("title")]
|
||||||
|
public string? Title { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("summary")]
|
||||||
|
public string? Summary { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("directedBy")]
|
||||||
|
public string? DirectedBy { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("writtenBy")]
|
||||||
|
public string[]? WrittenBy { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("airDate")]
|
||||||
|
public DateTime? AirDate { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("usViewersInMillions")]
|
||||||
|
public double? UsViewersInMillions { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace server.Models
|
||||||
|
{
|
||||||
|
public class ErrorResponse
|
||||||
|
{
|
||||||
|
public string Error { get; set; }
|
||||||
|
|
||||||
|
public ErrorResponse(string error)
|
||||||
|
{
|
||||||
|
Error = error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using MongoDB.Bson;
|
||||||
|
using MongoDB.Bson.Serialization.Attributes;
|
||||||
|
|
||||||
|
namespace server.Models
|
||||||
|
{
|
||||||
|
public class Quote
|
||||||
|
{
|
||||||
|
[BsonId]
|
||||||
|
[BsonRepresentation(BsonType.ObjectId)]
|
||||||
|
public string? Id { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("season")]
|
||||||
|
public int? Season { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("episode")]
|
||||||
|
public int Episode { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("text")]
|
||||||
|
public string? Text { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("source")]
|
||||||
|
public string? Source { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("narrator")]
|
||||||
|
public string? Narrator { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using MongoDB.Bson;
|
||||||
|
using MongoDB.Bson.Serialization.Attributes;
|
||||||
|
|
||||||
|
namespace server.Models
|
||||||
|
{
|
||||||
|
public class Season
|
||||||
|
{
|
||||||
|
[BsonId]
|
||||||
|
[BsonRepresentation(BsonType.ObjectId)]
|
||||||
|
public string? Id { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("seasonNumber")]
|
||||||
|
public int? SeasonNumber { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("numberOfEpisodes")]
|
||||||
|
public int? NumberOfEpisodes { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("dateFirstAired")]
|
||||||
|
public DateTime? DateFirstAired { get; set; }
|
||||||
|
|
||||||
|
[BsonElement("dateLastAired")]
|
||||||
|
public DateTime? DateLastAired { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace server.Persistence
|
||||||
|
{
|
||||||
|
public class DatabaseSettings : IDatabaseSettings
|
||||||
|
{
|
||||||
|
public string? ConnectionString { get; set; }
|
||||||
|
public string? DatabaseName { get; set; }
|
||||||
|
public string? SeasonsCollection { get; set; }
|
||||||
|
public string? EpisodesCollection { get; set; }
|
||||||
|
public string? CharactersCollection { get; set; }
|
||||||
|
public string? QuotesCollection { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.OpenApi.Writers;
|
||||||
|
using MongoDB.Driver;
|
||||||
|
using server.Models;
|
||||||
|
|
||||||
|
namespace server.Persistence
|
||||||
|
{
|
||||||
|
public class DbContext : IDbContext
|
||||||
|
{
|
||||||
|
public IMongoCollection<Season> Seasons { get; set; }
|
||||||
|
|
||||||
|
public IMongoCollection<Episode> Episodes { get; set; }
|
||||||
|
|
||||||
|
public IMongoCollection<Character> Characters { get; set; }
|
||||||
|
|
||||||
|
public IMongoCollection<Quote> Quotes { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public DbContext(IDatabaseSettings settings)
|
||||||
|
{
|
||||||
|
IMongoClient client = new MongoClient(settings.ConnectionString);
|
||||||
|
IMongoDatabase database = client.GetDatabase(settings.DatabaseName);
|
||||||
|
|
||||||
|
Seasons = database.GetCollection<Season>(settings.SeasonsCollection);
|
||||||
|
Episodes = database.GetCollection<Episode>(settings.EpisodesCollection);
|
||||||
|
Characters = database.GetCollection<Character>(settings.CharactersCollection);
|
||||||
|
Quotes = database.GetCollection<Quote>(settings.QuotesCollection);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace server.Persistence
|
||||||
|
{
|
||||||
|
public interface IDatabaseSettings
|
||||||
|
{
|
||||||
|
string? ConnectionString { get; set; }
|
||||||
|
string? DatabaseName { get; set; }
|
||||||
|
string? SeasonsCollection { get; set; }
|
||||||
|
string? EpisodesCollection { get; set; }
|
||||||
|
string? CharactersCollection { get; set; }
|
||||||
|
string? QuotesCollection { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MongoDB.Driver;
|
||||||
|
using server.Models;
|
||||||
|
|
||||||
|
namespace server.Persistence
|
||||||
|
{
|
||||||
|
public interface IDbContext
|
||||||
|
{
|
||||||
|
public IMongoCollection<Season> Seasons { get; set; }
|
||||||
|
|
||||||
|
public IMongoCollection<Episode> Episodes { get; set; }
|
||||||
|
|
||||||
|
public IMongoCollection<Character> Characters { get; set; }
|
||||||
|
|
||||||
|
public IMongoCollection<Quote> Quotes { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using server.Models;
|
||||||
|
|
||||||
|
namespace server.Persistence.Repositories
|
||||||
|
{
|
||||||
|
public interface ISeasonRepository
|
||||||
|
{
|
||||||
|
Task<List<Season>> GetSeasonsAsync();
|
||||||
|
Task<Season> GetSeasonByNumberAsync(int number);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
using MongoDB.Driver;
|
||||||
|
using server.Models;
|
||||||
|
|
||||||
|
namespace server.Persistence.Repositories
|
||||||
|
{
|
||||||
|
public class SeasonRepository : ISeasonRepository
|
||||||
|
{
|
||||||
|
private readonly IDbContext _context;
|
||||||
|
|
||||||
|
public SeasonRepository(IDbContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<Season>> GetSeasonsAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await _context.Seasons.Find(season => true).ToListAsync();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Season> GetSeasonByNumberAsync(int number)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await _context.Seasons.Find(season => season.SeasonNumber == number).SingleOrDefaultAsync();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+16
-3
@@ -1,5 +1,20 @@
|
|||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using MongoDB.Driver;
|
||||||
|
using server.Persistence;
|
||||||
|
using server.Persistence.Repositories;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
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.AddControllers();
|
builder.Services.AddControllers();
|
||||||
|
|
||||||
builder.Services.AddEndpointsApiExplorer();
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
@@ -7,11 +22,9 @@ builder.Services.AddSwaggerGen();
|
|||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
if (app.Environment.IsDevelopment())
|
|
||||||
{
|
|
||||||
app.UseSwagger();
|
app.UseSwagger();
|
||||||
|
|
||||||
app.UseSwaggerUI();
|
app.UseSwaggerUI();
|
||||||
}
|
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
namespace server
|
|
||||||
{
|
|
||||||
public class WeatherForecast
|
|
||||||
{
|
|
||||||
public DateTime Date { get; set; }
|
|
||||||
|
|
||||||
public int TemperatureC { get; set; }
|
|
||||||
|
|
||||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
|
||||||
|
|
||||||
public string? Summary { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
@@ -7,7 +7,8 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
|
<PackageReference Include="MongoDB.Driver" Version="2.16.0" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Reference in New Issue
Block a user