Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c72ffc613 | ||
|
|
ace3d0bd7e | ||
|
|
85025970ba | ||
|
|
2685e66d17 |
@@ -8,10 +8,10 @@ on:
|
|||||||
types: [closed]
|
types: [closed]
|
||||||
branches:
|
branches:
|
||||||
- production
|
- production
|
||||||
workflow_dispatch:
|
|
||||||
jobs:
|
jobs:
|
||||||
build_and_deploy_job:
|
build_and_deploy_job:
|
||||||
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') || github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Build and Deploy Job
|
name: Build and Deploy Job
|
||||||
steps:
|
steps:
|
||||||
@@ -43,4 +43,3 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_ZEALOUS_CLIFF_09A0A7B10 }}
|
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_ZEALOUS_CLIFF_09A0A7B10 }}
|
||||||
action: "close"
|
action: "close"
|
||||||
app_location: "/client"
|
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
name: Build and deploy ASP.Net Core app to Azure Web App - criminalmindsapi
|
|
||||||
env:
|
|
||||||
WORKING_DIRECTORY: ./server
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- production
|
|
||||||
workflow_dispatch:
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: windows-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Set up .NET Core
|
|
||||||
uses: actions/setup-dotnet@v4
|
|
||||||
with:
|
|
||||||
dotnet-version: '8.0.x'
|
|
||||||
- name: Build with dotnet
|
|
||||||
run: dotnet build --configuration Release
|
|
||||||
working-directory: ${{ env.WORKING_DIRECTORY }}
|
|
||||||
- name: dotnet publish
|
|
||||||
run: dotnet publish -c Release -o release
|
|
||||||
working-directory: ${{ env.WORKING_DIRECTORY }}
|
|
||||||
- name: Upload artifact for deployment job
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: server
|
|
||||||
path: ./server/release
|
|
||||||
deploy:
|
|
||||||
runs-on: windows-latest
|
|
||||||
needs: build
|
|
||||||
environment:
|
|
||||||
name: 'production'
|
|
||||||
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
|
|
||||||
permissions:
|
|
||||||
id-token: write
|
|
||||||
steps:
|
|
||||||
- name: Download artifact from build job
|
|
||||||
uses: actions/download-artifact@v4.1.7
|
|
||||||
with:
|
|
||||||
name: server
|
|
||||||
- name: Login to Azure
|
|
||||||
uses: azure/login@v1
|
|
||||||
with:
|
|
||||||
client-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID_936FD4F727B94B4CA24C582D0863DF06 }}
|
|
||||||
tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID_0FB05F3F513C480395D15114ABAF36E0 }}
|
|
||||||
subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_D1C0B88C12EF49D0BEEB5F2ACE6EC4FC }}
|
|
||||||
- name: Deploy to Azure Web App
|
|
||||||
id: deploy-to-webapp
|
|
||||||
uses: azure/webapps-deploy@v2
|
|
||||||
with:
|
|
||||||
app-name: 'criminalmindsapi'
|
|
||||||
slot-name: 'production'
|
|
||||||
package: .
|
|
||||||
|
|
||||||
Generated
+3801
-6161
File diff suppressed because it is too large
Load Diff
+4
-8
@@ -12,9 +12,7 @@
|
|||||||
"@testing-library/react": "^13.3.0",
|
"@testing-library/react": "^13.3.0",
|
||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
"bootstrap": "^5.1.3",
|
"bootstrap": "^5.1.3",
|
||||||
"http-proxy-middleware": "^2.0.7",
|
"http-proxy-middleware": "^2.0.6",
|
||||||
"loader-utils": ">=2.0.3",
|
|
||||||
"minimatch": ">=3.0.5",
|
|
||||||
"nth-check": ">=2.0.1",
|
"nth-check": ">=2.0.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-bootstrap": "^2.4.0",
|
"react-bootstrap": "^2.4.0",
|
||||||
@@ -23,9 +21,9 @@
|
|||||||
"react-loading-icons": "^1.1.0",
|
"react-loading-icons": "^1.1.0",
|
||||||
"react-router": "^6.3.0",
|
"react-router": "^6.3.0",
|
||||||
"react-router-dom": "^6.3.0",
|
"react-router-dom": "^6.3.0",
|
||||||
"react-scripts": "^5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"terser": ">=5.14.2",
|
"web-vitals": "^2.1.4",
|
||||||
"web-vitals": "^2.1.4"
|
"terser": ">=5.14.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "set HTTPS=true&&react-scripts start",
|
"start": "set HTTPS=true&&react-scripts start",
|
||||||
@@ -56,8 +54,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint-config-react-app": "^7.0.1",
|
"eslint-config-react-app": "^7.0.1",
|
||||||
"jest-editor-support": "^30.1.0",
|
"jest-editor-support": "^30.1.0",
|
||||||
"loader-utils": ">=2.0.3",
|
|
||||||
"minimatch": ">=3.0.5",
|
|
||||||
"terser": ">=5.14.2"
|
"terser": ">=5.14.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default function DataButton(props) {
|
||||||
|
|
||||||
|
const label = props.label;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
value={label.toLowerCase()}
|
||||||
|
onClick={(e) => props.handleClick(e.target.value)}
|
||||||
|
>
|
||||||
|
{props.label}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Container from 'react-bootstrap/Container';
|
||||||
|
import Row from 'react-bootstrap/Row';
|
||||||
|
|
||||||
|
export default function DataButtonHolder(props) {
|
||||||
|
return(
|
||||||
|
<Container fluid>
|
||||||
|
|
||||||
|
<Row className="d-flex flex-row justify-content-center align-items-center">
|
||||||
|
|
||||||
|
{props.children}
|
||||||
|
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Card from 'react-bootstrap/Card';
|
||||||
|
|
||||||
|
export default function QuoteCard(props) {
|
||||||
|
|
||||||
|
const text = props.quote.text;
|
||||||
|
const source = props.quote.source
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card style={{ width: '22rem' }} className='p-1 m-3'>
|
||||||
|
|
||||||
|
<Card.Body>
|
||||||
|
|
||||||
|
<Card.Text>{text}</Card.Text>
|
||||||
|
<Card.Text>{source}</Card.Text>
|
||||||
|
|
||||||
|
</Card.Body>
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
+84
-15
@@ -5,40 +5,107 @@ import CardHolder from '../components/cardHolder';
|
|||||||
import CharacterCard from '../components/characterCard';
|
import CharacterCard from '../components/characterCard';
|
||||||
import Loading from '../components/loading';
|
import Loading from '../components/loading';
|
||||||
import LoadingError from '../components/loadingError';
|
import LoadingError from '../components/loadingError';
|
||||||
|
import DataButtonHolder from '../components/dataButtonHolder';
|
||||||
|
import DataButton from '../components/dataButton';
|
||||||
|
|
||||||
import CharacterService from '../services/characterService';
|
import CharacterService from '../services/characterService';
|
||||||
|
import QuoteService from '../services/quoteService';
|
||||||
|
import QuoteCard from '../components/quoteCard';
|
||||||
const characterService = new CharacterService();
|
const characterService = new CharacterService();
|
||||||
|
const quoteService = new QuoteService();
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
|
|
||||||
const [characters, setCharacters] = useState([]);
|
const [currentData, setCurrentData] = useState('characters');
|
||||||
|
const [data, setData] = useState([]);
|
||||||
const [errorMessage, setErrorMessage] = useState(null);
|
const [errorMessage, setErrorMessage] = useState(null);
|
||||||
|
|
||||||
const getCharacters = async () => {
|
const getCharacters = async () => {
|
||||||
|
|
||||||
|
setData([]);
|
||||||
|
setCurrentData('characters');
|
||||||
|
|
||||||
const res = await characterService.getCharacters();
|
const res = await characterService.getCharacters();
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
|
|
||||||
return setErrorMessage('Failed to load characters.');
|
return setErrorMessage(`Failed to load characters`);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const characters = await res.json();
|
const characters = await res.json();
|
||||||
|
|
||||||
return setCharacters(characters);
|
return setData(characters);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getQuotes = async () => {
|
||||||
|
|
||||||
|
setData([]);
|
||||||
|
setCurrentData('quotes');
|
||||||
|
|
||||||
|
const res = await quoteService.getQuotes();
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
|
||||||
|
return setErrorMessage(`Failed to load quotes`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const quotes = await res.json();
|
||||||
|
|
||||||
|
return setData(quotes.slice(0,20));
|
||||||
|
}
|
||||||
|
|
||||||
|
const getEpisodes = () => {console.log('episodes')}
|
||||||
|
|
||||||
|
const getSeasons = () => {console.log('seasons')}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
getCharacters();
|
getCharacters();
|
||||||
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getCharacterCards = () => {
|
const models = [{ id: 1, name: 'Characters', handleClick: getCharacters},
|
||||||
|
{ id: 2, name: 'Quotes', handleClick: getQuotes },
|
||||||
|
{ id: 3, name: 'Episodes', handleClick: getEpisodes },
|
||||||
|
{ id: 4, name: 'Seasons', handleClick: getSeasons }];
|
||||||
|
|
||||||
return characters.map(character => {
|
const getDataButtons = () => {
|
||||||
|
|
||||||
|
return models.map(model => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DataButton
|
||||||
|
label={model.name}
|
||||||
|
handleClick={model.handleClick}
|
||||||
|
key={model.id}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCards = () => {
|
||||||
|
|
||||||
|
if (currentData === 'quotes') {
|
||||||
|
|
||||||
|
return data.map(quote => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
|
||||||
|
<QuoteCard
|
||||||
|
quote={quote}
|
||||||
|
key={quote.id}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.map(character => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
@@ -57,30 +124,32 @@ export default function Home() {
|
|||||||
|
|
||||||
setErrorMessage(null);
|
setErrorMessage(null);
|
||||||
|
|
||||||
if (errorMessage === 'Failed to load characters.') return await getCharacters();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Greeting />
|
<Greeting />
|
||||||
|
|
||||||
{characters.length > 0 ?
|
<DataButtonHolder>
|
||||||
|
{getDataButtons()}
|
||||||
|
</DataButtonHolder>
|
||||||
|
|
||||||
|
{data.length > 0 ?
|
||||||
|
|
||||||
<CardHolder>
|
<CardHolder>
|
||||||
|
|
||||||
{getCharacterCards()}
|
{getCards()}
|
||||||
|
|
||||||
</CardHolder>
|
</CardHolder>
|
||||||
|
|
||||||
: errorMessage ?
|
: errorMessage ?
|
||||||
|
|
||||||
<LoadingError
|
<LoadingError
|
||||||
message={errorMessage}
|
message={errorMessage}
|
||||||
handleClick={handleTryAgain}
|
handleClick={handleTryAgain}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
: <Loading />}
|
: <Loading />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { baseUrl, versionHeader, versionHeaderValue } from '../constants';
|
||||||
|
|
||||||
|
class QuoteService {
|
||||||
|
|
||||||
|
getQuotes = async () => {
|
||||||
|
|
||||||
|
const res = await fetch(`${baseUrl}/api/quotes`, {
|
||||||
|
headers: {
|
||||||
|
[versionHeader]: versionHeaderValue
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return res;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default QuoteService;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
@@ -9,16 +9,15 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentAssertions" Version="6.12.1" />
|
<PackageReference Include="FluentAssertions" Version="6.7.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.6" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||||
<PackageReference Include="moq" Version="4.20.72" />
|
<PackageReference Include="xunit" Version="2.4.1" />
|
||||||
<PackageReference Include="xunit" Version="2.9.2" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
<PackageReference Include="coverlet.collector" Version="3.1.2">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|||||||
@@ -92,7 +92,6 @@ namespace server.tests.v1.UnitTests
|
|||||||
character.Should().BeOfType<Quote>();
|
character.Should().BeOfType<Quote>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetQuoteByIdAsync_ValidIdForNonExistentQuote_Returns404StatusCodeWithProblemDetails()
|
public async Task GetQuoteByIdAsync_ValidIdForNonExistentQuote_Returns404StatusCodeWithProblemDetails()
|
||||||
{
|
{
|
||||||
var quoteId = "62b7d5506c1b407771829926";
|
var quoteId = "62b7d5506c1b407771829926";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||||
@@ -12,11 +12,12 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
|
<PackageReference Include="AspNetCoreRateLimit" Version="4.0.2" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.1.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.1.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.0.0" />
|
||||||
<PackageReference Include="MongoDB.Driver" Version="2.29.0" />
|
<PackageReference Include="MongoDB.Driver" Version="2.16.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.8.1" />
|
<PackageReference Include="Moq" Version="4.18.1" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
Reference in New Issue
Block a user