├── 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 |
8 |
9 | logo 10 |

11 | Edit src/App.tsx and save to reload. 12 |

13 | 19 | Learn React 20 | 21 |
22 |
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 | User 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 | User 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 | User 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 | User 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 | User 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 | User 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 | User 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 | User 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 | User 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 | User 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. --------------------------------------------------------------------------------