├── Chapter01
├── .gitignore
├── WebApplication1
│ ├── .gitignore
│ ├── ClientApp
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── public
│ │ │ ├── favicon.ico
│ │ │ ├── index.html
│ │ │ └── manifest.json
│ │ └── src
│ │ │ ├── App.js
│ │ │ ├── App.test.js
│ │ │ ├── components
│ │ │ ├── Counter.js
│ │ │ ├── FetchData.js
│ │ │ ├── Home.js
│ │ │ ├── Layout.js
│ │ │ ├── NavMenu.css
│ │ │ └── NavMenu.js
│ │ │ ├── custom.css
│ │ │ ├── index.js
│ │ │ └── registerServiceWorker.js
│ ├── Controllers
│ │ └── WeatherForecastController.cs
│ ├── Pages
│ │ ├── Error.cshtml
│ │ ├── Error.cshtml.cs
│ │ └── _ViewImports.cshtml
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── Startup.cs
│ ├── WeatherForecast.cs
│ ├── WebApplication1.csproj
│ ├── WebApplication1.sln
│ ├── appsettings.Development.json
│ └── appsettings.json
└── readme.md
├── Chapter02
├── backend
│ ├── .gitignore
│ ├── Controllers
│ │ └── WeatherForecastController.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── QandA.csproj
│ ├── QandA.sln
│ ├── Startup.cs
│ ├── WeatherForecast.cs
│ ├── appsettings.Development.json
│ └── appsettings.json
├── frontend
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── .prettierrc
│ ├── .vscode
│ │ └── settings.json
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── src
│ │ ├── App.css
│ │ ├── App.test.tsx
│ │ ├── App.tsx
│ │ ├── index.css
│ │ ├── index.tsx
│ │ ├── logo.svg
│ │ ├── react-app-env.d.ts
│ │ └── serviceWorker.ts
│ ├── tsconfig.json
│ └── yarn.lock
└── readme.md
├── Chapter03
├── frontend
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── .prettierrc
│ ├── .vscode
│ │ └── settings.json
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── src
│ │ ├── App.test.tsx
│ │ ├── App.tsx
│ │ ├── Header.tsx
│ │ ├── HomePage.tsx
│ │ ├── Icons.tsx
│ │ ├── Page.tsx
│ │ ├── PageTitle.tsx
│ │ ├── Question.tsx
│ │ ├── QuestionList.tsx
│ │ ├── QuestionsData.ts
│ │ ├── Styles.ts
│ │ ├── index.css
│ │ ├── index.tsx
│ │ ├── logo.svg
│ │ ├── react-app-env.d.ts
│ │ ├── serviceWorker.ts
│ │ └── user.svg
│ ├── tsconfig.json
│ └── yarn.lock
└── readme.md
├── Chapter04
├── frontend
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── .prettierrc
│ ├── .vscode
│ │ └── settings.json
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── src
│ │ ├── Answer.tsx
│ │ ├── AnswerList.tsx
│ │ ├── App.test.tsx
│ │ ├── App.tsx
│ │ ├── AskPage.tsx
│ │ ├── Header.tsx
│ │ ├── HomePage.tsx
│ │ ├── Icons.tsx
│ │ ├── NotFoundPage.tsx
│ │ ├── Page.tsx
│ │ ├── PageTitle.tsx
│ │ ├── Question.tsx
│ │ ├── QuestionList.tsx
│ │ ├── QuestionPage.tsx
│ │ ├── QuestionsData.ts
│ │ ├── SearchPage.tsx
│ │ ├── SignInPage.tsx
│ │ ├── Styles.ts
│ │ ├── index.css
│ │ ├── index.tsx
│ │ ├── logo.svg
│ │ ├── react-app-env.d.ts
│ │ ├── serviceWorker.ts
│ │ └── user.svg
│ ├── tsconfig.json
│ └── yarn.lock
└── readme.md
├── Chapter05
├── frontend
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── .prettierrc
│ ├── .vscode
│ │ └── settings.json
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── src
│ │ ├── Answer.tsx
│ │ ├── AnswerList.tsx
│ │ ├── App.test.tsx
│ │ ├── App.tsx
│ │ ├── AskPage.tsx
│ │ ├── Field.tsx
│ │ ├── Form.tsx
│ │ ├── Header.tsx
│ │ ├── HomePage.tsx
│ │ ├── Icons.tsx
│ │ ├── NotFoundPage.tsx
│ │ ├── Page.tsx
│ │ ├── PageTitle.tsx
│ │ ├── Question.tsx
│ │ ├── QuestionList.tsx
│ │ ├── QuestionPage.tsx
│ │ ├── QuestionsData.ts
│ │ ├── SearchPage.tsx
│ │ ├── SignInPage.tsx
│ │ ├── Styles.ts
│ │ ├── index.css
│ │ ├── index.tsx
│ │ ├── logo.svg
│ │ ├── react-app-env.d.ts
│ │ ├── serviceWorker.ts
│ │ └── user.svg
│ ├── tsconfig.json
│ └── yarn.lock
└── readme.md
├── Chapter06
├── frontend
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── .prettierrc
│ ├── .vscode
│ │ └── settings.json
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── src
│ │ ├── Answer.tsx
│ │ ├── AnswerList.tsx
│ │ ├── App.test.tsx
│ │ ├── App.tsx
│ │ ├── AskPage.tsx
│ │ ├── Field.tsx
│ │ ├── Form.tsx
│ │ ├── Header.tsx
│ │ ├── HomePage.tsx
│ │ ├── Icons.tsx
│ │ ├── NotFoundPage.tsx
│ │ ├── Page.tsx
│ │ ├── PageTitle.tsx
│ │ ├── Question.tsx
│ │ ├── QuestionList.tsx
│ │ ├── QuestionPage.tsx
│ │ ├── QuestionsData.ts
│ │ ├── SearchPage.tsx
│ │ ├── SignInPage.tsx
│ │ ├── Store.ts
│ │ ├── Styles.ts
│ │ ├── index.css
│ │ ├── index.tsx
│ │ ├── logo.svg
│ │ ├── react-app-env.d.ts
│ │ ├── serviceWorker.ts
│ │ └── user.svg
│ ├── tsconfig.json
│ └── yarn.lock
└── readme.md
├── Chapter07
├── backend
│ ├── .gitignore
│ ├── Controllers
│ │ └── WeatherForecastController.cs
│ ├── Data
│ │ ├── DataRepository.cs
│ │ ├── IDataRepository.cs
│ │ └── Models
│ │ │ ├── AnswerGetResponse.cs
│ │ │ ├── AnswerPostRequest.cs
│ │ │ ├── QuestionGetManyResponse.cs
│ │ │ ├── QuestionGetSingleResponse.cs
│ │ │ ├── QuestionPostRequest.cs
│ │ │ └── QuestionPutRequest.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── QandA.csproj
│ ├── QandA.sln
│ ├── SQLScripts
│ │ ├── 01-Tables.sql
│ │ └── 02-Sprocs.sql
│ ├── Startup.cs
│ ├── WeatherForecast.cs
│ ├── appsettings.Development.json
│ └── appsettings.json
└── readme.md
├── Chapter08
├── backend
│ ├── .gitignore
│ ├── Controllers
│ │ ├── QuestionsController.cs
│ │ └── WeatherForecastController.cs
│ ├── Data
│ │ ├── DataRepository.cs
│ │ ├── IDataRepository.cs
│ │ └── Models
│ │ │ ├── AnswerGetResponse.cs
│ │ │ ├── AnswerPostFullRequest.cs
│ │ │ ├── AnswerPostRequest.cs
│ │ │ ├── QuestionGetManyResponse.cs
│ │ │ ├── QuestionGetSingleResponse.cs
│ │ │ ├── QuestionPostFullRequest.cs
│ │ │ ├── QuestionPostRequest.cs
│ │ │ └── QuestionPutRequest.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── QandA.csproj
│ ├── QandA.sln
│ ├── SQLScripts
│ │ ├── 01-Tables.sql
│ │ └── 02-Sprocs.sql
│ ├── Startup.cs
│ ├── WeatherForecast.cs
│ ├── appsettings.Development.json
│ └── appsettings.json
└── readme.md
├── Chapter09
├── backend
│ ├── .gitignore
│ ├── Controllers
│ │ ├── QuestionsController.cs
│ │ └── WeatherForecastController.cs
│ ├── Data
│ │ ├── DataRepository.cs
│ │ ├── IDataRepository.cs
│ │ └── Models
│ │ │ ├── AnswerGetResponse.cs
│ │ │ ├── AnswerPostFullRequest.cs
│ │ │ ├── AnswerPostRequest.cs
│ │ │ ├── QuestionGetManyResponse.cs
│ │ │ ├── QuestionGetSingleResponse.cs
│ │ │ ├── QuestionPostFullRequest.cs
│ │ │ ├── QuestionPostRequest.cs
│ │ │ └── QuestionPutRequest.cs
│ ├── Hubs
│ │ └── QuestionsHub.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── QandA.csproj
│ ├── QandA.sln
│ ├── SQLScripts
│ │ ├── 01-Tables.sql
│ │ └── 02-Sprocs.sql
│ ├── Startup.cs
│ ├── WeatherForecast.cs
│ ├── appsettings.Development.json
│ └── appsettings.json
├── frontend
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── .prettierrc
│ ├── .vscode
│ │ └── settings.json
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── src
│ │ ├── Answer.tsx
│ │ ├── AnswerList.tsx
│ │ ├── App.test.tsx
│ │ ├── App.tsx
│ │ ├── AskPage.tsx
│ │ ├── Field.tsx
│ │ ├── Form.tsx
│ │ ├── Header.tsx
│ │ ├── HomePage.tsx
│ │ ├── Icons.tsx
│ │ ├── NotFoundPage.tsx
│ │ ├── Page.tsx
│ │ ├── PageTitle.tsx
│ │ ├── Question.tsx
│ │ ├── QuestionList.tsx
│ │ ├── QuestionPage.tsx
│ │ ├── QuestionsData.ts
│ │ ├── SearchPage.tsx
│ │ ├── SignInPage.tsx
│ │ ├── Styles.ts
│ │ ├── index.css
│ │ ├── index.tsx
│ │ ├── logo.svg
│ │ ├── react-app-env.d.ts
│ │ ├── serviceWorker.ts
│ │ └── user.svg
│ ├── tsconfig.json
│ └── yarn.lock
└── readme.md
├── Chapter10
├── backend
│ ├── .gitignore
│ ├── Controllers
│ │ ├── QuestionsController.cs
│ │ └── WeatherForecastController.cs
│ ├── Data
│ │ ├── DataRepository.cs
│ │ ├── IDataRepository.cs
│ │ ├── IQuestionCache.cs
│ │ ├── Models
│ │ │ ├── AnswerGetResponse.cs
│ │ │ ├── AnswerPostFullRequest.cs
│ │ │ ├── AnswerPostRequest.cs
│ │ │ ├── QuestionGetManyResponse.cs
│ │ │ ├── QuestionGetSingleResponse.cs
│ │ │ ├── QuestionPostFullRequest.cs
│ │ │ ├── QuestionPostRequest.cs
│ │ │ └── QuestionPutRequest.cs
│ │ └── QuestionCache.cs
│ ├── Hubs
│ │ └── QuestionsHub.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── QandA.csproj
│ ├── QandA.sln
│ ├── SQLScripts
│ │ ├── 01-Tables.sql
│ │ └── 02-Sprocs.sql
│ ├── Startup.cs
│ ├── WeatherForecast.cs
│ ├── appsettings.Development.json
│ └── appsettings.json
└── readme.md
├── Chapter11
├── backend
│ ├── .gitignore
│ ├── Authorization
│ │ ├── MustBeQuestionAuthorHandler.cs
│ │ └── MustBeQuestionAuthorRequirement.cs
│ ├── Controllers
│ │ ├── QuestionsController.cs
│ │ └── WeatherForecastController.cs
│ ├── Data
│ │ ├── DataRepository.cs
│ │ ├── IDataRepository.cs
│ │ ├── IQuestionCache.cs
│ │ ├── Models
│ │ │ ├── AnswerGetResponse.cs
│ │ │ ├── AnswerPostFullRequest.cs
│ │ │ ├── AnswerPostRequest.cs
│ │ │ ├── QuestionGetManyResponse.cs
│ │ │ ├── QuestionGetSingleResponse.cs
│ │ │ ├── QuestionPostFullRequest.cs
│ │ │ ├── QuestionPostRequest.cs
│ │ │ ├── QuestionPutRequest.cs
│ │ │ └── User.cs
│ │ └── QuestionCache.cs
│ ├── Hubs
│ │ └── QuestionsHub.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── QandA.csproj
│ ├── QandA.sln
│ ├── SQLScripts
│ │ ├── 01-Tables.sql
│ │ └── 02-Sprocs.sql
│ ├── Startup.cs
│ ├── WeatherForecast.cs
│ ├── appsettings.Development.json
│ └── appsettings.json
└── readme.md
├── Chapter12
├── Finish
│ ├── backend
│ │ ├── .gitignore
│ │ ├── Authorization
│ │ │ ├── MustBeQuestionAuthorHandler.cs
│ │ │ └── MustBeQuestionAuthorRequirement.cs
│ │ ├── Controllers
│ │ │ ├── QuestionsController.cs
│ │ │ └── WeatherForecastController.cs
│ │ ├── Data
│ │ │ ├── DataRepository.cs
│ │ │ ├── IDataRepository.cs
│ │ │ ├── IQuestionCache.cs
│ │ │ ├── Models
│ │ │ │ ├── AnswerGetResponse.cs
│ │ │ │ ├── AnswerPostFullRequest.cs
│ │ │ │ ├── AnswerPostRequest.cs
│ │ │ │ ├── QuestionGetManyResponse.cs
│ │ │ │ ├── QuestionGetSingleResponse.cs
│ │ │ │ ├── QuestionPostFullRequest.cs
│ │ │ │ ├── QuestionPostRequest.cs
│ │ │ │ ├── QuestionPutRequest.cs
│ │ │ │ └── User.cs
│ │ │ └── QuestionCache.cs
│ │ ├── Hubs
│ │ │ └── QuestionsHub.cs
│ │ ├── Program.cs
│ │ ├── Properties
│ │ │ └── launchSettings.json
│ │ ├── QandA.csproj
│ │ ├── QandA.sln
│ │ ├── SQLScripts
│ │ │ ├── 01-Tables.sql
│ │ │ └── 02-Sprocs.sql
│ │ ├── Startup.cs
│ │ ├── WeatherForecast.cs
│ │ ├── appsettings.Development.json
│ │ └── appsettings.json
│ └── frontend
│ │ ├── .eslintrc.json
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── .vscode
│ │ └── settings.json
│ │ ├── README.md
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ │ ├── src
│ │ ├── Answer.tsx
│ │ ├── AnswerList.tsx
│ │ ├── App.test.tsx
│ │ ├── App.tsx
│ │ ├── AppSettings.ts
│ │ ├── AskPage.tsx
│ │ ├── Auth.tsx
│ │ ├── AuthorizedPage.tsx
│ │ ├── Field.tsx
│ │ ├── Form.tsx
│ │ ├── Header.tsx
│ │ ├── HomePage.tsx
│ │ ├── Icons.tsx
│ │ ├── NotFoundPage.tsx
│ │ ├── Page.tsx
│ │ ├── PageTitle.tsx
│ │ ├── Question.tsx
│ │ ├── QuestionList.tsx
│ │ ├── QuestionPage.tsx
│ │ ├── QuestionsData.ts
│ │ ├── SearchPage.tsx
│ │ ├── SignInPage.tsx
│ │ ├── SignOutPage.tsx
│ │ ├── Styles.ts
│ │ ├── http.ts
│ │ ├── index.css
│ │ ├── index.tsx
│ │ ├── logo.svg
│ │ ├── react-app-env.d.ts
│ │ ├── serviceWorker.ts
│ │ └── user.svg
│ │ ├── tsconfig.json
│ │ └── yarn.lock
├── Start
│ ├── backend
│ │ ├── .gitignore
│ │ ├── Authorization
│ │ │ ├── MustBeQuestionAuthorHandler.cs
│ │ │ └── MustBeQuestionAuthorRequirement.cs
│ │ ├── Controllers
│ │ │ ├── QuestionsController.cs
│ │ │ └── WeatherForecastController.cs
│ │ ├── Data
│ │ │ ├── DataRepository.cs
│ │ │ ├── IDataRepository.cs
│ │ │ ├── IQuestionCache.cs
│ │ │ ├── Models
│ │ │ │ ├── AnswerGetResponse.cs
│ │ │ │ ├── AnswerPostFullRequest.cs
│ │ │ │ ├── AnswerPostRequest.cs
│ │ │ │ ├── QuestionGetManyResponse.cs
│ │ │ │ ├── QuestionGetSingleResponse.cs
│ │ │ │ ├── QuestionPostFullRequest.cs
│ │ │ │ ├── QuestionPostRequest.cs
│ │ │ │ ├── QuestionPutRequest.cs
│ │ │ │ └── User.cs
│ │ │ └── QuestionCache.cs
│ │ ├── Hubs
│ │ │ └── QuestionsHub.cs
│ │ ├── Program.cs
│ │ ├── Properties
│ │ │ └── launchSettings.json
│ │ ├── QandA.csproj
│ │ ├── QandA.sln
│ │ ├── SQLScripts
│ │ │ ├── 01-Tables.sql
│ │ │ └── 02-Sprocs.sql
│ │ ├── Startup.cs
│ │ ├── WeatherForecast.cs
│ │ ├── appsettings.Development.json
│ │ └── appsettings.json
│ └── frontend
│ │ ├── .eslintrc.json
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── .vscode
│ │ └── settings.json
│ │ ├── README.md
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ │ ├── src
│ │ ├── Answer.tsx
│ │ ├── AnswerList.tsx
│ │ ├── App.test.tsx
│ │ ├── App.tsx
│ │ ├── AppSettings.ts
│ │ ├── AskPage.tsx
│ │ ├── Field.tsx
│ │ ├── Form.tsx
│ │ ├── Header.tsx
│ │ ├── HomePage.tsx
│ │ ├── Icons.tsx
│ │ ├── NotFoundPage.tsx
│ │ ├── Page.tsx
│ │ ├── PageTitle.tsx
│ │ ├── Question.tsx
│ │ ├── QuestionList.tsx
│ │ ├── QuestionPage.tsx
│ │ ├── QuestionsData.ts
│ │ ├── SearchPage.tsx
│ │ ├── SignInPage.tsx
│ │ ├── SignOutPage.tsx
│ │ ├── Styles.ts
│ │ ├── index.css
│ │ ├── index.tsx
│ │ ├── logo.svg
│ │ ├── react-app-env.d.ts
│ │ ├── serviceWorker.ts
│ │ └── user.svg
│ │ ├── tsconfig.json
│ │ └── yarn.lock
└── readme.md
├── Chapter13
├── BackendTests
│ ├── .gitignore
│ ├── BackendTests.csproj
│ ├── Calc.cs
│ ├── CalcTests.cs
│ └── QuestionsControllerTests.cs
├── backend
│ ├── .gitignore
│ ├── Authorization
│ │ ├── MustBeQuestionAuthorHandler.cs
│ │ └── MustBeQuestionAuthorRequirement.cs
│ ├── Controllers
│ │ ├── QuestionsController.cs
│ │ └── WeatherForecastController.cs
│ ├── Data
│ │ ├── DataRepository.cs
│ │ ├── IDataRepository.cs
│ │ ├── IQuestionCache.cs
│ │ ├── Models
│ │ │ ├── AnswerGetResponse.cs
│ │ │ ├── AnswerPostFullRequest.cs
│ │ │ ├── AnswerPostRequest.cs
│ │ │ ├── QuestionGetManyResponse.cs
│ │ │ ├── QuestionGetSingleResponse.cs
│ │ │ ├── QuestionPostFullRequest.cs
│ │ │ ├── QuestionPostRequest.cs
│ │ │ ├── QuestionPutRequest.cs
│ │ │ └── User.cs
│ │ └── QuestionCache.cs
│ ├── Hubs
│ │ └── QuestionsHub.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── QandA.csproj
│ ├── QandA.sln
│ ├── SQLScripts
│ │ ├── 01-Tables.sql
│ │ └── 02-Sprocs.sql
│ ├── Startup.cs
│ ├── WeatherForecast.cs
│ ├── appsettings.Development.json
│ └── appsettings.json
├── frontend
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── .prettierrc
│ ├── .vscode
│ │ └── settings.json
│ ├── README.md
│ ├── cypress.json
│ ├── cypress
│ │ ├── fixtures
│ │ │ └── example.json
│ │ ├── integration
│ │ │ ├── examples
│ │ │ │ ├── actions.spec.js
│ │ │ │ ├── aliasing.spec.js
│ │ │ │ ├── assertions.spec.js
│ │ │ │ ├── connectors.spec.js
│ │ │ │ ├── cookies.spec.js
│ │ │ │ ├── cypress_api.spec.js
│ │ │ │ ├── files.spec.js
│ │ │ │ ├── local_storage.spec.js
│ │ │ │ ├── location.spec.js
│ │ │ │ ├── misc.spec.js
│ │ │ │ ├── navigation.spec.js
│ │ │ │ ├── network_requests.spec.js
│ │ │ │ ├── querying.spec.js
│ │ │ │ ├── spies_stubs_clocks.spec.js
│ │ │ │ ├── traversal.spec.js
│ │ │ │ ├── utilities.spec.js
│ │ │ │ ├── viewport.spec.js
│ │ │ │ ├── waiting.spec.js
│ │ │ │ └── window.spec.js
│ │ │ └── qanda.js
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── support
│ │ │ ├── commands.js
│ │ │ └── index.js
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── src
│ │ ├── Answer.tsx
│ │ ├── AnswerList.tsx
│ │ ├── App.tsx
│ │ ├── AppSettings.ts
│ │ ├── AskPage.tsx
│ │ ├── Auth.tsx
│ │ ├── AuthorizedPage.tsx
│ │ ├── Field.tsx
│ │ ├── Form.test.ts
│ │ ├── Form.tsx
│ │ ├── Header.tsx
│ │ ├── HomePage.test.tsx
│ │ ├── HomePage.tsx
│ │ ├── Icons.tsx
│ │ ├── NotFoundPage.tsx
│ │ ├── Page.test.tsx
│ │ ├── Page.tsx
│ │ ├── PageTitle.tsx
│ │ ├── Question.test.tsx
│ │ ├── Question.tsx
│ │ ├── QuestionList.tsx
│ │ ├── QuestionPage.tsx
│ │ ├── QuestionsData.ts
│ │ ├── SearchPage.tsx
│ │ ├── SignInPage.tsx
│ │ ├── SignOutPage.tsx
│ │ ├── Styles.ts
│ │ ├── http.ts
│ │ ├── index.css
│ │ ├── index.tsx
│ │ ├── logo.svg
│ │ ├── react-app-env.d.ts
│ │ ├── serviceWorker.ts
│ │ └── user.svg
│ ├── tsconfig.json
│ └── yarn.lock
└── readme.md
├── Chapter14
├── BackendTests
│ ├── .gitignore
│ ├── BackendTests.csproj
│ ├── Calc.cs
│ ├── CalcTests.cs
│ └── QuestionsControllerTests.cs
├── backend
│ ├── .gitignore
│ ├── Authorization
│ │ ├── MustBeQuestionAuthorHandler.cs
│ │ └── MustBeQuestionAuthorRequirement.cs
│ ├── Controllers
│ │ ├── QuestionsController.cs
│ │ └── WeatherForecastController.cs
│ ├── Data
│ │ ├── DataRepository.cs
│ │ ├── IDataRepository.cs
│ │ ├── IQuestionCache.cs
│ │ ├── Models
│ │ │ ├── AnswerGetResponse.cs
│ │ │ ├── AnswerPostFullRequest.cs
│ │ │ ├── AnswerPostRequest.cs
│ │ │ ├── QuestionGetManyResponse.cs
│ │ │ ├── QuestionGetSingleResponse.cs
│ │ │ ├── QuestionPostFullRequest.cs
│ │ │ ├── QuestionPostRequest.cs
│ │ │ ├── QuestionPutRequest.cs
│ │ │ └── User.cs
│ │ └── QuestionCache.cs
│ ├── Hubs
│ │ └── QuestionsHub.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── QandA.csproj
│ ├── QandA.sln
│ ├── SQLScripts
│ │ ├── 01-Tables.sql
│ │ └── 02-Sprocs.sql
│ ├── Startup.cs
│ ├── WeatherForecast.cs
│ ├── appsettings.Development.json
│ ├── appsettings.Production.json
│ ├── appsettings.Staging.json
│ └── appsettings.json
├── frontend
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── .prettierrc
│ ├── .vscode
│ │ └── settings.json
│ ├── README.md
│ ├── cypress.json
│ ├── cypress
│ │ ├── fixtures
│ │ │ └── example.json
│ │ ├── integration
│ │ │ ├── examples
│ │ │ │ ├── actions.spec.js
│ │ │ │ ├── aliasing.spec.js
│ │ │ │ ├── assertions.spec.js
│ │ │ │ ├── connectors.spec.js
│ │ │ │ ├── cookies.spec.js
│ │ │ │ ├── cypress_api.spec.js
│ │ │ │ ├── files.spec.js
│ │ │ │ ├── local_storage.spec.js
│ │ │ │ ├── location.spec.js
│ │ │ │ ├── misc.spec.js
│ │ │ │ ├── navigation.spec.js
│ │ │ │ ├── network_requests.spec.js
│ │ │ │ ├── querying.spec.js
│ │ │ │ ├── spies_stubs_clocks.spec.js
│ │ │ │ ├── traversal.spec.js
│ │ │ │ ├── utilities.spec.js
│ │ │ │ ├── viewport.spec.js
│ │ │ │ ├── waiting.spec.js
│ │ │ │ └── window.spec.js
│ │ │ └── qanda.js
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── support
│ │ │ ├── commands.js
│ │ │ └── index.js
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── manifest.json
│ │ └── web.config
│ ├── src
│ │ ├── Answer.tsx
│ │ ├── AnswerList.tsx
│ │ ├── App.tsx
│ │ ├── AppSettings.ts
│ │ ├── AskPage.tsx
│ │ ├── Auth.tsx
│ │ ├── AuthorizedPage.tsx
│ │ ├── Field.tsx
│ │ ├── Form.test.ts
│ │ ├── Form.tsx
│ │ ├── Header.tsx
│ │ ├── HomePage.test.tsx
│ │ ├── HomePage.tsx
│ │ ├── Icons.tsx
│ │ ├── NotFoundPage.tsx
│ │ ├── Page.test.tsx
│ │ ├── Page.tsx
│ │ ├── PageTitle.tsx
│ │ ├── Question.test.tsx
│ │ ├── Question.tsx
│ │ ├── QuestionList.tsx
│ │ ├── QuestionPage.tsx
│ │ ├── QuestionsData.ts
│ │ ├── SearchPage.tsx
│ │ ├── SignInPage.tsx
│ │ ├── SignOutPage.tsx
│ │ ├── Styles.ts
│ │ ├── http.ts
│ │ ├── index.css
│ │ ├── index.tsx
│ │ ├── logo.svg
│ │ ├── react-app-env.d.ts
│ │ ├── serviceWorker.ts
│ │ └── user.svg
│ ├── tsconfig.json
│ └── yarn.lock
└── readme.md
├── Chapter15
├── BackendTests
│ ├── .gitignore
│ ├── BackendTests.csproj
│ ├── Calc.cs
│ ├── CalcTests.cs
│ └── QuestionsControllerTests.cs
├── azure-pipelines.yml
├── backend
│ ├── .gitignore
│ ├── Authorization
│ │ ├── MustBeQuestionAuthorHandler.cs
│ │ └── MustBeQuestionAuthorRequirement.cs
│ ├── Controllers
│ │ ├── QuestionsController.cs
│ │ └── WeatherForecastController.cs
│ ├── Data
│ │ ├── DataRepository.cs
│ │ ├── IDataRepository.cs
│ │ ├── IQuestionCache.cs
│ │ ├── Models
│ │ │ ├── AnswerGetResponse.cs
│ │ │ ├── AnswerPostFullRequest.cs
│ │ │ ├── AnswerPostRequest.cs
│ │ │ ├── QuestionGetManyResponse.cs
│ │ │ ├── QuestionGetSingleResponse.cs
│ │ │ ├── QuestionPostFullRequest.cs
│ │ │ ├── QuestionPostRequest.cs
│ │ │ ├── QuestionPutRequest.cs
│ │ │ └── User.cs
│ │ └── QuestionCache.cs
│ ├── Hubs
│ │ └── QuestionsHub.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── QandA.csproj
│ ├── QandA.sln
│ ├── SQLScripts
│ │ ├── 01-Tables.sql
│ │ └── 02-Sprocs.sql
│ ├── Startup.cs
│ ├── WeatherForecast.cs
│ ├── appsettings.Development.json
│ ├── appsettings.Production.json
│ ├── appsettings.Staging.json
│ └── appsettings.json
├── frontend
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── .prettierrc
│ ├── .vscode
│ │ └── settings.json
│ ├── README.md
│ ├── cypress.json
│ ├── cypress
│ │ ├── fixtures
│ │ │ └── example.json
│ │ ├── integration
│ │ │ ├── examples
│ │ │ │ ├── actions.spec.js
│ │ │ │ ├── aliasing.spec.js
│ │ │ │ ├── assertions.spec.js
│ │ │ │ ├── connectors.spec.js
│ │ │ │ ├── cookies.spec.js
│ │ │ │ ├── cypress_api.spec.js
│ │ │ │ ├── files.spec.js
│ │ │ │ ├── local_storage.spec.js
│ │ │ │ ├── location.spec.js
│ │ │ │ ├── misc.spec.js
│ │ │ │ ├── navigation.spec.js
│ │ │ │ ├── network_requests.spec.js
│ │ │ │ ├── querying.spec.js
│ │ │ │ ├── spies_stubs_clocks.spec.js
│ │ │ │ ├── traversal.spec.js
│ │ │ │ ├── utilities.spec.js
│ │ │ │ ├── viewport.spec.js
│ │ │ │ ├── waiting.spec.js
│ │ │ │ └── window.spec.js
│ │ │ └── qanda.js
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── support
│ │ │ ├── commands.js
│ │ │ └── index.js
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── manifest.json
│ │ └── web.config
│ ├── src
│ │ ├── Answer.tsx
│ │ ├── AnswerList.tsx
│ │ ├── App.tsx
│ │ ├── AppSettings.ts
│ │ ├── AskPage.tsx
│ │ ├── Auth.tsx
│ │ ├── AuthorizedPage.tsx
│ │ ├── Field.tsx
│ │ ├── Form.test.ts
│ │ ├── Form.tsx
│ │ ├── Header.tsx
│ │ ├── HomePage.test.tsx
│ │ ├── HomePage.tsx
│ │ ├── Icons.tsx
│ │ ├── NotFoundPage.tsx
│ │ ├── Page.test.tsx
│ │ ├── Page.tsx
│ │ ├── PageTitle.tsx
│ │ ├── Question.test.tsx
│ │ ├── Question.tsx
│ │ ├── QuestionList.tsx
│ │ ├── QuestionPage.tsx
│ │ ├── QuestionsData.ts
│ │ ├── SearchPage.tsx
│ │ ├── SignInPage.tsx
│ │ ├── SignOutPage.tsx
│ │ ├── Styles.ts
│ │ ├── http.ts
│ │ ├── index.css
│ │ ├── index.tsx
│ │ ├── logo.svg
│ │ ├── react-app-env.d.ts
│ │ ├── serviceWorker.ts
│ │ └── user.svg
│ ├── tsconfig.json
│ └── yarn.lock
└── readme.md
├── LICENSE
└── README.md
/Chapter01/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.*~
3 | project.lock.json
4 | .DS_Store
5 | *.pyc
6 |
7 | # Visual Studio Code
8 | .vscode
9 |
10 | # User-specific files
11 | *.suo
12 | *.user
13 | *.userosscache
14 | *.sln.docstates
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | build/
24 | bld/
25 | [Bb]in/
26 | [Oo]bj/
27 | msbuild.log
28 | msbuild.err
29 | msbuild.wrn
30 |
31 | # Visual Studio 2015
32 | .vs/
--------------------------------------------------------------------------------
/Chapter01/WebApplication1/ClientApp/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/Chapter01/WebApplication1/ClientApp/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/ASP.NET-Core-3-and-React/50886f69b5195d2e978cf50a69b11d8102d8fca8/Chapter01/WebApplication1/ClientApp/public/favicon.ico
--------------------------------------------------------------------------------
/Chapter01/WebApplication1/ClientApp/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "WebApplication1",
3 | "name": "WebApplication1",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter01/WebApplication1/ClientApp/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Route } from 'react-router';
3 | import { Layout } from './components/Layout';
4 | import { Home } from './components/Home';
5 | import { FetchData } from './components/FetchData';
6 | import { Counter } from './components/Counter';
7 |
8 | import './custom.css'
9 |
10 | export default class App extends Component {
11 | static displayName = App.name;
12 |
13 | render () {
14 | return (
15 |
16 |
17 |
18 |
19 |
20 | );
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Chapter01/WebApplication1/ClientApp/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { MemoryRouter } from 'react-router-dom';
4 | import App from './App';
5 |
6 | it('renders without crashing', async () => {
7 | const div = document.createElement('div');
8 | ReactDOM.render(
9 |
10 |
11 | , div);
12 | await new Promise(resolve => setTimeout(resolve, 1000));
13 | });
14 |
--------------------------------------------------------------------------------
/Chapter01/WebApplication1/ClientApp/src/components/Layout.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Container } from 'reactstrap';
3 | import { NavMenu } from './NavMenu';
4 |
5 | export class Layout extends Component {
6 | static displayName = Layout.name;
7 |
8 | render () {
9 | return (
10 |
11 |
12 |
13 | {this.props.children}
14 |
15 |
16 | );
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Chapter01/WebApplication1/ClientApp/src/components/NavMenu.css:
--------------------------------------------------------------------------------
1 | a.navbar-brand {
2 | white-space: normal;
3 | text-align: center;
4 | word-break: break-all;
5 | }
6 |
7 | html {
8 | font-size: 14px;
9 | }
10 | @media (min-width: 768px) {
11 | html {
12 | font-size: 16px;
13 | }
14 | }
15 |
16 | .box-shadow {
17 | box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
18 | }
19 |
--------------------------------------------------------------------------------
/Chapter01/WebApplication1/ClientApp/src/custom.css:
--------------------------------------------------------------------------------
1 | /* Provide sufficient contrast against white background */
2 | a {
3 | color: #0366d6;
4 | }
5 |
6 | code {
7 | color: #E01A76;
8 | }
9 |
10 | .btn-primary {
11 | color: #fff;
12 | background-color: #1b6ec2;
13 | border-color: #1861ac;
14 | }
15 |
--------------------------------------------------------------------------------
/Chapter01/WebApplication1/ClientApp/src/index.js:
--------------------------------------------------------------------------------
1 | import 'bootstrap/dist/css/bootstrap.css';
2 | import React from 'react';
3 | import ReactDOM from 'react-dom';
4 | import { BrowserRouter } from 'react-router-dom';
5 | import App from './App';
6 | import registerServiceWorker from './registerServiceWorker';
7 |
8 | const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href');
9 | const rootElement = document.getElementById('root');
10 |
11 | ReactDOM.render(
12 |
13 |
14 | ,
15 | rootElement);
16 |
17 | registerServiceWorker();
18 |
19 |
--------------------------------------------------------------------------------
/Chapter01/WebApplication1/Pages/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @using WebApplication1
2 | @namespace WebApplication1.Pages
3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
4 |
--------------------------------------------------------------------------------
/Chapter01/WebApplication1/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore;
6 | using Microsoft.AspNetCore.Hosting;
7 | using Microsoft.Extensions.Configuration;
8 | using Microsoft.Extensions.Logging;
9 |
10 | namespace WebApplication1
11 | {
12 | public class Program
13 | {
14 | public static void Main(string[] args)
15 | {
16 | CreateWebHostBuilder(args).Build().Run();
17 | }
18 |
19 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
20 | WebHost.CreateDefaultBuilder(args)
21 | .UseStartup();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Chapter01/WebApplication1/WeatherForecast.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace WebApplication1
4 | {
5 | public class WeatherForecast
6 | {
7 | public DateTime Date { get; set; }
8 |
9 | public int TemperatureC { get; set; }
10 |
11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
12 |
13 | public string Summary { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter01/WebApplication1/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter01/WebApplication1/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Warning"
5 | }
6 | },
7 | "AllowedHosts": "*"
8 | }
9 |
--------------------------------------------------------------------------------
/Chapter01/readme.md:
--------------------------------------------------------------------------------
1 | # Chapter 1 - Understanding the ASP.NET Core React Template
2 |
3 | To restore the code for this chapter open `WebApplication1.sln` in Visual Studio and press *F5* to run the app.
4 |
--------------------------------------------------------------------------------
/Chapter02/backend/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.*~
3 | project.lock.json
4 | .DS_Store
5 | *.pyc
6 |
7 | # Visual Studio Code
8 | .vscode
9 |
10 | # User-specific files
11 | *.suo
12 | *.user
13 | *.userosscache
14 | *.sln.docstates
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | build/
24 | bld/
25 | [Bb]in/
26 | [Oo]bj/
27 | msbuild.log
28 | msbuild.err
29 | msbuild.wrn
30 |
31 | # Visual Studio 2015
32 | .vs/
--------------------------------------------------------------------------------
/Chapter02/backend/QandA.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Chapter02/backend/WeatherForecast.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace QandA
4 | {
5 | public class WeatherForecast
6 | {
7 | public DateTime Date { get; set; }
8 |
9 | public int TemperatureC { get; set; }
10 |
11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
12 |
13 | public string Summary { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter02/backend/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter02/backend/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*"
10 | }
11 |
--------------------------------------------------------------------------------
/Chapter02/frontend/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "react-app",
3 | "plugins": ["prettier"],
4 | "rules": {
5 | "prettier/prettier": "error"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter02/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/Chapter02/frontend/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "singleQuote": true,
4 | "semi": true,
5 | "tabWidth": 2,
6 | "trailingComma": "all"
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter02/frontend/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": [
3 | "javascript",
4 | "javascriptreact",
5 | { "language": "typescript", "autoFix": true },
6 | { "language": "typescriptreact", "autoFix": true }
7 | ],
8 | "editor.formatOnSave": true
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter02/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/ASP.NET-Core-3-and-React/50886f69b5195d2e978cf50a69b11d8102d8fca8/Chapter02/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/Chapter02/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter02/frontend/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | }
8 |
9 | .App-header {
10 | background-color: #282c34;
11 | min-height: 100vh;
12 | display: flex;
13 | flex-direction: column;
14 | align-items: center;
15 | justify-content: center;
16 | font-size: calc(10px + 2vmin);
17 | color: white;
18 | }
19 |
20 | .App-link {
21 | color: #09d3ac;
22 | }
23 |
--------------------------------------------------------------------------------
/Chapter02/frontend/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/Chapter02/frontend/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import logo from './logo.svg';
3 | import './App.css';
4 |
5 | const App: React.FC = () => {
6 | return (
7 |
23 | );
24 | };
25 |
26 | export default App;
27 |
--------------------------------------------------------------------------------
/Chapter02/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/Chapter02/frontend/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | ReactDOM.render(, document.getElementById('root'));
8 |
9 | // If you want your app to work offline and load faster, you can change
10 | // unregister() to register() below. Note this comes with some pitfalls.
11 | // Learn more about service workers: https://bit.ly/CRA-PWA
12 | serviceWorker.unregister();
13 |
--------------------------------------------------------------------------------
/Chapter02/frontend/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/Chapter02/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "preserve"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/Chapter02/readme.md:
--------------------------------------------------------------------------------
1 | # Chapter 2 - Creating Decoupled React and ASP.NET Core Apps
2 |
3 | To restore the frontend code for this chapter, open the `frontend` folder in Visual Studio Code and run `npm install` in the terminal. `npm start` will then run the app in dev mode.
4 |
5 | To restore the backend code for this chapter, open `QandA.sln` in the `backend` folder in Visual Studio and press *F5* to run the app.
6 |
--------------------------------------------------------------------------------
/Chapter03/frontend/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "react-app",
3 | "plugins": ["prettier"],
4 | "rules": {
5 | "prettier/prettier": "error"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter03/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/Chapter03/frontend/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "singleQuote": true,
4 | "semi": true,
5 | "tabWidth": 2,
6 | "trailingComma": "all"
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter03/frontend/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": [
3 | "javascript",
4 | "javascriptreact",
5 | { "language": "typescript", "autoFix": true },
6 | { "language": "typescriptreact", "autoFix": true }
7 | ],
8 | "editor.formatOnSave": true
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter03/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/ASP.NET-Core-3-and-React/50886f69b5195d2e978cf50a69b11d8102d8fca8/Chapter03/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/Chapter03/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter03/frontend/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/Chapter03/frontend/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Header } from './Header';
3 | import { HomePage } from './HomePage';
4 |
5 | /** @jsx jsx */
6 | import { css, jsx } from '@emotion/core';
7 | import { fontFamily, fontSize, gray2 } from './Styles';
8 |
9 | const App: React.FC = () => {
10 | return (
11 |
18 |
19 |
20 |
21 | );
22 | };
23 |
24 | export default App;
25 |
--------------------------------------------------------------------------------
/Chapter03/frontend/src/Icons.tsx:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { css, jsx } from '@emotion/core';
3 | import user from './user.svg';
4 |
5 | export const UserIcon = () => (
6 |
14 | );
15 |
--------------------------------------------------------------------------------
/Chapter03/frontend/src/Page.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | /** @jsx jsx */
3 | import { css, jsx } from '@emotion/core';
4 | import { PageTitle } from './PageTitle';
5 |
6 | interface Props {
7 | title?: string;
8 | }
9 | export const Page: FC = ({ title, children }) => (
10 |
17 | {title &&
{title}}
18 | {children}
19 |
20 | );
21 |
--------------------------------------------------------------------------------
/Chapter03/frontend/src/PageTitle.tsx:
--------------------------------------------------------------------------------
1 | import styled from '@emotion/styled';
2 |
3 | export const PageTitle = styled.h2`
4 | font-size: 15px;
5 | font-weight: bold;
6 | margin: 10px 0px 5px;
7 | text-align: center;
8 | text-transform: uppercase;
9 | `;
10 |
--------------------------------------------------------------------------------
/Chapter03/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | background-color: #f7f8fa;
4 | }
5 |
--------------------------------------------------------------------------------
/Chapter03/frontend/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | ReactDOM.render(, document.getElementById('root'));
8 |
9 | // If you want your app to work offline and load faster, you can change
10 | // unregister() to register() below. Note this comes with some pitfalls.
11 | // Learn more about service workers: https://bit.ly/CRA-PWA
12 | serviceWorker.unregister();
13 |
--------------------------------------------------------------------------------
/Chapter03/frontend/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/Chapter03/frontend/src/user.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Chapter03/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "preserve"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/Chapter03/readme.md:
--------------------------------------------------------------------------------
1 | # Chapter 3 - Getting Started with React and TypeScript
2 |
3 | To restore the frontend code for this chapter, open the `frontend` folder in Visual Studio Code and run `npm install` in the terminal. `npm start` will then run the app in dev mode.
4 |
--------------------------------------------------------------------------------
/Chapter04/frontend/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "react-app",
3 | "plugins": ["prettier"],
4 | "rules": {
5 | "prettier/prettier": "error"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter04/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/Chapter04/frontend/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "singleQuote": true,
4 | "semi": true,
5 | "tabWidth": 2,
6 | "trailingComma": "all"
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter04/frontend/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": [
3 | "javascript",
4 | "javascriptreact",
5 | { "language": "typescript", "autoFix": true },
6 | { "language": "typescriptreact", "autoFix": true }
7 | ],
8 | "editor.formatOnSave": true
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter04/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/ASP.NET-Core-3-and-React/50886f69b5195d2e978cf50a69b11d8102d8fca8/Chapter04/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/Chapter04/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter04/frontend/src/AnswerList.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import { AnswerData } from './QuestionsData';
3 | /** @jsx jsx */
4 | import { css, jsx } from '@emotion/core';
5 | import { Answer } from './Answer';
6 | import { gray5 } from './Styles';
7 |
8 | interface Props {
9 | data: AnswerData[];
10 | }
11 |
12 | export const AnswerList: FC = ({ data }) => (
13 |
20 | {data.map(answer => (
21 | -
27 |
28 |
29 | ))}
30 |
31 | );
32 |
--------------------------------------------------------------------------------
/Chapter04/frontend/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/Chapter04/frontend/src/AskPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Page } from './Page';
3 |
4 | export const AskPage = () => ;
5 | export default AskPage;
6 |
--------------------------------------------------------------------------------
/Chapter04/frontend/src/Icons.tsx:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { css, jsx } from '@emotion/core';
3 | import user from './user.svg';
4 |
5 | export const UserIcon = () => (
6 |
14 | );
15 |
--------------------------------------------------------------------------------
/Chapter04/frontend/src/NotFoundPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Page } from './Page';
3 |
4 | export const NotFoundPage = () => ;
5 |
--------------------------------------------------------------------------------
/Chapter04/frontend/src/Page.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | /** @jsx jsx */
3 | import { css, jsx } from '@emotion/core';
4 | import { PageTitle } from './PageTitle';
5 |
6 | interface Props {
7 | title?: string;
8 | }
9 | export const Page: FC = ({ title, children }) => (
10 |
17 | {title &&
{title}}
18 | {children}
19 |
20 | );
21 |
--------------------------------------------------------------------------------
/Chapter04/frontend/src/PageTitle.tsx:
--------------------------------------------------------------------------------
1 | import styled from '@emotion/styled';
2 |
3 | export const PageTitle = styled.h2`
4 | font-size: 15px;
5 | font-weight: bold;
6 | margin: 10px 0px 5px;
7 | text-align: center;
8 | text-transform: uppercase;
9 | `;
10 |
--------------------------------------------------------------------------------
/Chapter04/frontend/src/SignInPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Page } from './Page';
3 |
4 | export const SignInPage = () => ;
5 |
--------------------------------------------------------------------------------
/Chapter04/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | background-color: #f7f8fa;
4 | }
5 |
--------------------------------------------------------------------------------
/Chapter04/frontend/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | ReactDOM.render(, document.getElementById('root'));
8 |
9 | // If you want your app to work offline and load faster, you can change
10 | // unregister() to register() below. Note this comes with some pitfalls.
11 | // Learn more about service workers: https://bit.ly/CRA-PWA
12 | serviceWorker.unregister();
13 |
--------------------------------------------------------------------------------
/Chapter04/frontend/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/Chapter04/frontend/src/user.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Chapter04/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "preserve"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/Chapter04/readme.md:
--------------------------------------------------------------------------------
1 | # Chapter 4 - Routing with React Router
2 |
3 | To restore the frontend code for this chapter, open the `frontend` folder in Visual Studio Code and run `npm install` in the terminal. `npm start` will then run the app in dev mode.
--------------------------------------------------------------------------------
/Chapter05/frontend/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "react-app",
3 | "plugins": ["prettier"],
4 | "rules": {
5 | "prettier/prettier": "error"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter05/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/Chapter05/frontend/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "singleQuote": true,
4 | "semi": true,
5 | "tabWidth": 2,
6 | "trailingComma": "all"
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter05/frontend/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": [
3 | "javascript",
4 | "javascriptreact",
5 | { "language": "typescript", "autoFix": true },
6 | { "language": "typescriptreact", "autoFix": true }
7 | ],
8 | "editor.formatOnSave": true
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter05/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/ASP.NET-Core-3-and-React/50886f69b5195d2e978cf50a69b11d8102d8fca8/Chapter05/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/Chapter05/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter05/frontend/src/AnswerList.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import { AnswerData } from './QuestionsData';
3 | /** @jsx jsx */
4 | import { css, jsx } from '@emotion/core';
5 | import { Answer } from './Answer';
6 | import { gray5 } from './Styles';
7 |
8 | interface Props {
9 | data: AnswerData[];
10 | }
11 |
12 | export const AnswerList: FC = ({ data }) => (
13 |
20 | {data.map(answer => (
21 | -
27 |
28 |
29 | ))}
30 |
31 | );
32 |
--------------------------------------------------------------------------------
/Chapter05/frontend/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/Chapter05/frontend/src/Icons.tsx:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { css, jsx } from '@emotion/core';
3 | import user from './user.svg';
4 |
5 | export const UserIcon = () => (
6 |
14 | );
15 |
--------------------------------------------------------------------------------
/Chapter05/frontend/src/NotFoundPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Page } from './Page';
3 |
4 | export const NotFoundPage = () => ;
5 |
--------------------------------------------------------------------------------
/Chapter05/frontend/src/Page.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | /** @jsx jsx */
3 | import { css, jsx } from '@emotion/core';
4 | import { PageTitle } from './PageTitle';
5 |
6 | interface Props {
7 | title?: string;
8 | }
9 | export const Page: FC = ({ title, children }) => (
10 |
17 | {title &&
{title}}
18 | {children}
19 |
20 | );
21 |
--------------------------------------------------------------------------------
/Chapter05/frontend/src/PageTitle.tsx:
--------------------------------------------------------------------------------
1 | import styled from '@emotion/styled';
2 |
3 | export const PageTitle = styled.h2`
4 | font-size: 15px;
5 | font-weight: bold;
6 | margin: 10px 0px 5px;
7 | text-align: center;
8 | text-transform: uppercase;
9 | `;
10 |
--------------------------------------------------------------------------------
/Chapter05/frontend/src/SignInPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Page } from './Page';
3 |
4 | export const SignInPage = () => ;
5 |
--------------------------------------------------------------------------------
/Chapter05/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | background-color: #f7f8fa;
4 | }
5 |
--------------------------------------------------------------------------------
/Chapter05/frontend/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | ReactDOM.render(, document.getElementById('root'));
8 |
9 | // If you want your app to work offline and load faster, you can change
10 | // unregister() to register() below. Note this comes with some pitfalls.
11 | // Learn more about service workers: https://bit.ly/CRA-PWA
12 | serviceWorker.unregister();
13 |
--------------------------------------------------------------------------------
/Chapter05/frontend/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/Chapter05/frontend/src/user.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Chapter05/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "preserve"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/Chapter05/readme.md:
--------------------------------------------------------------------------------
1 | # Chapter 5 - Working with Forms
2 |
3 | To restore the frontend code for this chapter, open the `frontend` folder in Visual Studio Code and run `npm install` in the terminal. `npm start` will then run the app in dev mode.
--------------------------------------------------------------------------------
/Chapter06/frontend/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "react-app",
3 | "plugins": ["prettier"],
4 | "rules": {
5 | "prettier/prettier": "error"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter06/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/Chapter06/frontend/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "singleQuote": true,
4 | "semi": true,
5 | "tabWidth": 2,
6 | "trailingComma": "all"
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter06/frontend/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": [
3 | "javascript",
4 | "javascriptreact",
5 | { "language": "typescript", "autoFix": true },
6 | { "language": "typescriptreact", "autoFix": true }
7 | ],
8 | "editor.formatOnSave": true
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter06/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/ASP.NET-Core-3-and-React/50886f69b5195d2e978cf50a69b11d8102d8fca8/Chapter06/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/Chapter06/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter06/frontend/src/AnswerList.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import { AnswerData } from './QuestionsData';
3 | /** @jsx jsx */
4 | import { css, jsx } from '@emotion/core';
5 | import { Answer } from './Answer';
6 | import { gray5 } from './Styles';
7 |
8 | interface Props {
9 | data: AnswerData[];
10 | }
11 |
12 | export const AnswerList: FC = ({ data }) => (
13 |
20 | {data.map(answer => (
21 | -
27 |
28 |
29 | ))}
30 |
31 | );
32 |
--------------------------------------------------------------------------------
/Chapter06/frontend/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/Chapter06/frontend/src/Icons.tsx:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { css, jsx } from '@emotion/core';
3 | import user from './user.svg';
4 |
5 | export const UserIcon = () => (
6 |
14 | );
15 |
--------------------------------------------------------------------------------
/Chapter06/frontend/src/NotFoundPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Page } from './Page';
3 |
4 | export const NotFoundPage = () => ;
5 |
--------------------------------------------------------------------------------
/Chapter06/frontend/src/Page.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | /** @jsx jsx */
3 | import { css, jsx } from '@emotion/core';
4 | import { PageTitle } from './PageTitle';
5 |
6 | interface Props {
7 | title?: string;
8 | }
9 | export const Page: FC = ({ title, children }) => (
10 |
17 | {title &&
{title}}
18 | {children}
19 |
20 | );
21 |
--------------------------------------------------------------------------------
/Chapter06/frontend/src/PageTitle.tsx:
--------------------------------------------------------------------------------
1 | import styled from '@emotion/styled';
2 |
3 | export const PageTitle = styled.h2`
4 | font-size: 15px;
5 | font-weight: bold;
6 | margin: 10px 0px 5px;
7 | text-align: center;
8 | text-transform: uppercase;
9 | `;
10 |
--------------------------------------------------------------------------------
/Chapter06/frontend/src/SignInPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Page } from './Page';
3 |
4 | export const SignInPage = () => ;
5 |
--------------------------------------------------------------------------------
/Chapter06/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | background-color: #f7f8fa;
4 | }
5 |
--------------------------------------------------------------------------------
/Chapter06/frontend/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | ReactDOM.render(, document.getElementById('root'));
8 |
9 | // If you want your app to work offline and load faster, you can change
10 | // unregister() to register() below. Note this comes with some pitfalls.
11 | // Learn more about service workers: https://bit.ly/CRA-PWA
12 | serviceWorker.unregister();
13 |
--------------------------------------------------------------------------------
/Chapter06/frontend/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/Chapter06/frontend/src/user.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Chapter06/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "preserve"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/Chapter06/readme.md:
--------------------------------------------------------------------------------
1 | # Chapter 6 - Managing State with Redux
2 |
3 | To restore the frontend code for this chapter, open the `frontend` folder in Visual Studio Code and run `npm install` in the terminal. `npm start` will then run the app in dev mode.
--------------------------------------------------------------------------------
/Chapter07/backend/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.*~
3 | project.lock.json
4 | .DS_Store
5 | *.pyc
6 |
7 | # Visual Studio Code
8 | .vscode
9 |
10 | # User-specific files
11 | *.suo
12 | *.user
13 | *.userosscache
14 | *.sln.docstates
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | build/
24 | bld/
25 | [Bb]in/
26 | [Oo]bj/
27 | msbuild.log
28 | msbuild.err
29 | msbuild.wrn
30 |
31 | # Visual Studio 2015
32 | .vs/
--------------------------------------------------------------------------------
/Chapter07/backend/Data/Models/AnswerGetResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class AnswerGetResponse
9 | {
10 | public int AnswerId { get; set; }
11 | public string Content { get; set; }
12 | public string UserName { get; set; }
13 | public DateTime Created { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter07/backend/Data/Models/AnswerPostRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class AnswerPostRequest
9 | {
10 | public int QuestionId { get; set; }
11 | public string Content { get; set; }
12 | public string UserId { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter07/backend/Data/Models/QuestionGetManyResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionGetManyResponse
9 | {
10 | public int QuestionId { get; set; }
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter07/backend/Data/Models/QuestionGetSingleResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionGetSingleResponse
9 | {
10 | public int QuestionId { get; set; }
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | public string UserName { get; set; }
14 | public string UserId { get; set; }
15 | public DateTime Created { get; set; }
16 | public IEnumerable Answers { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Chapter07/backend/Data/Models/QuestionPostRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionPostRequest
9 | {
10 | public string Title { get; set; }
11 | public string Content { get; set; }
12 | public string UserId { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter07/backend/Data/Models/QuestionPutRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionPutRequest
9 | {
10 | public string Title { get; set; }
11 | public string Content { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Chapter07/backend/QandA.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Chapter07/backend/WeatherForecast.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace QandA
4 | {
5 | public class WeatherForecast
6 | {
7 | public DateTime Date { get; set; }
8 |
9 | public int TemperatureC { get; set; }
10 |
11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
12 |
13 | public string Summary { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter07/backend/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter07/backend/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "DefaultConnection": "Server=localhost\\SQLEXPRESS;Database=QandA;Trusted_Connection=True;"
4 | },
5 | "Logging": {
6 | "LogLevel": {
7 | "Default": "Information",
8 | "Microsoft": "Warning",
9 | "Microsoft.Hosting.Lifetime": "Information"
10 | }
11 | },
12 | "AllowedHosts": "*"
13 | }
14 |
--------------------------------------------------------------------------------
/Chapter07/readme.md:
--------------------------------------------------------------------------------
1 | # Chapter 7 - Interacting with the Database with Dapper
2 |
3 | To restore the backend code for this chapter, open `QandA.sln` in the `backend` folder in Visual Studio. Press *F5* to run the app.
4 |
5 |
--------------------------------------------------------------------------------
/Chapter08/backend/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.*~
3 | project.lock.json
4 | .DS_Store
5 | *.pyc
6 |
7 | # Visual Studio Code
8 | .vscode
9 |
10 | # User-specific files
11 | *.suo
12 | *.user
13 | *.userosscache
14 | *.sln.docstates
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | build/
24 | bld/
25 | [Bb]in/
26 | [Oo]bj/
27 | msbuild.log
28 | msbuild.err
29 | msbuild.wrn
30 |
31 | # Visual Studio 2015
32 | .vs/
--------------------------------------------------------------------------------
/Chapter08/backend/Data/Models/AnswerGetResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class AnswerGetResponse
9 | {
10 | public int AnswerId { get; set; }
11 | public string Content { get; set; }
12 | public string UserName { get; set; }
13 | public DateTime Created { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter08/backend/Data/Models/AnswerPostFullRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class AnswerPostFullRequest
9 | {
10 | public int QuestionId { get; set; }
11 | public string Content { get; set; }
12 | public string UserId { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter08/backend/Data/Models/AnswerPostRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 | using Microsoft.AspNetCore.Mvc.ModelBinding;
7 |
8 | namespace QandA.Data.Models
9 | {
10 | public class AnswerPostRequest
11 | {
12 | [Required]
13 | public int? QuestionId { get; set; }
14 | [Required]
15 | public string Content { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter08/backend/Data/Models/QuestionGetManyResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionGetManyResponse
9 | {
10 | public int QuestionId { get; set; }
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter08/backend/Data/Models/QuestionGetSingleResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionGetSingleResponse
9 | {
10 | public int QuestionId { get; set; }
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | public string UserId { get; set; }
14 | public string UserName { get; set; }
15 | public DateTime Created { get; set; }
16 | public IEnumerable Answers { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Chapter08/backend/Data/Models/QuestionPostFullRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionPostFullRequest
9 | {
10 | public string Title { get; set; }
11 | public string Content { get; set; }
12 | public string UserId { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter08/backend/Data/Models/QuestionPostRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 |
7 | namespace QandA.Data.Models
8 | {
9 | public class QuestionPostRequest
10 | {
11 | [Required]
12 | [StringLength(100)]
13 | public string Title { get; set; }
14 | [Required(ErrorMessage = "Please include some content for the question")]
15 | public string Content { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter08/backend/Data/Models/QuestionPutRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionPutRequest
9 | {
10 | [StringLength(100)]
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Chapter08/backend/QandA.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Chapter08/backend/SQLScripts/01-Tables.sql:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/ASP.NET-Core-3-and-React/50886f69b5195d2e978cf50a69b11d8102d8fca8/Chapter08/backend/SQLScripts/01-Tables.sql
--------------------------------------------------------------------------------
/Chapter08/backend/WeatherForecast.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace QandA
4 | {
5 | public class WeatherForecast
6 | {
7 | public DateTime Date { get; set; }
8 |
9 | public int TemperatureC { get; set; }
10 |
11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
12 |
13 | public string Summary { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter08/backend/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter08/backend/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "DefaultConnection": "Server=localhost\\SQLEXPRESS;Database=QandA;Trusted_Connection=True;"
4 | },
5 | "Logging": {
6 | "LogLevel": {
7 | "Default": "Information",
8 | "Microsoft": "Warning",
9 | "Microsoft.Hosting.Lifetime": "Information"
10 | }
11 | },
12 | "AllowedHosts": "*"
13 | }
14 |
--------------------------------------------------------------------------------
/Chapter08/readme.md:
--------------------------------------------------------------------------------
1 | # Chapter 8 - Creating REST API Endpoints
2 |
3 | To restore the backend code for this chapter, open `QandA.sln` in the `backend` folder in Visual Studio. Press *F5* to run the app.
4 |
--------------------------------------------------------------------------------
/Chapter09/backend/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.*~
3 | project.lock.json
4 | .DS_Store
5 | *.pyc
6 |
7 | # Visual Studio Code
8 | .vscode
9 |
10 | # User-specific files
11 | *.suo
12 | *.user
13 | *.userosscache
14 | *.sln.docstates
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | build/
24 | bld/
25 | [Bb]in/
26 | [Oo]bj/
27 | msbuild.log
28 | msbuild.err
29 | msbuild.wrn
30 |
31 | # Visual Studio 2015
32 | .vs/
--------------------------------------------------------------------------------
/Chapter09/backend/Data/Models/AnswerGetResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class AnswerGetResponse
9 | {
10 | public int AnswerId { get; set; }
11 | public string Content { get; set; }
12 | public string UserName { get; set; }
13 | public DateTime Created { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter09/backend/Data/Models/AnswerPostFullRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class AnswerPostFullRequest
9 | {
10 | public int QuestionId { get; set; }
11 | public string Content { get; set; }
12 | public string UserId { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter09/backend/Data/Models/AnswerPostRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 | using Microsoft.AspNetCore.Mvc.ModelBinding;
7 |
8 | namespace QandA.Data.Models
9 | {
10 | public class AnswerPostRequest
11 | {
12 | [Required]
13 | public int? QuestionId { get; set; }
14 | [Required]
15 | public string Content { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter09/backend/Data/Models/QuestionGetManyResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionGetManyResponse
9 | {
10 | public int QuestionId { get; set; }
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter09/backend/Data/Models/QuestionGetSingleResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionGetSingleResponse
9 | {
10 | public int QuestionId { get; set; }
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | public string UserId { get; set; }
14 | public string UserName { get; set; }
15 | public DateTime Created { get; set; }
16 | public IEnumerable Answers { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Chapter09/backend/Data/Models/QuestionPostFullRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionPostFullRequest
9 | {
10 | public string Title { get; set; }
11 | public string Content { get; set; }
12 | public string UserId { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter09/backend/Data/Models/QuestionPostRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 |
7 | namespace QandA.Data.Models
8 | {
9 | public class QuestionPostRequest
10 | {
11 | [Required]
12 | [StringLength(100)]
13 | public string Title { get; set; }
14 | [Required(ErrorMessage = "Please include some content for the question")]
15 | public string Content { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter09/backend/Data/Models/QuestionPutRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionPutRequest
9 | {
10 | [StringLength(100)]
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Chapter09/backend/QandA.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Chapter09/backend/SQLScripts/01-Tables.sql:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/ASP.NET-Core-3-and-React/50886f69b5195d2e978cf50a69b11d8102d8fca8/Chapter09/backend/SQLScripts/01-Tables.sql
--------------------------------------------------------------------------------
/Chapter09/backend/WeatherForecast.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace QandA
4 | {
5 | public class WeatherForecast
6 | {
7 | public DateTime Date { get; set; }
8 |
9 | public int TemperatureC { get; set; }
10 |
11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
12 |
13 | public string Summary { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter09/backend/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter09/backend/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "DefaultConnection": "Server=localhost\\SQLEXPRESS;Database=QandA;Trusted_Connection=True;"
4 | },
5 | "Logging": {
6 | "LogLevel": {
7 | "Default": "Information",
8 | "Microsoft": "Warning",
9 | "Microsoft.Hosting.Lifetime": "Information"
10 | }
11 | },
12 | "AllowedHosts": "*"
13 | }
14 |
--------------------------------------------------------------------------------
/Chapter09/frontend/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "react-app",
3 | "plugins": ["prettier"],
4 | "rules": {
5 | "prettier/prettier": "error"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter09/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/Chapter09/frontend/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "singleQuote": true,
4 | "semi": true,
5 | "tabWidth": 2,
6 | "trailingComma": "all"
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter09/frontend/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": [
3 | "javascript",
4 | "javascriptreact",
5 | { "language": "typescript", "autoFix": true },
6 | { "language": "typescriptreact", "autoFix": true }
7 | ],
8 | "editor.formatOnSave": true
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter09/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/ASP.NET-Core-3-and-React/50886f69b5195d2e978cf50a69b11d8102d8fca8/Chapter09/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/Chapter09/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter09/frontend/src/AnswerList.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import { AnswerData } from './QuestionsData';
3 | /** @jsx jsx */
4 | import { css, jsx } from '@emotion/core';
5 | import { Answer } from './Answer';
6 | import { gray5 } from './Styles';
7 |
8 | interface Props {
9 | data: AnswerData[];
10 | }
11 |
12 | export const AnswerList: FC = ({ data }) => (
13 |
20 | {data.map(answer => (
21 | -
27 |
28 |
29 | ))}
30 |
31 | );
32 |
--------------------------------------------------------------------------------
/Chapter09/frontend/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/Chapter09/frontend/src/Icons.tsx:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { css, jsx } from '@emotion/core';
3 | import user from './user.svg';
4 |
5 | export const UserIcon = () => (
6 |
14 | );
15 |
--------------------------------------------------------------------------------
/Chapter09/frontend/src/NotFoundPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Page } from './Page';
3 |
4 | export const NotFoundPage = () => ;
5 |
--------------------------------------------------------------------------------
/Chapter09/frontend/src/Page.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | /** @jsx jsx */
3 | import { css, jsx } from '@emotion/core';
4 | import { PageTitle } from './PageTitle';
5 |
6 | interface Props {
7 | title?: string;
8 | }
9 | export const Page: FC = ({ title, children }) => (
10 |
17 | {title &&
{title}}
18 | {children}
19 |
20 | );
21 |
--------------------------------------------------------------------------------
/Chapter09/frontend/src/PageTitle.tsx:
--------------------------------------------------------------------------------
1 | import styled from '@emotion/styled';
2 |
3 | export const PageTitle = styled.h2`
4 | font-size: 15px;
5 | font-weight: bold;
6 | margin: 10px 0px 5px;
7 | text-align: center;
8 | text-transform: uppercase;
9 | `;
10 |
--------------------------------------------------------------------------------
/Chapter09/frontend/src/SignInPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Page } from './Page';
3 |
4 | export const SignInPage = () => ;
5 |
--------------------------------------------------------------------------------
/Chapter09/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | background-color: #f7f8fa;
4 | }
5 |
--------------------------------------------------------------------------------
/Chapter09/frontend/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | ReactDOM.render(, document.getElementById('root'));
8 |
9 | // If you want your app to work offline and load faster, you can change
10 | // unregister() to register() below. Note this comes with some pitfalls.
11 | // Learn more about service workers: https://bit.ly/CRA-PWA
12 | serviceWorker.unregister();
13 |
--------------------------------------------------------------------------------
/Chapter09/frontend/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/Chapter09/frontend/src/user.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Chapter09/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "preserve"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/Chapter09/readme.md:
--------------------------------------------------------------------------------
1 | # Chapter 9 - Creating a Real-Time API with SignalR
2 |
3 | To restore the frontend code for this chapter, open the `frontend` folder in Visual Studio Code and run `npm install` in the terminal. `npm start` will then run the app in dev mode.
4 |
5 | To restore the backend code for this chapter, open `QandA.sln` in the `backend` folder in Visual Studio. Double check the connection string in `appsettings.json` points to your database and press *F5* to run the app.
6 |
--------------------------------------------------------------------------------
/Chapter10/backend/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.*~
3 | project.lock.json
4 | .DS_Store
5 | *.pyc
6 |
7 | # Visual Studio Code
8 | .vscode
9 |
10 | # User-specific files
11 | *.suo
12 | *.user
13 | *.userosscache
14 | *.sln.docstates
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | build/
24 | bld/
25 | [Bb]in/
26 | [Oo]bj/
27 | msbuild.log
28 | msbuild.err
29 | msbuild.wrn
30 |
31 | # Visual Studio 2015
32 | .vs/
--------------------------------------------------------------------------------
/Chapter10/backend/Data/IQuestionCache.cs:
--------------------------------------------------------------------------------
1 | using QandA.Data.Models;
2 |
3 | namespace QandA.Data
4 | {
5 | public interface IQuestionCache
6 | {
7 | QuestionGetSingleResponse Get(int questionId);
8 | void Remove(int questionId);
9 | void Set(QuestionGetSingleResponse question);
10 | }
11 | }
--------------------------------------------------------------------------------
/Chapter10/backend/Data/Models/AnswerGetResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class AnswerGetResponse
9 | {
10 | public int AnswerId { get; set; }
11 | public string Content { get; set; }
12 | public string UserName { get; set; }
13 | public DateTime Created { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter10/backend/Data/Models/AnswerPostFullRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class AnswerPostFullRequest
9 | {
10 | public int QuestionId { get; set; }
11 | public string Content { get; set; }
12 | public string UserId { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter10/backend/Data/Models/AnswerPostRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 | using Microsoft.AspNetCore.Mvc.ModelBinding;
7 |
8 | namespace QandA.Data.Models
9 | {
10 | public class AnswerPostRequest
11 | {
12 | [Required]
13 | public int? QuestionId { get; set; }
14 | [Required]
15 | public string Content { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter10/backend/Data/Models/QuestionGetManyResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionGetManyResponse
9 | {
10 | public int QuestionId { get; set; }
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | public List Answers { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter10/backend/Data/Models/QuestionGetSingleResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionGetSingleResponse
9 | {
10 | public int QuestionId { get; set; }
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | public string UserId { get; set; }
14 | public string UserName { get; set; }
15 | public DateTime Created { get; set; }
16 | public IEnumerable Answers { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Chapter10/backend/Data/Models/QuestionPostFullRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionPostFullRequest
9 | {
10 | public string Title { get; set; }
11 | public string Content { get; set; }
12 | public string UserId { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter10/backend/Data/Models/QuestionPostRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 |
7 | namespace QandA.Data.Models
8 | {
9 | public class QuestionPostRequest
10 | {
11 | [Required]
12 | [StringLength(100)]
13 | public string Title { get; set; }
14 | [Required(ErrorMessage = "Please include some content for the question")]
15 | public string Content { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter10/backend/Data/Models/QuestionPutRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionPutRequest
9 | {
10 | [StringLength(100)]
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Chapter10/backend/QandA.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Chapter10/backend/SQLScripts/01-Tables.sql:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/ASP.NET-Core-3-and-React/50886f69b5195d2e978cf50a69b11d8102d8fca8/Chapter10/backend/SQLScripts/01-Tables.sql
--------------------------------------------------------------------------------
/Chapter10/backend/WeatherForecast.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace QandA
4 | {
5 | public class WeatherForecast
6 | {
7 | public DateTime Date { get; set; }
8 |
9 | public int TemperatureC { get; set; }
10 |
11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
12 |
13 | public string Summary { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter10/backend/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter10/backend/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "DefaultConnection": "Server=localhost\\SQLEXPRESS;Database=QandA;Trusted_Connection=True;"
4 | },
5 | "Logging": {
6 | "LogLevel": {
7 | "Default": "Information",
8 | "Microsoft": "Warning",
9 | "Microsoft.Hosting.Lifetime": "Information"
10 | }
11 | },
12 | "AllowedHosts": "*"
13 | }
14 |
--------------------------------------------------------------------------------
/Chapter10/readme.md:
--------------------------------------------------------------------------------
1 | # Chapter 10 - Improving Performance and Scalability
2 |
3 | To restore the backend code for this chapter, open `QandA.sln` in the `backend` folder in Visual Studio. Double check the connection string in `appsettings.json` points to your database and press *F5* to run the app.
--------------------------------------------------------------------------------
/Chapter11/backend/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.*~
3 | project.lock.json
4 | .DS_Store
5 | *.pyc
6 |
7 | # Visual Studio Code
8 | .vscode
9 |
10 | # User-specific files
11 | *.suo
12 | *.user
13 | *.userosscache
14 | *.sln.docstates
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | build/
24 | bld/
25 | [Bb]in/
26 | [Oo]bj/
27 | msbuild.log
28 | msbuild.err
29 | msbuild.wrn
30 |
31 | # Visual Studio 2015
32 | .vs/
--------------------------------------------------------------------------------
/Chapter11/backend/Authorization/MustBeQuestionAuthorRequirement.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Authorization;
2 |
3 | namespace QandA.Authorization
4 | {
5 | public class MustBeQuestionAuthorRequirement : IAuthorizationRequirement
6 | {
7 | public MustBeQuestionAuthorRequirement()
8 | {
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/Chapter11/backend/Data/IQuestionCache.cs:
--------------------------------------------------------------------------------
1 | using QandA.Data.Models;
2 |
3 | namespace QandA.Data
4 | {
5 | public interface IQuestionCache
6 | {
7 | QuestionGetSingleResponse Get(int questionId);
8 | void Remove(int questionId);
9 | void Set(QuestionGetSingleResponse question);
10 | }
11 | }
--------------------------------------------------------------------------------
/Chapter11/backend/Data/Models/AnswerGetResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class AnswerGetResponse
9 | {
10 | public int AnswerId { get; set; }
11 | public string Content { get; set; }
12 | public string UserName { get; set; }
13 | public DateTime Created { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter11/backend/Data/Models/AnswerPostFullRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class AnswerPostFullRequest
9 | {
10 | public int QuestionId { get; set; }
11 | public string Content { get; set; }
12 | public string UserId { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter11/backend/Data/Models/AnswerPostRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 | using Microsoft.AspNetCore.Mvc.ModelBinding;
7 |
8 | namespace QandA.Data.Models
9 | {
10 | public class AnswerPostRequest
11 | {
12 | [Required]
13 | public int? QuestionId { get; set; }
14 | [Required]
15 | public string Content { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter11/backend/Data/Models/QuestionGetManyResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionGetManyResponse
9 | {
10 | public int QuestionId { get; set; }
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | public List Answers { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter11/backend/Data/Models/QuestionGetSingleResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionGetSingleResponse
9 | {
10 | public int QuestionId { get; set; }
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | public string UserId { get; set; }
14 | public string UserName { get; set; }
15 | public DateTime Created { get; set; }
16 | public IEnumerable Answers { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Chapter11/backend/Data/Models/QuestionPostFullRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionPostFullRequest
9 | {
10 | public string Title { get; set; }
11 | public string Content { get; set; }
12 | public string UserId { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter11/backend/Data/Models/QuestionPostRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 |
7 | namespace QandA.Data.Models
8 | {
9 | public class QuestionPostRequest
10 | {
11 | [Required]
12 | [StringLength(100)]
13 | public string Title { get; set; }
14 | [Required(ErrorMessage = "Please include some content for the question")]
15 | public string Content { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter11/backend/Data/Models/QuestionPutRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionPutRequest
9 | {
10 | [StringLength(100)]
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Chapter11/backend/Data/Models/User.cs:
--------------------------------------------------------------------------------
1 | namespace QandA.Data.Models
2 | {
3 | public class User
4 | {
5 | public string Name { get; set; }
6 | }
7 | }
--------------------------------------------------------------------------------
/Chapter11/backend/QandA.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Chapter11/backend/SQLScripts/01-Tables.sql:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/ASP.NET-Core-3-and-React/50886f69b5195d2e978cf50a69b11d8102d8fca8/Chapter11/backend/SQLScripts/01-Tables.sql
--------------------------------------------------------------------------------
/Chapter11/backend/WeatherForecast.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace QandA
4 | {
5 | public class WeatherForecast
6 | {
7 | public DateTime Date { get; set; }
8 |
9 | public int TemperatureC { get; set; }
10 |
11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
12 |
13 | public string Summary { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter11/backend/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter11/backend/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "DefaultConnection": "Server=localhost\\SQLEXPRESS;Database=QandA;Trusted_Connection=True;"
4 | },
5 | "Auth0": {
6 | "Authority": "https://your-tenant-id.auth0.com/",
7 | "Audience": "https://qanda"
8 | },
9 | "Logging": {
10 | "LogLevel": {
11 | "Default": "Information",
12 | "Microsoft": "Warning",
13 | "Microsoft.Hosting.Lifetime": "Information"
14 | }
15 | },
16 | "AllowedHosts": "*"
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter11/readme.md:
--------------------------------------------------------------------------------
1 | # Chapter 11 - Securing the Backend
2 |
3 | To restore the backend code for this chapter, open `QandA.sln` in the `backend` folder in Visual Studio. Double check the connection string in `appsettings.json` points to your database and that the Auth0 settings are correct. Press *F5* to run the app.
--------------------------------------------------------------------------------
/Chapter12/Finish/backend/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.*~
3 | project.lock.json
4 | .DS_Store
5 | *.pyc
6 |
7 | # Visual Studio Code
8 | .vscode
9 |
10 | # User-specific files
11 | *.suo
12 | *.user
13 | *.userosscache
14 | *.sln.docstates
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | build/
24 | bld/
25 | [Bb]in/
26 | [Oo]bj/
27 | msbuild.log
28 | msbuild.err
29 | msbuild.wrn
30 |
31 | # Visual Studio 2015
32 | .vs/
--------------------------------------------------------------------------------
/Chapter12/Finish/backend/Authorization/MustBeQuestionAuthorRequirement.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Authorization;
2 |
3 | namespace QandA.Authorization
4 | {
5 | public class MustBeQuestionAuthorRequirement : IAuthorizationRequirement
6 | {
7 | public MustBeQuestionAuthorRequirement()
8 | {
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/Chapter12/Finish/backend/Data/IQuestionCache.cs:
--------------------------------------------------------------------------------
1 | using QandA.Data.Models;
2 |
3 | namespace QandA.Data
4 | {
5 | public interface IQuestionCache
6 | {
7 | QuestionGetSingleResponse Get(int questionId);
8 | void Remove(int questionId);
9 | void Set(QuestionGetSingleResponse question);
10 | }
11 | }
--------------------------------------------------------------------------------
/Chapter12/Finish/backend/Data/Models/AnswerGetResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class AnswerGetResponse
9 | {
10 | public int AnswerId { get; set; }
11 | public string Content { get; set; }
12 | public string UserName { get; set; }
13 | public DateTime Created { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter12/Finish/backend/Data/Models/AnswerPostFullRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class AnswerPostFullRequest
9 | {
10 | public int QuestionId { get; set; }
11 | public string Content { get; set; }
12 | public string UserId { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter12/Finish/backend/Data/Models/AnswerPostRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 | using Microsoft.AspNetCore.Mvc.ModelBinding;
7 |
8 | namespace QandA.Data.Models
9 | {
10 | public class AnswerPostRequest
11 | {
12 | [Required]
13 | public int? QuestionId { get; set; }
14 | [Required]
15 | public string Content { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter12/Finish/backend/Data/Models/QuestionGetManyResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionGetManyResponse
9 | {
10 | public int QuestionId { get; set; }
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | public List Answers { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter12/Finish/backend/Data/Models/QuestionGetSingleResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionGetSingleResponse
9 | {
10 | public int QuestionId { get; set; }
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | public string UserId { get; set; }
14 | public string UserName { get; set; }
15 | public DateTime Created { get; set; }
16 | public IEnumerable Answers { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Chapter12/Finish/backend/Data/Models/QuestionPostFullRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionPostFullRequest
9 | {
10 | public string Title { get; set; }
11 | public string Content { get; set; }
12 | public string UserId { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter12/Finish/backend/Data/Models/QuestionPostRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 |
7 | namespace QandA.Data.Models
8 | {
9 | public class QuestionPostRequest
10 | {
11 | [Required]
12 | [StringLength(100)]
13 | public string Title { get; set; }
14 | [Required(ErrorMessage = "Please include some content for the question")]
15 | public string Content { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter12/Finish/backend/Data/Models/QuestionPutRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionPutRequest
9 | {
10 | [StringLength(100)]
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Chapter12/Finish/backend/Data/Models/User.cs:
--------------------------------------------------------------------------------
1 | namespace QandA.Data.Models
2 | {
3 | public class User
4 | {
5 | public string Name { get; set; }
6 | }
7 | }
--------------------------------------------------------------------------------
/Chapter12/Finish/backend/QandA.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Chapter12/Finish/backend/SQLScripts/01-Tables.sql:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/ASP.NET-Core-3-and-React/50886f69b5195d2e978cf50a69b11d8102d8fca8/Chapter12/Finish/backend/SQLScripts/01-Tables.sql
--------------------------------------------------------------------------------
/Chapter12/Finish/backend/WeatherForecast.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace QandA
4 | {
5 | public class WeatherForecast
6 | {
7 | public DateTime Date { get; set; }
8 |
9 | public int TemperatureC { get; set; }
10 |
11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
12 |
13 | public string Summary { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter12/Finish/backend/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter12/Finish/backend/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "DefaultConnection": "Server=localhost\\SQLEXPRESS;Database=QandA;Trusted_Connection=True;"
4 | },
5 | "Auth0": {
6 | "Authority": "https://your-tenant-id.auth0.com/",
7 | "Audience": "https://qanda"
8 | },
9 | "Logging": {
10 | "LogLevel": {
11 | "Default": "Information",
12 | "Microsoft": "Warning",
13 | "Microsoft.Hosting.Lifetime": "Information"
14 | }
15 | },
16 | "AllowedHosts": "*"
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter12/Finish/frontend/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "react-app",
3 | "plugins": ["prettier"],
4 | "rules": {
5 | "prettier/prettier": "error"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter12/Finish/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/Chapter12/Finish/frontend/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "singleQuote": true,
4 | "semi": true,
5 | "tabWidth": 2,
6 | "trailingComma": "all"
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter12/Finish/frontend/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": [
3 | "javascript",
4 | "javascriptreact",
5 | { "language": "typescript", "autoFix": true },
6 | { "language": "typescriptreact", "autoFix": true }
7 | ],
8 | "editor.formatOnSave": true
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter12/Finish/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/ASP.NET-Core-3-and-React/50886f69b5195d2e978cf50a69b11d8102d8fca8/Chapter12/Finish/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/Chapter12/Finish/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter12/Finish/frontend/src/AnswerList.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import { AnswerData } from './QuestionsData';
3 | /** @jsx jsx */
4 | import { css, jsx } from '@emotion/core';
5 | import { Answer } from './Answer';
6 | import { gray5 } from './Styles';
7 |
8 | interface Props {
9 | data: AnswerData[];
10 | }
11 |
12 | export const AnswerList: FC = ({ data }) => (
13 |
20 | {data.map(answer => (
21 | -
27 |
28 |
29 | ))}
30 |
31 | );
32 |
--------------------------------------------------------------------------------
/Chapter12/Finish/frontend/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/Chapter12/Finish/frontend/src/AppSettings.ts:
--------------------------------------------------------------------------------
1 | export const server = 'http://localhost:17525';
2 |
3 | export const webAPIUrl = `${server}/api`;
4 |
5 | export const authSettings = {
6 | domain: 'your-tenantid.auth0.com',
7 | client_id: 'your-clientid',
8 | redirect_uri: window.location.origin + '/signin-callback',
9 | scope: 'openid profile QandAAPI email',
10 | audience: 'https://qanda',
11 | };
12 |
--------------------------------------------------------------------------------
/Chapter12/Finish/frontend/src/AuthorizedPage.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC, Fragment } from 'react';
2 | import { Page } from './Page';
3 | import { useAuth } from './Auth';
4 |
5 | export const AuthorizedPage: FC = ({ children }) => {
6 | const { isAuthenticated } = useAuth();
7 | if (isAuthenticated) {
8 | return {children};
9 | } else {
10 | return ;
11 | }
12 | };
13 |
--------------------------------------------------------------------------------
/Chapter12/Finish/frontend/src/Icons.tsx:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { css, jsx } from '@emotion/core';
3 | import user from './user.svg';
4 |
5 | export const UserIcon = () => (
6 |
14 | );
15 |
--------------------------------------------------------------------------------
/Chapter12/Finish/frontend/src/NotFoundPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Page } from './Page';
3 |
4 | export const NotFoundPage = () => ;
5 |
--------------------------------------------------------------------------------
/Chapter12/Finish/frontend/src/Page.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | /** @jsx jsx */
3 | import { css, jsx } from '@emotion/core';
4 | import { PageTitle } from './PageTitle';
5 |
6 | interface Props {
7 | title?: string;
8 | }
9 | export const Page: FC = ({ title, children }) => (
10 |
17 | {title &&
{title}}
18 | {children}
19 |
20 | );
21 |
--------------------------------------------------------------------------------
/Chapter12/Finish/frontend/src/PageTitle.tsx:
--------------------------------------------------------------------------------
1 | import styled from '@emotion/styled';
2 |
3 | export const PageTitle = styled.h2`
4 | font-size: 15px;
5 | font-weight: bold;
6 | margin: 10px 0px 5px;
7 | text-align: center;
8 | text-transform: uppercase;
9 | `;
10 |
--------------------------------------------------------------------------------
/Chapter12/Finish/frontend/src/SignInPage.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react';
2 | import { Page } from './Page';
3 | import { StatusText } from './Styles';
4 | import { useAuth } from './Auth';
5 |
6 | type SigninAction = 'signin' | 'signin-callback';
7 |
8 | interface Props {
9 | action: SigninAction;
10 | }
11 |
12 | export const SignInPage: FC = ({ action }) => {
13 | const { signIn } = useAuth();
14 |
15 | if (action === 'signin') {
16 | signIn();
17 | }
18 |
19 | return (
20 |
21 | Signing in ...
22 |
23 | );
24 | };
25 |
--------------------------------------------------------------------------------
/Chapter12/Finish/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | background-color: #f7f8fa;
4 | }
5 |
--------------------------------------------------------------------------------
/Chapter12/Finish/frontend/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | ReactDOM.render(, document.getElementById('root'));
8 |
9 | // If you want your app to work offline and load faster, you can change
10 | // unregister() to register() below. Note this comes with some pitfalls.
11 | // Learn more about service workers: https://bit.ly/CRA-PWA
12 | serviceWorker.unregister();
13 |
--------------------------------------------------------------------------------
/Chapter12/Finish/frontend/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/Chapter12/Finish/frontend/src/user.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Chapter12/Finish/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "preserve"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/Chapter12/Start/backend/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.*~
3 | project.lock.json
4 | .DS_Store
5 | *.pyc
6 |
7 | # Visual Studio Code
8 | .vscode
9 |
10 | # User-specific files
11 | *.suo
12 | *.user
13 | *.userosscache
14 | *.sln.docstates
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | build/
24 | bld/
25 | [Bb]in/
26 | [Oo]bj/
27 | msbuild.log
28 | msbuild.err
29 | msbuild.wrn
30 |
31 | # Visual Studio 2015
32 | .vs/
--------------------------------------------------------------------------------
/Chapter12/Start/backend/Authorization/MustBeQuestionAuthorRequirement.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Authorization;
2 |
3 | namespace QandA.Authorization
4 | {
5 | public class MustBeQuestionAuthorRequirement : IAuthorizationRequirement
6 | {
7 | public MustBeQuestionAuthorRequirement()
8 | {
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/Chapter12/Start/backend/Data/IQuestionCache.cs:
--------------------------------------------------------------------------------
1 | using QandA.Data.Models;
2 |
3 | namespace QandA.Data
4 | {
5 | public interface IQuestionCache
6 | {
7 | QuestionGetSingleResponse Get(int questionId);
8 | void Remove(int questionId);
9 | void Set(QuestionGetSingleResponse question);
10 | }
11 | }
--------------------------------------------------------------------------------
/Chapter12/Start/backend/Data/Models/AnswerGetResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class AnswerGetResponse
9 | {
10 | public int AnswerId { get; set; }
11 | public string Content { get; set; }
12 | public string UserName { get; set; }
13 | public DateTime Created { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter12/Start/backend/Data/Models/AnswerPostFullRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class AnswerPostFullRequest
9 | {
10 | public int QuestionId { get; set; }
11 | public string Content { get; set; }
12 | public string UserId { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter12/Start/backend/Data/Models/AnswerPostRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 | using Microsoft.AspNetCore.Mvc.ModelBinding;
7 |
8 | namespace QandA.Data.Models
9 | {
10 | public class AnswerPostRequest
11 | {
12 | [Required]
13 | public int? QuestionId { get; set; }
14 | [Required]
15 | public string Content { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter12/Start/backend/Data/Models/QuestionGetManyResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionGetManyResponse
9 | {
10 | public int QuestionId { get; set; }
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | public List Answers { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter12/Start/backend/Data/Models/QuestionGetSingleResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionGetSingleResponse
9 | {
10 | public int QuestionId { get; set; }
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | public string UserId { get; set; }
14 | public string UserName { get; set; }
15 | public DateTime Created { get; set; }
16 | public IEnumerable Answers { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Chapter12/Start/backend/Data/Models/QuestionPostFullRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionPostFullRequest
9 | {
10 | public string Title { get; set; }
11 | public string Content { get; set; }
12 | public string UserId { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter12/Start/backend/Data/Models/QuestionPostRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 |
7 | namespace QandA.Data.Models
8 | {
9 | public class QuestionPostRequest
10 | {
11 | [Required]
12 | [StringLength(100)]
13 | public string Title { get; set; }
14 | [Required(ErrorMessage = "Please include some content for the question")]
15 | public string Content { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter12/Start/backend/Data/Models/QuestionPutRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionPutRequest
9 | {
10 | [StringLength(100)]
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Chapter12/Start/backend/Data/Models/User.cs:
--------------------------------------------------------------------------------
1 | namespace QandA.Data.Models
2 | {
3 | public class User
4 | {
5 | public string Name { get; set; }
6 | }
7 | }
--------------------------------------------------------------------------------
/Chapter12/Start/backend/QandA.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Chapter12/Start/backend/SQLScripts/01-Tables.sql:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/ASP.NET-Core-3-and-React/50886f69b5195d2e978cf50a69b11d8102d8fca8/Chapter12/Start/backend/SQLScripts/01-Tables.sql
--------------------------------------------------------------------------------
/Chapter12/Start/backend/WeatherForecast.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace QandA
4 | {
5 | public class WeatherForecast
6 | {
7 | public DateTime Date { get; set; }
8 |
9 | public int TemperatureC { get; set; }
10 |
11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
12 |
13 | public string Summary { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter12/Start/backend/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter12/Start/backend/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "DefaultConnection": "Server=localhost\\SQLEXPRESS;Database=QandA;Trusted_Connection=True;"
4 | },
5 | "Auth0": {
6 | "Authority": "https://your-tenant-id.auth0.com/",
7 | "Audience": "https://qanda"
8 | },
9 | "Logging": {
10 | "LogLevel": {
11 | "Default": "Information",
12 | "Microsoft": "Warning",
13 | "Microsoft.Hosting.Lifetime": "Information"
14 | }
15 | },
16 | "AllowedHosts": "*"
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter12/Start/frontend/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "react-app",
3 | "plugins": ["prettier"],
4 | "rules": {
5 | "prettier/prettier": "error"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter12/Start/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/Chapter12/Start/frontend/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "singleQuote": true,
4 | "semi": true,
5 | "tabWidth": 2,
6 | "trailingComma": "all"
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter12/Start/frontend/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": [
3 | "javascript",
4 | "javascriptreact",
5 | { "language": "typescript", "autoFix": true },
6 | { "language": "typescriptreact", "autoFix": true }
7 | ],
8 | "editor.formatOnSave": true
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter12/Start/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/ASP.NET-Core-3-and-React/50886f69b5195d2e978cf50a69b11d8102d8fca8/Chapter12/Start/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/Chapter12/Start/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter12/Start/frontend/src/AnswerList.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import { AnswerData } from './QuestionsData';
3 | /** @jsx jsx */
4 | import { css, jsx } from '@emotion/core';
5 | import { Answer } from './Answer';
6 | import { gray5 } from './Styles';
7 |
8 | interface Props {
9 | data: AnswerData[];
10 | }
11 |
12 | export const AnswerList: FC = ({ data }) => (
13 |
20 | {data.map(answer => (
21 | -
27 |
28 |
29 | ))}
30 |
31 | );
32 |
--------------------------------------------------------------------------------
/Chapter12/Start/frontend/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/Chapter12/Start/frontend/src/AppSettings.ts:
--------------------------------------------------------------------------------
1 | export const server = 'http://localhost:17525';
2 |
3 | export const webAPIUrl = `${server}/api`;
4 |
5 | export const authSettings = {
6 | domain: 'your-tenantid.auth0.com',
7 | client_id: 'your-clientid',
8 | redirect_uri: window.location.origin + '/signin-callback',
9 | scope: 'openid profile QandAAPI email',
10 | audience: 'https://qanda',
11 | };
12 |
--------------------------------------------------------------------------------
/Chapter12/Start/frontend/src/Icons.tsx:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { css, jsx } from '@emotion/core';
3 | import user from './user.svg';
4 |
5 | export const UserIcon = () => (
6 |
14 | );
15 |
--------------------------------------------------------------------------------
/Chapter12/Start/frontend/src/NotFoundPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Page } from './Page';
3 |
4 | export const NotFoundPage = () => ;
5 |
--------------------------------------------------------------------------------
/Chapter12/Start/frontend/src/Page.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | /** @jsx jsx */
3 | import { css, jsx } from '@emotion/core';
4 | import { PageTitle } from './PageTitle';
5 |
6 | interface Props {
7 | title?: string;
8 | }
9 | export const Page: FC = ({ title, children }) => (
10 |
17 | {title &&
{title}}
18 | {children}
19 |
20 | );
21 |
--------------------------------------------------------------------------------
/Chapter12/Start/frontend/src/PageTitle.tsx:
--------------------------------------------------------------------------------
1 | import styled from '@emotion/styled';
2 |
3 | export const PageTitle = styled.h2`
4 | font-size: 15px;
5 | font-weight: bold;
6 | margin: 10px 0px 5px;
7 | text-align: center;
8 | text-transform: uppercase;
9 | `;
10 |
--------------------------------------------------------------------------------
/Chapter12/Start/frontend/src/SignInPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Page } from './Page';
3 |
4 | export const SignInPage = () => ;
5 |
--------------------------------------------------------------------------------
/Chapter12/Start/frontend/src/SignOutPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Page } from './Page';
3 |
4 | export const SignOutPage = () => ;
5 |
--------------------------------------------------------------------------------
/Chapter12/Start/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | background-color: #f7f8fa;
4 | }
5 |
--------------------------------------------------------------------------------
/Chapter12/Start/frontend/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | ReactDOM.render(, document.getElementById('root'));
8 |
9 | // If you want your app to work offline and load faster, you can change
10 | // unregister() to register() below. Note this comes with some pitfalls.
11 | // Learn more about service workers: https://bit.ly/CRA-PWA
12 | serviceWorker.unregister();
13 |
--------------------------------------------------------------------------------
/Chapter12/Start/frontend/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/Chapter12/Start/frontend/src/user.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Chapter12/Start/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "preserve"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/Chapter12/readme.md:
--------------------------------------------------------------------------------
1 | # Chapter 12 - Interacting with RESTful APIs
2 |
3 | To restore the frontend code for this chapter, open the `Finish\frontend` folder in Visual Studio Code and run `npm install` in the terminal. Enter your Auth0 settings in `AppSettings.ts`. `npm start` will then run the app in dev mode.
4 |
5 | To restore the backend code for this chapter, open `QandA.sln` in the `Finish\backend` folder in Visual Studio. Double check the connection string in `appsettings.json` points to your database and that the Auth0 settings are correct. Press *F5* to run the app.
--------------------------------------------------------------------------------
/Chapter13/BackendTests/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.*~
3 | project.lock.json
4 | .DS_Store
5 | *.pyc
6 |
7 | # Visual Studio Code
8 | .vscode
9 |
10 | # User-specific files
11 | *.suo
12 | *.user
13 | *.userosscache
14 | *.sln.docstates
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | build/
24 | bld/
25 | [Bb]in/
26 | [Oo]bj/
27 | msbuild.log
28 | msbuild.err
29 | msbuild.wrn
30 |
31 | # Visual Studio 2015
32 | .vs/
--------------------------------------------------------------------------------
/Chapter13/BackendTests/BackendTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/Chapter13/BackendTests/Calc.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Xunit;
3 |
4 | namespace BackendTests
5 | {
6 | public static class Calc
7 | {
8 | public static decimal Add(decimal a, decimal b)
9 | {
10 | return a + b;
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Chapter13/BackendTests/CalcTests.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | namespace BackendTests
4 | {
5 | public class CalcTests
6 | {
7 | [Fact]
8 | public void Add_When2Integers_ShouldReturnCorrectInteger()
9 | {
10 | var result = Calc.Add(1, 1);
11 | Assert.Equal(2, result);
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/Chapter13/backend/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.*~
3 | project.lock.json
4 | .DS_Store
5 | *.pyc
6 |
7 | # Visual Studio Code
8 | .vscode
9 |
10 | # User-specific files
11 | *.suo
12 | *.user
13 | *.userosscache
14 | *.sln.docstates
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | build/
24 | bld/
25 | [Bb]in/
26 | [Oo]bj/
27 | msbuild.log
28 | msbuild.err
29 | msbuild.wrn
30 |
31 | # Visual Studio 2015
32 | .vs/
--------------------------------------------------------------------------------
/Chapter13/backend/Authorization/MustBeQuestionAuthorRequirement.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Authorization;
2 |
3 | namespace QandA.Authorization
4 | {
5 | public class MustBeQuestionAuthorRequirement : IAuthorizationRequirement
6 | {
7 | public MustBeQuestionAuthorRequirement()
8 | {
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/Chapter13/backend/Data/IQuestionCache.cs:
--------------------------------------------------------------------------------
1 | using QandA.Data.Models;
2 |
3 | namespace QandA.Data
4 | {
5 | public interface IQuestionCache
6 | {
7 | QuestionGetSingleResponse Get(int questionId);
8 | void Remove(int questionId);
9 | void Set(QuestionGetSingleResponse question);
10 | }
11 | }
--------------------------------------------------------------------------------
/Chapter13/backend/Data/Models/AnswerGetResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class AnswerGetResponse
9 | {
10 | public int AnswerId { get; set; }
11 | public string Content { get; set; }
12 | public string UserName { get; set; }
13 | public DateTime Created { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter13/backend/Data/Models/AnswerPostFullRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class AnswerPostFullRequest
9 | {
10 | public int QuestionId { get; set; }
11 | public string Content { get; set; }
12 | public string UserId { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter13/backend/Data/Models/AnswerPostRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 | using Microsoft.AspNetCore.Mvc.ModelBinding;
7 |
8 | namespace QandA.Data.Models
9 | {
10 | public class AnswerPostRequest
11 | {
12 | [Required]
13 | public int? QuestionId { get; set; }
14 | [Required]
15 | public string Content { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter13/backend/Data/Models/QuestionGetManyResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionGetManyResponse
9 | {
10 | public int QuestionId { get; set; }
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | public List Answers { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter13/backend/Data/Models/QuestionGetSingleResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionGetSingleResponse
9 | {
10 | public int QuestionId { get; set; }
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | public string UserId { get; set; }
14 | public string UserName { get; set; }
15 | public DateTime Created { get; set; }
16 | public IEnumerable Answers { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Chapter13/backend/Data/Models/QuestionPostFullRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionPostFullRequest
9 | {
10 | public string Title { get; set; }
11 | public string Content { get; set; }
12 | public string UserId { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter13/backend/Data/Models/QuestionPostRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 |
7 | namespace QandA.Data.Models
8 | {
9 | public class QuestionPostRequest
10 | {
11 | [Required]
12 | [StringLength(100)]
13 | public string Title { get; set; }
14 | [Required(ErrorMessage = "Please include some content for the question")]
15 | public string Content { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter13/backend/Data/Models/QuestionPutRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionPutRequest
9 | {
10 | [StringLength(100)]
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Chapter13/backend/Data/Models/User.cs:
--------------------------------------------------------------------------------
1 | namespace QandA.Data.Models
2 | {
3 | public class User
4 | {
5 | public string Name { get; set; }
6 | }
7 | }
--------------------------------------------------------------------------------
/Chapter13/backend/QandA.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Chapter13/backend/SQLScripts/01-Tables.sql:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/ASP.NET-Core-3-and-React/50886f69b5195d2e978cf50a69b11d8102d8fca8/Chapter13/backend/SQLScripts/01-Tables.sql
--------------------------------------------------------------------------------
/Chapter13/backend/WeatherForecast.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace QandA
4 | {
5 | public class WeatherForecast
6 | {
7 | public DateTime Date { get; set; }
8 |
9 | public int TemperatureC { get; set; }
10 |
11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
12 |
13 | public string Summary { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter13/backend/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter13/backend/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "DefaultConnection": "Server=localhost\\SQLEXPRESS;Database=QandA;Trusted_Connection=True;"
4 | },
5 | "Auth0": {
6 | "Authority": "https://your-tenant-id.auth0.com/",
7 | "Audience": "https://qanda"
8 | },
9 | "Logging": {
10 | "LogLevel": {
11 | "Default": "Information",
12 | "Microsoft": "Warning",
13 | "Microsoft.Hosting.Lifetime": "Information"
14 | }
15 | },
16 | "AllowedHosts": "*"
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter13/frontend/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "react-app",
3 | "plugins": ["prettier"],
4 | "rules": {
5 | "prettier/prettier": "error"
6 | },
7 | "globals": {
8 | "cy": true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Chapter13/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/Chapter13/frontend/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "singleQuote": true,
4 | "semi": true,
5 | "tabWidth": 2,
6 | "trailingComma": "all"
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter13/frontend/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": [
3 | "javascript",
4 | "javascriptreact",
5 | { "language": "typescript", "autoFix": true },
6 | { "language": "typescriptreact", "autoFix": true }
7 | ],
8 | "editor.formatOnSave": true
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter13/frontend/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "chromeWebSecurity": false,
3 | "baseUrl": "http://localhost:3000"
4 | }
5 |
--------------------------------------------------------------------------------
/Chapter13/frontend/cypress/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io",
4 | "body": "Fixtures are a great way to mock data for responses to routes"
5 | }
--------------------------------------------------------------------------------
/Chapter13/frontend/cypress/integration/examples/window.spec.js:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | context('Window', () => {
4 | beforeEach(() => {
5 | cy.visit('https://example.cypress.io/commands/window')
6 | })
7 |
8 | it('cy.window() - get the global window object', () => {
9 | // https://on.cypress.io/window
10 | cy.window().should('have.property', 'top')
11 | })
12 |
13 | it('cy.document() - get the document object', () => {
14 | // https://on.cypress.io/document
15 | cy.document().should('have.property', 'charset').and('eq', 'UTF-8')
16 | })
17 |
18 | it('cy.title() - get the title', () => {
19 | // https://on.cypress.io/title
20 | cy.title().should('include', 'Kitchen Sink')
21 | })
22 | })
23 |
--------------------------------------------------------------------------------
/Chapter13/frontend/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example plugins/index.js can be used to load plugins
3 | //
4 | // You can change the location of this file or turn off loading
5 | // the plugins file with the 'pluginsFile' configuration option.
6 | //
7 | // You can read more here:
8 | // https://on.cypress.io/plugins-guide
9 | // ***********************************************************
10 |
11 | // This function is called when a project is opened or re-opened (e.g. due to
12 | // the project's config changing)
13 |
14 | module.exports = (on, config) => {
15 | // `on` is used to hook into various events Cypress emits
16 | // `config` is the resolved Cypress config
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter13/frontend/cypress/support/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands'
18 |
19 | // Alternatively you can use CommonJS syntax:
20 | // require('./commands')
21 |
--------------------------------------------------------------------------------
/Chapter13/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/ASP.NET-Core-3-and-React/50886f69b5195d2e978cf50a69b11d8102d8fca8/Chapter13/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/Chapter13/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter13/frontend/src/AnswerList.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import { AnswerData } from './QuestionsData';
3 | /** @jsx jsx */
4 | import { css, jsx } from '@emotion/core';
5 | import { Answer } from './Answer';
6 | import { gray5 } from './Styles';
7 |
8 | interface Props {
9 | data: AnswerData[];
10 | }
11 |
12 | export const AnswerList: FC = ({ data }) => (
13 |
20 | {data.map(answer => (
21 | -
27 |
28 |
29 | ))}
30 |
31 | );
32 |
--------------------------------------------------------------------------------
/Chapter13/frontend/src/AppSettings.ts:
--------------------------------------------------------------------------------
1 | export const server = 'http://localhost:17525';
2 |
3 | export const webAPIUrl = `${server}/api`;
4 |
5 | export const authSettings = {
6 | domain: 'your-tenantid.auth0.com',
7 | client_id: 'your-clientid',
8 | redirect_uri: window.location.origin + '/signin-callback',
9 | scope: 'openid profile QandAAPI email',
10 | audience: 'https://qanda',
11 | };
12 |
--------------------------------------------------------------------------------
/Chapter13/frontend/src/AuthorizedPage.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC, Fragment } from 'react';
2 | import { Page } from './Page';
3 | import { useAuth } from './Auth';
4 |
5 | export const AuthorizedPage: FC = ({ children }) => {
6 | const { isAuthenticated } = useAuth();
7 | if (isAuthenticated) {
8 | return {children};
9 | } else {
10 | return ;
11 | }
12 | };
13 |
--------------------------------------------------------------------------------
/Chapter13/frontend/src/Form.test.ts:
--------------------------------------------------------------------------------
1 | import { required } from './Form';
2 |
3 | test('When required is called with empty string, an error should be returned', () => {
4 | const result = required('');
5 | expect(result).toBe('This must be populated');
6 | });
7 |
8 | test('When required is called with a value, an empty string should be returned', () => {
9 | const result = required('test');
10 | expect(result).toBe('');
11 | });
--------------------------------------------------------------------------------
/Chapter13/frontend/src/Icons.tsx:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { css, jsx } from '@emotion/core';
3 | import user from './user.svg';
4 |
5 | export const UserIcon = () => (
6 |
14 | );
15 |
--------------------------------------------------------------------------------
/Chapter13/frontend/src/NotFoundPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Page } from './Page';
3 |
4 | export const NotFoundPage = () => ;
5 |
--------------------------------------------------------------------------------
/Chapter13/frontend/src/Page.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import { Page } from './Page';
4 |
5 | afterEach(cleanup);
6 |
7 | test('When the Page component is rendered, it should contain the correct title and content', () => {
8 | const { getByText } = render(
9 |
10 | Test content
11 | ,
12 | );
13 | const title = getByText('Title test');
14 | expect(title).not.toBeNull();
15 | const content = getByText('Test content');
16 | expect(content).not.toBeNull();
17 | });
18 |
--------------------------------------------------------------------------------
/Chapter13/frontend/src/Page.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | /** @jsx jsx */
3 | import { css, jsx } from '@emotion/core';
4 | import { PageTitle } from './PageTitle';
5 |
6 | interface Props {
7 | title?: string;
8 | }
9 | export const Page: FC = ({ title, children }) => (
10 |
17 | {title &&
{title}}
18 | {children}
19 |
20 | );
21 |
--------------------------------------------------------------------------------
/Chapter13/frontend/src/PageTitle.tsx:
--------------------------------------------------------------------------------
1 | import styled from '@emotion/styled';
2 |
3 | export const PageTitle = styled.h2`
4 | font-size: 15px;
5 | font-weight: bold;
6 | margin: 10px 0px 5px;
7 | text-align: center;
8 | text-transform: uppercase;
9 | `;
10 |
--------------------------------------------------------------------------------
/Chapter13/frontend/src/SignInPage.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react';
2 | import { Page } from './Page';
3 | import { StatusText } from './Styles';
4 | import { useAuth } from './Auth';
5 |
6 | type SigninAction = 'signin' | 'signin-callback';
7 |
8 | interface Props {
9 | action: SigninAction;
10 | }
11 |
12 | export const SignInPage: FC = ({ action }) => {
13 | const { signIn } = useAuth();
14 |
15 | if (action === 'signin') {
16 | signIn();
17 | }
18 |
19 | return (
20 |
21 | Signing in ...
22 |
23 | );
24 | };
25 |
--------------------------------------------------------------------------------
/Chapter13/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | background-color: #f7f8fa;
4 | }
5 |
--------------------------------------------------------------------------------
/Chapter13/frontend/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | ReactDOM.render(, document.getElementById('root'));
8 |
9 | // If you want your app to work offline and load faster, you can change
10 | // unregister() to register() below. Note this comes with some pitfalls.
11 | // Learn more about service workers: https://bit.ly/CRA-PWA
12 | serviceWorker.unregister();
13 |
--------------------------------------------------------------------------------
/Chapter13/frontend/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/Chapter13/frontend/src/user.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Chapter13/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "preserve"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/Chapter13/readme.md:
--------------------------------------------------------------------------------
1 | # Chapter 13 - Adding Automated Tests
2 |
3 | To restore the frontend code for this chapter, open the `frontend` folder in Visual Studio Code and run `npm install` in the terminal. Enter your Auth0 settings in `AppSettings.ts`. Put your test username and password in the cypress tests.
4 |
5 | - `npm start` will then run the app in dev mode.
6 | - `npm test` will run the Jest tests
7 | - `npm run cy:open` will open the Cypress tests
8 |
9 | To restore the backend code for this chapter, open `QandA.sln` in the `backend` folder in Visual Studio. Double check the connection string in `appsettings.json` points to your database and that the Auth0 settings are correct.
10 | - The tests can be run using the *Test Explorer* window
11 | - The backend can be run by pressing *F5*
--------------------------------------------------------------------------------
/Chapter14/BackendTests/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.*~
3 | project.lock.json
4 | .DS_Store
5 | *.pyc
6 |
7 | # Visual Studio Code
8 | .vscode
9 |
10 | # User-specific files
11 | *.suo
12 | *.user
13 | *.userosscache
14 | *.sln.docstates
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | build/
24 | bld/
25 | [Bb]in/
26 | [Oo]bj/
27 | msbuild.log
28 | msbuild.err
29 | msbuild.wrn
30 |
31 | # Visual Studio 2015
32 | .vs/
--------------------------------------------------------------------------------
/Chapter14/BackendTests/BackendTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Chapter14/BackendTests/Calc.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Xunit;
3 |
4 | namespace BackendTests
5 | {
6 | public static class Calc
7 | {
8 | public static decimal Add(decimal a, decimal b)
9 | {
10 | return a + b;
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Chapter14/BackendTests/CalcTests.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | namespace BackendTests
4 | {
5 | public class CalcTests
6 | {
7 | [Fact]
8 | public void Add_When2Integers_ShouldReturnCorrectInteger()
9 | {
10 | var result = Calc.Add(1, 1);
11 | Assert.Equal(2, result);
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/Chapter14/backend/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.*~
3 | project.lock.json
4 | .DS_Store
5 | *.pyc
6 |
7 | # Visual Studio Code
8 | .vscode
9 |
10 | # User-specific files
11 | *.suo
12 | *.user
13 | *.userosscache
14 | *.sln.docstates
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | build/
24 | bld/
25 | [Bb]in/
26 | [Oo]bj/
27 | msbuild.log
28 | msbuild.err
29 | msbuild.wrn
30 |
31 | # Visual Studio 2015
32 | .vs/
--------------------------------------------------------------------------------
/Chapter14/backend/Authorization/MustBeQuestionAuthorRequirement.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Authorization;
2 |
3 | namespace QandA.Authorization
4 | {
5 | public class MustBeQuestionAuthorRequirement : IAuthorizationRequirement
6 | {
7 | public MustBeQuestionAuthorRequirement()
8 | {
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/Chapter14/backend/Data/IQuestionCache.cs:
--------------------------------------------------------------------------------
1 | using QandA.Data.Models;
2 |
3 | namespace QandA.Data
4 | {
5 | public interface IQuestionCache
6 | {
7 | QuestionGetSingleResponse Get(int questionId);
8 | void Remove(int questionId);
9 | void Set(QuestionGetSingleResponse question);
10 | }
11 | }
--------------------------------------------------------------------------------
/Chapter14/backend/Data/Models/AnswerGetResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class AnswerGetResponse
9 | {
10 | public int AnswerId { get; set; }
11 | public string Content { get; set; }
12 | public string UserName { get; set; }
13 | public DateTime Created { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter14/backend/Data/Models/AnswerPostFullRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class AnswerPostFullRequest
9 | {
10 | public int QuestionId { get; set; }
11 | public string Content { get; set; }
12 | public string UserId { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter14/backend/Data/Models/AnswerPostRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 | using Microsoft.AspNetCore.Mvc.ModelBinding;
7 |
8 | namespace QandA.Data.Models
9 | {
10 | public class AnswerPostRequest
11 | {
12 | [Required]
13 | public int? QuestionId { get; set; }
14 | [Required]
15 | public string Content { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter14/backend/Data/Models/QuestionGetManyResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionGetManyResponse
9 | {
10 | public int QuestionId { get; set; }
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | public List Answers { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter14/backend/Data/Models/QuestionGetSingleResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionGetSingleResponse
9 | {
10 | public int QuestionId { get; set; }
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | public string UserId { get; set; }
14 | public string UserName { get; set; }
15 | public DateTime Created { get; set; }
16 | public IEnumerable Answers { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Chapter14/backend/Data/Models/QuestionPostFullRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionPostFullRequest
9 | {
10 | public string Title { get; set; }
11 | public string Content { get; set; }
12 | public string UserId { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter14/backend/Data/Models/QuestionPostRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 |
7 | namespace QandA.Data.Models
8 | {
9 | public class QuestionPostRequest
10 | {
11 | [Required]
12 | [StringLength(100)]
13 | public string Title { get; set; }
14 | [Required(ErrorMessage = "Please include some content for the question")]
15 | public string Content { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter14/backend/Data/Models/QuestionPutRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionPutRequest
9 | {
10 | [StringLength(100)]
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Chapter14/backend/Data/Models/User.cs:
--------------------------------------------------------------------------------
1 | namespace QandA.Data.Models
2 | {
3 | public class User
4 | {
5 | public string Name { get; set; }
6 | }
7 | }
--------------------------------------------------------------------------------
/Chapter14/backend/QandA.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Chapter14/backend/SQLScripts/01-Tables.sql:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/ASP.NET-Core-3-and-React/50886f69b5195d2e978cf50a69b11d8102d8fca8/Chapter14/backend/SQLScripts/01-Tables.sql
--------------------------------------------------------------------------------
/Chapter14/backend/WeatherForecast.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace QandA
4 | {
5 | public class WeatherForecast
6 | {
7 | public DateTime Date { get; set; }
8 |
9 | public int TemperatureC { get; set; }
10 |
11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
12 |
13 | public string Summary { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter14/backend/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "DefaultConnection": "Server=localhost\\SQLEXPRESS;Database=QandA;Trusted_Connection=True;"
4 | },
5 | "Frontend": "http://localhost:3000"
6 | }
7 |
--------------------------------------------------------------------------------
/Chapter14/backend/appsettings.Production.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "DefaultConnection": "Server=tcp:your-server.database.windows.net,1433;Initial Catalog=your-db;Persist Security Info=False;User ID=qanda;Password=your-password;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
4 | },
5 | "Frontend": "https://qanda-frontend.azurewebsites.net"
6 | }
--------------------------------------------------------------------------------
/Chapter14/backend/appsettings.Staging.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "DefaultConnection": "Server=tcp:your-server.database.windows.net,1433;Initial Catalog=your-db-staging;Persist Security Info=False;User ID=qanda;Password=your-password;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
4 | },
5 | "Frontend": "https://qanda-frontend-staging.azurewebsites.net"
6 | }
--------------------------------------------------------------------------------
/Chapter14/backend/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "DefaultConnection": "Server=localhost\\SQLEXPRESS;Database=QandA;Trusted_Connection=True;"
4 | },
5 | "Auth0": {
6 | "Authority": "https://your-tenant-id.auth0.com/",
7 | "Audience": "https://qanda"
8 | },
9 | "Logging": {
10 | "LogLevel": {
11 | "Default": "Information",
12 | "Microsoft": "Warning",
13 | "Microsoft.Hosting.Lifetime": "Information"
14 | }
15 | },
16 | "AllowedHosts": "*"
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter14/frontend/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "react-app",
3 | "plugins": ["prettier"],
4 | "rules": {
5 | "prettier/prettier": "error"
6 | },
7 | "globals": {
8 | "cy": true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Chapter14/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/Chapter14/frontend/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "singleQuote": true,
4 | "semi": true,
5 | "tabWidth": 2,
6 | "trailingComma": "all"
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter14/frontend/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": [
3 | "javascript",
4 | "javascriptreact",
5 | { "language": "typescript", "autoFix": true },
6 | { "language": "typescriptreact", "autoFix": true }
7 | ],
8 | "editor.formatOnSave": true
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter14/frontend/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "chromeWebSecurity": false,
3 | "baseUrl": "http://localhost:3000"
4 | }
5 |
--------------------------------------------------------------------------------
/Chapter14/frontend/cypress/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io",
4 | "body": "Fixtures are a great way to mock data for responses to routes"
5 | }
--------------------------------------------------------------------------------
/Chapter14/frontend/cypress/integration/examples/window.spec.js:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | context('Window', () => {
4 | beforeEach(() => {
5 | cy.visit('https://example.cypress.io/commands/window')
6 | })
7 |
8 | it('cy.window() - get the global window object', () => {
9 | // https://on.cypress.io/window
10 | cy.window().should('have.property', 'top')
11 | })
12 |
13 | it('cy.document() - get the document object', () => {
14 | // https://on.cypress.io/document
15 | cy.document().should('have.property', 'charset').and('eq', 'UTF-8')
16 | })
17 |
18 | it('cy.title() - get the title', () => {
19 | // https://on.cypress.io/title
20 | cy.title().should('include', 'Kitchen Sink')
21 | })
22 | })
23 |
--------------------------------------------------------------------------------
/Chapter14/frontend/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example plugins/index.js can be used to load plugins
3 | //
4 | // You can change the location of this file or turn off loading
5 | // the plugins file with the 'pluginsFile' configuration option.
6 | //
7 | // You can read more here:
8 | // https://on.cypress.io/plugins-guide
9 | // ***********************************************************
10 |
11 | // This function is called when a project is opened or re-opened (e.g. due to
12 | // the project's config changing)
13 |
14 | module.exports = (on, config) => {
15 | // `on` is used to hook into various events Cypress emits
16 | // `config` is the resolved Cypress config
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter14/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/ASP.NET-Core-3-and-React/50886f69b5195d2e978cf50a69b11d8102d8fca8/Chapter14/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/Chapter14/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter14/frontend/public/web.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/Chapter14/frontend/src/AnswerList.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import { AnswerData } from './QuestionsData';
3 | /** @jsx jsx */
4 | import { css, jsx } from '@emotion/core';
5 | import { Answer } from './Answer';
6 | import { gray5 } from './Styles';
7 |
8 | interface Props {
9 | data: AnswerData[];
10 | }
11 |
12 | export const AnswerList: FC = ({ data }) => (
13 |
20 | {data.map(answer => (
21 | -
27 |
28 |
29 | ))}
30 |
31 | );
32 |
--------------------------------------------------------------------------------
/Chapter14/frontend/src/AppSettings.ts:
--------------------------------------------------------------------------------
1 | export const server =
2 | process.env.REACT_APP_ENV === 'production'
3 | ? 'https://your-backend.azurewebsites.net'
4 | : process.env.REACT_APP_ENV === 'staging'
5 | ? 'https://your-backend-staging.azurewebsites.net'
6 | : 'http://localhost:17525';
7 |
8 | export const webAPIUrl = `${server}/api`;
9 |
10 | export const authSettings = {
11 | domain: 'your-tenantid.auth0.com',
12 | client_id: 'your-clientid',
13 | redirect_uri: window.location.origin + '/signin-callback',
14 | scope: 'openid profile QandAAPI email',
15 | audience: 'https://qanda',
16 | };
17 |
--------------------------------------------------------------------------------
/Chapter14/frontend/src/AuthorizedPage.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC, Fragment } from 'react';
2 | import { Page } from './Page';
3 | import { useAuth } from './Auth';
4 |
5 | export const AuthorizedPage: FC = ({ children }) => {
6 | const { isAuthenticated } = useAuth();
7 | if (isAuthenticated) {
8 | return {children};
9 | } else {
10 | return ;
11 | }
12 | };
13 |
--------------------------------------------------------------------------------
/Chapter14/frontend/src/Form.test.ts:
--------------------------------------------------------------------------------
1 | import { required } from './Form';
2 |
3 | test('When required is called with empty string, an error should be returned', () => {
4 | const result = required('');
5 | expect(result).toBe('This must be populated');
6 | });
7 |
8 | test('When required is called with a value, an empty string should be returned', () => {
9 | const result = required('test');
10 | expect(result).toBe('');
11 | });
--------------------------------------------------------------------------------
/Chapter14/frontend/src/Icons.tsx:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { css, jsx } from '@emotion/core';
3 | import user from './user.svg';
4 |
5 | export const UserIcon = () => (
6 |
14 | );
15 |
--------------------------------------------------------------------------------
/Chapter14/frontend/src/NotFoundPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Page } from './Page';
3 |
4 | export const NotFoundPage = () => ;
5 |
--------------------------------------------------------------------------------
/Chapter14/frontend/src/Page.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import { Page } from './Page';
4 |
5 | afterEach(cleanup);
6 |
7 | test('When the Page component is rendered, it should contain the correct title and content', () => {
8 | const { getByText } = render(
9 |
10 | Test content
11 | ,
12 | );
13 | const title = getByText('Title test');
14 | expect(title).not.toBeNull();
15 | const content = getByText('Test content');
16 | expect(content).not.toBeNull();
17 | });
18 |
--------------------------------------------------------------------------------
/Chapter14/frontend/src/Page.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | /** @jsx jsx */
3 | import { css, jsx } from '@emotion/core';
4 | import { PageTitle } from './PageTitle';
5 |
6 | interface Props {
7 | title?: string;
8 | }
9 | export const Page: FC = ({ title, children }) => (
10 |
17 | {title &&
{title}}
18 | {children}
19 |
20 | );
21 |
--------------------------------------------------------------------------------
/Chapter14/frontend/src/PageTitle.tsx:
--------------------------------------------------------------------------------
1 | import styled from '@emotion/styled';
2 |
3 | export const PageTitle = styled.h2`
4 | font-size: 15px;
5 | font-weight: bold;
6 | margin: 10px 0px 5px;
7 | text-align: center;
8 | text-transform: uppercase;
9 | `;
10 |
--------------------------------------------------------------------------------
/Chapter14/frontend/src/SignInPage.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react';
2 | import { Page } from './Page';
3 | import { StatusText } from './Styles';
4 | import { useAuth } from './Auth';
5 |
6 | type SigninAction = 'signin' | 'signin-callback';
7 |
8 | interface Props {
9 | action: SigninAction;
10 | }
11 |
12 | export const SignInPage: FC = ({ action }) => {
13 | const { signIn } = useAuth();
14 |
15 | if (action === 'signin') {
16 | signIn();
17 | }
18 |
19 | return (
20 |
21 | Signing in ...
22 |
23 | );
24 | };
25 |
--------------------------------------------------------------------------------
/Chapter14/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | background-color: #f7f8fa;
4 | }
5 |
--------------------------------------------------------------------------------
/Chapter14/frontend/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | ReactDOM.render(, document.getElementById('root'));
8 |
9 | // If you want your app to work offline and load faster, you can change
10 | // unregister() to register() below. Note this comes with some pitfalls.
11 | // Learn more about service workers: https://bit.ly/CRA-PWA
12 | serviceWorker.unregister();
13 |
--------------------------------------------------------------------------------
/Chapter14/frontend/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/Chapter14/frontend/src/user.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Chapter14/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "preserve"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/Chapter14/readme.md:
--------------------------------------------------------------------------------
1 | # Chapter 14 - Configuring and Deploying to Azure
2 |
3 | To restore the frontend code for this chapter, open the `frontend` folder in Visual Studio Code and run `npm install` in the terminal. Enter your backend paths and Auth0 settings in `AppSettings.ts`.
4 |
5 | To restore the backend code for this chapter, open `QandA.sln` in the `backend` folder in Visual Studio. Put your connection string and Auth0 settings in all the `appsettings.*.json` files.
--------------------------------------------------------------------------------
/Chapter15/BackendTests/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.*~
3 | project.lock.json
4 | .DS_Store
5 | *.pyc
6 |
7 | # Visual Studio Code
8 | .vscode
9 |
10 | # User-specific files
11 | *.suo
12 | *.user
13 | *.userosscache
14 | *.sln.docstates
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | build/
24 | bld/
25 | [Bb]in/
26 | [Oo]bj/
27 | msbuild.log
28 | msbuild.err
29 | msbuild.wrn
30 |
31 | # Visual Studio 2015
32 | .vs/
--------------------------------------------------------------------------------
/Chapter15/BackendTests/BackendTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Chapter15/BackendTests/Calc.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Xunit;
3 |
4 | namespace BackendTests
5 | {
6 | public static class Calc
7 | {
8 | public static decimal Add(decimal a, decimal b)
9 | {
10 | return a + b;
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Chapter15/BackendTests/CalcTests.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | namespace BackendTests
4 | {
5 | public class CalcTests
6 | {
7 | [Fact]
8 | public void Add_When2Integers_ShouldReturnCorrectInteger()
9 | {
10 | var result = Calc.Add(1, 1);
11 | Assert.Equal(2, result);
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/Chapter15/backend/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.*~
3 | project.lock.json
4 | .DS_Store
5 | *.pyc
6 |
7 | # Visual Studio Code
8 | .vscode
9 |
10 | # User-specific files
11 | *.suo
12 | *.user
13 | *.userosscache
14 | *.sln.docstates
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | build/
24 | bld/
25 | [Bb]in/
26 | [Oo]bj/
27 | msbuild.log
28 | msbuild.err
29 | msbuild.wrn
30 |
31 | # Visual Studio 2015
32 | .vs/
--------------------------------------------------------------------------------
/Chapter15/backend/Authorization/MustBeQuestionAuthorRequirement.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Authorization;
2 |
3 | namespace QandA.Authorization
4 | {
5 | public class MustBeQuestionAuthorRequirement : IAuthorizationRequirement
6 | {
7 | public MustBeQuestionAuthorRequirement()
8 | {
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/Chapter15/backend/Data/IQuestionCache.cs:
--------------------------------------------------------------------------------
1 | using QandA.Data.Models;
2 |
3 | namespace QandA.Data
4 | {
5 | public interface IQuestionCache
6 | {
7 | QuestionGetSingleResponse Get(int questionId);
8 | void Remove(int questionId);
9 | void Set(QuestionGetSingleResponse question);
10 | }
11 | }
--------------------------------------------------------------------------------
/Chapter15/backend/Data/Models/AnswerGetResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class AnswerGetResponse
9 | {
10 | public int AnswerId { get; set; }
11 | public string Content { get; set; }
12 | public string UserName { get; set; }
13 | public DateTime Created { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter15/backend/Data/Models/AnswerPostFullRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class AnswerPostFullRequest
9 | {
10 | public int QuestionId { get; set; }
11 | public string Content { get; set; }
12 | public string UserId { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter15/backend/Data/Models/AnswerPostRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 | using Microsoft.AspNetCore.Mvc.ModelBinding;
7 |
8 | namespace QandA.Data.Models
9 | {
10 | public class AnswerPostRequest
11 | {
12 | [Required]
13 | public int? QuestionId { get; set; }
14 | [Required]
15 | public string Content { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter15/backend/Data/Models/QuestionGetManyResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionGetManyResponse
9 | {
10 | public int QuestionId { get; set; }
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | public List Answers { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter15/backend/Data/Models/QuestionGetSingleResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionGetSingleResponse
9 | {
10 | public int QuestionId { get; set; }
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | public string UserId { get; set; }
14 | public string UserName { get; set; }
15 | public DateTime Created { get; set; }
16 | public IEnumerable Answers { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Chapter15/backend/Data/Models/QuestionPostFullRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionPostFullRequest
9 | {
10 | public string Title { get; set; }
11 | public string Content { get; set; }
12 | public string UserId { get; set; }
13 | public string UserName { get; set; }
14 | public DateTime Created { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Chapter15/backend/Data/Models/QuestionPostRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 |
7 | namespace QandA.Data.Models
8 | {
9 | public class QuestionPostRequest
10 | {
11 | [Required]
12 | [StringLength(100)]
13 | public string Title { get; set; }
14 | [Required(ErrorMessage = "Please include some content for the question")]
15 | public string Content { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter15/backend/Data/Models/QuestionPutRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.ComponentModel.DataAnnotations;
6 | namespace QandA.Data.Models
7 | {
8 | public class QuestionPutRequest
9 | {
10 | [StringLength(100)]
11 | public string Title { get; set; }
12 | public string Content { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Chapter15/backend/Data/Models/User.cs:
--------------------------------------------------------------------------------
1 | namespace QandA.Data.Models
2 | {
3 | public class User
4 | {
5 | public string Name { get; set; }
6 | }
7 | }
--------------------------------------------------------------------------------
/Chapter15/backend/QandA.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Chapter15/backend/SQLScripts/01-Tables.sql:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/ASP.NET-Core-3-and-React/50886f69b5195d2e978cf50a69b11d8102d8fca8/Chapter15/backend/SQLScripts/01-Tables.sql
--------------------------------------------------------------------------------
/Chapter15/backend/WeatherForecast.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace QandA
4 | {
5 | public class WeatherForecast
6 | {
7 | public DateTime Date { get; set; }
8 |
9 | public int TemperatureC { get; set; }
10 |
11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
12 |
13 | public string Summary { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter15/backend/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "DefaultConnection": "Server=localhost\\SQLEXPRESS;Database=QandA;Trusted_Connection=True;"
4 | },
5 | "Frontend": "http://localhost:3000"
6 | }
7 |
--------------------------------------------------------------------------------
/Chapter15/backend/appsettings.Production.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "DefaultConnection": "Server=tcp:your-server.database.windows.net,1433;Initial Catalog=your-db;Persist Security Info=False;User ID=qanda;Password=your-password;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
4 | },
5 | "Frontend": "https://qanda-frontend.azurewebsites.net"
6 | }
--------------------------------------------------------------------------------
/Chapter15/backend/appsettings.Staging.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "DefaultConnection": "Server=tcp:your-server.database.windows.net,1433;Initial Catalog=your-db-staging;Persist Security Info=False;User ID=qanda;Password=your-password;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
4 | },
5 | "Frontend": "https://qanda-frontend-staging.azurewebsites.net"
6 | }
--------------------------------------------------------------------------------
/Chapter15/backend/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "DefaultConnection": "Server=localhost\\SQLEXPRESS;Database=QandA;Trusted_Connection=True;"
4 | },
5 | "Auth0": {
6 | "Authority": "https://your-tenant-id.auth0.com/",
7 | "Audience": "https://qanda"
8 | },
9 | "Logging": {
10 | "LogLevel": {
11 | "Default": "Information",
12 | "Microsoft": "Warning",
13 | "Microsoft.Hosting.Lifetime": "Information"
14 | }
15 | },
16 | "AllowedHosts": "*"
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter15/frontend/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "react-app",
3 | "plugins": ["prettier"],
4 | "rules": {
5 | "prettier/prettier": "error"
6 | },
7 | "globals": {
8 | "cy": true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Chapter15/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/Chapter15/frontend/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "singleQuote": true,
4 | "semi": true,
5 | "tabWidth": 2,
6 | "trailingComma": "all"
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter15/frontend/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.validate": [
3 | "javascript",
4 | "javascriptreact",
5 | { "language": "typescript", "autoFix": true },
6 | { "language": "typescriptreact", "autoFix": true }
7 | ],
8 | "editor.formatOnSave": true
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter15/frontend/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "chromeWebSecurity": false,
3 | "baseUrl": "http://localhost:3000"
4 | }
5 |
--------------------------------------------------------------------------------
/Chapter15/frontend/cypress/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io",
4 | "body": "Fixtures are a great way to mock data for responses to routes"
5 | }
--------------------------------------------------------------------------------
/Chapter15/frontend/cypress/integration/examples/window.spec.js:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | context('Window', () => {
4 | beforeEach(() => {
5 | cy.visit('https://example.cypress.io/commands/window')
6 | })
7 |
8 | it('cy.window() - get the global window object', () => {
9 | // https://on.cypress.io/window
10 | cy.window().should('have.property', 'top')
11 | })
12 |
13 | it('cy.document() - get the document object', () => {
14 | // https://on.cypress.io/document
15 | cy.document().should('have.property', 'charset').and('eq', 'UTF-8')
16 | })
17 |
18 | it('cy.title() - get the title', () => {
19 | // https://on.cypress.io/title
20 | cy.title().should('include', 'Kitchen Sink')
21 | })
22 | })
23 |
--------------------------------------------------------------------------------
/Chapter15/frontend/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example plugins/index.js can be used to load plugins
3 | //
4 | // You can change the location of this file or turn off loading
5 | // the plugins file with the 'pluginsFile' configuration option.
6 | //
7 | // You can read more here:
8 | // https://on.cypress.io/plugins-guide
9 | // ***********************************************************
10 |
11 | // This function is called when a project is opened or re-opened (e.g. due to
12 | // the project's config changing)
13 |
14 | module.exports = (on, config) => {
15 | // `on` is used to hook into various events Cypress emits
16 | // `config` is the resolved Cypress config
17 | }
18 |
--------------------------------------------------------------------------------
/Chapter15/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/ASP.NET-Core-3-and-React/50886f69b5195d2e978cf50a69b11d8102d8fca8/Chapter15/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/Chapter15/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter15/frontend/public/web.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/Chapter15/frontend/src/AnswerList.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import { AnswerData } from './QuestionsData';
3 | /** @jsx jsx */
4 | import { css, jsx } from '@emotion/core';
5 | import { Answer } from './Answer';
6 | import { gray5 } from './Styles';
7 |
8 | interface Props {
9 | data: AnswerData[];
10 | }
11 |
12 | export const AnswerList: FC = ({ data }) => (
13 |
20 | {data.map(answer => (
21 | -
27 |
28 |
29 | ))}
30 |
31 | );
32 |
--------------------------------------------------------------------------------
/Chapter15/frontend/src/AppSettings.ts:
--------------------------------------------------------------------------------
1 | export const server =
2 | process.env.REACT_APP_ENV === 'production'
3 | ? 'https://your-backend.azurewebsites.net'
4 | : process.env.REACT_APP_ENV === 'staging'
5 | ? 'https://your-backend-staging.azurewebsites.net'
6 | : 'http://localhost:17525';
7 |
8 | export const webAPIUrl = `${server}/api`;
9 |
10 | export const authSettings = {
11 | domain: 'your-tenantid.auth0.com',
12 | client_id: 'your-clientid',
13 | redirect_uri: window.location.origin + '/signin-callback',
14 | scope: 'openid profile QandAAPI email',
15 | audience: 'https://qanda',
16 | };
17 |
18 |
--------------------------------------------------------------------------------
/Chapter15/frontend/src/AuthorizedPage.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC, Fragment } from 'react';
2 | import { Page } from './Page';
3 | import { useAuth } from './Auth';
4 |
5 | export const AuthorizedPage: FC = ({ children }) => {
6 | const { isAuthenticated } = useAuth();
7 | if (isAuthenticated) {
8 | return {children};
9 | } else {
10 | return ;
11 | }
12 | };
13 |
--------------------------------------------------------------------------------
/Chapter15/frontend/src/Form.test.ts:
--------------------------------------------------------------------------------
1 | import { required } from './Form';
2 |
3 | test('When required is called with empty string, an error should be returned', () => {
4 | const result = required('');
5 | expect(result).toBe('This must be populated');
6 | });
7 |
8 | test('When required is called with a value, an empty string should be returned', () => {
9 | const result = required('test');
10 | expect(result).toBe('');
11 | });
--------------------------------------------------------------------------------
/Chapter15/frontend/src/Icons.tsx:
--------------------------------------------------------------------------------
1 | /** @jsx jsx */
2 | import { css, jsx } from '@emotion/core';
3 | import user from './user.svg';
4 |
5 | export const UserIcon = () => (
6 |
14 | );
15 |
--------------------------------------------------------------------------------
/Chapter15/frontend/src/NotFoundPage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Page } from './Page';
3 |
4 | export const NotFoundPage = () => ;
5 |
--------------------------------------------------------------------------------
/Chapter15/frontend/src/Page.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, cleanup } from '@testing-library/react';
3 | import { Page } from './Page';
4 |
5 | afterEach(cleanup);
6 |
7 | test('When the Page component is rendered, it should contain the correct title and content', () => {
8 | const { getByText } = render(
9 |
10 | Test content
11 | ,
12 | );
13 | const title = getByText('Title test');
14 | expect(title).not.toBeNull();
15 | const content = getByText('Test content');
16 | expect(content).not.toBeNull();
17 | });
18 |
--------------------------------------------------------------------------------
/Chapter15/frontend/src/Page.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | /** @jsx jsx */
3 | import { css, jsx } from '@emotion/core';
4 | import { PageTitle } from './PageTitle';
5 |
6 | interface Props {
7 | title?: string;
8 | }
9 | export const Page: FC = ({ title, children }) => (
10 |
17 | {title &&
{title}}
18 | {children}
19 |
20 | );
21 |
--------------------------------------------------------------------------------
/Chapter15/frontend/src/PageTitle.tsx:
--------------------------------------------------------------------------------
1 | import styled from '@emotion/styled';
2 |
3 | export const PageTitle = styled.h2`
4 | font-size: 15px;
5 | font-weight: bold;
6 | margin: 10px 0px 5px;
7 | text-align: center;
8 | text-transform: uppercase;
9 | `;
10 |
--------------------------------------------------------------------------------
/Chapter15/frontend/src/SignInPage.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react';
2 | import { Page } from './Page';
3 | import { StatusText } from './Styles';
4 | import { useAuth } from './Auth';
5 |
6 | type SigninAction = 'signin' | 'signin-callback';
7 |
8 | interface Props {
9 | action: SigninAction;
10 | }
11 |
12 | export const SignInPage: FC = ({ action }) => {
13 | const { signIn } = useAuth();
14 |
15 | if (action === 'signin') {
16 | signIn();
17 | }
18 |
19 | return (
20 |
21 | Signing in ...
22 |
23 | );
24 | };
25 |
--------------------------------------------------------------------------------
/Chapter15/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | background-color: #f7f8fa;
4 | }
5 |
--------------------------------------------------------------------------------
/Chapter15/frontend/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | ReactDOM.render(, document.getElementById('root'));
8 |
9 | // If you want your app to work offline and load faster, you can change
10 | // unregister() to register() below. Note this comes with some pitfalls.
11 | // Learn more about service workers: https://bit.ly/CRA-PWA
12 | serviceWorker.unregister();
13 |
--------------------------------------------------------------------------------
/Chapter15/frontend/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/Chapter15/frontend/src/user.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Chapter15/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "preserve"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/Chapter15/readme.md:
--------------------------------------------------------------------------------
1 | # Chapter 15 - Implementing CI and CD with Azure DevOps
2 |
3 | To restore the frontend code for this chapter, open the `frontend` folder in Visual Studio Code and run `npm install` in the terminal. Enter your backend paths and Auth0 settings in `AppSettings.ts`. Put your test username and password in the cypress tests.
4 |
5 | To restore the backend code for this chapter, open `QandA.sln` in the `backend` folder in Visual Studio. Put your connection string and Auth0 settings in all the `appsettings.*.json` files.
--------------------------------------------------------------------------------