Repository: rolling-scopes/rsschool-app Branch: master Commit: a3363fecfac5 Files: 1783 Total size: 5.0 MB Directory structure: gitextract_5q18uku4/ ├── .agents/ │ └── skills/ │ ├── nestjs-best-practices/ │ │ ├── .github/ │ │ │ └── workflows/ │ │ │ ├── branch-protection.yml │ │ │ └── deploy.yml │ │ ├── .gitignore │ │ ├── AGENTS.md │ │ ├── README.md │ │ ├── SKILL.md │ │ ├── rules/ │ │ │ ├── api-use-dto-serialization.md │ │ │ ├── api-use-interceptors.md │ │ │ ├── api-use-pipes.md │ │ │ ├── api-versioning.md │ │ │ ├── arch-avoid-circular-deps.md │ │ │ ├── arch-feature-modules.md │ │ │ ├── arch-module-sharing.md │ │ │ ├── arch-single-responsibility.md │ │ │ ├── arch-use-events.md │ │ │ ├── arch-use-repository-pattern.md │ │ │ ├── db-avoid-n-plus-one.md │ │ │ ├── db-use-migrations.md │ │ │ ├── db-use-transactions.md │ │ │ ├── devops-graceful-shutdown.md │ │ │ ├── devops-use-config-module.md │ │ │ ├── devops-use-logging.md │ │ │ ├── di-avoid-service-locator.md │ │ │ ├── di-interface-segregation.md │ │ │ ├── di-liskov-substitution.md │ │ │ ├── di-prefer-constructor-injection.md │ │ │ ├── di-scope-awareness.md │ │ │ ├── di-use-interfaces-tokens.md │ │ │ ├── error-handle-async-errors.md │ │ │ ├── error-throw-http-exceptions.md │ │ │ ├── error-use-exception-filters.md │ │ │ ├── micro-use-health-checks.md │ │ │ ├── micro-use-patterns.md │ │ │ ├── micro-use-queues.md │ │ │ ├── perf-async-hooks.md │ │ │ ├── perf-lazy-loading.md │ │ │ ├── perf-optimize-database.md │ │ │ ├── perf-use-caching.md │ │ │ ├── security-auth-jwt.md │ │ │ ├── security-rate-limiting.md │ │ │ ├── security-sanitize-output.md │ │ │ ├── security-use-guards.md │ │ │ ├── security-validate-all-input.md │ │ │ ├── test-e2e-supertest.md │ │ │ ├── test-mock-external-services.md │ │ │ └── test-use-testing-module.md │ │ └── scripts/ │ │ ├── build-agents.ts │ │ ├── build.sh │ │ └── package.json │ ├── typeorm/ │ │ └── SKILL.md │ └── typescript-advanced-types/ │ └── SKILL.md ├── .dockerignore ├── .editorconfig ├── .eslintignore ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug-report.md │ │ ├── data-issue-report.md │ │ └── feature-request.md │ ├── auto-label.json │ ├── copilot-instructions.md │ ├── pull_request_template.md │ └── workflows/ │ ├── deploy-sloths.yaml │ ├── deploy.yaml │ ├── pull_request.yml │ ├── pull_request_close.yml │ └── renovate.yml ├── .gitignore ├── .oxfmtrc.json ├── AGENTS.md ├── CONTRIBUTING.md ├── DOMAIN.md ├── GUIDELINES.md ├── LICENSE ├── README.md ├── VITEST.md ├── client/ │ ├── .dockerignore │ ├── Dockerfile │ ├── Dockerfile.lambda │ ├── README.md │ ├── eslint.config.mjs │ ├── next-env.d.ts │ ├── next.config.mjs │ ├── next.config.prod.mjs │ ├── package.json │ ├── playwright.config.ts │ ├── public/ │ │ └── static/ │ │ └── empty.txt │ ├── specs/ │ │ └── smoke.spec.ts │ ├── src/ │ │ ├── __mocks__/ │ │ │ ├── axios.js │ │ │ ├── hooks/ │ │ │ │ ├── index.ts │ │ │ │ ├── useMessage.tsx │ │ │ │ └── useTheme.tsx │ │ │ └── next/ │ │ │ └── config.ts │ │ ├── __tests__/ │ │ │ ├── ProfilePage.test.tsx │ │ │ └── __snapshots__/ │ │ │ └── ProfilePage.test.tsx.snap │ │ ├── api/ │ │ │ ├── .gitignore │ │ │ ├── .npmignore │ │ │ ├── .openapi-generator/ │ │ │ │ ├── FILES │ │ │ │ └── VERSION │ │ │ ├── .openapi-generator-ignore │ │ │ ├── api.ts │ │ │ ├── base.ts │ │ │ ├── common.ts │ │ │ ├── configuration.ts │ │ │ ├── git_push.sh │ │ │ └── index.ts │ │ ├── components/ │ │ │ ├── Analytics.tsx │ │ │ ├── Comment.tsx │ │ │ ├── CountBadge/ │ │ │ │ ├── CountBadge.tsx │ │ │ │ └── index.tsx │ │ │ ├── CoursePageLayout.tsx │ │ │ ├── DevTools/ │ │ │ │ ├── DevToolsContainer.tsx │ │ │ │ ├── DevToolsCurrentUser.tsx │ │ │ │ ├── DevToolsUsers.tsx │ │ │ │ └── index.ts │ │ │ ├── Footer/ │ │ │ │ ├── Donation.tsx │ │ │ │ ├── Feedback.tsx │ │ │ │ ├── FooterLayout.tsx │ │ │ │ ├── Help.tsx │ │ │ │ ├── Menu.tsx │ │ │ │ ├── SocialNetworks.tsx │ │ │ │ └── index.tsx │ │ │ ├── HeaderMiniBannerCarousel.module.css │ │ │ ├── HeaderMiniBannerCarousel.test.tsx │ │ │ ├── HeaderMiniBannerCarousel.tsx │ │ │ ├── Heroes/ │ │ │ │ ├── HeroesCountBadge.tsx │ │ │ │ ├── HeroesRadarTab.tsx │ │ │ │ └── HeroesRadarTable.tsx │ │ │ ├── LoadingScreen.module.css │ │ │ ├── MentorOptions.tsx │ │ │ ├── Profile/ │ │ │ │ ├── AboutCard.tsx │ │ │ │ ├── CommonCard.tsx │ │ │ │ ├── CommonCardWithSettingsModal.tsx │ │ │ │ ├── ContactsCard.tsx │ │ │ │ ├── ContactsCardForm.tsx │ │ │ │ ├── DiscordCard.tsx │ │ │ │ ├── EducationCard.tsx │ │ │ │ ├── EmailConfirmation.tsx │ │ │ │ ├── InterviewCard.tsx │ │ │ │ ├── InterviewModal.tsx │ │ │ │ ├── LanguagesCard.tsx │ │ │ │ ├── MainCard.tsx │ │ │ │ ├── MentorStatsCard.tsx │ │ │ │ ├── MentorStatsModal.tsx │ │ │ │ ├── ObfuscateConfirmationModal.tsx │ │ │ │ ├── ProfileSettingsModal.module.css │ │ │ │ ├── ProfileSettingsModal.tsx │ │ │ │ ├── PublicFeedbackCard.tsx │ │ │ │ ├── PublicFeedbackModal.tsx │ │ │ │ ├── StudentLeaveCourse.tsx │ │ │ │ ├── StudentStatsCard.tsx │ │ │ │ ├── StudentStatsModal.tsx │ │ │ │ ├── __test__/ │ │ │ │ │ ├── AboutCard.test.tsx │ │ │ │ │ ├── CommonCard.test.tsx │ │ │ │ │ ├── CommonCardWithSettingsModal.test.tsx │ │ │ │ │ ├── ContactsCard.test.tsx │ │ │ │ │ ├── ContactsCardForm.test.tsx │ │ │ │ │ ├── EducationCard.test.tsx │ │ │ │ │ ├── InterviewCard.test.tsx │ │ │ │ │ ├── InterviewModal.test.tsx │ │ │ │ │ ├── MainCard.test.tsx │ │ │ │ │ ├── MentorStatsCard.test.tsx │ │ │ │ │ ├── MentorStatsModal.test.tsx │ │ │ │ │ ├── ProfileSettingsModal.test.tsx │ │ │ │ │ ├── PublicFeedbackCard.test.tsx │ │ │ │ │ ├── PublicFeedbackModal.test.tsx │ │ │ │ │ ├── StudentStatsCard.test.tsx │ │ │ │ │ ├── StudentStatsModal.test.tsx │ │ │ │ │ └── __snapshots__/ │ │ │ │ │ ├── AboutCard.test.tsx.snap │ │ │ │ │ ├── CommonCard.test.tsx.snap │ │ │ │ │ ├── CommonCardWithSettingsModal.test.tsx.snap │ │ │ │ │ ├── ContactsCard.test.tsx.snap │ │ │ │ │ ├── ContactsCardForm.test.tsx.snap │ │ │ │ │ ├── EducationCard.test.tsx.snap │ │ │ │ │ ├── MainCard.test.tsx.snap │ │ │ │ │ ├── ProfileSettingsModal.test.tsx.snap │ │ │ │ │ ├── PublicFeedbackCard.test.tsx.snap │ │ │ │ │ ├── PublicFeedbackModal.test.tsx.snap │ │ │ │ │ ├── StudentStatsCard.test.tsx.snap │ │ │ │ │ └── StudentStatsModal.test.tsx.snap │ │ │ │ └── ui/ │ │ │ │ ├── DateWidget.tsx │ │ │ │ ├── ExpandButtonWidget.tsx │ │ │ │ ├── InterviewerWidget.tsx │ │ │ │ ├── IsGoodCandidateWidget.tsx │ │ │ │ ├── LegacyScreeningFeedback.tsx │ │ │ │ ├── PrescreeningFeedback.tsx │ │ │ │ ├── ScoreWidget.tsx │ │ │ │ ├── __tests__/ │ │ │ │ │ ├── DateWidget.test.tsx │ │ │ │ │ ├── ExpandButtonWidget.test.tsx │ │ │ │ │ ├── InterviewerWidget.test.tsx │ │ │ │ │ ├── IsGoodCandidateWidget.test.tsx │ │ │ │ │ └── ScoreWidget.test.tsx │ │ │ │ └── index.ts │ │ │ ├── RegistrationPageLayout.tsx │ │ │ ├── SelectLanguages.tsx │ │ │ ├── SettingsItem.tsx │ │ │ ├── SlothImage.tsx │ │ │ ├── Student/ │ │ │ │ ├── AssignStudentModal.tsx │ │ │ │ ├── DashboardDetails.module.css │ │ │ │ ├── DashboardDetails.tsx │ │ │ │ └── index.ts │ │ │ ├── StudentDiscord.tsx │ │ │ ├── TabsWithCounter/ │ │ │ │ └── renderers.tsx │ │ │ ├── Warning/ │ │ │ │ └── index.tsx │ │ │ ├── WelcomeCard.tsx │ │ │ ├── __tests__/ │ │ │ │ ├── CopyToClipboardButton.test.tsx │ │ │ │ ├── GithubUserLink.test.tsx │ │ │ │ ├── Rating.test.tsx │ │ │ │ └── StudenDiscrod.test.tsx │ │ │ ├── common/ │ │ │ │ └── CustomPopconfirm.tsx │ │ │ ├── useLoading.tsx │ │ │ ├── withGoogleMaps.tsx │ │ │ └── withSession.tsx │ │ ├── configs/ │ │ │ ├── cdn.ts │ │ │ ├── course-icons.ts │ │ │ ├── discord-integration.ts │ │ │ ├── gcp.ts │ │ │ ├── heroes-badges.ts │ │ │ ├── registry.ts │ │ │ └── timezones.ts │ │ ├── data/ │ │ │ ├── course-leave-reasons.ts │ │ │ ├── english.ts │ │ │ ├── eventTypes.ts │ │ │ ├── index.ts │ │ │ ├── interviews/ │ │ │ │ ├── __tests__/ │ │ │ │ │ └── templateValidator.test.ts │ │ │ │ ├── angular.ts │ │ │ │ ├── corejs1.ts │ │ │ │ ├── corejs2.ts │ │ │ │ ├── index.ts │ │ │ │ ├── react.ts │ │ │ │ ├── shortTrackJavaScript.ts │ │ │ │ ├── shortTrackPerformance.ts │ │ │ │ ├── shortTrackScreening.ts │ │ │ │ ├── shortTrackTypeScript.ts │ │ │ │ ├── technical-screening.tsx │ │ │ │ ├── templateValidator.ts │ │ │ │ └── types.ts │ │ │ ├── skills.ts │ │ │ ├── taskTypes.ts │ │ │ └── tshirts.ts │ │ ├── domain/ │ │ │ ├── course.test.ts │ │ │ ├── course.ts │ │ │ ├── interview.test.ts │ │ │ ├── interview.tsx │ │ │ ├── user.test.tsx │ │ │ └── user.ts │ │ ├── hooks/ │ │ │ └── index.ts │ │ ├── index.d.ts │ │ ├── modules/ │ │ │ ├── AutoTest/ │ │ │ │ ├── components/ │ │ │ │ │ ├── AttemptsAnswers/ │ │ │ │ │ │ └── AttemptsAnswers.tsx │ │ │ │ │ ├── AutoTestTaskCard/ │ │ │ │ │ │ └── AutoTestTaskCard.tsx │ │ │ │ │ ├── Coding/ │ │ │ │ │ │ ├── Coding.test.tsx │ │ │ │ │ │ └── Coding.tsx │ │ │ │ │ ├── Exercise/ │ │ │ │ │ │ └── Exercise.tsx │ │ │ │ │ ├── JupyterNotebook/ │ │ │ │ │ │ └── JupyterNotebook.tsx │ │ │ │ │ ├── Question/ │ │ │ │ │ │ ├── Question.module.css │ │ │ │ │ │ └── Question.tsx │ │ │ │ │ ├── SelfEducation/ │ │ │ │ │ │ ├── SelfEducation.module.css │ │ │ │ │ │ ├── SelfEducation.test.tsx │ │ │ │ │ │ └── SelfEducation.tsx │ │ │ │ │ ├── StatusTabs/ │ │ │ │ │ │ ├── StatusTabs.test.tsx │ │ │ │ │ │ ├── StatusTabs.tsx │ │ │ │ │ │ └── renderers.tsx │ │ │ │ │ ├── TaskCard/ │ │ │ │ │ │ ├── TaskCard.test.tsx │ │ │ │ │ │ └── TaskCard.tsx │ │ │ │ │ ├── TaskCardColumn/ │ │ │ │ │ │ └── TaskCardColumn.tsx │ │ │ │ │ ├── TaskDeadlineDate/ │ │ │ │ │ │ ├── TaskDeadlineDate.test.tsx │ │ │ │ │ │ └── TaskDeadlineDate.tsx │ │ │ │ │ ├── TaskDescription/ │ │ │ │ │ │ └── TaskDescription.tsx │ │ │ │ │ ├── VerificationInformation/ │ │ │ │ │ │ ├── VerificationInformation.test.tsx │ │ │ │ │ │ └── VerificationInformation.tsx │ │ │ │ │ ├── VerificationsTable/ │ │ │ │ │ │ ├── VerificationsTable.test.tsx │ │ │ │ │ │ ├── VerificationsTable.tsx │ │ │ │ │ │ └── renderers.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── hooks/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useAttemptsMessage/ │ │ │ │ │ │ ├── useAttemptsMessage.test.ts │ │ │ │ │ │ └── useAttemptsMessage.ts │ │ │ │ │ ├── useCourseTaskSubmit/ │ │ │ │ │ │ ├── useCourseTaskSubmit.test.ts │ │ │ │ │ │ └── useCourseTaskSubmit.ts │ │ │ │ │ ├── useCourseTaskVerifications/ │ │ │ │ │ │ └── useCourseTaskVerifications.ts │ │ │ │ │ └── useVerificationsAnswers/ │ │ │ │ │ └── useVerificationsAnswers.ts │ │ │ │ ├── pages/ │ │ │ │ │ ├── AutoTests/ │ │ │ │ │ │ └── AutoTests.tsx │ │ │ │ │ ├── Task/ │ │ │ │ │ │ └── Task.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── types.ts │ │ │ │ └── utils/ │ │ │ │ └── map.ts │ │ │ ├── Contributor/ │ │ │ │ ├── components/ │ │ │ │ │ ├── ContributorModal.tsx │ │ │ │ │ └── ContributorsTable.tsx │ │ │ │ └── pages/ │ │ │ │ └── ContributorPage.tsx │ │ │ ├── Course/ │ │ │ │ ├── components/ │ │ │ │ │ ├── CourseNoAccess.tsx │ │ │ │ │ └── NoSubmissionAvailable/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── contexts/ │ │ │ │ │ ├── ActiveCourseContext.tsx │ │ │ │ │ ├── SessionContext.test.tsx │ │ │ │ │ ├── SessionContext.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── pages/ │ │ │ │ ├── CouseNoAccess/ │ │ │ │ │ └── index.tsx │ │ │ │ └── Student/ │ │ │ │ └── CrossCheckSubmit/ │ │ │ │ └── index.tsx │ │ │ ├── CourseManagement/ │ │ │ │ ├── components/ │ │ │ │ │ ├── CertificateCriteriaModal/ │ │ │ │ │ │ ├── CertificateCriteriaModal.test.tsx │ │ │ │ │ │ └── CertificateCriteriaModal.tsx │ │ │ │ │ ├── CourseEventModal/ │ │ │ │ │ │ ├── formState.ts │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── CourseModal/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── CourseTaskModal/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── CoursesListModal/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── ExpelCriteriaModal/ │ │ │ │ │ │ ├── ExpelCriteriaModal.test.tsx │ │ │ │ │ │ └── ExpelCriteriaModal.tsx │ │ │ │ │ ├── ExpelledStudentsStats.tsx │ │ │ │ │ ├── SelectCourseTasks/ │ │ │ │ │ │ ├── SelectCourseTasks.test.tsx │ │ │ │ │ │ └── SelectCourseTasks.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── hooks/ │ │ │ │ └── useExpelledStats.ts │ │ │ ├── CourseStatistics/ │ │ │ │ ├── components/ │ │ │ │ │ ├── CountriesChart/ │ │ │ │ │ │ └── CountriesChart.tsx │ │ │ │ │ ├── DonutChart/ │ │ │ │ │ │ └── DonutChart.tsx │ │ │ │ │ ├── EpamMentorsStatsCard/ │ │ │ │ │ │ ├── EpamMentorsStatsCard.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── LiquidChart/ │ │ │ │ │ │ └── LiquidChart.tsx │ │ │ │ │ ├── MentorsCountriesCard/ │ │ │ │ │ │ ├── MentorsCountriesCard.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── StatCards/ │ │ │ │ │ │ ├── StatCards.module.css │ │ │ │ │ │ ├── StatCards.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── StatScopeSelector/ │ │ │ │ │ │ ├── StatScopeSelector.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── StudentsCertificatesCountriesCard/ │ │ │ │ │ │ ├── StudentsCertificatesCountriesCard.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── StudentsCountriesCard/ │ │ │ │ │ │ ├── StudentsCountriesCard.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── StudentsEligibleForCertificationCard/ │ │ │ │ │ │ ├── StudentsEligibleForCertificationCard.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── StudentsStatsCard/ │ │ │ │ │ │ ├── StudentsStatsCard.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── StudentsWithCertificateCard/ │ │ │ │ │ │ ├── StudentsWithCertificateCard.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── StudentsWithMentorsCard/ │ │ │ │ │ │ ├── StudentsWithMentorsCard.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── TaskPerformanceCard/ │ │ │ │ │ ├── TaskPerformanceCard.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── constants.ts │ │ │ │ ├── data.ts │ │ │ │ ├── hooks/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── useCourseStats.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── pages/ │ │ │ │ └── CourseStatistics.tsx │ │ │ ├── CrossCheck/ │ │ │ │ ├── AddCriteriaForCrossCheck.tsx │ │ │ │ ├── CriteriaActions.tsx │ │ │ │ ├── CriteriaTypeSelect.tsx │ │ │ │ ├── DeleteAllCrossCheckCriteriaButton.tsx │ │ │ │ ├── EditableCellForCrossCheck.tsx │ │ │ │ ├── EditableCriteriaInput.tsx │ │ │ │ ├── EditableTableForCrossCheck.tsx │ │ │ │ ├── ExportJSONButton.tsx │ │ │ │ ├── UploadCriteriaJSON.tsx │ │ │ │ ├── __tests__/ │ │ │ │ │ ├── AddCriteriaForCrossCheck.test.tsx │ │ │ │ │ ├── ExportJSONButton.test.tsx │ │ │ │ │ ├── UploadCriteriaJSON.test.tsx │ │ │ │ │ └── __snapshots__/ │ │ │ │ │ └── AddCriteriaForCrossCheck.test.tsx.snap │ │ │ │ ├── components/ │ │ │ │ │ ├── CriteriaForm.tsx │ │ │ │ │ ├── CrossCheckAssignmentLink.tsx │ │ │ │ │ ├── CrossCheckCriteriaForm.module.css │ │ │ │ │ ├── CrossCheckCriteriaForm.tsx │ │ │ │ │ ├── CrossCheckHistory.tsx │ │ │ │ │ ├── DragHandle.module.css │ │ │ │ │ ├── DragHandle.tsx │ │ │ │ │ ├── DragSortTable.tsx │ │ │ │ │ ├── SolutionReview/ │ │ │ │ │ │ ├── Message/ │ │ │ │ │ │ │ ├── Message.test.tsx │ │ │ │ │ │ │ ├── Message.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── MessageSendingPanel/ │ │ │ │ │ │ │ ├── MessageSendingPanel.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── SolutionReview.module.css │ │ │ │ │ │ ├── SolutionReview.tsx │ │ │ │ │ │ ├── UserAvatar/ │ │ │ │ │ │ │ ├── UserAvatar.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── Username/ │ │ │ │ │ │ │ ├── Username.test.tsx │ │ │ │ │ │ │ ├── Username.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── helpers.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── SolutionReviewSettingsPanel/ │ │ │ │ │ │ ├── SolutionReviewSettingsPanel.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── SubmittedStatus.tsx │ │ │ │ │ └── criteria/ │ │ │ │ │ ├── CrossCheckCriteria.tsx │ │ │ │ │ ├── CrossCheckCriteriaModal.tsx │ │ │ │ │ ├── PenaltyCriteria.tsx │ │ │ │ │ ├── SubtaskCriteria.tsx │ │ │ │ │ └── TitleCriteria.tsx │ │ │ │ ├── constants.ts │ │ │ │ ├── hooks/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── useSolutionReviewSettings.ts │ │ │ │ ├── index.tsx │ │ │ │ └── utils/ │ │ │ │ ├── addKeyAndIndex.tsx │ │ │ │ ├── arrayMoveImmutable.tsx │ │ │ │ └── getCriteriaStatusColor.ts │ │ │ ├── CrossCheckPairs/ │ │ │ │ ├── components/ │ │ │ │ │ ├── BadReview/ │ │ │ │ │ │ ├── BadReviewControllers.tsx │ │ │ │ │ │ └── BadReviewTable.tsx │ │ │ │ │ └── CrossCheckPairsTable/ │ │ │ │ │ ├── CrossCheckPairsTable.module.css │ │ │ │ │ └── CrossCheckPairsTable.tsx │ │ │ │ ├── data/ │ │ │ │ │ └── getCrossCheckPairsColumns.tsx │ │ │ │ └── pages/ │ │ │ │ └── CrossCheckPairs/ │ │ │ │ ├── CrossCheckPairs.tsx │ │ │ │ └── index.ts │ │ │ ├── Discipline/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DisciplineModal.tsx │ │ │ │ │ └── DisciplineTable.tsx │ │ │ │ └── pages/ │ │ │ │ └── DisciplinePage.tsx │ │ │ ├── DiscordAdmin/ │ │ │ │ ├── components/ │ │ │ │ │ ├── DiscordServersModal.tsx │ │ │ │ │ └── DiscordServersTable.tsx │ │ │ │ ├── hooks/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── useDiscordServers.ts │ │ │ │ ├── index.ts │ │ │ │ └── pages/ │ │ │ │ └── DiscordAdminPage/ │ │ │ │ ├── DiscordAdminPage.tsx │ │ │ │ └── index.ts │ │ │ ├── EventsAdmin/ │ │ │ │ ├── components/ │ │ │ │ │ ├── EventsModal.tsx │ │ │ │ │ └── EventsTable.tsx │ │ │ │ ├── hooks/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── useEvents.ts │ │ │ │ ├── index.ts │ │ │ │ └── pages/ │ │ │ │ └── EventsAdminPage/ │ │ │ │ ├── EventsAdminPage.tsx │ │ │ │ └── index.ts │ │ │ ├── Feedback/ │ │ │ │ ├── components/ │ │ │ │ │ └── FeedbackForm.tsx │ │ │ │ └── data/ │ │ │ │ └── softSkills.ts │ │ │ ├── Home/ │ │ │ │ ├── components/ │ │ │ │ │ ├── CourseLinks/ │ │ │ │ │ │ ├── CourseLinks.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── CourseSelector/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── HomeSummary/ │ │ │ │ │ │ ├── HomeSummary.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── NoCourse/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── RegistryBanner/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── SystemAlerts/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── data/ │ │ │ │ │ ├── links.tsx │ │ │ │ │ └── loadHomeData.ts │ │ │ │ ├── hooks/ │ │ │ │ │ ├── useActiveCourse.test.tsx │ │ │ │ │ ├── useActiveCourse.tsx │ │ │ │ │ └── useStudentSummary.tsx │ │ │ │ └── pages/ │ │ │ │ └── HomePage/ │ │ │ │ └── index.tsx │ │ │ ├── Interview/ │ │ │ │ └── Student/ │ │ │ │ ├── components/ │ │ │ │ │ ├── AlertDescription.module.css │ │ │ │ │ ├── AlertDescription.tsx │ │ │ │ │ ├── ExtraInfo.tsx │ │ │ │ │ ├── InterviewCard.tsx │ │ │ │ │ ├── InterviewDescription.tsx │ │ │ │ │ ├── NoInterviewsAlert.tsx │ │ │ │ │ └── StatusLabel.tsx │ │ │ │ ├── data/ │ │ │ │ │ └── getInterviewCardDetails.tsx │ │ │ │ └── index.ts │ │ │ ├── Interviews/ │ │ │ │ ├── data/ │ │ │ │ │ ├── getInterviewData.ts │ │ │ │ │ ├── getStageInterviewData.ts │ │ │ │ │ └── index.ts │ │ │ │ └── pages/ │ │ │ │ ├── InterviewFeedback/ │ │ │ │ │ └── index.tsx │ │ │ │ └── StageInterviewFeedback/ │ │ │ │ ├── CustomQuestion.tsx │ │ │ │ ├── FormItem.tsx │ │ │ │ ├── NestedRadio.tsx │ │ │ │ ├── QuestionCard.tsx │ │ │ │ ├── QuestionList.tsx │ │ │ │ ├── QuestionsPicker.tsx │ │ │ │ ├── StageInterviewFeedback.tsx │ │ │ │ ├── StepContext.tsx │ │ │ │ ├── StepForm.tsx │ │ │ │ ├── Steps.tsx │ │ │ │ ├── StepsContent.tsx │ │ │ │ ├── StudentInfo.tsx │ │ │ │ ├── SubHeader.module.css │ │ │ │ ├── SubHeader.tsx │ │ │ │ ├── feedbackTemplateHandler.test.ts │ │ │ │ ├── feedbackTemplateHandler.ts │ │ │ │ └── index.ts │ │ │ ├── Mentor/ │ │ │ │ ├── components/ │ │ │ │ │ ├── Instructions/ │ │ │ │ │ │ ├── Instructions.tsx │ │ │ │ │ │ ├── constants.ts │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── renderers.tsx │ │ │ │ │ ├── MentorDashboard/ │ │ │ │ │ │ ├── MentorDashboard.test.tsx │ │ │ │ │ │ ├── MentorDashboard.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── Notification/ │ │ │ │ │ │ ├── Notification.test.tsx │ │ │ │ │ │ ├── Notification.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── ReviewRandomTask/ │ │ │ │ │ │ ├── ReviewRandomTask.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── SubmitReviewModal/ │ │ │ │ │ │ ├── SubmitReviewModal.test.tsx │ │ │ │ │ │ ├── SubmitReviewModal.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── TaskSolutionsTable/ │ │ │ │ │ │ ├── TaskSolutionsTable.test.tsx │ │ │ │ │ │ ├── TaskSolutionsTable.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── renderers.tsx │ │ │ │ │ ├── TaskStatusTabs/ │ │ │ │ │ │ ├── TaskStatusTabs.test.tsx │ │ │ │ │ │ ├── TaskStatusTabs.tsx │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── renderers.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── constants.ts │ │ │ │ ├── data/ │ │ │ │ │ └── softSkills.ts │ │ │ │ ├── hooks/ │ │ │ │ │ ├── useMentorDashboard.tsx │ │ │ │ │ └── useMentorStudents.tsx │ │ │ │ └── pages/ │ │ │ │ ├── InterviewWaitingList/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── Interviews/ │ │ │ │ │ ├── components/ │ │ │ │ │ │ ├── InterviewCard.module.css │ │ │ │ │ │ ├── InterviewCard.tsx │ │ │ │ │ │ ├── InterviewDetails.tsx │ │ │ │ │ │ ├── InterviewsList.module.css │ │ │ │ │ │ ├── InterviewsList.tsx │ │ │ │ │ │ ├── InterviewsSummary.tsx │ │ │ │ │ │ ├── MentorPreferencesModal.tsx │ │ │ │ │ │ ├── RegistrationNotice.test.tsx │ │ │ │ │ │ ├── RegistrationNoticeAlert.module.css │ │ │ │ │ │ ├── RegistrationNoticeAlert.tsx │ │ │ │ │ │ ├── SelectMentorModal.tsx │ │ │ │ │ │ ├── StudentInterview.module.css │ │ │ │ │ │ ├── StudentInterview.tsx │ │ │ │ │ │ ├── WaitListAlert.module.css │ │ │ │ │ │ └── WaitListAlert.tsx │ │ │ │ │ ├── hooks/ │ │ │ │ │ │ └── useAlert.ts │ │ │ │ │ ├── index.module.css │ │ │ │ │ └── index.tsx │ │ │ │ ├── StudentFeedback/ │ │ │ │ │ └── index.tsx │ │ │ │ └── Students/ │ │ │ │ └── index.tsx │ │ │ ├── MentorRegistry/ │ │ │ │ ├── components/ │ │ │ │ │ ├── InviteMentorsModal.tsx │ │ │ │ │ ├── MentorRegistryDeleteModal.tsx │ │ │ │ │ ├── MentorRegistryResendModal.tsx │ │ │ │ │ ├── MentorRegistryTable.tsx │ │ │ │ │ ├── MentorRegistryTableContainer.module.css │ │ │ │ │ └── MentorRegistryTableContainer.tsx │ │ │ │ ├── constants.ts │ │ │ │ └── index.ts │ │ │ ├── MentorTasksReview/ │ │ │ │ ├── components/ │ │ │ │ │ ├── AssignReviewerModal/ │ │ │ │ │ │ ├── AssignReviewerModal.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── ReviewsTable/ │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── renderers.tsx │ │ │ │ └── pages/ │ │ │ │ ├── MentorTasksReview.constants.ts │ │ │ │ └── MentorTasksReview.tsx │ │ │ ├── MentorsHallOfFame/ │ │ │ │ ├── components/ │ │ │ │ │ └── MentorCard/ │ │ │ │ │ ├── MentorCard.module.css │ │ │ │ │ ├── MentorCard.test.tsx │ │ │ │ │ └── MentorCard.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── pages/ │ │ │ │ │ ├── MentorsHallOfFamePage.test.tsx │ │ │ │ │ └── MentorsHallOfFamePage.tsx │ │ │ │ ├── services/ │ │ │ │ │ ├── mentors-hall-of-fame.service.test.ts │ │ │ │ │ └── mentors-hall-of-fame.service.ts │ │ │ │ └── types.ts │ │ │ ├── NotAccess/ │ │ │ │ ├── NotAccess.tsx │ │ │ │ └── index.ts │ │ │ ├── Notifications/ │ │ │ │ ├── components/ │ │ │ │ │ ├── Consents.tsx │ │ │ │ │ ├── NotificationSettingsModal.module.css │ │ │ │ │ ├── NotificationSettingsModal.tsx │ │ │ │ │ ├── NotificationSettingsTable.tsx │ │ │ │ │ ├── NotificationsUserSettingsTable.module.css │ │ │ │ │ └── NotificationsUserSettingsTable.tsx │ │ │ │ ├── pages/ │ │ │ │ │ ├── AdminNotificationsPage/ │ │ │ │ │ │ ├── AdminNotificationsSettingsPage.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── ConnectionConfirmedPage.tsx │ │ │ │ │ └── UserNotificationsSettingsPage.tsx │ │ │ │ └── services/ │ │ │ │ └── notifications.ts │ │ │ ├── Opportunities/ │ │ │ │ ├── components/ │ │ │ │ │ ├── AvatarCv/ │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── EditCv/ │ │ │ │ │ │ ├── ContactsForm/ │ │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── GeneralInfoForm/ │ │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── VisibleCoursesForm/ │ │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── form-validation.ts │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── EditViewCv/ │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── ExpirationTooltip/ │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── Link/ │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── NameTitle/ │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── NoConsentView/ │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── PublicLink/ │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── StudentStatus/ │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── ViewCv/ │ │ │ │ │ ├── AboutSection/ │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── ActionButtons/ │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── BaseSection/ │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── ContactsSection/ │ │ │ │ │ │ ├── ContactsList/ │ │ │ │ │ │ │ ├── index.module.css │ │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── CoursesSection/ │ │ │ │ │ │ ├── CoursesSection.module.css │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── DataTextValue/ │ │ │ │ │ │ ├── DataTextValue.module.css │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── FeedbackSection/ │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── GratitudeSection/ │ │ │ │ │ │ ├── GratitudeList/ │ │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── PersonalSection/ │ │ │ │ │ │ ├── PersonalSection.module.css │ │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── index.test.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── constants.ts │ │ │ │ ├── data/ │ │ │ │ │ ├── __snapshots__/ │ │ │ │ │ │ └── getPersonalToRender.test.tsx.snap │ │ │ │ │ ├── getContactsToRender.test.tsx │ │ │ │ │ ├── getContactsToRender.ts │ │ │ │ │ ├── getPersonalToRender.test.tsx │ │ │ │ │ └── getPersonalToRender.tsx │ │ │ │ ├── hooks/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useExpiration.test.tsx │ │ │ │ │ ├── useExpiration.ts │ │ │ │ │ ├── useResumeData.test.tsx │ │ │ │ │ ├── useResumeData.tsx │ │ │ │ │ ├── useViewData.test.tsx │ │ │ │ │ └── useViewData.tsx │ │ │ │ ├── models.ts │ │ │ │ ├── pages/ │ │ │ │ │ ├── EditPage/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── PublicPage/ │ │ │ │ │ ├── getServerSideProps.ts │ │ │ │ │ └── index.tsx │ │ │ │ └── transformers/ │ │ │ │ ├── index.ts │ │ │ │ ├── splitDataForForms.test.ts │ │ │ │ ├── splitDataForForms.ts │ │ │ │ ├── transformFieldsData.test.ts │ │ │ │ ├── transformFieldsData.ts │ │ │ │ ├── transformInitialCvData.test.ts │ │ │ │ └── transformInitialCvData.ts │ │ │ ├── Profile/ │ │ │ │ └── components/ │ │ │ │ └── MentorEndorsement/ │ │ │ │ ├── MentorEndorsement.tsx │ │ │ │ └── index.tsx │ │ │ ├── Prompts/ │ │ │ │ ├── components/ │ │ │ │ │ ├── PromptModal.tsx │ │ │ │ │ └── PromptTable.tsx │ │ │ │ └── pages/ │ │ │ │ └── PromptPage.tsx │ │ │ ├── Registry/ │ │ │ │ ├── components/ │ │ │ │ │ ├── Cards/ │ │ │ │ │ │ ├── AdditionalInfo/ │ │ │ │ │ │ │ ├── AdditionalInfo.test.tsx │ │ │ │ │ │ │ └── AdditionalInfo.tsx │ │ │ │ │ │ ├── ContactInfo/ │ │ │ │ │ │ │ ├── ContactInfo.test.tsx │ │ │ │ │ │ │ └── ContactInfo.tsx │ │ │ │ │ │ ├── CourseDetails/ │ │ │ │ │ │ │ ├── CourseDetails.test.tsx │ │ │ │ │ │ │ └── CourseDetails.tsx │ │ │ │ │ │ ├── Disciplines/ │ │ │ │ │ │ │ ├── Disciplines.test.tsx │ │ │ │ │ │ │ └── Disciplines.tsx │ │ │ │ │ │ ├── PersonalInfo/ │ │ │ │ │ │ │ ├── PersonalInfo.test.tsx │ │ │ │ │ │ │ └── PersonalInfo.tsx │ │ │ │ │ │ └── Preferences/ │ │ │ │ │ │ ├── Preferences.test.tsx │ │ │ │ │ │ └── Preferences.tsx │ │ │ │ │ ├── CourseCertificateAlert/ │ │ │ │ │ │ └── CourseCertificateAlert.tsx │ │ │ │ │ ├── CourseLabel/ │ │ │ │ │ │ └── CourseLabel.tsx │ │ │ │ │ ├── DataProcessingCheckbox/ │ │ │ │ │ │ ├── DataProcessingCheckbox.test.tsx │ │ │ │ │ │ └── DataProcessingCheckbox.tsx │ │ │ │ │ ├── Footer/ │ │ │ │ │ │ └── Footer.tsx │ │ │ │ │ ├── FormButtons/ │ │ │ │ │ │ ├── FormButtons.test.tsx │ │ │ │ │ │ └── FormButtons.tsx │ │ │ │ │ ├── FormCard/ │ │ │ │ │ │ └── FormCard.tsx │ │ │ │ │ ├── FormSections/ │ │ │ │ │ │ ├── DoneSection/ │ │ │ │ │ │ │ ├── DoneSection.test.tsx │ │ │ │ │ │ │ └── DoneSection.tsx │ │ │ │ │ │ ├── GeneralSection/ │ │ │ │ │ │ │ ├── GeneralSection.test.tsx │ │ │ │ │ │ │ └── GeneralSection.tsx │ │ │ │ │ │ └── MentorshipSection/ │ │ │ │ │ │ ├── MentorshipSection.test.tsx │ │ │ │ │ │ └── MentorshipSection.tsx │ │ │ │ │ ├── Header/ │ │ │ │ │ │ └── Header.tsx │ │ │ │ │ ├── LanguagesMentoring/ │ │ │ │ │ │ ├── LanguagesMentoring.test.tsx │ │ │ │ │ │ └── LanguagesMentoring.tsx │ │ │ │ │ ├── NoCourses/ │ │ │ │ │ │ └── NoCourses.tsx │ │ │ │ │ ├── RegistrationForm/ │ │ │ │ │ │ ├── RegistrationForm.test.tsx │ │ │ │ │ │ └── RegistrationForm.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── constants/ │ │ │ │ │ └── index.ts │ │ │ │ ├── hooks/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useFormLayout/ │ │ │ │ │ │ └── useFormLayout.ts │ │ │ │ │ ├── useMentorData/ │ │ │ │ │ │ └── useMentorData.tsx │ │ │ │ │ └── useStudentData/ │ │ │ │ │ └── useStudentData.tsx │ │ │ │ └── pages/ │ │ │ │ ├── Mentor/ │ │ │ │ │ └── Mentor.tsx │ │ │ │ ├── Student/ │ │ │ │ │ └── Student.tsx │ │ │ │ └── index.ts │ │ │ ├── Schedule/ │ │ │ │ ├── components/ │ │ │ │ │ ├── AdditionalActions/ │ │ │ │ │ │ ├── AdditionalActions.test.tsx │ │ │ │ │ │ ├── AdditionalActions.tsx │ │ │ │ │ │ ├── helpers.ts │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── EventDetails/ │ │ │ │ │ │ ├── EventDetails.module.css │ │ │ │ │ │ ├── EventDetails.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── FilteredTags/ │ │ │ │ │ │ ├── FilteredTags.test.tsx │ │ │ │ │ │ ├── FilteredTags.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── MobileItemCard/ │ │ │ │ │ │ ├── MobileItemCard.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── SettingsDrawer/ │ │ │ │ │ │ ├── ChangeTagColors.tsx │ │ │ │ │ │ ├── SettingsDrawer.tsx │ │ │ │ │ │ ├── ShowTableColumns.tsx │ │ │ │ │ │ ├── TimeZone.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── SettingsPanel/ │ │ │ │ │ │ ├── SettingsPanel.test.tsx │ │ │ │ │ │ ├── SettingsPanel.tsx │ │ │ │ │ │ ├── helpers.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── StatusTabs/ │ │ │ │ │ │ ├── StatusTabs.test.tsx │ │ │ │ │ │ ├── StatusTabs.tsx │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── renderers.tsx │ │ │ │ │ └── TableView/ │ │ │ │ │ ├── TableView.test.tsx │ │ │ │ │ ├── TableView.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ └── renderers.tsx │ │ │ │ ├── constants.ts │ │ │ │ ├── hooks/ │ │ │ │ │ └── useScheduleSettings.ts │ │ │ │ ├── index.ts │ │ │ │ ├── pages/ │ │ │ │ │ └── SchedulePage/ │ │ │ │ │ └── index.tsx │ │ │ │ └── utils.ts │ │ │ ├── Score/ │ │ │ │ ├── components/ │ │ │ │ │ ├── ExportCsvButton/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── ScoreTable/ │ │ │ │ │ │ ├── ScoreTableTabs.tsx │ │ │ │ │ │ ├── Summary.tsx │ │ │ │ │ │ ├── index.module.css │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── SettingsDrawer/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── data/ │ │ │ │ │ ├── getColumns.tsx │ │ │ │ │ ├── getExportCsvUrl.ts │ │ │ │ │ ├── getTaskColumns.tsx │ │ │ │ │ └── isExportEnabled.ts │ │ │ │ ├── hooks/ │ │ │ │ │ ├── types.ts │ │ │ │ │ └── useScorePaging.tsx │ │ │ │ └── pages/ │ │ │ │ └── ScorePage/ │ │ │ │ ├── UpdateAlert.module.css │ │ │ │ ├── UpdateAlert.tsx │ │ │ │ └── index.tsx │ │ │ ├── StudentDashboard/ │ │ │ │ ├── components/ │ │ │ │ │ ├── AvailableReviewCard/ │ │ │ │ │ │ ├── AvailableReviewCard.test.tsx │ │ │ │ │ │ ├── AvailableReviewCard.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── CommonDashboardCard.tsx │ │ │ │ │ ├── MainStatsCard.tsx │ │ │ │ │ ├── MentorCard/ │ │ │ │ │ │ ├── MentorCard.test.tsx │ │ │ │ │ │ ├── MentorCard.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── MentorInfo/ │ │ │ │ │ │ ├── MentorInfo.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── NextEventCard/ │ │ │ │ │ │ ├── NextEventCard.module.css │ │ │ │ │ │ ├── NextEventCard.test.tsx │ │ │ │ │ │ ├── NextEventCard.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── renderers.tsx │ │ │ │ │ ├── RepositoryCard.tsx │ │ │ │ │ ├── SubmitTaskSolution/ │ │ │ │ │ │ ├── SubmitTaskSolution.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TasksChart.tsx │ │ │ │ │ ├── TasksStatsCard.tsx │ │ │ │ │ ├── TasksStatsModal.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── hooks/ │ │ │ │ │ ├── useDashboardData.ts │ │ │ │ │ ├── useSubmitTaskSolution.test.ts │ │ │ │ │ └── useSubmitTaskSolution.ts │ │ │ │ └── index.ts │ │ │ ├── Students/ │ │ │ │ ├── Pages/ │ │ │ │ │ └── Students.tsx │ │ │ │ └── components/ │ │ │ │ ├── CourseItem/ │ │ │ │ │ └── index.tsx │ │ │ │ ├── StudentInfo/ │ │ │ │ │ └── index.tsx │ │ │ │ └── StudentsTable/ │ │ │ │ ├── index.tsx │ │ │ │ └── renderers.tsx │ │ │ ├── Tasks/ │ │ │ │ ├── components/ │ │ │ │ │ ├── CrossCheckTaskCriteriaPanel/ │ │ │ │ │ │ ├── CrossCheckTaskCriteriaPanel.test.tsx │ │ │ │ │ │ └── CrossCheckTaskCriteriaPanel.tsx │ │ │ │ │ ├── GitHubPanel/ │ │ │ │ │ │ ├── GitHubPanel.test.tsx │ │ │ │ │ │ └── GitHubPanel.tsx │ │ │ │ │ ├── JsonAttributesPanel/ │ │ │ │ │ │ ├── JsonAttributesPanel.test.tsx │ │ │ │ │ │ └── JsonAttributesPanel.tsx │ │ │ │ │ ├── TaskModal/ │ │ │ │ │ │ ├── TaskModal.test.tsx │ │ │ │ │ │ └── TaskModal.tsx │ │ │ │ │ ├── TaskSettings/ │ │ │ │ │ │ ├── TaskSettings.test.tsx │ │ │ │ │ │ └── TaskSettings.tsx │ │ │ │ │ ├── TasksTable/ │ │ │ │ │ │ ├── TasksTable.test.tsx │ │ │ │ │ │ └── TasksTable.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── pages/ │ │ │ │ │ ├── TasksPage/ │ │ │ │ │ │ └── TasksPage.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── types.ts │ │ │ │ └── utils/ │ │ │ │ └── test-utils.ts │ │ │ ├── TeamDistribution/ │ │ │ │ ├── components/ │ │ │ │ │ ├── SubmitScoreModal/ │ │ │ │ │ │ ├── SubmitScoreModal.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── TeamDistributionCard/ │ │ │ │ │ │ ├── Actions.test.tsx │ │ │ │ │ │ ├── Actions.tsx │ │ │ │ │ │ ├── CardTitle.test.tsx │ │ │ │ │ │ ├── CardTitle.tsx │ │ │ │ │ │ ├── DistributionPeriod.tsx │ │ │ │ │ │ ├── TeamDistributionCard.test.tsx │ │ │ │ │ │ ├── TeamDistributionCard.tsx │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── renderers.tsx │ │ │ │ │ ├── TeamDistributionModal/ │ │ │ │ │ │ ├── TeamDistributionModal.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── WelcomeCard/ │ │ │ │ │ ├── WelcomeCard.test.tsx │ │ │ │ │ ├── WelcomeCard.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── hooks/ │ │ │ │ │ ├── useSubmitTeamScore.test.tsx │ │ │ │ │ └── useSubmitTeamScore.tsx │ │ │ │ └── pages/ │ │ │ │ └── TeamDistributions/ │ │ │ │ ├── TeamDistributions.tsx │ │ │ │ └── index.tsx │ │ │ ├── Teams/ │ │ │ │ ├── Pages/ │ │ │ │ │ └── Teams.tsx │ │ │ │ ├── components/ │ │ │ │ │ ├── JoinTeamModal/ │ │ │ │ │ │ └── JoinTeamModal.tsx │ │ │ │ │ ├── MyTeamSection/ │ │ │ │ │ │ └── MyTeamSection.tsx │ │ │ │ │ ├── StudentsTable/ │ │ │ │ │ │ ├── StudentsTable.tsx │ │ │ │ │ │ └── renderers.tsx │ │ │ │ │ ├── StudentsWithoutTeamSection/ │ │ │ │ │ │ └── StudentsWithoutTeamSection.tsx │ │ │ │ │ ├── TeamModal/ │ │ │ │ │ │ ├── TeamModal.test.tsx │ │ │ │ │ │ └── TeamModal.tsx │ │ │ │ │ ├── TeamsHeader/ │ │ │ │ │ │ ├── ActionCard.tsx │ │ │ │ │ │ └── TeamsHeader.tsx │ │ │ │ │ ├── TeamsSection/ │ │ │ │ │ │ ├── TeamsSection.tsx │ │ │ │ │ │ └── renderers.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── hooks/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── useDistribution/ │ │ │ │ │ ├── useDistribution.test.ts │ │ │ │ │ └── useDistribution.ts │ │ │ │ └── index.tsx │ │ │ ├── UserGroupsAdmin/ │ │ │ │ ├── components/ │ │ │ │ │ ├── UserGroupsModal.tsx │ │ │ │ │ └── UserGroupsTable.tsx │ │ │ │ ├── hooks/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── useUserGroups.ts │ │ │ │ ├── index.ts │ │ │ │ └── pages/ │ │ │ │ └── UserGroupsAdminPage/ │ │ │ │ ├── UserGroupsAdminPage.tsx │ │ │ │ └── index.ts │ │ │ └── UsersAdmin/ │ │ │ ├── hooks/ │ │ │ │ ├── index.ts │ │ │ │ └── useUsersSearch.ts │ │ │ ├── index.ts │ │ │ └── pages/ │ │ │ └── UsersAdminPage/ │ │ │ ├── UsersAdminPage.tsx │ │ │ └── index.ts │ │ ├── pages/ │ │ │ ├── 404.tsx │ │ │ ├── _app.tsx │ │ │ ├── _document.tsx │ │ │ ├── admin/ │ │ │ │ ├── auto-test-task/ │ │ │ │ │ └── [taskId].tsx │ │ │ │ ├── auto-test.tsx │ │ │ │ ├── contributors.tsx │ │ │ │ ├── courses.tsx │ │ │ │ ├── disciplines.tsx │ │ │ │ ├── discord-telegram.tsx │ │ │ │ ├── events.tsx │ │ │ │ ├── mentor-registry.module.css │ │ │ │ ├── mentor-registry.tsx │ │ │ │ ├── notifications.tsx │ │ │ │ ├── prompts.tsx │ │ │ │ ├── registrations.tsx │ │ │ │ ├── students.tsx │ │ │ │ ├── tasks.tsx │ │ │ │ ├── user-group.tsx │ │ │ │ └── users.tsx │ │ │ ├── applicants/ │ │ │ │ └── index.tsx │ │ │ ├── course/ │ │ │ │ ├── 403.tsx │ │ │ │ ├── admin/ │ │ │ │ │ ├── certified-students.tsx │ │ │ │ │ ├── cross-check-table.tsx │ │ │ │ │ ├── events.tsx │ │ │ │ │ ├── interviews.tsx │ │ │ │ │ ├── mentor-tasks-review.tsx │ │ │ │ │ ├── mentors.tsx │ │ │ │ │ ├── reports.tsx │ │ │ │ │ ├── stage-interviews.tsx │ │ │ │ │ ├── students.tsx │ │ │ │ │ ├── tasks.tsx │ │ │ │ │ └── users.tsx │ │ │ │ ├── interview/ │ │ │ │ │ └── [type]/ │ │ │ │ │ └── feedback.tsx │ │ │ │ ├── mentor/ │ │ │ │ │ ├── confirm.tsx │ │ │ │ │ ├── dashboard.tsx │ │ │ │ │ ├── expel-student.tsx │ │ │ │ │ ├── feedback/ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── interview-technical-screening.tsx │ │ │ │ │ ├── interview-wait-list.tsx │ │ │ │ │ ├── interviews.tsx │ │ │ │ │ └── students.tsx │ │ │ │ ├── schedule.tsx │ │ │ │ ├── score.tsx │ │ │ │ ├── stats.tsx │ │ │ │ ├── student/ │ │ │ │ │ ├── auto-test/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── task.tsx │ │ │ │ │ ├── cross-check-review.tsx │ │ │ │ │ ├── cross-check-submit.tsx │ │ │ │ │ ├── dashboard.module.css │ │ │ │ │ ├── dashboard.tsx │ │ │ │ │ └── interviews.tsx │ │ │ │ ├── submit-scores.tsx │ │ │ │ ├── team-distributions.tsx │ │ │ │ └── teams.tsx │ │ │ ├── cv/ │ │ │ │ ├── [uuid].tsx │ │ │ │ └── edit.tsx │ │ │ ├── gratitude.tsx │ │ │ ├── heroes.tsx │ │ │ ├── index.tsx │ │ │ ├── login/ │ │ │ │ ├── index.module.css │ │ │ │ └── index.tsx │ │ │ ├── mentors-hall-of-fame.tsx │ │ │ ├── profile/ │ │ │ │ ├── connection-confirmed.tsx │ │ │ │ ├── index.module.css │ │ │ │ ├── index.tsx │ │ │ │ └── notifications.tsx │ │ │ └── registry/ │ │ │ ├── epamlearningjs.tsx │ │ │ ├── mentor.tsx │ │ │ └── student.tsx │ │ ├── providers/ │ │ │ ├── DevToolsProvider.tsx │ │ │ ├── MessageProvider.tsx │ │ │ ├── ThemeProvider.tsx │ │ │ └── index.ts │ │ ├── reset.d.ts │ │ ├── services/ │ │ │ ├── cdn.ts │ │ │ ├── check.ts │ │ │ ├── course.ts │ │ │ ├── courses.ts │ │ │ ├── features.tsx │ │ │ ├── files.ts │ │ │ ├── formatter.ts │ │ │ ├── gratitude.ts │ │ │ ├── mentorRegistry.ts │ │ │ ├── models.ts │ │ │ ├── reference-data/ │ │ │ │ └── stageInterview.ts │ │ │ ├── routes.ts │ │ │ ├── user.ts │ │ │ ├── validators.test.ts │ │ │ └── validators.ts │ │ ├── setupTests.ts │ │ ├── shared/ │ │ │ ├── components/ │ │ │ │ ├── CommentModal.tsx │ │ │ │ ├── CopyToClipboardButton.tsx │ │ │ │ ├── FilteredTags.tsx │ │ │ │ ├── Forms/ │ │ │ │ │ ├── CommentInput.tsx │ │ │ │ │ ├── CourseTaskSelect.tsx │ │ │ │ │ ├── GdprCheckbox.tsx │ │ │ │ │ ├── Heroes/ │ │ │ │ │ │ ├── index.module.css │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── LocationSelect.tsx │ │ │ │ │ ├── MarkdownInput.tsx │ │ │ │ │ ├── ModalForm.tsx │ │ │ │ │ ├── ModalSubmitForm.test.tsx │ │ │ │ │ ├── ModalSubmitForm.tsx │ │ │ │ │ ├── PreparedComment.tsx │ │ │ │ │ ├── ScoreInput.tsx │ │ │ │ │ ├── __tests__/ │ │ │ │ │ │ ├── CourseTaskSelect.test.tsx │ │ │ │ │ │ └── __snapshots__/ │ │ │ │ │ │ └── CourseTaskSelect.test.tsx.snap │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useGoogleMapsPlaces.test.ts │ │ │ │ │ └── useGoogleMapsPlaces.ts │ │ │ │ ├── GithubAvatar.tsx │ │ │ │ ├── GithubUserLink.module.css │ │ │ │ ├── GithubUserLink.tsx │ │ │ │ ├── Header.module.css │ │ │ │ ├── Header.tsx │ │ │ │ ├── Icons/ │ │ │ │ │ ├── CourseIcon.tsx │ │ │ │ │ ├── DeadlineIcon.tsx │ │ │ │ │ ├── DiscordFilled.tsx │ │ │ │ │ ├── DiscordOutlined.tsx │ │ │ │ │ ├── GitHubLogoIcon.tsx │ │ │ │ │ ├── HealthMask.tsx │ │ │ │ │ ├── LinkedInIcon.tsx │ │ │ │ │ ├── PublicSvgIcon.tsx │ │ │ │ │ ├── RSLogoIcon.tsx │ │ │ │ │ ├── ScoreIcon.tsx │ │ │ │ │ ├── TelegramIcon.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── LoadingScreen.module.css │ │ │ │ ├── LoadingScreen.tsx │ │ │ │ ├── MentorSearch.tsx │ │ │ │ ├── NonTouchTooltip.tsx │ │ │ │ ├── PageLayout.tsx │ │ │ │ ├── PersonSelect.tsx │ │ │ │ ├── Rating.tsx │ │ │ │ ├── ScoreCard.module.css │ │ │ │ ├── ScoreCard.tsx │ │ │ │ ├── ScoreSelector.module.css │ │ │ │ ├── ScoreSelector.tsx │ │ │ │ ├── Sider/ │ │ │ │ │ ├── AdminSider.test.tsx │ │ │ │ │ ├── AdminSider.tsx │ │ │ │ │ └── data/ │ │ │ │ │ └── menuItems.tsx │ │ │ │ ├── SolidarityUkraine.tsx │ │ │ │ ├── StudentMentorModal.tsx │ │ │ │ ├── StudentSearch.tsx │ │ │ │ ├── Table/ │ │ │ │ │ ├── PersonCell.tsx │ │ │ │ │ ├── columns.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── renderers.tsx │ │ │ │ │ └── sorters.ts │ │ │ │ ├── ThemeSwitch.tsx │ │ │ │ ├── Timer.tsx │ │ │ │ ├── TooltipedButton.tsx │ │ │ │ └── UserSearch.tsx │ │ │ ├── hooks/ │ │ │ │ ├── useMessage.tsx │ │ │ │ ├── useModal/ │ │ │ │ │ ├── useModalForm.test.tsx │ │ │ │ │ └── useModalForm.tsx │ │ │ │ ├── useTheme.tsx │ │ │ │ └── useWindowDimensions.ts │ │ │ └── utils/ │ │ │ ├── onlyDefined.ts │ │ │ ├── pagination.ts │ │ │ ├── queryParams-utils.test.ts │ │ │ ├── queryParams-utils.ts │ │ │ ├── text-utils.test.ts │ │ │ └── text-utils.ts │ │ ├── styles/ │ │ │ └── main.css │ │ └── utils/ │ │ ├── dynamicWithSkeleton.tsx │ │ ├── optionalQueryString.ts │ │ └── profilePageUtils.ts │ ├── tsconfig.json │ └── vitest.config.mts ├── common/ │ ├── README.md │ └── models/ │ ├── index.ts │ ├── interview.ts │ ├── profile.ts │ ├── stage-interview-feedback.ts │ └── user.ts ├── docker-compose.yml ├── docs/ │ ├── .nojekyll │ ├── CNAME │ ├── README.md │ ├── _sidebar.md │ ├── code-of-conduct.md │ ├── index.html │ └── platform/ │ ├── about.md │ ├── adding-tests.md │ ├── choose-kata-languages.md │ ├── cross-check-flow.md │ ├── cross-check-scheduling.md │ ├── cv.md │ ├── notifications.md │ ├── pull-request-review-process.md │ ├── shedule.md │ ├── tasks.md │ └── typical-problems.md ├── eslint.config.mjs ├── nestjs/ │ ├── .dockerignore │ ├── .swcrc │ ├── Dockerfile │ ├── Dockerfile.lambda │ ├── README.md │ ├── eslint.config.mjs │ ├── nest-cli.json │ ├── openapitools.json │ ├── package.json │ ├── src/ │ │ ├── activity/ │ │ │ ├── activity.controller.ts │ │ │ ├── activity.module.ts │ │ │ └── dto/ │ │ │ ├── activity.dto.ts │ │ │ ├── create-activity-webhook.dto.ts │ │ │ └── create-activity.dto.ts │ │ ├── alerts/ │ │ │ ├── alerts.controller.ts │ │ │ ├── alerts.module.ts │ │ │ ├── alerts.service.ts │ │ │ └── dto/ │ │ │ ├── alert.dto.ts │ │ │ ├── create-alert.dto.ts │ │ │ ├── index.ts │ │ │ └── update-alert.dto.ts │ │ ├── app.module.ts │ │ ├── auth/ │ │ │ ├── auth-user.model.spec.ts │ │ │ ├── auth-user.model.ts │ │ │ ├── auth.controller.spec.ts │ │ │ ├── auth.controller.ts │ │ │ ├── auth.module.ts │ │ │ ├── auth.service.spec.ts │ │ │ ├── auth.service.ts │ │ │ ├── constants.ts │ │ │ ├── course.guard.ts │ │ │ ├── default.guard.ts │ │ │ ├── dto/ │ │ │ │ └── auth-connection.dto.ts │ │ │ ├── index.ts │ │ │ ├── role.decorator.ts │ │ │ ├── role.guard.ts │ │ │ └── strategies/ │ │ │ ├── basic.strategy.ts │ │ │ ├── dev.strategy.ts │ │ │ ├── github.strategy.ts │ │ │ └── jwt.strategy.ts │ │ ├── auto-test/ │ │ │ ├── auto-test.controller.ts │ │ │ ├── auto-test.module.ts │ │ │ ├── auto-test.service.ts │ │ │ └── dto/ │ │ │ ├── auto-test-task.dto.ts │ │ │ ├── basic-auto-test-task.dto.ts │ │ │ └── task-solution.dto.ts │ │ ├── certificates/ │ │ │ ├── certificates.controller.ts │ │ │ ├── certificates.module.ts │ │ │ ├── certificates.service.ts │ │ │ └── dto/ │ │ │ ├── certificate-metadata.dto.ts │ │ │ └── save-certificate-dto.ts │ │ ├── cloud-api/ │ │ │ ├── cloud-api.module.ts │ │ │ └── cloud-api.service.ts │ │ ├── config/ │ │ │ ├── config.module.ts │ │ │ ├── config.service.ts │ │ │ └── index.ts │ │ ├── constants.ts │ │ ├── contributors/ │ │ │ ├── contributors.controller.ts │ │ │ ├── contributors.module.ts │ │ │ ├── contributors.service.ts │ │ │ ├── dto/ │ │ │ │ ├── contributor.dto.ts │ │ │ │ ├── create-contributor.dto.ts │ │ │ │ ├── index.ts │ │ │ │ └── update-contributor.dto.ts │ │ │ └── index.ts │ │ ├── core/ │ │ │ ├── core.module.ts │ │ │ ├── decorators/ │ │ │ │ ├── index.ts │ │ │ │ └── student-id.decorator.ts │ │ │ ├── dto/ │ │ │ │ ├── id-name.dto.ts │ │ │ │ ├── index.ts │ │ │ │ ├── pagination.dto.ts │ │ │ │ └── person.dto.ts │ │ │ ├── filters/ │ │ │ │ ├── entity-not-found.filter.ts │ │ │ │ ├── index.ts │ │ │ │ └── sentry.filter.ts │ │ │ ├── jwt/ │ │ │ │ └── jwt.service.ts │ │ │ ├── middlewares/ │ │ │ │ ├── index.ts │ │ │ │ ├── logger.middleware.ts │ │ │ │ └── no-cache.middleware.ts │ │ │ ├── paginate/ │ │ │ │ ├── dto/ │ │ │ │ │ └── Paginate.dto.ts │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── pino.ts │ │ │ ├── subscribers/ │ │ │ │ ├── base-subscriber.ts │ │ │ │ ├── course-event.subscriber.ts │ │ │ │ └── course-task.subscriber.ts │ │ │ ├── templates/ │ │ │ │ └── index.ts │ │ │ └── validation/ │ │ │ ├── index.ts │ │ │ ├── validation.exception.ts │ │ │ └── validation.filter.ts │ │ ├── courses/ │ │ │ ├── course-access.service.ts │ │ │ ├── course-events/ │ │ │ │ ├── course-events.controller.ts │ │ │ │ ├── course-events.service.ts │ │ │ │ ├── dto/ │ │ │ │ │ ├── course-event.dto.ts │ │ │ │ │ ├── create-course-event.dto.ts │ │ │ │ │ └── update-course-event.dto.ts │ │ │ │ └── index.ts │ │ │ ├── course-mentors/ │ │ │ │ ├── course-mentors.controller.ts │ │ │ │ ├── course-mentors.service.ts │ │ │ │ ├── dto/ │ │ │ │ │ ├── mentor-details.dto.ts │ │ │ │ │ └── search-mentor.dto.ts │ │ │ │ └── index.ts │ │ │ ├── course-schedule/ │ │ │ │ ├── course-icalendar.controller.ts │ │ │ │ ├── course-icalendar.service.ts │ │ │ │ ├── course-schedule.controller.ts │ │ │ │ ├── course-schedule.service.spec.ts │ │ │ │ ├── course-schedule.service.ts │ │ │ │ ├── dto/ │ │ │ │ │ ├── course-copy-from.dto.ts │ │ │ │ │ ├── course-schedule-hash.dto.ts │ │ │ │ │ ├── course-schedule-item.dto.ts │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── course-students/ │ │ │ │ ├── course-students.controller.ts │ │ │ │ ├── course-students.service.ts │ │ │ │ └── dto/ │ │ │ │ ├── mentor-student-summary.dto.ts │ │ │ │ ├── result.dto.ts │ │ │ │ ├── student-status.dto.ts │ │ │ │ └── student-summary.dto.ts │ │ │ ├── course-tasks/ │ │ │ │ ├── course-tasks.controller.ts │ │ │ │ ├── course-tasks.service.ts │ │ │ │ ├── dto/ │ │ │ │ │ ├── course-task-detailed.dto.ts │ │ │ │ │ ├── course-task.dto.ts │ │ │ │ │ ├── create-course-task.dto.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── update-course-task.dto.ts │ │ │ │ └── index.ts │ │ │ ├── course-users/ │ │ │ │ ├── course-users.controller.spec.ts │ │ │ │ ├── course-users.controller.ts │ │ │ │ ├── course-users.service.spec.ts │ │ │ │ ├── course-users.service.ts │ │ │ │ ├── dto/ │ │ │ │ │ ├── course-roles.dto.ts │ │ │ │ │ ├── course-user.dto.ts │ │ │ │ │ └── update-user.dto.ts │ │ │ │ └── types.ts │ │ │ ├── courses.controller.ts │ │ │ ├── courses.module.ts │ │ │ ├── courses.service.ts │ │ │ ├── cross-checks/ │ │ │ │ ├── course-cross-checks.controller.ts │ │ │ │ ├── course-cross-checks.service.spec.ts │ │ │ │ ├── course-cross-checks.service.ts │ │ │ │ ├── cross-check-feedback.guard.ts │ │ │ │ ├── dto/ │ │ │ │ │ ├── available-review-stats.dto.ts │ │ │ │ │ ├── check-tasks-pairs.dto.ts │ │ │ │ │ ├── cross-check-criteria-data.dto.ts │ │ │ │ │ ├── cross-check-feedback.dto.ts │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── dto/ │ │ │ │ ├── course.dto.ts │ │ │ │ ├── create-course.dto.ts │ │ │ │ ├── export-course.dto.ts │ │ │ │ ├── index.ts │ │ │ │ ├── leave-course.dto.ts │ │ │ │ ├── update-course.dto.ts │ │ │ │ └── used-course.dto.ts │ │ │ ├── expelled-stats.service.test.ts │ │ │ ├── expelled-stats.service.ts │ │ │ ├── index.ts │ │ │ ├── interviews/ │ │ │ │ ├── cross-mentor-distribution.service.ts │ │ │ │ ├── dto/ │ │ │ │ │ ├── available-student.dto.ts │ │ │ │ │ ├── get-interview-feedback.dto.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interview-comment.dto.ts │ │ │ │ │ ├── interview-distribute.dto.ts │ │ │ │ │ ├── interview-pair.dto.ts │ │ │ │ │ ├── interview.dto.ts │ │ │ │ │ ├── put-interview-feedback.dto.ts │ │ │ │ │ └── registration-interview.dto.ts │ │ │ │ ├── index.ts │ │ │ │ ├── interviewFeedback.service.ts │ │ │ │ ├── interviews.controller.ts │ │ │ │ └── interviews.service.ts │ │ │ ├── mentor-reviews/ │ │ │ │ ├── dto/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── mentor-review-assign.dto.ts │ │ │ │ │ ├── mentor-reviews-query.dto.ts │ │ │ │ │ └── mentor-reviews.dto.ts │ │ │ │ ├── index.ts │ │ │ │ ├── mentor-reviews.controller.ts │ │ │ │ └── mentor-reviews.service.ts │ │ │ ├── mentors/ │ │ │ │ ├── dto/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── mentor-dashboard.dto.ts │ │ │ │ │ ├── mentor-options.dto.ts │ │ │ │ │ └── mentor-student.dto.ts │ │ │ │ ├── index.ts │ │ │ │ ├── mentors.controller.ts │ │ │ │ └── mentors.service.ts │ │ │ ├── score/ │ │ │ │ ├── dto/ │ │ │ │ │ ├── score-query.dto.ts │ │ │ │ │ └── score.dto.ts │ │ │ │ ├── index.ts │ │ │ │ ├── score.controller.ts │ │ │ │ ├── score.service.ts │ │ │ │ └── write-score.service.ts │ │ │ ├── stats/ │ │ │ │ ├── course-stats.controller.ts │ │ │ │ ├── course-stats.service.ts │ │ │ │ ├── dto/ │ │ │ │ │ ├── countries-stats.dto.ts │ │ │ │ │ ├── course-mentors-stats.dto.ts │ │ │ │ │ ├── course-stats.dto.ts │ │ │ │ │ ├── expelled-stats.dto.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── task-performance-stats.dto.ts │ │ │ │ └── index.ts │ │ │ ├── students/ │ │ │ │ ├── dto/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── student.dto.ts │ │ │ │ │ ├── user-students-query.dto.ts │ │ │ │ │ └── user-students.dto.ts │ │ │ │ ├── feedbacks/ │ │ │ │ │ ├── dto/ │ │ │ │ │ │ ├── create-student-feedback.dto.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── student-feedback.dto.ts │ │ │ │ │ │ └── update-student-feedback.dto.ts │ │ │ │ │ ├── feedbacks.controller.ts │ │ │ │ │ ├── feedbacks.service.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── students.controller.ts │ │ │ │ └── students.service.ts │ │ │ ├── task-solutions/ │ │ │ │ ├── dto/ │ │ │ │ │ ├── create-task-solution.dto.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── task-solution.dto.ts │ │ │ │ ├── index.ts │ │ │ │ ├── task-solutions.controller.ts │ │ │ │ └── task-solutions.service.ts │ │ │ ├── task-verifications/ │ │ │ │ ├── dto/ │ │ │ │ │ ├── create-task-verification.dto.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── self-education.dto.ts │ │ │ │ │ └── task-verifications-attempts.dto.ts │ │ │ │ ├── self-education.service.test.ts │ │ │ │ ├── self-education.service.ts │ │ │ │ ├── task-verifications.controller.ts │ │ │ │ ├── task-verifications.service.test.ts │ │ │ │ └── task-verifications.service.ts │ │ │ ├── tasks/ │ │ │ │ ├── dto/ │ │ │ │ │ └── check-tasks-deadline.ts │ │ │ │ ├── tasks.controller.ts │ │ │ │ └── tasks.service.ts │ │ │ └── team-distribution/ │ │ │ ├── distribute-students.service.ts │ │ │ ├── dto/ │ │ │ │ ├── create-team-distribution.dto.ts │ │ │ │ ├── create-team.dto.ts │ │ │ │ ├── index.ts │ │ │ │ ├── join-team.dto.ts │ │ │ │ ├── team-distribution-student.dto.ts │ │ │ │ ├── team-distribution.dto.ts │ │ │ │ ├── team.dto.ts │ │ │ │ ├── update-team-distribution.dto.ts │ │ │ │ └── update-team-dto.ts │ │ │ ├── registered-student-guard.ts │ │ │ ├── team-distribution-student.service.test.ts │ │ │ ├── team-distribution-student.service.ts │ │ │ ├── team-distribution.controller.ts │ │ │ ├── team-distribution.service.test.ts │ │ │ ├── team-distribution.service.ts │ │ │ ├── team-lead-or-manager.guard.ts │ │ │ ├── team.controller.ts │ │ │ └── team.service.ts │ │ ├── cross-check/ │ │ │ ├── cross-check.module.ts │ │ │ ├── cross-check.service.spec.ts │ │ │ ├── cross-check.service.ts │ │ │ └── tasks-filtering.ts │ │ ├── devtools/ │ │ │ ├── devtools.controller.ts │ │ │ ├── devtools.module.ts │ │ │ ├── devtools.service.ts │ │ │ └── dto/ │ │ │ └── devtools.users-dto.ts │ │ ├── disciplines/ │ │ │ ├── disciplines.controller.test.ts │ │ │ ├── disciplines.controller.ts │ │ │ ├── disciplines.module.ts │ │ │ ├── disciplines.service.test.ts │ │ │ ├── disciplines.service.ts │ │ │ ├── dto/ │ │ │ │ ├── create-discipline.dto.ts │ │ │ │ ├── discipline-ids.dto.ts │ │ │ │ ├── discipline.dto.ts │ │ │ │ ├── index.ts │ │ │ │ └── update-discipline.dto.ts │ │ │ └── index.ts │ │ ├── discord-servers/ │ │ │ ├── discord-servers.controller.ts │ │ │ ├── discord-servers.module.ts │ │ │ ├── discord-servers.service.ts │ │ │ └── dto/ │ │ │ ├── create-discord-server.dto.ts │ │ │ ├── discord-server.dto.ts │ │ │ ├── index.ts │ │ │ └── update-discord-server.dto.ts │ │ ├── events/ │ │ │ ├── dto/ │ │ │ │ ├── create-event.dto.ts │ │ │ │ ├── event.dto.ts │ │ │ │ ├── index.ts │ │ │ │ └── update-event.dto.ts │ │ │ ├── events.controller.ts │ │ │ ├── events.module.ts │ │ │ └── events.service.ts │ │ ├── gratitudes/ │ │ │ ├── discord.service.ts │ │ │ ├── dto/ │ │ │ │ ├── badge.dto.ts │ │ │ │ ├── country.dto.ts │ │ │ │ ├── create-gratitude.dto.ts │ │ │ │ ├── gratitude.dto.ts │ │ │ │ ├── hero-radar.dto.ts │ │ │ │ ├── heroes-radar-badge.dto.ts │ │ │ │ ├── heroes-radar-query.dto.ts │ │ │ │ ├── heroes-radar.dto.ts │ │ │ │ └── index.ts │ │ │ ├── gratitudes.controller.ts │ │ │ ├── gratitudes.module.ts │ │ │ ├── gratitudes.service.ts │ │ │ └── index.ts │ │ ├── listeners/ │ │ │ ├── course.listener.ts │ │ │ ├── index.ts │ │ │ └── listeners.module.ts │ │ ├── main.ts │ │ ├── mentors-hall-of-fame/ │ │ │ ├── dto/ │ │ │ │ ├── course-stats.dto.ts │ │ │ │ ├── index.ts │ │ │ │ └── top-mentor.dto.ts │ │ │ ├── index.ts │ │ │ ├── mentors-hall-of-fame.controller.test.ts │ │ │ ├── mentors-hall-of-fame.controller.ts │ │ │ ├── mentors-hall-of-fame.module.ts │ │ │ ├── mentors-hall-of-fame.service.test.ts │ │ │ └── mentors-hall-of-fame.service.ts │ │ ├── migrations.ts │ │ ├── notifications/ │ │ │ ├── dto/ │ │ │ │ ├── notification.dto.ts │ │ │ │ └── update-notification.dto.ts │ │ │ ├── email-template.ts │ │ │ ├── notifications.controller.ts │ │ │ ├── notifications.module.ts │ │ │ └── notifications.service.ts │ │ ├── openapi-spec.ts │ │ ├── opportunities/ │ │ │ ├── dto/ │ │ │ │ ├── applicant-resume.dto.ts │ │ │ │ ├── consent.dto.ts │ │ │ │ ├── form-data.dto.ts │ │ │ │ ├── give-consent-dto.ts │ │ │ │ ├── resume.dto.ts │ │ │ │ ├── status.dto.ts │ │ │ │ └── visibility.dto.ts │ │ │ ├── opportunities.controller.ts │ │ │ ├── opportunities.module.ts │ │ │ ├── opportunities.service.test.ts │ │ │ └── opportunities.service.ts │ │ ├── ormconfig.ts │ │ ├── profile/ │ │ │ ├── dto/ │ │ │ │ ├── endorsement.dto.ts │ │ │ │ ├── index.ts │ │ │ │ ├── personal-profile.dto.ts │ │ │ │ ├── profile-course.dto.ts │ │ │ │ ├── profile.dto.ts │ │ │ │ ├── update-profile.dto.ts │ │ │ │ └── update-user.dto.ts │ │ │ ├── endorsement.service.ts │ │ │ ├── index.ts │ │ │ ├── profile.controller.ts │ │ │ ├── profile.module.ts │ │ │ └── profile.service.ts │ │ ├── prompts/ │ │ │ ├── dto/ │ │ │ │ ├── create-prompt.dto.ts │ │ │ │ ├── index.ts │ │ │ │ ├── prompt.dto.ts │ │ │ │ └── update-prompt.dto.ts │ │ │ ├── prompts.controller.ts │ │ │ ├── prompts.module.ts │ │ │ └── prompts.service.ts │ │ ├── registry/ │ │ │ ├── constants.ts │ │ │ ├── dto/ │ │ │ │ ├── approve-mentor.dto.ts │ │ │ │ ├── comment-mentor-registry.dto.ts │ │ │ │ ├── invite-mentors.dto.ts │ │ │ │ └── mentor-registry.dto.ts │ │ │ ├── registry.controller.ts │ │ │ ├── registry.module.ts │ │ │ └── registry.service.ts │ │ ├── repositories/ │ │ │ ├── dto/ │ │ │ │ ├── create-repository-event.dto.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── repositories.controller.ts │ │ │ ├── repositories.module.ts │ │ │ └── repositories.service.ts │ │ ├── reset.d.ts │ │ ├── schedule/ │ │ │ ├── dto/ │ │ │ │ └── check-schedule-changes.ts │ │ │ ├── schedule.controller.ts │ │ │ ├── schedule.module.ts │ │ │ └── schedule.service.ts │ │ ├── session/ │ │ │ ├── dto/ │ │ │ │ └── auth-user.dto.ts │ │ │ ├── session.controller.ts │ │ │ └── session.module.ts │ │ ├── setup.ts │ │ ├── spec.json │ │ ├── tasks/ │ │ │ ├── dto/ │ │ │ │ ├── create-task.dto.ts │ │ │ │ ├── index.ts │ │ │ │ ├── task.dto.ts │ │ │ │ └── update-task.dto.ts │ │ │ ├── tasks-criteria/ │ │ │ │ ├── dto/ │ │ │ │ │ ├── criteria.dto.ts │ │ │ │ │ └── task-criteria.dto.ts │ │ │ │ ├── index.ts │ │ │ │ ├── tasks-criteria.controller.ts │ │ │ │ └── tasks-criteria.service.ts │ │ │ ├── tasks.controller.ts │ │ │ ├── tasks.module.ts │ │ │ └── tasks.service.ts │ │ ├── user-groups/ │ │ │ ├── dto/ │ │ │ │ ├── create-user-group.dto.ts │ │ │ │ ├── index.ts │ │ │ │ ├── update-user-group.dto.ts │ │ │ │ └── user-group.dto.ts │ │ │ ├── index.ts │ │ │ ├── user-groups.controller.ts │ │ │ ├── user-groups.module.ts │ │ │ └── user-groups.service.ts │ │ ├── users/ │ │ │ ├── dto/ │ │ │ │ ├── index.ts │ │ │ │ ├── user-search.dto.ts │ │ │ │ └── user.dto.ts │ │ │ ├── index.ts │ │ │ ├── users.controller.ts │ │ │ ├── users.module.ts │ │ │ └── users.service.ts │ │ ├── users-notifications/ │ │ │ ├── dto/ │ │ │ │ ├── notification-connection-exists.dto.ts │ │ │ │ ├── notification-connection.dto.ts │ │ │ │ ├── notification-user-connections.dto.ts │ │ │ │ ├── notification-user-settings.dto.ts │ │ │ │ ├── send-user-notification.dto.ts │ │ │ │ ├── update-notification-user-settings.dto.ts │ │ │ │ └── upsert-notification-connection.dto.ts │ │ │ ├── index.ts │ │ │ ├── users-notifications.module.ts │ │ │ ├── users.notifications.controller.ts │ │ │ └── users.notifications.service.ts │ │ └── utils/ │ │ ├── index.ts │ │ ├── shuffle.test.ts │ │ └── shuffle.ts │ ├── test/ │ │ └── app.e2e-spec.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── vitest.config.mts ├── package.json ├── renovate.json ├── server/ │ ├── .dockerignore │ ├── .swcrc │ ├── Dockerfile │ ├── Dockerfile.lambda │ ├── README.md │ ├── eslint.config.mjs │ ├── package.json │ ├── public/ │ │ └── swagger.yml │ ├── src/ │ │ ├── app.ts │ │ ├── config.ts │ │ ├── dataSource.ts │ │ ├── dataSourceOptions.ts │ │ ├── index.ts │ │ ├── logger.ts │ │ ├── migrations/ │ │ │ ├── 1630340371992-UserMigration.ts │ │ │ ├── 1630341383942-TaskResult.ts │ │ │ ├── 1630342025950-StudentMigration.ts │ │ │ ├── 1630342266002-UserMigration.ts │ │ │ ├── 1630347897950-StudentMigration.ts │ │ │ ├── 1632333725126-ResumeMigration.ts │ │ │ ├── 1635365797478-User.ts │ │ │ ├── 1637591194886-StageInterview.ts │ │ │ ├── 1638302439645-CourseMigration.ts │ │ │ ├── 1639418471577-Indicies.ts │ │ │ ├── 1639427578702-Update.ts │ │ │ ├── 1639502600339-Student.ts │ │ │ ├── 1642884123347-ResumeSelectCourses.ts │ │ │ ├── 1643481312933-Task.ts │ │ │ ├── 1643550350939-LoginState.ts │ │ │ ├── 1643926895264-Notifications.ts │ │ │ ├── 1644695410918-NotificationConnection.ts │ │ │ ├── 1645364514538-RepositoryEvent.ts │ │ │ ├── 1645654601903-Opportunitites.ts │ │ │ ├── 1647103154082-CrossCheckScheduling.ts │ │ │ ├── 1647175301446-TaskSolutionConstraint.ts │ │ │ ├── 1647550751147-NotificationType.ts │ │ │ ├── 1647885219936-LoginStateUserId.ts │ │ │ ├── 1649505252996-CourseLogo.ts │ │ │ ├── 1649868994688-CourseLogo.ts │ │ │ ├── 1650652882300-DiscordChannel.ts │ │ │ ├── 1652870756742-Resume.ts │ │ │ ├── 1656326258991-History.ts │ │ │ ├── 1661034658479-Feedback.ts │ │ │ ├── 1661087975938-Discipline.ts │ │ │ ├── 1661106736439-Disciplines.ts │ │ │ ├── 1661107174477-Disciplines.ts │ │ │ ├── 1661616212488-NotificationCategory.ts │ │ │ ├── 1662275601017-CourseTask.ts │ │ │ ├── 1664183799115-CourseEvent.ts │ │ │ ├── 1666348642811-TaskCriteria.ts │ │ │ ├── 1666621080327-TaskSolutionResult.ts │ │ │ ├── 1671475396333-Tasks.ts │ │ │ ├── 1672142743107-TeamDistribution.ts │ │ │ ├── 1672386450861-TeamDistribution.ts │ │ │ ├── 1673090827105-TaskVerification.ts │ │ │ ├── 1673692838338-User.ts │ │ │ ├── 1674128274839-Team.ts │ │ │ ├── 1674377676805-TeamDistributionStudent.ts │ │ │ ├── 1674755854609-Resume.ts │ │ │ ├── 1675245424426-UserGroup.ts │ │ │ ├── 1675345245770-Course.ts │ │ │ ├── 1676139987317-User.ts │ │ │ ├── 1685197747051-MentorRegistry.ts │ │ │ ├── 1686657350908-InterviewScore.ts │ │ │ ├── 1687009744110-Prompt.ts │ │ │ ├── 1691520611773-Temperature.ts │ │ │ ├── 1691524327332-Temperature.ts │ │ │ ├── 1693930286280-CourseUsersActivist.ts │ │ │ ├── 1699808604000-AddMinStudentPerMentorColumnToCourse.ts │ │ │ ├── 1700391857109-Obfuscation.ts │ │ │ ├── 1712137476312-Course.ts │ │ │ ├── 1730926720293-CourseTask.ts │ │ │ ├── 1734874453585-Contributor.ts │ │ │ ├── 1736458672717-Course.ts │ │ │ ├── 1738250779923-CoursePersonalMentoringDates.ts │ │ │ ├── 1746467689328-Course.ts │ │ │ ├── 1747380525126-CourseTaskInterviewCreatingPairs.ts │ │ │ ├── 1760699701354-AddCourseLeaveSurveyResponse.ts │ │ │ └── index.ts │ │ ├── models/ │ │ │ ├── alert.ts │ │ │ ├── certificate.ts │ │ │ ├── contributor.ts │ │ │ ├── course-leave-survey-response.entity.ts │ │ │ ├── course.ts │ │ │ ├── courseEvent.ts │ │ │ ├── courseManager.ts │ │ │ ├── courseTask.ts │ │ │ ├── courseUser.ts │ │ │ ├── data/ │ │ │ │ ├── available-languages.data.ts │ │ │ │ ├── index.ts │ │ │ │ └── language-levels.data.ts │ │ │ ├── discipline.ts │ │ │ ├── discordServer.ts │ │ │ ├── event.ts │ │ │ ├── feedback.ts │ │ │ ├── history.ts │ │ │ ├── index.ts │ │ │ ├── loginState.ts │ │ │ ├── mentor.ts │ │ │ ├── mentorRegistry.ts │ │ │ ├── notification.ts │ │ │ ├── notificationChannel.ts │ │ │ ├── notificationChannelSettings.ts │ │ │ ├── notificationUserConnection.ts │ │ │ ├── notificationUserSettings.ts │ │ │ ├── privateFeedback.ts │ │ │ ├── profilePermissions.ts │ │ │ ├── prompt.ts │ │ │ ├── registry.ts │ │ │ ├── repositoryEvent.ts │ │ │ ├── resume.ts │ │ │ ├── session.ts │ │ │ ├── stageInterview.ts │ │ │ ├── stageInterviewFeedback.ts │ │ │ ├── stageInterviewStudent.ts │ │ │ ├── student-feedback.ts │ │ │ ├── student.ts │ │ │ ├── task.ts │ │ │ ├── taskArtefact.ts │ │ │ ├── taskChecker.ts │ │ │ ├── taskCriteria.ts │ │ │ ├── taskInterviewResult.ts │ │ │ ├── taskInterviewStudent.ts │ │ │ ├── taskResult.ts │ │ │ ├── taskSolution.ts │ │ │ ├── taskSolutionChecker.ts │ │ │ ├── taskSolutionResult.ts │ │ │ ├── taskVerification.ts │ │ │ ├── team.ts │ │ │ ├── teamDistribution.ts │ │ │ ├── teamDistributionStudent.ts │ │ │ ├── user.ts │ │ │ └── userGroup.ts │ │ ├── repositories/ │ │ │ ├── courseTask.repository.ts │ │ │ ├── crossCheck.repository.ts │ │ │ ├── feedback.repository.ts │ │ │ ├── interview.repository.ts │ │ │ ├── mentor.repository.ts │ │ │ ├── mentorRegistry.repository.ts │ │ │ ├── repositoryEvent.repository.ts │ │ │ ├── stageInterview.repository.ts │ │ │ ├── stageInterviewFeedback.repository.ts │ │ │ ├── student.repository.ts │ │ │ └── user.repository.ts │ │ ├── reset.d.ts │ │ ├── routes/ │ │ │ ├── checks/ │ │ │ │ ├── getBadComment.ts │ │ │ │ ├── getMaxScoreCheckers.ts │ │ │ │ └── index.ts │ │ │ ├── common.ts │ │ │ ├── course/ │ │ │ │ ├── certificates.ts │ │ │ │ ├── crossCheck/ │ │ │ │ │ ├── createCompletion.ts │ │ │ │ │ ├── createDistribution.ts │ │ │ │ │ ├── createMessage.ts │ │ │ │ │ ├── createResult.ts │ │ │ │ │ ├── createSolution.ts │ │ │ │ │ ├── deleteSolution.ts │ │ │ │ │ ├── getAssignments.ts │ │ │ │ │ ├── getResult.ts │ │ │ │ │ ├── getSolution.ts │ │ │ │ │ ├── getTaskDetails.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── updateMessage.ts │ │ │ │ ├── events.ts │ │ │ │ ├── index.ts │ │ │ │ ├── interviews.ts │ │ │ │ ├── mentor.ts │ │ │ │ ├── repository.ts │ │ │ │ ├── schedule.ts │ │ │ │ ├── score/ │ │ │ │ │ ├── createMultipleScores.ts │ │ │ │ │ ├── createSingleScore.ts │ │ │ │ │ ├── getScoreByStudent.ts │ │ │ │ │ ├── getScoreCsv.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── recalculateScore.ts │ │ │ │ ├── stageInterview/ │ │ │ │ │ ├── cancelInterview.ts │ │ │ │ │ ├── createFeedback.ts │ │ │ │ │ ├── createInterview.ts │ │ │ │ │ ├── createInterviews.ts │ │ │ │ │ ├── getFeedback.ts │ │ │ │ │ ├── getInterviewStudent.ts │ │ │ │ │ ├── getInterviewerStudents.ts │ │ │ │ │ ├── getInterviews.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── updateInterview.ts │ │ │ │ ├── student.ts │ │ │ │ ├── students.ts │ │ │ │ ├── taskArtefact.ts │ │ │ │ ├── taskVerifications.ts │ │ │ │ └── tasks/ │ │ │ │ ├── createCourseTaskDistribution.ts │ │ │ │ ├── getCourseTasksDetails.ts │ │ │ │ └── index.ts │ │ │ ├── feedback.ts │ │ │ ├── file/ │ │ │ │ ├── index.ts │ │ │ │ └── upload.ts │ │ │ ├── guards.ts │ │ │ ├── index.ts │ │ │ ├── logging.ts │ │ │ ├── me.ts │ │ │ ├── middlewares.ts │ │ │ ├── profile/ │ │ │ │ ├── __test__/ │ │ │ │ │ └── permissions.test.ts │ │ │ │ ├── index.ts │ │ │ │ ├── info.ts │ │ │ │ ├── me.ts │ │ │ │ ├── mentor-stats.ts │ │ │ │ ├── permissions.ts │ │ │ │ ├── public-feedback.ts │ │ │ │ ├── stage-interview-feedback.ts │ │ │ │ ├── student-stats.ts │ │ │ │ └── user-info.ts │ │ │ ├── registry/ │ │ │ │ └── index.ts │ │ │ ├── repository/ │ │ │ │ ├── events.ts │ │ │ │ └── index.ts │ │ │ ├── task/ │ │ │ │ └── index.ts │ │ │ ├── taskVerification/ │ │ │ │ └── index.ts │ │ │ ├── tasks/ │ │ │ │ └── index.ts │ │ │ ├── users/ │ │ │ │ └── index.ts │ │ │ ├── utils.ts │ │ │ └── validators.ts │ │ ├── rules/ │ │ │ ├── __tests__/ │ │ │ │ └── mentors.test.ts │ │ │ ├── index.ts │ │ │ ├── interviews.ts │ │ │ ├── mentors.ts │ │ │ ├── name.ts │ │ │ └── types.ts │ │ ├── schedule.ts │ │ └── services/ │ │ ├── aws.service.ts │ │ ├── check.service.ts │ │ ├── course.service.ts │ │ ├── crossCheck.service.ts │ │ ├── distribution/ │ │ │ ├── __tests__/ │ │ │ │ ├── crossCheck.test.ts │ │ │ │ ├── crossMentor.test.ts │ │ │ │ └── shuffle.test.ts │ │ │ ├── crossCheckDistribution.service.ts │ │ │ ├── crossMentorDistribution.service.ts │ │ │ ├── index.ts │ │ │ └── shuffle.ts │ │ ├── github.service.ts │ │ ├── index.ts │ │ ├── interview.service.ts │ │ ├── notification.service.ts │ │ ├── operationResult.ts │ │ ├── repository.service.ts │ │ ├── score/ │ │ │ ├── index.ts │ │ │ └── score.service.ts │ │ ├── stageInterview.service.ts │ │ ├── student.service.ts │ │ ├── taskResults.service.ts │ │ ├── taskVerification.service.ts │ │ ├── tasks.service.ts │ │ └── user.service.ts │ ├── swaggerDef.js │ ├── tsconfig.json │ └── vitest.config.mts ├── setup/ │ ├── backup-local.sql │ ├── cdk/ │ │ ├── App.ts │ │ ├── DockerFunctionConstruct.ts │ │ ├── Stack.ts │ │ ├── cdk.json │ │ └── package.json │ ├── docker-compose.yml │ ├── dump-local.sh │ ├── dump.sh │ ├── nginx/ │ │ └── nginx.conf │ ├── restore-local.sh │ └── restore.sh ├── skills-lock.json ├── tools/ │ └── sloths/ │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── .oxfmtrc.json │ ├── README.md │ ├── env.d.ts │ ├── index.html │ ├── package.json │ ├── public/ │ │ └── cdn/ │ │ ├── cleaned/ │ │ │ └── filelist.json │ │ └── stickers/ │ │ ├── activist/ │ │ │ └── metadata.json │ │ ├── codewars/ │ │ │ └── metadata.json │ │ ├── congrats/ │ │ │ └── metadata.json │ │ └── metadata.json │ ├── src/ │ │ ├── App.vue │ │ ├── assets/ │ │ │ ├── locales/ │ │ │ │ └── en.json │ │ │ └── styles/ │ │ │ ├── base.css │ │ │ ├── fonts.css │ │ │ ├── main.css │ │ │ └── variables.css │ │ ├── common/ │ │ │ ├── const.ts │ │ │ └── types.ts │ │ ├── components/ │ │ │ ├── about/ │ │ │ │ ├── AboutSection.vue │ │ │ │ └── AboutTeammate.vue │ │ │ ├── background/ │ │ │ │ └── BackgroundView.vue │ │ │ ├── buttons/ │ │ │ │ ├── CustomBtn.vue │ │ │ │ ├── IconBtn.vue │ │ │ │ └── ImageBtn.vue │ │ │ ├── catalog/ │ │ │ │ ├── SlothCard.vue │ │ │ │ └── SlothInfo.vue │ │ │ ├── controls-list/ │ │ │ │ ├── ControlsList.vue │ │ │ │ ├── PaginationList.vue │ │ │ │ ├── SearchText.vue │ │ │ │ ├── SortingList.vue │ │ │ │ └── TagCloud.vue │ │ │ ├── footer/ │ │ │ │ └── FooterView.vue │ │ │ ├── guess/ │ │ │ │ └── GuessInfo.vue │ │ │ ├── header/ │ │ │ │ └── HeaderView.vue │ │ │ ├── home/ │ │ │ │ ├── HomeAbout.vue │ │ │ │ ├── HomeCatalog.vue │ │ │ │ └── HomeCategory.vue │ │ │ ├── loader/ │ │ │ │ └── LoaderView.vue │ │ │ ├── memory/ │ │ │ │ ├── GameField.vue │ │ │ │ └── MemoryInfo.vue │ │ │ ├── mixins/ │ │ │ │ └── sort-mixin.ts │ │ │ ├── modal/ │ │ │ │ ├── AlertModal.vue │ │ │ │ └── ModalWindow.vue │ │ │ └── switchers/ │ │ │ ├── SoundSwitcher.vue │ │ │ └── ThemeSwitcher.vue │ │ ├── i18n.ts │ │ ├── main.ts │ │ ├── router/ │ │ │ └── index.ts │ │ ├── services/ │ │ │ ├── error-handler.ts │ │ │ └── sloths-service.ts │ │ ├── shims-vue.d.ts │ │ ├── stores/ │ │ │ ├── alert-modal.ts │ │ │ ├── audio-on.ts │ │ │ ├── cleaned.ts │ │ │ ├── counter.ts │ │ │ ├── loader.ts │ │ │ ├── pages-store.ts │ │ │ ├── pagination.ts │ │ │ ├── search-text.ts │ │ │ ├── sloth-info.ts │ │ │ ├── sloths.ts │ │ │ ├── sorting-list.ts │ │ │ ├── tag-cloud.ts │ │ │ └── theme.ts │ │ ├── utils/ │ │ │ ├── audio.ts │ │ │ ├── canvas-utils.ts │ │ │ ├── game-utils.ts │ │ │ └── userTheme.ts │ │ └── views/ │ │ ├── 404.vue │ │ ├── About.vue │ │ ├── Catalog.vue │ │ ├── Create.vue │ │ ├── Guess.vue │ │ ├── Home.vue │ │ ├── Memory.vue │ │ └── Merch.vue │ ├── tsconfig.config.json │ ├── tsconfig.json │ └── vite.config.ts ├── turbo.json └── vitest.shared.mts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .agents/skills/nestjs-best-practices/.github/workflows/branch-protection.yml ================================================ name: Branch Protection on: pull_request: branches: [main] jobs: check-branch: runs-on: ubuntu-latest steps: - name: Block docs branch merge to main if: github.head_ref == 'docs' run: | echo "::error::Merging 'docs' branch into 'main' is not allowed." echo "" echo "The 'docs' branch contains the documentation website which should" echo "remain separate from the main skill files to keep installations lightweight." echo "" echo "If you need to sync changes, cherry-pick specific commits instead." exit 1 - name: Branch check passed if: github.head_ref != 'docs' run: echo "Branch check passed - not merging from docs branch" ================================================ FILE: .agents/skills/nestjs-best-practices/.github/workflows/deploy.yml ================================================ name: Deploy to GitHub Pages on: push: branches: [docs] paths: - 'website/**' workflow_dispatch: permissions: contents: read pages: write id-token: write concurrency: group: pages cancel-in-progress: false jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node uses: actions/setup-node@v4 with: node-version: 20 cache: npm cache-dependency-path: website/package-lock.json - name: Setup Pages uses: actions/configure-pages@v4 - name: Install dependencies run: npm ci working-directory: website - name: Build run: npm run build working-directory: website - name: Copy index.html to 404.html for SPA routing run: cp website/dist/index.html website/dist/404.html - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: path: website/dist deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest needs: build steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 ================================================ FILE: .agents/skills/nestjs-best-practices/.gitignore ================================================ # Dependencies node_modules/ # Website (lives on docs branch) website/ # Build outputs dist/ *.js *.d.ts *.js.map # IDE .idea/ .vscode/ *.swp *.swo # OS .DS_Store Thumbs.db # Logs *.log npm-debug.log* # Environment .env .env.local ================================================ FILE: .agents/skills/nestjs-best-practices/AGENTS.md ================================================ # NestJS Best Practices **Version 1.1.0** NestJS Best Practices January 2026 > **Note:** > This document is mainly for agents and LLMs to follow when maintaining, > generating, or refactoring NestJS codebases. Humans may also find it > useful, but guidance here is optimized for automation and consistency > by AI-assisted workflows. --- ## Abstract Comprehensive best practices and architecture guide for NestJS applications, designed for AI agents and LLMs. Contains 40 rules across 10 categories, prioritized by impact from critical (architecture, dependency injection) to incremental (DevOps patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation. --- ## Table of Contents 1. [Architecture](#1-architecture) — **CRITICAL** - 1.1 [Avoid Circular Dependencies](#11-avoid-circular-dependencies) - 1.2 [Organize by Feature Modules](#12-organize-by-feature-modules) - 1.3 [Use Proper Module Sharing Patterns](#13-use-proper-module-sharing-patterns) - 1.4 [Single Responsibility for Services](#14-single-responsibility-for-services) - 1.5 [Use Event-Driven Architecture for Decoupling](#15-use-event-driven-architecture-for-decoupling) - 1.6 [Use Repository Pattern for Data Access](#16-use-repository-pattern-for-data-access) 2. [Dependency Injection](#2-dependency-injection) — **CRITICAL** - 2.1 [Avoid Service Locator Anti-Pattern](#21-avoid-service-locator-anti-pattern) - 2.2 [Apply Interface Segregation Principle](#22-apply-interface-segregation-principle) - 2.3 [Honor Liskov Substitution Principle](#23-honor-liskov-substitution-principle) - 2.4 [Prefer Constructor Injection](#24-prefer-constructor-injection) - 2.5 [Understand Provider Scopes](#25-understand-provider-scopes) - 2.6 [Use Injection Tokens for Interfaces](#26-use-injection-tokens-for-interfaces) 3. [Error Handling](#3-error-handling) — **HIGH** - 3.1 [Handle Async Errors Properly](#31-handle-async-errors-properly) - 3.2 [Throw HTTP Exceptions from Services](#32-throw-http-exceptions-from-services) - 3.3 [Use Exception Filters for Error Handling](#33-use-exception-filters-for-error-handling) 4. [Security](#4-security) — **HIGH** - 4.1 [Implement Secure JWT Authentication](#41-implement-secure-jwt-authentication) - 4.2 [Implement Rate Limiting](#42-implement-rate-limiting) - 4.3 [Sanitize Output to Prevent XSS](#43-sanitize-output-to-prevent-xss) - 4.4 [Use Guards for Authentication and Authorization](#44-use-guards-for-authentication-and-authorization) - 4.5 [Validate All Input with DTOs and Pipes](#45-validate-all-input-with-dtos-and-pipes) 5. [Performance](#5-performance) — **HIGH** - 5.1 [Use Async Lifecycle Hooks Correctly](#51-use-async-lifecycle-hooks-correctly) - 5.2 [Use Lazy Loading for Large Modules](#52-use-lazy-loading-for-large-modules) - 5.3 [Optimize Database Queries](#53-optimize-database-queries) - 5.4 [Use Caching Strategically](#54-use-caching-strategically) 6. [Testing](#6-testing) — **MEDIUM-HIGH** - 6.1 [Use Supertest for E2E Testing](#61-use-supertest-for-e2e-testing) - 6.2 [Mock External Services in Tests](#62-mock-external-services-in-tests) - 6.3 [Use Testing Module for Unit Tests](#63-use-testing-module-for-unit-tests) 7. [Database & ORM](#7-database-orm) — **MEDIUM-HIGH** - 7.1 [Avoid N+1 Query Problems](#71-avoid-n-1-query-problems) - 7.2 [Use Database Migrations](#72-use-database-migrations) - 7.3 [Use Transactions for Multi-Step Operations](#73-use-transactions-for-multi-step-operations) 8. [API Design](#8-api-design) — **MEDIUM** - 8.1 [Use DTOs and Serialization for API Responses](#81-use-dtos-and-serialization-for-api-responses) - 8.2 [Use Interceptors for Cross-Cutting Concerns](#82-use-interceptors-for-cross-cutting-concerns) - 8.3 [Use Pipes for Input Transformation](#83-use-pipes-for-input-transformation) - 8.4 [Use API Versioning for Breaking Changes](#84-use-api-versioning-for-breaking-changes) 9. [Microservices](#9-microservices) — **MEDIUM** - 9.1 [Implement Health Checks for Microservices](#91-implement-health-checks-for-microservices) - 9.2 [Use Message and Event Patterns Correctly](#92-use-message-and-event-patterns-correctly) - 9.3 [Use Message Queues for Background Jobs](#93-use-message-queues-for-background-jobs) 10. [DevOps & Deployment](#10-devops-deployment) — **LOW-MEDIUM** - 10.1 [Implement Graceful Shutdown](#101-implement-graceful-shutdown) - 10.2 [Use ConfigModule for Environment Configuration](#102-use-configmodule-for-environment-configuration) - 10.3 [Use Structured Logging](#103-use-structured-logging) --- ## 1. Architecture **Section Impact: CRITICAL** ### 1.1 Avoid Circular Dependencies **Impact: CRITICAL** — "#1 cause of runtime crashes" Circular dependencies occur when Module A imports Module B, and Module B imports Module A (directly or transitively). NestJS can sometimes resolve these through forward references, but they indicate architectural problems and should be avoided. This is the #1 cause of runtime crashes in NestJS applications. **Incorrect (circular module imports):** ```typescript // users.module.ts @Module({ imports: [OrdersModule], // Orders needs Users, Users needs Orders = circular providers: [UsersService], exports: [UsersService], }) export class UsersModule {} // orders.module.ts @Module({ imports: [UsersModule], // Circular dependency! providers: [OrdersService], exports: [OrdersService], }) export class OrdersModule {} ``` **Correct (extract shared logic or use events):** ```typescript // Option 1: Extract shared logic to a third module // shared.module.ts @Module({ providers: [SharedService], exports: [SharedService], }) export class SharedModule {} // users.module.ts @Module({ imports: [SharedModule], providers: [UsersService], }) export class UsersModule {} // orders.module.ts @Module({ imports: [SharedModule], providers: [OrdersService], }) export class OrdersModule {} // Option 2: Use events for decoupled communication // users.service.ts @Injectable() export class UsersService { constructor(private eventEmitter: EventEmitter2) {} async createUser(data: CreateUserDto) { const user = await this.userRepo.save(data); this.eventEmitter.emit('user.created', user); return user; } } // orders.service.ts @Injectable() export class OrdersService { @OnEvent('user.created') handleUserCreated(user: User) { // React to user creation without direct dependency } } ``` Reference: [NestJS Circular Dependency](https://docs.nestjs.com/fundamentals/circular-dependency) --- ### 1.2 Organize by Feature Modules **Impact: CRITICAL** — "3-5x faster onboarding and development" Organize your application into feature modules that encapsulate related functionality. Each feature module should be self-contained with its own controllers, services, entities, and DTOs. Avoid organizing by technical layer (all controllers together, all services together). This enables 3-5x faster onboarding and feature development. **Incorrect (technical layer organization):** ```typescript // Technical layer organization (anti-pattern) src/ ├── controllers/ │ ├── users.controller.ts │ ├── orders.controller.ts │ └── products.controller.ts ├── services/ │ ├── users.service.ts │ ├── orders.service.ts │ └── products.service.ts ├── entities/ │ ├── user.entity.ts │ ├── order.entity.ts │ └── product.entity.ts └── app.module.ts // Imports everything directly ``` **Correct (feature module organization):** ```typescript // Feature module organization src/ ├── users/ │ ├── dto/ │ │ ├── create-user.dto.ts │ │ └── update-user.dto.ts │ ├── entities/ │ │ └── user.entity.ts │ ├── users.controller.ts │ ├── users.service.ts │ ├── users.repository.ts │ └── users.module.ts ├── orders/ │ ├── dto/ │ ├── entities/ │ ├── orders.controller.ts │ ├── orders.service.ts │ └── orders.module.ts ├── shared/ │ ├── guards/ │ ├── interceptors/ │ ├── filters/ │ └── shared.module.ts └── app.module.ts // users.module.ts @Module({ imports: [TypeOrmModule.forFeature([User])], controllers: [UsersController], providers: [UsersService, UsersRepository], exports: [UsersService], // Only export what others need }) export class UsersModule {} // app.module.ts @Module({ imports: [ ConfigModule.forRoot(), TypeOrmModule.forRoot(), UsersModule, OrdersModule, SharedModule, ], }) export class AppModule {} ``` Reference: [NestJS Modules](https://docs.nestjs.com/modules) --- ### 1.3 Use Proper Module Sharing Patterns **Impact: CRITICAL** — Prevents duplicate instances, memory leaks, and state inconsistency NestJS modules are singletons by default. When a service is properly exported from a module and that module is imported elsewhere, the same instance is shared. However, providing a service in multiple modules creates separate instances, leading to memory waste, state inconsistency, and confusing behavior. Always encapsulate services in dedicated modules, export them explicitly, and import the module where needed. **Incorrect (service provided in multiple modules):** ```typescript // StorageService provided directly in multiple modules - WRONG // storage.service.ts @Injectable() export class StorageService { private cache = new Map(); // Each instance has separate state! store(key: string, value: any) { this.cache.set(key, value); } } // app.module.ts @Module({ providers: [StorageService], // Instance #1 controllers: [AppController], }) export class AppModule {} // videos.module.ts @Module({ providers: [StorageService], // Instance #2 - different from AppModule! controllers: [VideosController], }) export class VideosModule {} // Problems: // 1. Two separate StorageService instances exist // 2. cache.set() in VideosModule doesn't affect AppModule's cache // 3. Memory wasted on duplicate instances // 4. Debugging nightmares when state doesn't sync ``` **Correct (dedicated module with exports):** ```typescript // storage/storage.module.ts @Module({ providers: [StorageService], exports: [StorageService], // Make available to importers }) export class StorageModule {} // videos/videos.module.ts @Module({ imports: [StorageModule], // Import the module, not the service controllers: [VideosController], providers: [VideosService], }) export class VideosModule {} // channels/channels.module.ts @Module({ imports: [StorageModule], // Same instance shared controllers: [ChannelsController], providers: [ChannelsService], }) export class ChannelsModule {} // app.module.ts @Module({ imports: [ StorageModule, // Only if AppModule itself needs StorageService VideosModule, ChannelsModule, ], }) export class AppModule {} // Now all modules share the SAME StorageService instance ``` **When to use @Global() (sparingly):** ```typescript // ONLY for truly cross-cutting concerns @Global() @Module({ providers: [ConfigService, LoggerService], exports: [ConfigService, LoggerService], }) export class CoreModule {} // Import once in AppModule @Module({ imports: [CoreModule], // Registered globally, available everywhere }) export class AppModule {} // Other modules don't need to import CoreModule @Module({ controllers: [UsersController], providers: [UsersService], // Can inject ConfigService without importing }) export class UsersModule {} // WARNING: Don't make everything global! // - Hides dependencies (can't see what a module needs from imports) // - Makes testing harder // - Reserve for: config, logging, database connections ``` **Module re-exporting pattern:** ```typescript // common.module.ts - shared utilities @Module({ providers: [DateService, ValidationService], exports: [DateService, ValidationService], }) export class CommonModule {} // core.module.ts - re-exports common for convenience @Module({ imports: [CommonModule, DatabaseModule], exports: [CommonModule, DatabaseModule], // Re-export for consumers }) export class CoreModule {} // feature.module.ts - imports CoreModule, gets both @Module({ imports: [CoreModule], // Gets CommonModule + DatabaseModule controllers: [FeatureController], }) export class FeatureModule {} ``` Reference: [NestJS Modules](https://docs.nestjs.com/modules#shared-modules) --- ### 1.4 Single Responsibility for Services **Impact: CRITICAL** — "40%+ improvement in testability" Each service should have a single, well-defined responsibility. Avoid "god services" that handle multiple unrelated concerns. If a service name includes "And" or handles more than one domain concept, it likely violates single responsibility. This reduces complexity and improves testability by 40%+. **Incorrect (god service anti-pattern):** ```typescript // God service anti-pattern @Injectable() export class UserAndOrderService { constructor( private userRepo: UserRepository, private orderRepo: OrderRepository, private mailer: MailService, private payment: PaymentService, ) {} async createUser(dto: CreateUserDto) { const user = await this.userRepo.save(dto); await this.mailer.sendWelcome(user); return user; } async createOrder(userId: string, dto: CreateOrderDto) { const order = await this.orderRepo.save({ userId, ...dto }); await this.payment.charge(order); await this.mailer.sendOrderConfirmation(order); return order; } async calculateOrderStats(userId: string) { // Stats logic mixed in } async validatePayment(orderId: string) { // Payment logic mixed in } } ``` **Correct (focused services with single responsibility):** ```typescript // Focused services with single responsibility @Injectable() export class UsersService { constructor(private userRepo: UserRepository) {} async create(dto: CreateUserDto): Promise { return this.userRepo.save(dto); } async findById(id: string): Promise { return this.userRepo.findOneOrFail({ where: { id } }); } } @Injectable() export class OrdersService { constructor(private orderRepo: OrderRepository) {} async create(userId: string, dto: CreateOrderDto): Promise { return this.orderRepo.save({ userId, ...dto }); } async findByUser(userId: string): Promise { return this.orderRepo.find({ where: { userId } }); } } @Injectable() export class OrderStatsService { constructor(private orderRepo: OrderRepository) {} async calculateForUser(userId: string): Promise { // Focused stats calculation } } // Orchestration in controller or dedicated orchestrator @Controller('orders') export class OrdersController { constructor( private orders: OrdersService, private payment: PaymentService, private notifications: NotificationService, ) {} @Post() async create(@CurrentUser() user: User, @Body() dto: CreateOrderDto) { const order = await this.orders.create(user.id, dto); await this.payment.charge(order); await this.notifications.sendOrderConfirmation(order); return order; } } ``` Reference: [NestJS Providers](https://docs.nestjs.com/providers) --- ### 1.5 Use Event-Driven Architecture for Decoupling **Impact: MEDIUM-HIGH** — Enables async processing and modularity Use `@nestjs/event-emitter` for intra-service events and message brokers for inter-service communication. Events allow modules to react to changes without direct dependencies, improving modularity and enabling async processing. **Incorrect (direct service coupling):** ```typescript // Direct service coupling @Injectable() export class OrdersService { constructor( private inventoryService: InventoryService, private emailService: EmailService, private analyticsService: AnalyticsService, private notificationService: NotificationService, private loyaltyService: LoyaltyService, ) {} async createOrder(dto: CreateOrderDto): Promise { const order = await this.repo.save(dto); // Tight coupling - OrdersService knows about all consumers await this.inventoryService.reserve(order.items); await this.emailService.sendConfirmation(order); await this.analyticsService.track('order_created', order); await this.notificationService.push(order.userId, 'Order placed'); await this.loyaltyService.addPoints(order.userId, order.total); // Adding new behavior requires modifying this service return order; } } ``` **Correct (event-driven decoupling):** ```typescript // Use EventEmitter for decoupling import { EventEmitter2 } from '@nestjs/event-emitter'; // Define event export class OrderCreatedEvent { constructor( public readonly orderId: string, public readonly userId: string, public readonly items: OrderItem[], public readonly total: number, ) {} } // Service emits events @Injectable() export class OrdersService { constructor( private eventEmitter: EventEmitter2, private repo: Repository, ) {} async createOrder(dto: CreateOrderDto): Promise { const order = await this.repo.save(dto); // Emit event - no knowledge of consumers this.eventEmitter.emit('order.created', new OrderCreatedEvent(order.id, order.userId, order.items, order.total)); return order; } } // Listeners in separate modules @Injectable() export class InventoryListener { @OnEvent('order.created') async handleOrderCreated(event: OrderCreatedEvent): Promise { await this.inventoryService.reserve(event.items); } } @Injectable() export class EmailListener { @OnEvent('order.created') async handleOrderCreated(event: OrderCreatedEvent): Promise { await this.emailService.sendConfirmation(event.orderId); } } @Injectable() export class AnalyticsListener { @OnEvent('order.created') async handleOrderCreated(event: OrderCreatedEvent): Promise { await this.analyticsService.track('order_created', { orderId: event.orderId, total: event.total, }); } } ``` Reference: [NestJS Events](https://docs.nestjs.com/techniques/events) --- ### 1.6 Use Repository Pattern for Data Access **Impact: HIGH** — Decouples business logic from database Create custom repositories to encapsulate complex queries and database logic. This keeps services focused on business logic, makes testing easier with mock repositories, and allows changing database implementations without affecting business code. **Incorrect (complex queries in services):** ```typescript // Complex queries in services @Injectable() export class UsersService { constructor(@InjectRepository(User) private repo: Repository) {} async findActiveWithOrders(minOrders: number): Promise { // Complex query logic mixed with business logic return this.repo .createQueryBuilder('user') .leftJoinAndSelect('user.orders', 'order') .where('user.isActive = :active', { active: true }) .andWhere('user.deletedAt IS NULL') .groupBy('user.id') .having('COUNT(order.id) >= :min', { min: minOrders }) .orderBy('user.createdAt', 'DESC') .getMany(); } // Service becomes bloated with query logic } ``` **Correct (custom repository with encapsulated queries):** ```typescript // Custom repository with encapsulated queries @Injectable() export class UsersRepository { constructor(@InjectRepository(User) private repo: Repository) {} async findById(id: string): Promise { return this.repo.findOne({ where: { id } }); } async findByEmail(email: string): Promise { return this.repo.findOne({ where: { email } }); } async findActiveWithMinOrders(minOrders: number): Promise { return this.repo .createQueryBuilder('user') .leftJoinAndSelect('user.orders', 'order') .where('user.isActive = :active', { active: true }) .andWhere('user.deletedAt IS NULL') .groupBy('user.id') .having('COUNT(order.id) >= :min', { min: minOrders }) .orderBy('user.createdAt', 'DESC') .getMany(); } async save(user: User): Promise { return this.repo.save(user); } } // Clean service with business logic only @Injectable() export class UsersService { constructor(private usersRepo: UsersRepository) {} async getActiveUsersWithOrders(): Promise { return this.usersRepo.findActiveWithMinOrders(1); } async create(dto: CreateUserDto): Promise { const existing = await this.usersRepo.findByEmail(dto.email); if (existing) { throw new ConflictException('Email already registered'); } const user = new User(); user.email = dto.email; user.name = dto.name; return this.usersRepo.save(user); } } ``` Reference: [Repository Pattern](https://martinfowler.com/eaaCatalog/repository.html) --- ## 2. Dependency Injection **Section Impact: CRITICAL** ### 2.1 Avoid Service Locator Anti-Pattern **Impact: HIGH** — Hides dependencies and breaks testability Avoid using `ModuleRef.get()` or global containers to resolve dependencies at runtime. This hides dependencies, makes code harder to test, and breaks the benefits of dependency injection. Use constructor injection instead. **Incorrect (service locator anti-pattern):** ```typescript // Use ModuleRef to get dependencies dynamically @Injectable() export class OrdersService { constructor(private moduleRef: ModuleRef) {} async createOrder(dto: CreateOrderDto): Promise { // Dependencies are hidden - not visible in constructor const usersService = this.moduleRef.get(UsersService); const inventoryService = this.moduleRef.get(InventoryService); const paymentService = this.moduleRef.get(PaymentService); const user = await usersService.findOne(dto.userId); // ... rest of logic } } // Global singleton container class ServiceContainer { private static instance: ServiceContainer; private services = new Map(); static getInstance(): ServiceContainer { if (!this.instance) { this.instance = new ServiceContainer(); } return this.instance; } get(key: string): T { return this.services.get(key); } } ``` **Correct (constructor injection with explicit dependencies):** ```typescript // Use constructor injection - dependencies are explicit @Injectable() export class OrdersService { constructor( private usersService: UsersService, private inventoryService: InventoryService, private paymentService: PaymentService, ) {} async createOrder(dto: CreateOrderDto): Promise { const user = await this.usersService.findOne(dto.userId); const inventory = await this.inventoryService.check(dto.items); // Dependencies are clear and testable } } // Easy to test with mocks describe('OrdersService', () => { let service: OrdersService; beforeEach(async () => { const module = await Test.createTestingModule({ providers: [ OrdersService, { provide: UsersService, useValue: mockUsersService }, { provide: InventoryService, useValue: mockInventoryService }, { provide: PaymentService, useValue: mockPaymentService }, ], }).compile(); service = module.get(OrdersService); }); }); // VALID: Factory pattern for dynamic instantiation @Injectable() export class HandlerFactory { constructor(private moduleRef: ModuleRef) {} getHandler(type: string): Handler { switch (type) { case 'email': return this.moduleRef.get(EmailHandler); case 'sms': return this.moduleRef.get(SmsHandler); default: return this.moduleRef.get(DefaultHandler); } } } ``` Reference: [NestJS Module Reference](https://docs.nestjs.com/fundamentals/module-ref) --- ### 2.2 Apply Interface Segregation Principle **Impact: HIGH** — Reduces coupling and improves testability by 30-50% Clients should not be forced to depend on interfaces they don't use. In NestJS, this means keeping interfaces small and focused on specific capabilities rather than creating "fat" interfaces that bundle unrelated methods. When a service only needs to send emails, it shouldn't depend on an interface that also includes SMS, push notifications, and logging. Split large interfaces into role-based ones. **Incorrect (fat interface forcing unused dependencies):** ```typescript // Fat interface - forces all consumers to depend on everything interface NotificationService { sendEmail(to: string, subject: string, body: string): Promise; sendSms(phone: string, message: string): Promise; sendPush(userId: string, notification: PushPayload): Promise; sendSlack(channel: string, message: string): Promise; logNotification(type: string, payload: any): Promise; getDeliveryStatus(id: string): Promise; retryFailed(id: string): Promise; scheduleNotification(dto: ScheduleDto): Promise; } // Consumer only needs email, but must mock everything for tests @Injectable() export class OrdersService { constructor( private notifications: NotificationService, // Depends on 8 methods, uses 1 ) {} async confirmOrder(order: Order): Promise { await this.notifications.sendEmail( order.customer.email, 'Order Confirmed', `Your order ${order.id} has been confirmed.`, ); } } // Testing is painful - must mock unused methods const mockNotificationService = { sendEmail: jest.fn(), sendSms: jest.fn(), // Never used, but required sendPush: jest.fn(), // Never used, but required sendSlack: jest.fn(), // Never used, but required logNotification: jest.fn(), // Never used, but required getDeliveryStatus: jest.fn(), // Never used, but required retryFailed: jest.fn(), // Never used, but required scheduleNotification: jest.fn(), // Never used, but required }; ``` **Correct (segregated interfaces by capability):** ```typescript // Segregated interfaces - each focused on one capability interface EmailSender { sendEmail(to: string, subject: string, body: string): Promise; } interface SmsSender { sendSms(phone: string, message: string): Promise; } interface PushSender { sendPush(userId: string, notification: PushPayload): Promise; } interface NotificationLogger { logNotification(type: string, payload: any): Promise; } interface NotificationScheduler { scheduleNotification(dto: ScheduleDto): Promise; } // Implementation can implement multiple interfaces @Injectable() export class NotificationService implements EmailSender, SmsSender, PushSender { async sendEmail(to: string, subject: string, body: string): Promise { // Email implementation } async sendSms(phone: string, message: string): Promise { // SMS implementation } async sendPush(userId: string, notification: PushPayload): Promise { // Push implementation } } // Or separate implementations @Injectable() export class SendGridEmailService implements EmailSender { async sendEmail(to: string, subject: string, body: string): Promise { // SendGrid-specific implementation } } // Consumer depends only on what it needs @Injectable() export class OrdersService { constructor( @Inject(EMAIL_SENDER) private emailSender: EmailSender, // Minimal dependency ) {} async confirmOrder(order: Order): Promise { await this.emailSender.sendEmail( order.customer.email, 'Order Confirmed', `Your order ${order.id} has been confirmed.`, ); } } // Testing is simple - only mock what's used const mockEmailSender: EmailSender = { sendEmail: jest.fn(), }; // Module registration with tokens export const EMAIL_SENDER = Symbol('EMAIL_SENDER'); export const SMS_SENDER = Symbol('SMS_SENDER'); @Module({ providers: [ { provide: EMAIL_SENDER, useClass: SendGridEmailService }, { provide: SMS_SENDER, useClass: TwilioSmsService }, ], exports: [EMAIL_SENDER, SMS_SENDER], }) export class NotificationModule {} ``` **Combining interfaces when needed:** ```typescript // Sometimes a consumer legitimately needs multiple capabilities interface EmailAndSmsSender extends EmailSender, SmsSender {} // Or use intersection types type MultiChannelSender = EmailSender & SmsSender & PushSender; // Consumer that genuinely needs multiple channels @Injectable() export class AlertService { constructor( @Inject(MULTI_CHANNEL_SENDER) private sender: EmailSender & SmsSender, ) {} async sendCriticalAlert(user: User, message: string): Promise { await Promise.all([ this.sender.sendEmail(user.email, 'Critical Alert', message), this.sender.sendSms(user.phone, message), ]); } } ``` Reference: [Interface Segregation Principle](https://en.wikipedia.org/wiki/Interface_segregation_principle) --- ### 2.3 Honor Liskov Substitution Principle **Impact: HIGH** — Ensures implementations are truly interchangeable without breaking callers Subtypes must be substitutable for their base types without altering program correctness. In NestJS with dependency injection, this means any implementation of an interface or abstract class must honor the contract completely. A mock payment service used in tests must behave like a real payment service (return similar shapes, handle errors the same way). Violating LSP causes subtle bugs when swapping implementations. **Incorrect (implementation violates the contract):** ```typescript // Base interface with clear contract interface PaymentGateway { /** * Charges the specified amount. * @returns PaymentResult on success * @throws PaymentFailedException on payment failure */ charge(amount: number, currency: string): Promise; } // Production implementation - follows the contract @Injectable() export class StripeService implements PaymentGateway { async charge(amount: number, currency: string): Promise { const response = await this.stripe.charges.create({ amount, currency }); return { success: true, transactionId: response.id, amount }; } } // Mock that violates LSP - different behavior! @Injectable() export class MockPaymentService implements PaymentGateway { async charge(amount: number, currency: string): Promise { // VIOLATION 1: Throws for valid input (contract says return PaymentResult) if (amount > 1000) { throw new Error('Mock does not support large amounts'); } // VIOLATION 2: Returns null instead of PaymentResult if (currency !== 'USD') { return null as any; // Real service would convert or reject properly } // VIOLATION 3: Missing required field return { success: true } as PaymentResult; // Missing transactionId! } } // Consumer trusts the contract @Injectable() export class OrdersService { constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {} async checkout(order: Order): Promise { const result = await this.payment.charge(order.total, order.currency); // These fail with MockPaymentService: await this.saveTransaction(result.transactionId); // undefined! await this.sendReceipt(result); // might be null! } } ``` **Correct (implementations honor the contract):** ```typescript // Well-defined interface with documented behavior interface PaymentGateway { /** * Charges the specified amount. * @param amount - Amount in smallest currency unit (cents) * @param currency - ISO 4217 currency code * @returns PaymentResult with transactionId, success status, and amount * @throws PaymentFailedException if charge is declined * @throws InvalidCurrencyException if currency is not supported */ charge(amount: number, currency: string): Promise; /** * Refunds a previous charge. * @throws TransactionNotFoundException if transactionId is invalid */ refund(transactionId: string, amount?: number): Promise; } // Production implementation @Injectable() export class StripeService implements PaymentGateway { async charge(amount: number, currency: string): Promise { try { const response = await this.stripe.charges.create({ amount, currency }); return { success: true, transactionId: response.id, amount: response.amount, }; } catch (error) { if (error.type === 'card_error') { throw new PaymentFailedException(error.message); } throw error; } } async refund(transactionId: string, amount?: number): Promise { // Implementation... } } // Mock that honors LSP - same contract, same behavior shape @Injectable() export class MockPaymentService implements PaymentGateway { private transactions = new Map(); async charge(amount: number, currency: string): Promise { // Honor the contract: validate currency like real service would if (!['USD', 'EUR', 'GBP'].includes(currency)) { throw new InvalidCurrencyException(`Unsupported currency: ${currency}`); } // Simulate decline for specific test scenarios if (amount === 99999) { throw new PaymentFailedException('Card declined (test scenario)'); } // Return same shape as production const result: PaymentResult = { success: true, transactionId: `mock_${Date.now()}_${Math.random().toString(36)}`, amount, }; this.transactions.set(result.transactionId, result); return result; } async refund(transactionId: string, amount?: number): Promise { // Honor the contract: throw if transaction not found if (!this.transactions.has(transactionId)) { throw new TransactionNotFoundException(transactionId); } return { success: true, refundId: `refund_${transactionId}`, amount: amount ?? this.transactions.get(transactionId)!.amount, }; } } // Consumer can swap implementations safely @Injectable() export class OrdersService { constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {} async checkout(order: Order): Promise { try { const result = await this.payment.charge(order.total, order.currency); // Works with both StripeService and MockPaymentService order.transactionId = result.transactionId; order.status = 'paid'; return order; } catch (error) { if (error instanceof PaymentFailedException) { order.status = 'payment_failed'; return order; } throw error; } } } ``` **Testing LSP compliance:** ```typescript // Shared test suite that any implementation must pass function testPaymentGatewayContract(createGateway: () => PaymentGateway) { describe('PaymentGateway contract', () => { let gateway: PaymentGateway; beforeEach(() => { gateway = createGateway(); }); it('returns PaymentResult with all required fields', async () => { const result = await gateway.charge(1000, 'USD'); expect(result).toHaveProperty('success'); expect(result).toHaveProperty('transactionId'); expect(result).toHaveProperty('amount'); expect(typeof result.transactionId).toBe('string'); }); it('throws InvalidCurrencyException for unsupported currency', async () => { await expect(gateway.charge(1000, 'INVALID')).rejects.toThrow(InvalidCurrencyException); }); it('throws TransactionNotFoundException for invalid refund', async () => { await expect(gateway.refund('nonexistent')).rejects.toThrow(TransactionNotFoundException); }); }); } // Run against all implementations describe('StripeService', () => { testPaymentGatewayContract(() => new StripeService(mockStripeClient)); }); describe('MockPaymentService', () => { testPaymentGatewayContract(() => new MockPaymentService()); }); ``` Reference: [Liskov Substitution Principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle) --- ### 2.4 Prefer Constructor Injection **Impact: CRITICAL** — Required for proper DI and testing Always use constructor injection over property injection. Constructor injection makes dependencies explicit, enables TypeScript type checking, ensures dependencies are available when the class is instantiated, and improves testability. This is required for proper DI, testing, and TypeScript support. **Incorrect (property injection with hidden dependencies):** ```typescript // Property injection - avoid unless necessary @Injectable() export class UsersService { @Inject() private userRepo: UserRepository; // Hidden dependency @Inject('CONFIG') private config: ConfigType; // Also hidden async findAll() { return this.userRepo.find(); } } // Problems: // 1. Dependencies not visible in constructor // 2. Service can be instantiated without dependencies in tests // 3. TypeScript can't enforce dependency types at instantiation ``` **Correct (constructor injection with explicit dependencies):** ```typescript // Constructor injection - explicit and testable @Injectable() export class UsersService { constructor( private readonly userRepo: UserRepository, @Inject('CONFIG') private readonly config: ConfigType, ) {} async findAll(): Promise { return this.userRepo.find(); } } // Testing is straightforward describe('UsersService', () => { let service: UsersService; let mockRepo: jest.Mocked; beforeEach(() => { mockRepo = { find: jest.fn(), save: jest.fn(), } as any; service = new UsersService(mockRepo, { dbUrl: 'test' }); }); it('should find all users', async () => { mockRepo.find.mockResolvedValue([{ id: '1', name: 'Test' }]); const result = await service.findAll(); expect(result).toHaveLength(1); }); }); // Only use property injection for optional dependencies @Injectable() export class LoggingService { @Optional() @Inject('ANALYTICS') private analytics?: AnalyticsService; log(message: string) { console.log(message); this.analytics?.track('log', message); // Optional enhancement } } ``` Reference: [NestJS Providers](https://docs.nestjs.com/providers) --- ### 2.5 Understand Provider Scopes **Impact: CRITICAL** — Prevents data leaks and performance issues NestJS has three provider scopes: DEFAULT (singleton), REQUEST (per-request instance), and TRANSIENT (new instance for each injection). Most providers should be singletons. Request-scoped providers have performance implications as they bubble up through the dependency tree. Understanding scopes prevents memory leaks and incorrect data sharing. **Incorrect (wrong scope usage):** ```typescript // Request-scoped when not needed (performance hit) @Injectable({ scope: Scope.REQUEST }) export class UsersService { // This creates a new instance for EVERY request // All dependencies also become request-scoped async findAll() { return this.userRepo.find(); } } // Singleton with mutable request state @Injectable() // Default: singleton export class RequestContextService { private userId: string; // DANGER: Shared across all requests! setUser(userId: string) { this.userId = userId; // Overwrites for all concurrent requests } getUser() { return this.userId; // Returns wrong user! } } ``` **Correct (appropriate scope for each use case):** ```typescript // Singleton for stateless services (default, most common) @Injectable() export class UsersService { constructor(private readonly userRepo: UserRepository) {} async findById(id: string): Promise { return this.userRepo.findOne({ where: { id } }); } } // Request-scoped ONLY when you need request context @Injectable({ scope: Scope.REQUEST }) export class RequestContextService { private userId: string; setUser(userId: string) { this.userId = userId; } getUser(): string { return this.userId; } } // Better: Use NestJS built-in request context import { REQUEST } from '@nestjs/core'; import { Request } from 'express'; @Injectable({ scope: Scope.REQUEST }) export class AuditService { constructor(@Inject(REQUEST) private request: Request) {} log(action: string) { console.log(`User ${this.request.user?.id} performed ${action}`); } } // Best: Use ClsModule for async context (no scope bubble-up) import { ClsService } from 'nestjs-cls'; @Injectable() // Stays singleton! export class AuditService { constructor(private cls: ClsService) {} log(action: string) { const userId = this.cls.get('userId'); console.log(`User ${userId} performed ${action}`); } } ``` Reference: [NestJS Injection Scopes](https://docs.nestjs.com/fundamentals/injection-scopes) --- ### 2.6 Use Injection Tokens for Interfaces **Impact: HIGH** — Enables interface-based DI at runtime TypeScript interfaces are erased at compile time and can't be used as injection tokens. Use string tokens, symbols, or abstract classes when you want to inject implementations of interfaces. This enables swapping implementations for testing or different environments. **Incorrect (interface can't be used as token):** ```typescript // Interface can't be used as injection token interface PaymentGateway { charge(amount: number): Promise; } @Injectable() export class StripeService implements PaymentGateway { charge(amount: number) { /* ... */ } } @Injectable() export class OrdersService { // This WON'T work - PaymentGateway doesn't exist at runtime constructor(private payment: PaymentGateway) {} } ``` **Correct (symbol tokens or abstract classes):** ```typescript // Option 1: String/Symbol tokens (most flexible) export const PAYMENT_GATEWAY = Symbol('PAYMENT_GATEWAY'); export interface PaymentGateway { charge(amount: number): Promise; } @Injectable() export class StripeService implements PaymentGateway { async charge(amount: number): Promise { // Stripe implementation } } @Injectable() export class MockPaymentService implements PaymentGateway { async charge(amount: number): Promise { return { success: true, id: 'mock-id' }; } } // Module registration @Module({ providers: [ { provide: PAYMENT_GATEWAY, useClass: process.env.NODE_ENV === 'test' ? MockPaymentService : StripeService, }, ], exports: [PAYMENT_GATEWAY], }) export class PaymentModule {} // Injection @Injectable() export class OrdersService { constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {} async createOrder(dto: CreateOrderDto) { await this.payment.charge(dto.amount); } } // Option 2: Abstract class (carries runtime type info) export abstract class PaymentGateway { abstract charge(amount: number): Promise; } @Injectable() export class StripeService extends PaymentGateway { async charge(amount: number): Promise { // Implementation } } // No @Inject needed with abstract class @Injectable() export class OrdersService { constructor(private payment: PaymentGateway) {} } ``` Reference: [NestJS Custom Providers](https://docs.nestjs.com/fundamentals/custom-providers) --- ## 3. Error Handling **Section Impact: HIGH** ### 3.1 Handle Async Errors Properly **Impact: HIGH** — Prevents process crashes from unhandled rejections NestJS automatically catches errors from async route handlers, but errors from background tasks, event handlers, and manually created promises can crash your application. Always handle async errors explicitly and use global handlers as a safety net. **Incorrect (fire-and-forget without error handling):** ```typescript // Fire-and-forget without error handling @Injectable() export class UsersService { async createUser(dto: CreateUserDto): Promise { const user = await this.repo.save(dto); // Fire and forget - if this fails, error is unhandled! this.emailService.sendWelcome(user.email); return user; } } // Unhandled promise in event handler @Injectable() export class OrdersService { @OnEvent('order.created') handleOrderCreated(event: OrderCreatedEvent) { // This returns a promise but it's not awaited! this.processOrder(event); // Errors will crash the process } private async processOrder(event: OrderCreatedEvent): Promise { await this.inventoryService.reserve(event.items); await this.notificationService.send(event.userId); } } // Missing try-catch in scheduled tasks @Cron('0 0 * * *') async dailyCleanup(): Promise { await this.cleanupService.run(); // If this throws, no error handling } ``` **Correct (explicit async error handling):** ```typescript // Handle fire-and-forget with explicit catch @Injectable() export class UsersService { private readonly logger = new Logger(UsersService.name); async createUser(dto: CreateUserDto): Promise { const user = await this.repo.save(dto); // Explicitly catch and log errors this.emailService.sendWelcome(user.email).catch(error => { this.logger.error('Failed to send welcome email', error.stack); // Optionally queue for retry }); return user; } } // Properly handle async event handlers @Injectable() export class OrdersService { private readonly logger = new Logger(OrdersService.name); @OnEvent('order.created') async handleOrderCreated(event: OrderCreatedEvent): Promise { try { await this.processOrder(event); } catch (error) { this.logger.error('Failed to process order', { event, error }); // Don't rethrow - would crash the process await this.deadLetterQueue.add('order.created', event); } } } // Safe scheduled tasks @Injectable() export class CleanupService { private readonly logger = new Logger(CleanupService.name); @Cron('0 0 * * *') async dailyCleanup(): Promise { try { await this.cleanupService.run(); this.logger.log('Daily cleanup completed'); } catch (error) { this.logger.error('Daily cleanup failed', error.stack); // Alert or retry logic } } } // Global unhandled rejection handler in main.ts async function bootstrap() { const app = await NestFactory.create(AppModule); const logger = new Logger('Bootstrap'); process.on('unhandledRejection', (reason, promise) => { logger.error('Unhandled Rejection at:', promise, 'reason:', reason); }); process.on('uncaughtException', error => { logger.error('Uncaught Exception:', error); process.exit(1); }); await app.listen(3000); } ``` Reference: [Node.js Unhandled Rejections](https://nodejs.org/api/process.html#event-unhandledrejection) --- ### 3.2 Throw HTTP Exceptions from Services **Impact: HIGH** — Keeps controllers thin and simplifies error handling It's acceptable (and often preferable) to throw `HttpException` subclasses from services in HTTP applications. This keeps controllers thin and allows services to communicate appropriate error states. For truly layer-agnostic services, use domain exceptions that map to HTTP status codes. **Incorrect (return error objects instead of throwing):** ```typescript // Return error objects instead of throwing @Injectable() export class UsersService { async findById(id: string): Promise<{ user?: User; error?: string }> { const user = await this.repo.findOne({ where: { id } }); if (!user) { return { error: 'User not found' }; // Controller must check this } return { user }; } } @Controller('users') export class UsersController { @Get(':id') async findOne(@Param('id') id: string) { const result = await this.usersService.findById(id); if (result.error) { throw new NotFoundException(result.error); } return result.user; } } ``` **Correct (throw exceptions directly from service):** ```typescript // Throw exceptions directly from service @Injectable() export class UsersService { constructor(private readonly repo: UserRepository) {} async findById(id: string): Promise { const user = await this.repo.findOne({ where: { id } }); if (!user) { throw new NotFoundException(`User #${id} not found`); } return user; } async create(dto: CreateUserDto): Promise { const existing = await this.repo.findOne({ where: { email: dto.email }, }); if (existing) { throw new ConflictException('Email already registered'); } return this.repo.save(dto); } async update(id: string, dto: UpdateUserDto): Promise { const user = await this.findById(id); // Throws if not found Object.assign(user, dto); return this.repo.save(user); } } // Controller stays thin @Controller('users') export class UsersController { @Get(':id') findOne(@Param('id') id: string): Promise { return this.usersService.findById(id); } @Post() create(@Body() dto: CreateUserDto): Promise { return this.usersService.create(dto); } } // For layer-agnostic services, use domain exceptions export class EntityNotFoundException extends Error { constructor( public readonly entity: string, public readonly id: string, ) { super(`${entity} with ID "${id}" not found`); } } // Map to HTTP in exception filter @Catch(EntityNotFoundException) export class EntityNotFoundFilter implements ExceptionFilter { catch(exception: EntityNotFoundException, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); response.status(404).json({ statusCode: 404, message: exception.message, entity: exception.entity, id: exception.id, }); } } ``` Reference: [NestJS Exception Filters](https://docs.nestjs.com/exception-filters) --- ### 3.3 Use Exception Filters for Error Handling **Impact: HIGH** — Consistent, centralized error handling Never catch exceptions and manually format error responses in controllers. Use NestJS exception filters to handle errors consistently across your application. Create custom exception filters for specific error types and a global filter for unhandled exceptions. **Incorrect (manual error handling in controllers):** ```typescript // Manual error handling in controllers @Controller('users') export class UsersController { @Get(':id') async findOne(@Param('id') id: string, @Res() res: Response) { try { const user = await this.usersService.findById(id); if (!user) { return res.status(404).json({ statusCode: 404, message: 'User not found', }); } return res.json(user); } catch (error) { console.error(error); return res.status(500).json({ statusCode: 500, message: 'Internal server error', }); } } } ``` **Correct (exception filters with consistent handling):** ```typescript // Use built-in and custom exceptions @Controller('users') export class UsersController { @Get(':id') async findOne(@Param('id') id: string): Promise { const user = await this.usersService.findById(id); if (!user) { throw new NotFoundException(`User #${id} not found`); } return user; } } // Custom domain exception export class UserNotFoundException extends NotFoundException { constructor(userId: string) { super({ statusCode: 404, error: 'Not Found', message: `User with ID "${userId}" not found`, code: 'USER_NOT_FOUND', }); } } // Custom exception filter for domain errors @Catch(DomainException) export class DomainExceptionFilter implements ExceptionFilter { catch(exception: DomainException, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); const request = ctx.getRequest(); const status = exception.getStatus?.() || 400; response.status(status).json({ statusCode: status, code: exception.code, message: exception.message, timestamp: new Date().toISOString(), path: request.url, }); } } // Global exception filter for unhandled errors @Catch() export class AllExceptionsFilter implements ExceptionFilter { constructor(private readonly logger: Logger) {} catch(exception: unknown, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); const request = ctx.getRequest(); const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR; const message = exception instanceof HttpException ? exception.message : 'Internal server error'; this.logger.error(`${request.method} ${request.url}`, exception instanceof Error ? exception.stack : exception); response.status(status).json({ statusCode: status, message, timestamp: new Date().toISOString(), path: request.url, }); } } // Register globally in main.ts app.useGlobalFilters(new AllExceptionsFilter(app.get(Logger)), new DomainExceptionFilter()); // Or via module @Module({ providers: [ { provide: APP_FILTER, useClass: AllExceptionsFilter, }, ], }) export class AppModule {} ``` Reference: [NestJS Exception Filters](https://docs.nestjs.com/exception-filters) --- ## 4. Security **Section Impact: HIGH** ### 4.1 Implement Secure JWT Authentication **Impact: CRITICAL** — Essential for secure APIs Use `@nestjs/jwt` with `@nestjs/passport` for authentication. Store secrets securely, use appropriate token lifetimes, implement refresh tokens, and validate tokens properly. Never expose sensitive data in JWT payloads. **Incorrect (insecure JWT implementation):** ```typescript // Hardcode secrets @Module({ imports: [ JwtModule.register({ secret: 'my-secret-key', // Exposed in code signOptions: { expiresIn: '7d' }, // Too long }), ], }) export class AuthModule {} // Store sensitive data in JWT async login(user: User): Promise<{ accessToken: string }> { const payload = { sub: user.id, email: user.email, password: user.password, // NEVER include password! ssn: user.ssn, // NEVER include sensitive data! isAdmin: user.isAdmin, // Can be tampered if not verified }; return { accessToken: this.jwtService.sign(payload) }; } // Skip token validation @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor() { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), secretOrKey: 'my-secret', }); } async validate(payload: any): Promise { return payload; // No validation of user existence } } ``` **Correct (secure JWT with refresh tokens):** ```typescript // Secure JWT configuration @Module({ imports: [ JwtModule.registerAsync({ imports: [ConfigModule], inject: [ConfigService], useFactory: (config: ConfigService) => ({ secret: config.get('JWT_SECRET'), signOptions: { expiresIn: '15m', // Short-lived access tokens issuer: config.get('JWT_ISSUER'), audience: config.get('JWT_AUDIENCE'), }, }), }), PassportModule.register({ defaultStrategy: 'jwt' }), ], }) export class AuthModule {} // Minimal JWT payload @Injectable() export class AuthService { async login(user: User): Promise { // Only include necessary, non-sensitive data const payload: JwtPayload = { sub: user.id, email: user.email, roles: user.roles, iat: Math.floor(Date.now() / 1000), }; const accessToken = this.jwtService.sign(payload); const refreshToken = await this.createRefreshToken(user.id); return { accessToken, refreshToken, expiresIn: 900 }; } private async createRefreshToken(userId: string): Promise { const token = randomBytes(32).toString('hex'); const hashedToken = await bcrypt.hash(token, 10); await this.refreshTokenRepo.save({ userId, token: hashedToken, expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days }); return token; } } // Proper JWT strategy with validation @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor( private config: ConfigService, private usersService: UsersService, ) { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), secretOrKey: config.get('JWT_SECRET'), ignoreExpiration: false, issuer: config.get('JWT_ISSUER'), audience: config.get('JWT_AUDIENCE'), }); } async validate(payload: JwtPayload): Promise { // Verify user still exists and is active const user = await this.usersService.findById(payload.sub); if (!user || !user.isActive) { throw new UnauthorizedException('User not found or inactive'); } // Verify token wasn't issued before password change if (user.passwordChangedAt) { const tokenIssuedAt = new Date(payload.iat * 1000); if (tokenIssuedAt < user.passwordChangedAt) { throw new UnauthorizedException('Token invalidated by password change'); } } return user; } } ``` Reference: [NestJS Authentication](https://docs.nestjs.com/security/authentication) --- ### 4.2 Implement Rate Limiting **Impact: HIGH** — Protects against abuse and ensures fair resource usage Use `@nestjs/throttler` to limit request rates per client. Apply different limits for different endpoints - stricter for auth endpoints, more relaxed for read operations. Consider using Redis for distributed rate limiting in clustered deployments. **Incorrect (no rate limiting on sensitive endpoints):** ```typescript // No rate limiting on sensitive endpoints @Controller('auth') export class AuthController { @Post('login') async login(@Body() dto: LoginDto): Promise { // Attackers can brute-force credentials return this.authService.login(dto); } @Post('forgot-password') async forgotPassword(@Body() dto: ForgotPasswordDto): Promise { // Can be abused to spam users with emails return this.authService.sendResetEmail(dto.email); } } // Same limits for all endpoints @UseGuards(ThrottlerGuard) @Controller('api') export class ApiController { @Get('public-data') async getPublic() {} // Should allow more requests @Post('process-payment') async payment() {} // Should be more restrictive } ``` **Correct (configured throttler with endpoint-specific limits):** ```typescript // Configure throttler globally with multiple limits import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler'; @Module({ imports: [ ThrottlerModule.forRoot([ { name: 'short', ttl: 1000, // 1 second limit: 3, // 3 requests per second }, { name: 'medium', ttl: 10000, // 10 seconds limit: 20, // 20 requests per 10 seconds }, { name: 'long', ttl: 60000, // 1 minute limit: 100, // 100 requests per minute }, ]), ], providers: [ { provide: APP_GUARD, useClass: ThrottlerGuard, }, ], }) export class AppModule {} // Override limits per endpoint @Controller('auth') export class AuthController { @Post('login') @Throttle({ short: { limit: 5, ttl: 60000 } }) // 5 attempts per minute async login(@Body() dto: LoginDto): Promise { return this.authService.login(dto); } @Post('forgot-password') @Throttle({ short: { limit: 3, ttl: 3600000 } }) // 3 per hour async forgotPassword(@Body() dto: ForgotPasswordDto): Promise { return this.authService.sendResetEmail(dto.email); } } // Skip throttling for certain routes @Controller('health') export class HealthController { @Get() @SkipThrottle() check(): string { return 'OK'; } } // Custom throttle per user type @Injectable() export class CustomThrottlerGuard extends ThrottlerGuard { protected async getTracker(req: Request): Promise { // Use user ID if authenticated, IP otherwise return req.user?.id || req.ip; } protected async getLimit(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest(); // Higher limits for authenticated users if (request.user) { return request.user.isPremium ? 1000 : 200; } return 50; // Anonymous users } } ``` Reference: [NestJS Throttler](https://docs.nestjs.com/security/rate-limiting) --- ### 4.3 Sanitize Output to Prevent XSS **Impact: HIGH** — XSS vulnerabilities can compromise user sessions and data While NestJS APIs typically return JSON (which browsers don't execute), XSS risks exist when rendering HTML, storing user content, or when frontend frameworks improperly handle API responses. Sanitize user-generated content before storage and use proper Content-Type headers. **Incorrect (storing raw HTML without sanitization):** ```typescript // Store raw HTML from users @Injectable() export class CommentsService { async create(dto: CreateCommentDto): Promise { // User can inject: return this.repo.save({ content: dto.content, // Raw, unsanitized authorId: dto.authorId, }); } } // Return HTML without sanitization @Controller('pages') export class PagesController { @Get(':slug') @Header('Content-Type', 'text/html') async getPage(@Param('slug') slug: string): Promise { const page = await this.pagesService.findBySlug(slug); // If page.content contains user input, XSS is possible return `${page.content}`; } } // Reflect user input in errors @Get(':id') async findOne(@Param('id') id: string): Promise { const user = await this.repo.findOne({ where: { id } }); if (!user) { // XSS if id contains malicious content and error is rendered throw new NotFoundException(`User ${id} not found`); } return user; } ``` **Correct (sanitize content and use proper headers):** ```typescript // Sanitize HTML content before storage import * as sanitizeHtml from 'sanitize-html'; @Injectable() export class CommentsService { private readonly sanitizeOptions: sanitizeHtml.IOptions = { allowedTags: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'], allowedAttributes: { a: ['href', 'title'], }, allowedSchemes: ['http', 'https', 'mailto'], }; async create(dto: CreateCommentDto): Promise { return this.repo.save({ content: sanitizeHtml(dto.content, this.sanitizeOptions), authorId: dto.authorId, }); } } // Use validation pipe to strip HTML import { Transform } from 'class-transformer'; export class CreatePostDto { @IsString() @MaxLength(1000) @Transform(({ value }) => sanitizeHtml(value, { allowedTags: [] })) title: string; @IsString() @Transform(({ value }) => sanitizeHtml(value, { allowedTags: ['p', 'br', 'b', 'i', 'a'], allowedAttributes: { a: ['href'] }, }), ) content: string; } // Set proper Content-Type headers @Controller('api') export class ApiController { @Get('data') @Header('Content-Type', 'application/json') async getData(): Promise { // JSON response - browser won't execute scripts return this.service.getData(); } } // Sanitize error messages @Get(':id') async findOne(@Param('id', ParseUUIDPipe) id: string): Promise { const user = await this.repo.findOne({ where: { id } }); if (!user) { // UUID validation ensures safe format throw new NotFoundException('User not found'); } return user; } // Use Helmet for CSP headers import helmet from 'helmet'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.use( helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], scriptSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], imgSrc: ["'self'", 'data:', 'https:'], }, }, }), ); await app.listen(3000); } ``` Reference: [OWASP XSS Prevention](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html) --- ### 4.4 Use Guards for Authentication and Authorization **Impact: HIGH** — Enforces access control before handlers execute Guards determine whether a request should be handled based on authentication state, roles, permissions, or other conditions. They run after middleware but before pipes and interceptors, making them ideal for access control. Use guards instead of manual checks in controllers. **Incorrect (manual auth checks in every handler):** ```typescript // Manual auth checks in every handler @Controller('admin') export class AdminController { @Get('users') async getUsers(@Request() req) { if (!req.user) { throw new UnauthorizedException(); } if (!req.user.roles.includes('admin')) { throw new ForbiddenException(); } return this.adminService.getUsers(); } @Delete('users/:id') async deleteUser(@Request() req, @Param('id') id: string) { if (!req.user) { throw new UnauthorizedException(); } if (!req.user.roles.includes('admin')) { throw new ForbiddenException(); } return this.adminService.deleteUser(id); } } ``` **Correct (guards with declarative decorators):** ```typescript // JWT Auth Guard @Injectable() export class JwtAuthGuard implements CanActivate { constructor( private jwtService: JwtService, private reflector: Reflector, ) {} async canActivate(context: ExecutionContext): Promise { // Check for @Public() decorator const isPublic = this.reflector.getAllAndOverride('isPublic', [context.getHandler(), context.getClass()]); if (isPublic) return true; const request = context.switchToHttp().getRequest(); const token = this.extractToken(request); if (!token) { throw new UnauthorizedException('No token provided'); } try { request.user = await this.jwtService.verifyAsync(token); return true; } catch { throw new UnauthorizedException('Invalid token'); } } private extractToken(request: Request): string | undefined { const [type, token] = request.headers.authorization?.split(' ') ?? []; return type === 'Bearer' ? token : undefined; } } // Roles Guard @Injectable() export class RolesGuard implements CanActivate { constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { const requiredRoles = this.reflector.getAllAndOverride('roles', [context.getHandler(), context.getClass()]); if (!requiredRoles) return true; const { user } = context.switchToHttp().getRequest(); return requiredRoles.some(role => user.roles?.includes(role)); } } // Decorators export const Public = () => SetMetadata('isPublic', true); export const Roles = (...roles: Role[]) => SetMetadata('roles', roles); // Register guards globally @Module({ providers: [ { provide: APP_GUARD, useClass: JwtAuthGuard }, { provide: APP_GUARD, useClass: RolesGuard }, ], }) export class AppModule {} // Clean controller @Controller('admin') @Roles(Role.Admin) // Applied to all routes export class AdminController { @Get('users') getUsers(): Promise { return this.adminService.getUsers(); } @Delete('users/:id') deleteUser(@Param('id') id: string): Promise { return this.adminService.deleteUser(id); } @Public() // Override: no auth required @Get('health') health() { return { status: 'ok' }; } } ``` Reference: [NestJS Guards](https://docs.nestjs.com/guards) --- ### 4.5 Validate All Input with DTOs and Pipes **Impact: HIGH** — First line of defense against attacks Always validate incoming data using class-validator decorators on DTOs and the global ValidationPipe. Never trust user input. Validate all request bodies, query parameters, and route parameters before processing. **Incorrect (trust raw input without validation):** ```typescript // Trust raw input without validation @Controller('users') export class UsersController { @Post() create(@Body() body: any) { // body could contain anything - SQL injection, XSS, etc. return this.usersService.create(body); } @Get() findAll(@Query() query: any) { // query.limit could be "'; DROP TABLE users; --" return this.usersService.findAll(query.limit); } } // DTOs without validation decorators export class CreateUserDto { name: string; // No validation email: string; // Could be "not-an-email" age: number; // Could be "abc" or -999 } ``` **Correct (validated DTOs with global ValidationPipe):** ```typescript // Enable ValidationPipe globally in main.ts async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes( new ValidationPipe({ whitelist: true, // Strip unknown properties forbidNonWhitelisted: true, // Throw on unknown properties transform: true, // Auto-transform to DTO types transformOptions: { enableImplicitConversion: true, }, }), ); await app.listen(3000); } // Create well-validated DTOs import { IsString, IsEmail, IsInt, Min, Max, IsOptional, MinLength, MaxLength, Matches, IsNotEmpty, } from 'class-validator'; import { Transform, Type } from 'class-transformer'; export class CreateUserDto { @IsString() @IsNotEmpty() @MinLength(2) @MaxLength(100) @Transform(({ value }) => value?.trim()) name: string; @IsEmail() @Transform(({ value }) => value?.toLowerCase().trim()) email: string; @IsInt() @Min(0) @Max(150) age: number; @IsString() @MinLength(8) @MaxLength(100) @Matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/, { message: 'Password must contain uppercase, lowercase, and number', }) password: string; } // Query DTO with defaults and transformation export class FindUsersQueryDto { @IsOptional() @IsString() @MaxLength(100) search?: string; @IsOptional() @Type(() => Number) @IsInt() @Min(1) @Max(100) limit: number = 20; @IsOptional() @Type(() => Number) @IsInt() @Min(0) offset: number = 0; } // Param validation export class UserIdParamDto { @IsUUID('4') id: string; } @Controller('users') export class UsersController { @Post() create(@Body() dto: CreateUserDto): Promise { // dto is guaranteed to be valid return this.usersService.create(dto); } @Get() findAll(@Query() query: FindUsersQueryDto): Promise { // query.limit is a number, query.search is sanitized return this.usersService.findAll(query); } @Get(':id') findOne(@Param() params: UserIdParamDto): Promise { // params.id is a valid UUID return this.usersService.findById(params.id); } } ``` Reference: [NestJS Validation](https://docs.nestjs.com/techniques/validation) --- ## 5. Performance **Section Impact: HIGH** ### 5.1 Use Async Lifecycle Hooks Correctly **Impact: HIGH** — Improper async handling blocks application startup NestJS lifecycle hooks (`onModuleInit`, `onApplicationBootstrap`, etc.) support async operations. However, misusing them can block application startup or cause race conditions. Understand the lifecycle order and use hooks appropriately. **Incorrect (fire-and-forget async without await):** ```typescript // Fire-and-forget async without await @Injectable() export class DatabaseService implements OnModuleInit { onModuleInit() { // This runs but doesn't block - app starts before DB is ready! this.connect(); } private async connect() { await this.pool.connect(); console.log('Database connected'); } } // Heavy blocking operations in constructor @Injectable() export class ConfigService { private config: Config; constructor() { // BLOCKS entire module instantiation synchronously this.config = fs.readFileSync('config.json'); } } ``` **Correct (return promises from async hooks):** ```typescript // Return promise from async hooks @Injectable() export class DatabaseService implements OnModuleInit { private pool: Pool; async onModuleInit(): Promise { // NestJS waits for this to complete before continuing await this.pool.connect(); console.log('Database connected'); } async onModuleDestroy(): Promise { // Clean up resources on shutdown await this.pool.end(); console.log('Database disconnected'); } } // Use onApplicationBootstrap for cross-module dependencies @Injectable() export class CacheWarmerService implements OnApplicationBootstrap { constructor( private cache: CacheService, private products: ProductsService, ) {} async onApplicationBootstrap(): Promise { // All modules are initialized, safe to warm cache const products = await this.products.findPopular(); await this.cache.warmup(products); } } // Heavy init in async hooks, not constructor @Injectable() export class ConfigService implements OnModuleInit { private config: Config; constructor() { // Keep constructor synchronous and fast } async onModuleInit(): Promise { // Async loading in lifecycle hook this.config = await this.loadConfig(); } private async loadConfig(): Promise { const file = await fs.promises.readFile('config.json'); return JSON.parse(file.toString()); } get(key: string): T { return this.config[key]; } } // Enable shutdown hooks in main.ts async function bootstrap() { const app = await NestFactory.create(AppModule); app.enableShutdownHooks(); // Enable SIGTERM/SIGINT handling await app.listen(3000); } ``` Reference: [NestJS Lifecycle Events](https://docs.nestjs.com/fundamentals/lifecycle-events) --- ### 5.2 Use Lazy Loading for Large Modules **Impact: MEDIUM** — Improves startup time for large applications NestJS supports lazy-loading modules, which defers initialization until first use. This is valuable for large applications where some features are rarely used, serverless deployments where cold start time matters, or when certain modules have heavy initialization costs. **Incorrect (loading everything eagerly):** ```typescript // Load everything eagerly in a large app @Module({ imports: [ UsersModule, OrdersModule, PaymentsModule, ReportsModule, // Heavy, rarely used AnalyticsModule, // Heavy, rarely used AdminModule, // Only admins use this LegacyModule, // Migration module, rarely used BulkImportModule, // Used once a month ], }) export class AppModule {} // All modules initialize at startup, even if never used // Slow cold starts in serverless // Memory wasted on unused modules ``` **Correct (lazy load rarely-used modules):** ```typescript // Use LazyModuleLoader for optional modules import { LazyModuleLoader } from '@nestjs/core'; @Injectable() export class ReportsService { constructor(private lazyModuleLoader: LazyModuleLoader) {} async generateReport(type: string): Promise { // Load module only when needed const { ReportsModule } = await import('./reports/reports.module'); const moduleRef = await this.lazyModuleLoader.load(() => ReportsModule); const reportsService = moduleRef.get(ReportsGeneratorService); return reportsService.generate(type); } } // Lazy load admin features with caching @Injectable() export class AdminService { private adminModule: ModuleRef | null = null; constructor(private lazyModuleLoader: LazyModuleLoader) {} private async getAdminModule(): Promise { if (!this.adminModule) { const { AdminModule } = await import('./admin/admin.module'); this.adminModule = await this.lazyModuleLoader.load(() => AdminModule); } return this.adminModule; } async runAdminTask(task: string): Promise { const moduleRef = await this.getAdminModule(); const taskRunner = moduleRef.get(AdminTaskRunner); await taskRunner.run(task); } } // Reusable lazy loader service @Injectable() export class ModuleLoaderService { private loadedModules = new Map(); constructor(private lazyModuleLoader: LazyModuleLoader) {} async load(key: string, importFn: () => Promise<{ default: Type } | Type>): Promise { if (!this.loadedModules.has(key)) { const module = await importFn(); const moduleType = 'default' in module ? module.default : module; const moduleRef = await this.lazyModuleLoader.load(() => moduleType); this.loadedModules.set(key, moduleRef); } return this.loadedModules.get(key)!; } } // Preload modules in background after startup @Injectable() export class ModulePreloader implements OnApplicationBootstrap { constructor(private lazyModuleLoader: LazyModuleLoader) {} async onApplicationBootstrap(): Promise { setTimeout(async () => { await this.preloadModule(() => import('./reports/reports.module')); }, 5000); // 5 seconds after startup } private async preloadModule(importFn: () => Promise): Promise { try { const module = await importFn(); const moduleType = module.default || Object.values(module)[0]; await this.lazyModuleLoader.load(() => moduleType); } catch (error) { console.warn('Failed to preload module', error); } } } ``` Reference: [NestJS Lazy Loading Modules](https://docs.nestjs.com/fundamentals/lazy-loading-modules) --- ### 5.3 Optimize Database Queries **Impact: HIGH** — Database queries are typically the largest source of latency Select only needed columns, use proper indexes, avoid over-fetching relations, and consider query performance when designing your data access. Most API slowness traces back to inefficient database queries. **Incorrect (over-fetching data and missing indexes):** ```typescript // Select everything when you need few fields @Injectable() export class UsersService { async findAllEmails(): Promise { const users = await this.repo.find(); // Fetches ALL columns for ALL users return users.map(u => u.email); } async getUserSummary(id: string): Promise { const user = await this.repo.findOne({ where: { id }, relations: ['posts', 'posts.comments', 'posts.comments.author', 'followers'], }); // Over-fetches massive relation tree return { name: user.name, postCount: user.posts.length }; } } // No indexes on frequently queried columns @Entity() export class Order { @Column() userId: string; // No index - full table scan on every lookup @Column() status: string; // No index - slow status filtering } ``` **Correct (select only needed data with proper indexes):** ```typescript // Select only needed columns @Injectable() export class UsersService { async findAllEmails(): Promise { const users = await this.repo.find({ select: ['email'], // Only fetch email column }); return users.map(u => u.email); } // Use QueryBuilder for complex selections async getUserSummary(id: string): Promise { return this.repo .createQueryBuilder('user') .select('user.name', 'name') .addSelect('COUNT(post.id)', 'postCount') .leftJoin('user.posts', 'post') .where('user.id = :id', { id }) .groupBy('user.id') .getRawOne(); } // Fetch relations only when needed async getFullProfile(id: string): Promise { return this.repo.findOne({ where: { id }, relations: ['posts'], // Only immediate relation select: { id: true, name: true, email: true, posts: { id: true, title: true, }, }, }); } } // Add indexes on frequently queried columns @Entity() @Index(['userId']) @Index(['status']) @Index(['createdAt']) @Index(['userId', 'status']) // Composite index for common query pattern export class Order { @PrimaryGeneratedColumn('uuid') id: string; @Column() userId: string; @Column() status: string; @CreateDateColumn() createdAt: Date; } // Always paginate large datasets @Injectable() export class OrdersService { async findAll(page = 1, limit = 20): Promise> { const [items, total] = await this.repo.findAndCount({ skip: (page - 1) * limit, take: limit, order: { createdAt: 'DESC' }, }); return { items, meta: { page, limit, total, totalPages: Math.ceil(total / limit), }, }; } } ``` Reference: [TypeORM Query Builder](https://typeorm.io/select-query-builder) --- ### 5.4 Use Caching Strategically **Impact: HIGH** — Dramatically reduces database load and response times Implement caching for expensive operations, frequently accessed data, and external API calls. Use NestJS CacheModule with appropriate TTLs and cache invalidation strategies. Don't cache everything - focus on high-impact areas. **Incorrect (no caching or caching everything):** ```typescript // No caching for expensive, repeated queries @Injectable() export class ProductsService { async getPopular(): Promise { // Runs complex aggregation query EVERY request return this.productsRepo .createQueryBuilder('p') .leftJoin('p.orders', 'o') .select('p.*, COUNT(o.id) as orderCount') .groupBy('p.id') .orderBy('orderCount', 'DESC') .limit(20) .getMany(); } } // Cache everything without thought @Injectable() export class UsersService { @CacheKey('users') @CacheTTL(3600) @UseInterceptors(CacheInterceptor) async findAll(): Promise { // Caching user list for 1 hour is wrong if data changes frequently return this.usersRepo.find(); } } ``` **Correct (strategic caching with proper invalidation):** ```typescript // Setup caching module @Module({ imports: [ CacheModule.registerAsync({ imports: [ConfigModule], inject: [ConfigService], useFactory: (config: ConfigService) => ({ stores: [new KeyvRedis(config.get('REDIS_URL'))], ttl: 60 * 1000, // Default 60s }), }), ], }) export class AppModule {} // Manual caching for granular control @Injectable() export class ProductsService { constructor( @Inject(CACHE_MANAGER) private cache: Cache, private productsRepo: ProductRepository, ) {} async getPopular(): Promise { const cacheKey = 'products:popular'; // Try cache first const cached = await this.cache.get(cacheKey); if (cached) return cached; // Cache miss - fetch and cache const products = await this.fetchPopularProducts(); await this.cache.set(cacheKey, products, 5 * 60 * 1000); // 5 min TTL return products; } // Invalidate cache on changes async updateProduct(id: string, dto: UpdateProductDto): Promise { const product = await this.productsRepo.save({ id, ...dto }); await this.cache.del('products:popular'); // Invalidate return product; } } // Decorator-based caching with auto-interceptor @Controller('categories') @UseInterceptors(CacheInterceptor) export class CategoriesController { @Get() @CacheTTL(30 * 60 * 1000) // 30 minutes - categories rarely change findAll(): Promise { return this.categoriesService.findAll(); } @Get(':id') @CacheTTL(60 * 1000) // 1 minute @CacheKey('category') findOne(@Param('id') id: string): Promise { return this.categoriesService.findOne(id); } } // Event-based cache invalidation @Injectable() export class CacheInvalidationService { constructor(@Inject(CACHE_MANAGER) private cache: Cache) {} @OnEvent('product.created') @OnEvent('product.updated') @OnEvent('product.deleted') async invalidateProductCaches(event: ProductEvent) { await Promise.all([this.cache.del('products:popular'), this.cache.del(`product:${event.productId}`)]); } } ``` Reference: [NestJS Caching](https://docs.nestjs.com/techniques/caching) --- ## 6. Testing **Section Impact: MEDIUM-HIGH** ### 6.1 Use Supertest for E2E Testing **Impact: HIGH** — Validates the full request/response cycle End-to-end tests use Supertest to make real HTTP requests against your NestJS application. They test the full stack including middleware, guards, pipes, and interceptors. E2E tests catch integration issues that unit tests miss. **Incorrect (no proper E2E setup or teardown):** ```typescript // Only unit test controllers describe('UsersController', () => { it('should return users', async () => { const service = { findAll: jest.fn().mockResolvedValue([]) }; const controller = new UsersController(service as any); const result = await controller.findAll(); expect(result).toEqual([]); // Doesn't test: routes, guards, pipes, serialization }); }); // E2E tests without proper setup/teardown describe('Users API', () => { it('should create user', async () => { const app = await NestFactory.create(AppModule); // No proper initialization // No cleanup after test // Hits real database }); }); ``` **Correct (proper E2E setup with Supertest):** ```typescript // Proper E2E test setup import { Test, TestingModule } from '@nestjs/testing'; import { INestApplication, ValidationPipe } from '@nestjs/common'; import * as request from 'supertest'; import { AppModule } from '../src/app.module'; describe('UsersController (e2e)', () => { let app: INestApplication; beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [AppModule], }).compile(); app = moduleFixture.createNestApplication(); // Apply same config as production app.useGlobalPipes( new ValidationPipe({ whitelist: true, transform: true, forbidNonWhitelisted: true, }), ); await app.init(); }); afterAll(async () => { await app.close(); }); describe('/users (POST)', () => { it('should create a user', () => { return request(app.getHttpServer()) .post('/users') .send({ name: 'John', email: 'john@test.com' }) .expect(201) .expect(res => { expect(res.body).toHaveProperty('id'); expect(res.body.name).toBe('John'); expect(res.body.email).toBe('john@test.com'); }); }); it('should return 400 for invalid email', () => { return request(app.getHttpServer()) .post('/users') .send({ name: 'John', email: 'invalid-email' }) .expect(400) .expect(res => { expect(res.body.message).toContain('email'); }); }); }); describe('/users/:id (GET)', () => { it('should return 404 for non-existent user', () => { return request(app.getHttpServer()).get('/users/non-existent-id').expect(404); }); }); }); // Testing with authentication describe('Protected Routes (e2e)', () => { let app: INestApplication; let authToken: string; beforeAll(async () => { const moduleFixture = await Test.createTestingModule({ imports: [AppModule], }).compile(); app = moduleFixture.createNestApplication(); app.useGlobalPipes(new ValidationPipe({ whitelist: true })); await app.init(); // Get auth token const loginResponse = await request(app.getHttpServer()) .post('/auth/login') .send({ email: 'test@test.com', password: 'password' }); authToken = loginResponse.body.accessToken; }); it('should return 401 without token', () => { return request(app.getHttpServer()).get('/users/me').expect(401); }); it('should return user profile with valid token', () => { return request(app.getHttpServer()) .get('/users/me') .set('Authorization', `Bearer ${authToken}`) .expect(200) .expect(res => { expect(res.body.email).toBe('test@test.com'); }); }); }); // Database isolation for E2E tests describe('Orders API (e2e)', () => { let app: INestApplication; let dataSource: DataSource; beforeAll(async () => { const moduleFixture = await Test.createTestingModule({ imports: [ ConfigModule.forRoot({ envFilePath: '.env.test', // Test database config }), AppModule, ], }).compile(); app = moduleFixture.createNestApplication(); dataSource = moduleFixture.get(DataSource); await app.init(); }); beforeEach(async () => { // Clean database between tests await dataSource.synchronize(true); }); afterAll(async () => { await dataSource.destroy(); await app.close(); }); }); ``` Reference: [NestJS E2E Testing](https://docs.nestjs.com/fundamentals/testing#end-to-end-testing) --- ### 6.2 Mock External Services in Tests **Impact: HIGH** — Ensures fast, reliable, deterministic tests Never call real external services (APIs, databases, message queues) in unit tests. Mock them to ensure tests are fast, deterministic, and don't incur costs. Use realistic mock data and test edge cases like timeouts and errors. **Incorrect (calling real APIs and databases):** ```typescript // Call real APIs in tests describe('PaymentService', () => { it('should process payment', async () => { const service = new PaymentService(new StripeClient(realApiKey)); // Hits real Stripe API! const result = await service.charge('tok_visa', 1000); // Slow, costs money, flaky }); }); // Use real database describe('UsersService', () => { beforeEach(async () => { await connection.query('DELETE FROM users'); // Modifies real DB }); it('should create user', async () => { const user = await service.create({ email: 'test@test.com' }); // Side effects on shared database }); }); // Incomplete mocks const mockHttpService = { get: jest.fn().mockResolvedValue({ data: {} }), // Missing error scenarios, missing other methods }; ``` **Correct (mock all external dependencies):** ```typescript // Mock HTTP service properly describe('WeatherService', () => { let service: WeatherService; let httpService: jest.Mocked; beforeEach(async () => { const module = await Test.createTestingModule({ providers: [ WeatherService, { provide: HttpService, useValue: { get: jest.fn(), post: jest.fn(), }, }, ], }).compile(); service = module.get(WeatherService); httpService = module.get(HttpService); }); it('should return weather data', async () => { const mockResponse = { data: { temperature: 72, humidity: 45 }, status: 200, statusText: 'OK', headers: {}, config: {}, }; httpService.get.mockReturnValue(of(mockResponse)); const result = await service.getWeather('NYC'); expect(result).toEqual({ temperature: 72, humidity: 45 }); }); it('should handle API timeout', async () => { httpService.get.mockReturnValue(throwError(() => new Error('ETIMEDOUT'))); await expect(service.getWeather('NYC')).rejects.toThrow('Weather service unavailable'); }); it('should handle rate limiting', async () => { httpService.get.mockReturnValue( throwError(() => ({ response: { status: 429, data: { message: 'Rate limited' } }, })), ); await expect(service.getWeather('NYC')).rejects.toThrow(TooManyRequestsException); }); }); // Mock repository instead of database describe('UsersService', () => { let service: UsersService; let repo: jest.Mocked>; beforeEach(async () => { const mockRepo = { find: jest.fn(), findOne: jest.fn(), save: jest.fn(), delete: jest.fn(), createQueryBuilder: jest.fn(), }; const module = await Test.createTestingModule({ providers: [UsersService, { provide: getRepositoryToken(User), useValue: mockRepo }], }).compile(); service = module.get(UsersService); repo = module.get(getRepositoryToken(User)); }); it('should find user by id', async () => { const mockUser = { id: '1', name: 'John', email: 'john@test.com' }; repo.findOne.mockResolvedValue(mockUser); const result = await service.findById('1'); expect(result).toEqual(mockUser); expect(repo.findOne).toHaveBeenCalledWith({ where: { id: '1' } }); }); }); // Create mock factory for complex SDKs function createMockStripe(): jest.Mocked { return { paymentIntents: { create: jest.fn(), retrieve: jest.fn(), confirm: jest.fn(), cancel: jest.fn(), }, customers: { create: jest.fn(), retrieve: jest.fn(), }, } as any; } // Mock time for time-dependent tests describe('TokenService', () => { beforeEach(() => { jest.useFakeTimers(); jest.setSystemTime(new Date('2024-01-15')); }); afterEach(() => { jest.useRealTimers(); }); it('should expire token after 1 hour', async () => { const token = await service.createToken(); // Fast-forward time jest.advanceTimersByTime(61 * 60 * 1000); expect(await service.isValid(token)).toBe(false); }); }); ``` Reference: [Jest Mocking](https://jestjs.io/docs/mock-functions) --- ### 6.3 Use Testing Module for Unit Tests **Impact: HIGH** — Enables proper isolated testing with mocked dependencies Use `@nestjs/testing` module to create isolated test environments with mocked dependencies. This ensures your tests run fast, don't depend on external services, and properly test your business logic in isolation. **Incorrect (manual instantiation bypassing DI):** ```typescript // Instantiate services manually without DI describe('UsersService', () => { it('should create user', async () => { // Manual instantiation bypasses DI const repo = new UserRepository(); // Real repo! const service = new UsersService(repo); const user = await service.create({ name: 'Test' }); // This hits the real database! }); }); // Test implementation details describe('UsersController', () => { it('should call service', async () => { const service = { create: jest.fn() }; const controller = new UsersController(service as any); await controller.create({ name: 'Test' }); expect(service.create).toHaveBeenCalled(); // Tests implementation, not behavior }); }); ``` **Correct (use Test.createTestingModule with mocked dependencies):** ```typescript // Use Test.createTestingModule for proper DI import { Test, TestingModule } from '@nestjs/testing'; describe('UsersService', () => { let service: UsersService; let repo: jest.Mocked; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ UsersService, { provide: UserRepository, useValue: { save: jest.fn(), findOne: jest.fn(), find: jest.fn(), }, }, ], }).compile(); service = module.get(UsersService); repo = module.get(UserRepository); }); afterEach(() => { jest.clearAllMocks(); }); describe('create', () => { it('should save and return user', async () => { const dto = { name: 'John', email: 'john@test.com' }; const expectedUser = { id: '1', ...dto }; repo.save.mockResolvedValue(expectedUser); const result = await service.create(dto); expect(result).toEqual(expectedUser); expect(repo.save).toHaveBeenCalledWith(dto); }); it('should throw on duplicate email', async () => { repo.findOne.mockResolvedValue({ id: '1', email: 'test@test.com' }); await expect(service.create({ name: 'Test', email: 'test@test.com' })).rejects.toThrow(ConflictException); }); }); describe('findById', () => { it('should return user when found', async () => { const user = { id: '1', name: 'John' }; repo.findOne.mockResolvedValue(user); const result = await service.findById('1'); expect(result).toEqual(user); }); it('should throw NotFoundException when not found', async () => { repo.findOne.mockResolvedValue(null); await expect(service.findById('999')).rejects.toThrow(NotFoundException); }); }); }); // Testing guards and interceptors describe('RolesGuard', () => { let guard: RolesGuard; let reflector: Reflector; beforeEach(async () => { const module = await Test.createTestingModule({ providers: [RolesGuard, Reflector], }).compile(); guard = module.get(RolesGuard); reflector = module.get(Reflector); }); it('should allow when no roles required', () => { const context = createMockExecutionContext({ user: { roles: [] } }); jest.spyOn(reflector, 'getAllAndOverride').mockReturnValue(undefined); expect(guard.canActivate(context)).toBe(true); }); it('should allow admin for admin-only route', () => { const context = createMockExecutionContext({ user: { roles: ['admin'] } }); jest.spyOn(reflector, 'getAllAndOverride').mockReturnValue(['admin']); expect(guard.canActivate(context)).toBe(true); }); }); function createMockExecutionContext(request: Partial): ExecutionContext { return { switchToHttp: () => ({ getRequest: () => request, }), getHandler: () => jest.fn(), getClass: () => jest.fn(), } as ExecutionContext; } ``` Reference: [NestJS Testing](https://docs.nestjs.com/fundamentals/testing) --- ## 7. Database & ORM **Section Impact: MEDIUM-HIGH** ### 7.1 Avoid N+1 Query Problems **Impact: HIGH** — N+1 queries are one of the most common performance killers N+1 queries occur when you fetch a list of entities, then make an additional query for each entity to load related data. Use eager loading with `relations`, query builder joins, or DataLoader to batch queries efficiently. **Incorrect (lazy loading in loops causes N+1):** ```typescript // Lazy loading in loops causes N+1 @Injectable() export class OrdersService { async getOrdersWithItems(userId: string): Promise { const orders = await this.orderRepo.find({ where: { userId } }); // 1 query for orders for (const order of orders) { // N additional queries - one per order! order.items = await this.itemRepo.find({ where: { orderId: order.id } }); } return orders; } } // Accessing lazy relations without loading @Controller('users') export class UsersController { @Get() async findAll(): Promise { const users = await this.userRepo.find(); // If User.posts is lazy-loaded, serializing triggers N queries return users; // Each user.posts access = 1 query } } ``` **Correct (use relations for eager loading):** ```typescript // Use relations option for eager loading @Injectable() export class OrdersService { async getOrdersWithItems(userId: string): Promise { // Single query with JOIN return this.orderRepo.find({ where: { userId }, relations: ['items', 'items.product'], }); } } // Use QueryBuilder for complex joins @Injectable() export class UsersService { async getUsersWithPostCounts(): Promise { return this.userRepo .createQueryBuilder('user') .leftJoin('user.posts', 'post') .select('user.id', 'id') .addSelect('user.name', 'name') .addSelect('COUNT(post.id)', 'postCount') .groupBy('user.id') .getRawMany(); } async getActiveUsersWithPosts(): Promise { return this.userRepo .createQueryBuilder('user') .leftJoinAndSelect('user.posts', 'post') .leftJoinAndSelect('post.comments', 'comment') .where('user.isActive = :active', { active: true }) .andWhere('post.status = :status', { status: 'published' }) .getMany(); } } // Use find options for specific fields async getOrderSummaries(userId: string): Promise { return this.orderRepo.find({ where: { userId }, relations: ['items'], select: { id: true, total: true, status: true, items: { id: true, quantity: true, price: true, }, }, }); } // Use DataLoader for GraphQL to batch and cache queries import DataLoader from 'dataloader'; @Injectable({ scope: Scope.REQUEST }) export class PostsLoader { constructor(private postsService: PostsService) {} readonly batchPosts = new DataLoader(async (userIds) => { // Single query for all users' posts const posts = await this.postsService.findByUserIds([...userIds]); // Group by userId const postsMap = new Map(); for (const post of posts) { const userPosts = postsMap.get(post.userId) || []; userPosts.push(post); postsMap.set(post.userId, userPosts); } // Return in same order as input return userIds.map((id) => postsMap.get(id) || []); }); } // In resolver @ResolveField() async posts(@Parent() user: User): Promise { // DataLoader batches multiple calls into single query return this.postsLoader.batchPosts.load(user.id); } // Enable query logging in development to detect N+1 TypeOrmModule.forRoot({ logging: ['query', 'error'], logger: 'advanced-console', }); ``` Reference: [TypeORM Relations](https://typeorm.io/relations) --- ### 7.2 Use Database Migrations **Impact: HIGH** — Enables safe, repeatable database schema changes Never use `synchronize: true` in production. Use migrations for all schema changes. Migrations provide version control for your database, enable safe rollbacks, and ensure consistency across all environments. **Incorrect (using synchronize or manual SQL):** ```typescript // Use synchronize in production TypeOrmModule.forRoot({ type: 'postgres', synchronize: true, // DANGEROUS in production! // Can drop columns, tables, or data }); // Manual SQL in production @Injectable() export class DatabaseService { async addColumn(): Promise { await this.dataSource.query('ALTER TABLE users ADD COLUMN age INT'); // No version control, no rollback, inconsistent across envs } } // Modify entities without migration @Entity() export class User { @Column() email: string; @Column() // Added without migration newField: string; // Will crash in production if synchronize is false } ``` **Correct (use migrations for all schema changes):** ```typescript // Configure TypeORM for migrations // data-source.ts export const dataSource = new DataSource({ type: 'postgres', host: process.env.DB_HOST, port: parseInt(process.env.DB_PORT), username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, entities: ['dist/**/*.entity.js'], migrations: ['dist/migrations/*.js'], synchronize: false, // Always false in production migrationsRun: true, // Run migrations on startup }); // app.module.ts TypeOrmModule.forRootAsync({ inject: [ConfigService], useFactory: (config: ConfigService) => ({ type: 'postgres', host: config.get('DB_HOST'), synchronize: config.get('NODE_ENV') === 'development', // Only in dev migrations: ['dist/migrations/*.js'], migrationsRun: true, }), }); // migrations/1705312800000-AddUserAge.ts import { MigrationInterface, QueryRunner } from 'typeorm'; export class AddUserAge1705312800000 implements MigrationInterface { name = 'AddUserAge1705312800000'; public async up(queryRunner: QueryRunner): Promise { // Add column with default to handle existing rows await queryRunner.query(` ALTER TABLE "users" ADD "age" integer DEFAULT 0 `); // Add index for frequently queried columns await queryRunner.query(` CREATE INDEX "IDX_users_age" ON "users" ("age") `); } public async down(queryRunner: QueryRunner): Promise { // Always implement down for rollback await queryRunner.query(`DROP INDEX "IDX_users_age"`); await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "age"`); } } // Safe column rename (two-step) export class RenameNameToFullName1705312900000 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { // Step 1: Add new column await queryRunner.query(` ALTER TABLE "users" ADD "full_name" varchar(255) `); // Step 2: Copy data await queryRunner.query(` UPDATE "users" SET "full_name" = "name" `); // Step 3: Add NOT NULL constraint await queryRunner.query(` ALTER TABLE "users" ALTER COLUMN "full_name" SET NOT NULL `); // Step 4: Drop old column (after verifying app works) await queryRunner.query(` ALTER TABLE "users" DROP COLUMN "name" `); } public async down(queryRunner: QueryRunner): Promise { await queryRunner.query(`ALTER TABLE "users" ADD "name" varchar(255)`); await queryRunner.query(`UPDATE "users" SET "name" = "full_name"`); await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "full_name"`); } } ``` Reference: [TypeORM Migrations](https://typeorm.io/migrations) --- ### 7.3 Use Transactions for Multi-Step Operations **Impact: HIGH** — Ensures data consistency in multi-step operations When multiple database operations must succeed or fail together, wrap them in a transaction. This prevents partial updates that leave your data in an inconsistent state. Use TypeORM's transaction APIs or the DataSource query runner for complex scenarios. **Incorrect (multiple saves without transaction):** ```typescript // Multiple saves without transaction @Injectable() export class OrdersService { async createOrder(userId: string, items: OrderItem[]): Promise { // If any step fails, data is inconsistent const order = await this.orderRepo.save({ userId, status: 'pending' }); for (const item of items) { await this.orderItemRepo.save({ orderId: order.id, ...item }); await this.inventoryRepo.decrement({ productId: item.productId }, 'stock', item.quantity); } await this.paymentService.charge(order.id); // If payment fails, order and inventory are already modified! return order; } } ``` **Correct (use DataSource.transaction for automatic rollback):** ```typescript // Use DataSource.transaction() for automatic rollback @Injectable() export class OrdersService { constructor(private dataSource: DataSource) {} async createOrder(userId: string, items: OrderItem[]): Promise { return this.dataSource.transaction(async manager => { // All operations use the same transactional manager const order = await manager.save(Order, { userId, status: 'pending' }); for (const item of items) { await manager.save(OrderItem, { orderId: order.id, ...item }); await manager.decrement(Inventory, { productId: item.productId }, 'stock', item.quantity); } // If this throws, everything rolls back await this.paymentService.chargeWithManager(manager, order.id); return order; }); } } // QueryRunner for manual transaction control @Injectable() export class TransferService { constructor(private dataSource: DataSource) {} async transfer(fromId: string, toId: string, amount: number): Promise { const queryRunner = this.dataSource.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); try { // Debit source account await queryRunner.manager.decrement(Account, { id: fromId }, 'balance', amount); // Verify sufficient funds const source = await queryRunner.manager.findOne(Account, { where: { id: fromId }, }); if (source.balance < 0) { throw new BadRequestException('Insufficient funds'); } // Credit destination account await queryRunner.manager.increment(Account, { id: toId }, 'balance', amount); // Log the transaction await queryRunner.manager.save(TransactionLog, { fromId, toId, amount, timestamp: new Date(), }); await queryRunner.commitTransaction(); } catch (error) { await queryRunner.rollbackTransaction(); throw error; } finally { await queryRunner.release(); } } } // Repository method with transaction support @Injectable() export class UsersRepository { constructor( @InjectRepository(User) private repo: Repository, private dataSource: DataSource, ) {} async createWithProfile(userData: CreateUserDto, profileData: CreateProfileDto): Promise { return this.dataSource.transaction(async manager => { const user = await manager.save(User, userData); await manager.save(Profile, { ...profileData, userId: user.id }); return user; }); } } ``` Reference: [TypeORM Transactions](https://typeorm.io/transactions) --- ## 8. API Design **Section Impact: MEDIUM** ### 8.1 Use DTOs and Serialization for API Responses **Impact: MEDIUM** — Response DTOs prevent accidental data exposure and ensure consistency Never return entity objects directly from controllers. Use response DTOs with class-transformer's `@Exclude()` and `@Expose()` decorators to control exactly what data is sent to clients. This prevents accidental exposure of sensitive fields and provides a stable API contract. **Incorrect (returning entities directly or manual spreading):** ```typescript // Return entities directly @Controller('users') export class UsersController { @Get(':id') async findOne(@Param('id') id: string): Promise { return this.usersService.findById(id); // Returns: { id, email, passwordHash, ssn, internalNotes, ... } // Exposes sensitive data! } } // Manual object spreading (error-prone) @Get(':id') async findOne(@Param('id') id: string) { const user = await this.usersService.findById(id); return { id: user.id, email: user.email, name: user.name, // Easy to forget to exclude sensitive fields // Hard to maintain across endpoints }; } ``` **Correct (use class-transformer with @Exclude and response DTOs):** ```typescript // Enable class-transformer globally async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector))); await app.listen(3000); } // Entity with serialization control @Entity() export class User { @PrimaryGeneratedColumn('uuid') id: string; @Column() email: string; @Column() name: string; @Column() @Exclude() // Never include in responses passwordHash: string; @Column({ nullable: true }) @Exclude() ssn: string; @Column({ default: false }) @Exclude({ toPlainOnly: true }) // Exclude from response, allow in requests isAdmin: boolean; @CreateDateColumn() createdAt: Date; @Column() @Exclude() internalNotes: string; } // Now returning entity is safe @Controller('users') export class UsersController { @Get(':id') async findOne(@Param('id') id: string): Promise { return this.usersService.findById(id); // Returns: { id, email, name, createdAt } // Sensitive fields excluded automatically } } // For different response shapes, use explicit DTOs export class UserResponseDto { @Expose() id: string; @Expose() email: string; @Expose() name: string; @Expose() @Transform(({ obj }) => obj.posts?.length || 0) postCount: number; constructor(partial: Partial) { Object.assign(this, partial); } } export class UserDetailResponseDto extends UserResponseDto { @Expose() createdAt: Date; @Expose() @Type(() => PostResponseDto) posts: PostResponseDto[]; } // Controller with explicit DTOs @Controller('users') export class UsersController { @Get() @SerializeOptions({ type: UserResponseDto }) async findAll(): Promise { const users = await this.usersService.findAll(); return users.map(u => plainToInstance(UserResponseDto, u)); } @Get(':id') async findOne(@Param('id') id: string): Promise { const user = await this.usersService.findByIdWithPosts(id); return plainToInstance(UserDetailResponseDto, user, { excludeExtraneousValues: true, }); } } // Groups for conditional serialization export class UserDto { @Expose() id: string; @Expose() name: string; @Expose({ groups: ['admin'] }) email: string; @Expose({ groups: ['admin'] }) createdAt: Date; @Expose({ groups: ['admin', 'owner'] }) settings: UserSettings; } @Controller('users') export class UsersController { @Get() @SerializeOptions({ groups: ['public'] }) async findAllPublic(): Promise { // Returns: { id, name } } @Get('admin') @UseGuards(AdminGuard) @SerializeOptions({ groups: ['admin'] }) async findAllAdmin(): Promise { // Returns: { id, name, email, createdAt } } @Get('me') @SerializeOptions({ groups: ['owner'] }) async getProfile(@CurrentUser() user: User): Promise { // Returns: { id, name, settings } } } ``` Reference: [NestJS Serialization](https://docs.nestjs.com/techniques/serialization) --- ### 8.2 Use Interceptors for Cross-Cutting Concerns **Impact: MEDIUM-HIGH** — Interceptors provide clean separation for cross-cutting logic Interceptors can transform responses, add logging, handle caching, and measure performance without polluting your business logic. They wrap the route handler execution, giving you access to both the request and response streams. **Incorrect (logging and transformation in every method):** ```typescript // Logging in every controller method @Controller('users') export class UsersController { @Get() async findAll(): Promise { const start = Date.now(); this.logger.log('findAll called'); const users = await this.usersService.findAll(); this.logger.log(`findAll completed in ${Date.now() - start}ms`); return users; } @Get(':id') async findOne(@Param('id') id: string): Promise { const start = Date.now(); this.logger.log(`findOne called with id: ${id}`); const user = await this.usersService.findOne(id); this.logger.log(`findOne completed in ${Date.now() - start}ms`); return user; } // Repeated in every method! } // Manual response wrapping @Get() async findAll(): Promise<{ data: User[]; meta: Meta }> { const users = await this.usersService.findAll(); return { data: users, meta: { timestamp: new Date(), count: users.length }, }; } ``` **Correct (use interceptors for cross-cutting concerns):** ```typescript // Logging interceptor @Injectable() export class LoggingInterceptor implements NestInterceptor { private readonly logger = new Logger('HTTP'); intercept(context: ExecutionContext, next: CallHandler): Observable { const request = context.switchToHttp().getRequest(); const { method, url, body } = request; const now = Date.now(); return next.handle().pipe( tap({ next: (data) => { const response = context.switchToHttp().getResponse(); this.logger.log( `${method} ${url} ${response.statusCode} - ${Date.now() - now}ms`, ); }, error: (error) => { this.logger.error( `${method} ${url} ${error.status || 500} - ${Date.now() - now}ms`, error.stack, ); }, }), ); } } // Response transformation interceptor @Injectable() export class TransformInterceptor implements NestInterceptor> { intercept(context: ExecutionContext, next: CallHandler): Observable> { return next.handle().pipe( map((data) => ({ data, meta: { timestamp: new Date().toISOString(), path: context.switchToHttp().getRequest().url, }, })), ); } } // Timeout interceptor @Injectable() export class TimeoutInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable { return next.handle().pipe( timeout(5000), catchError((err) => { if (err instanceof TimeoutError) { throw new RequestTimeoutException('Request timed out'); } throw err; }), ); } } // Apply globally or per-controller @Module({ providers: [ { provide: APP_INTERCEPTOR, useClass: LoggingInterceptor }, { provide: APP_INTERCEPTOR, useClass: TransformInterceptor }, ], }) export class AppModule {} // Or per-controller @Controller('users') @UseInterceptors(LoggingInterceptor) export class UsersController { @Get() async findAll(): Promise { // Clean business logic only return this.usersService.findAll(); } } // Custom cache interceptor with TTL @Injectable() export class HttpCacheInterceptor implements NestInterceptor { constructor( private cacheManager: Cache, private reflector: Reflector, ) {} async intercept(context: ExecutionContext, next: CallHandler): Promise> { const request = context.switchToHttp().getRequest(); // Only cache GET requests if (request.method !== 'GET') { return next.handle(); } const cacheKey = this.generateKey(request); const ttl = this.reflector.get('cacheTTL', context.getHandler()) || 300; const cached = await this.cacheManager.get(cacheKey); if (cached) { return of(cached); } return next.handle().pipe( tap((response) => { this.cacheManager.set(cacheKey, response, ttl); }), ); } private generateKey(request: Request): string { return `cache:${request.url}:${JSON.stringify(request.query)}`; } } // Usage with custom TTL @Get() @SetMetadata('cacheTTL', 600) @UseInterceptors(HttpCacheInterceptor) async findAll(): Promise { return this.usersService.findAll(); } // Error mapping interceptor @Injectable() export class ErrorMappingInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable { return next.handle().pipe( catchError((error) => { if (error instanceof EntityNotFoundError) { throw new NotFoundException(error.message); } if (error instanceof QueryFailedError) { if (error.message.includes('duplicate')) { throw new ConflictException('Resource already exists'); } } throw error; }), ); } } ``` Reference: [NestJS Interceptors](https://docs.nestjs.com/interceptors) --- ### 8.3 Use Pipes for Input Transformation **Impact: MEDIUM** — Pipes ensure clean, validated data reaches your handlers Use built-in pipes like `ParseIntPipe`, `ParseUUIDPipe`, and `DefaultValuePipe` for common transformations. Create custom pipes for business-specific transformations. Pipes separate validation/transformation logic from controllers. **Incorrect (manual type parsing in handlers):** ```typescript // Manual type parsing in handlers @Controller('users') export class UsersController { @Get(':id') async findOne(@Param('id') id: string): Promise { // Manual validation in every handler const uuid = id.trim(); if (!isUUID(uuid)) { throw new BadRequestException('Invalid UUID'); } return this.usersService.findOne(uuid); } @Get() async findAll( @Query('page') page: string, @Query('limit') limit: string, ): Promise { // Manual parsing and defaults const pageNum = parseInt(page) || 1; const limitNum = parseInt(limit) || 10; return this.usersService.findAll(pageNum, limitNum); } } // Type coercion without validation @Get() async search(@Query('price') price: string): Promise { const priceNum = +price; // NaN if invalid, no error return this.productsService.findByPrice(priceNum); } ``` **Correct (use built-in and custom pipes):** ```typescript // Use built-in pipes for common transformations @Controller('users') export class UsersController { @Get(':id') async findOne(@Param('id', ParseUUIDPipe) id: string): Promise { // id is guaranteed to be a valid UUID return this.usersService.findOne(id); } @Get() async findAll( @Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number, @Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number, ): Promise { // Automatic defaults and type conversion return this.usersService.findAll(page, limit); } @Get('by-status/:status') async findByStatus( @Param('status', new ParseEnumPipe(UserStatus)) status: UserStatus, ): Promise { return this.usersService.findByStatus(status); } } // Custom pipe for business logic @Injectable() export class ParseDatePipe implements PipeTransform { transform(value: string): Date { const date = new Date(value); if (isNaN(date.getTime())) { throw new BadRequestException('Invalid date format'); } return date; } } @Get('reports') async getReports( @Query('from', ParseDatePipe) from: Date, @Query('to', ParseDatePipe) to: Date, ): Promise { return this.reportsService.findBetween(from, to); } // Custom transformation pipes @Injectable() export class NormalizeEmailPipe implements PipeTransform { transform(value: string): string { if (!value) return value; return value.trim().toLowerCase(); } } // Parse comma-separated values @Injectable() export class ParseArrayPipe implements PipeTransform { transform(value: string): string[] { if (!value) return []; return value.split(',').map((v) => v.trim()).filter(Boolean); } } @Get('products') async findProducts( @Query('ids', ParseArrayPipe) ids: string[], @Query('email', NormalizeEmailPipe) email: string, ): Promise { // ids is already an array, email is normalized return this.productsService.findByIds(ids); } // Sanitize HTML input @Injectable() export class SanitizeHtmlPipe implements PipeTransform { transform(value: string): string { if (!value) return value; return sanitizeHtml(value, { allowedTags: [] }); } } // Global validation pipe with transformation app.useGlobalPipes( new ValidationPipe({ whitelist: true, // Strip non-DTO properties transform: true, // Auto-transform to DTO types transformOptions: { enableImplicitConversion: true, // Convert query strings to numbers }, forbidNonWhitelisted: true, // Throw on extra properties }), ); // DTO with transformation decorators export class FindProductsDto { @IsOptional() @Type(() => Number) @IsInt() @Min(1) page?: number = 1; @IsOptional() @Type(() => Number) @IsInt() @Min(1) @Max(100) limit?: number = 10; @IsOptional() @Transform(({ value }) => value?.toLowerCase()) @IsString() search?: string; @IsOptional() @Transform(({ value }) => value?.split(',')) @IsArray() @IsString({ each: true }) categories?: string[]; } @Get() async findAll(@Query() dto: FindProductsDto): Promise { // dto is already transformed and validated return this.productsService.findAll(dto); } // Pipe error customization @Injectable() export class CustomParseIntPipe extends ParseIntPipe { constructor() { super({ exceptionFactory: (error) => new BadRequestException(`${error} must be a valid integer`), }); } } // Or use options on built-in pipes @Get(':id') async findOne( @Param( 'id', new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE, exceptionFactory: () => new NotAcceptableException('ID must be numeric'), }), ) id: number, ): Promise { return this.itemsService.findOne(id); } ``` Reference: [NestJS Pipes](https://docs.nestjs.com/pipes) --- ### 8.4 Use API Versioning for Breaking Changes **Impact: MEDIUM** — Versioning allows you to evolve APIs without breaking existing clients Use NestJS built-in versioning when making breaking changes to your API. Choose a versioning strategy (URI, header, or media type) and apply it consistently. This allows old clients to continue working while new clients use updated endpoints. **Incorrect (breaking changes without versioning):** ```typescript // Breaking changes without versioning @Controller('users') export class UsersController { @Get(':id') async findOne(@Param('id') id: string): Promise { // Original response: { id, name, email } // Later changed to: { id, firstName, lastName, emailAddress } // Old clients break! return this.usersService.findOne(id); } } // Manual versioning in routes @Controller('v1/users') export class UsersV1Controller {} @Controller('v2/users') export class UsersV2Controller {} // Inconsistent, error-prone, hard to maintain ``` **Correct (use NestJS built-in versioning):** ```typescript // Enable versioning in main.ts async function bootstrap() { const app = await NestFactory.create(AppModule); // URI versioning: /v1/users, /v2/users app.enableVersioning({ type: VersioningType.URI, defaultVersion: '1', }); // Or header versioning: X-API-Version: 1 app.enableVersioning({ type: VersioningType.HEADER, header: 'X-API-Version', defaultVersion: '1', }); // Or media type: Accept: application/json;v=1 app.enableVersioning({ type: VersioningType.MEDIA_TYPE, key: 'v=', defaultVersion: '1', }); await app.listen(3000); } // Version-specific controllers @Controller('users') @Version('1') export class UsersV1Controller { @Get(':id') async findOne(@Param('id') id: string): Promise { const user = await this.usersService.findOne(id); // V1 response format return { id: user.id, name: user.name, email: user.email, }; } } @Controller('users') @Version('2') export class UsersV2Controller { @Get(':id') async findOne(@Param('id') id: string): Promise { const user = await this.usersService.findOne(id); // V2 response format with breaking changes return { id: user.id, firstName: user.firstName, lastName: user.lastName, emailAddress: user.email, createdAt: user.createdAt, }; } } // Per-route versioning - different versions for different routes @Controller('users') export class UsersController { @Get() @Version('1') findAllV1(): Promise { return this.usersService.findAllV1(); } @Get() @Version('2') findAllV2(): Promise { return this.usersService.findAllV2(); } @Get(':id') @Version(['1', '2']) // Same handler for multiple versions findOne(@Param('id') id: string): Promise { return this.usersService.findOne(id); } @Post() @Version(VERSION_NEUTRAL) // Available in all versions create(@Body() dto: CreateUserDto): Promise { return this.usersService.create(dto); } } // Shared service with version-specific logic @Injectable() export class UsersService { async findOne(id: string, version: string): Promise { const user = await this.repo.findOne({ where: { id } }); if (version === '1') { return this.toV1Response(user); } return this.toV2Response(user); } private toV1Response(user: User): UserV1Response { return { id: user.id, name: `${user.firstName} ${user.lastName}`, email: user.email, }; } private toV2Response(user: User): UserV2Response { return { id: user.id, firstName: user.firstName, lastName: user.lastName, emailAddress: user.email, createdAt: user.createdAt, }; } } // Controller extracts version @Controller('users') export class UsersController { @Get(':id') async findOne(@Param('id') id: string, @Headers('X-API-Version') version: string = '1'): Promise { return this.usersService.findOne(id, version); } } // Deprecation strategy - mark old versions as deprecated @Controller('users') @Version('1') @UseInterceptors(DeprecationInterceptor) export class UsersV1Controller { // All V1 routes will include deprecation warning } @Injectable() export class DeprecationInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable { const response = context.switchToHttp().getResponse(); response.setHeader('Deprecation', 'true'); response.setHeader('Sunset', 'Sat, 1 Jan 2025 00:00:00 GMT'); response.setHeader('Link', '; rel="successor-version"'); return next.handle(); } } ``` Reference: [NestJS Versioning](https://docs.nestjs.com/techniques/versioning) --- ## 9. Microservices **Section Impact: MEDIUM** ### 9.1 Implement Health Checks for Microservices **Impact: MEDIUM-HIGH** — Health checks enable orchestrators to manage service lifecycle Implement liveness and readiness probes using `@nestjs/terminus`. Liveness checks determine if the service should be restarted. Readiness checks determine if the service can accept traffic. Proper health checks enable Kubernetes and load balancers to route traffic correctly. **Incorrect (simple ping that doesn't check dependencies):** ```typescript // Simple ping that doesn't check dependencies @Controller('health') export class HealthController { @Get() check(): string { return 'OK'; // Service might be unhealthy but returns OK } } // Health check that blocks on slow dependencies @Controller('health') export class HealthController { @Get() async check(): Promise { // If database is slow, health check times out await this.userRepo.findOne({ where: { id: '1' } }); await this.redis.ping(); await this.externalApi.healthCheck(); return 'OK'; } } ``` **Correct (use @nestjs/terminus for comprehensive health checks):** ```typescript // Use @nestjs/terminus for comprehensive health checks import { HealthCheckService, HttpHealthIndicator, TypeOrmHealthIndicator, HealthCheck, DiskHealthIndicator, MemoryHealthIndicator, } from '@nestjs/terminus'; @Controller('health') export class HealthController { constructor( private health: HealthCheckService, private http: HttpHealthIndicator, private db: TypeOrmHealthIndicator, private disk: DiskHealthIndicator, private memory: MemoryHealthIndicator, ) {} // Liveness probe - is the service alive? @Get('live') @HealthCheck() liveness() { return this.health.check([ // Basic checks only () => this.memory.checkHeap('memory_heap', 200 * 1024 * 1024), // 200MB ]); } // Readiness probe - can the service handle traffic? @Get('ready') @HealthCheck() readiness() { return this.health.check([ () => this.db.pingCheck('database'), () => this.http.pingCheck('redis', 'http://redis:6379', { timeout: 1000 }), () => this.disk.checkStorage('disk', { path: '/', thresholdPercent: 0.9 }), ]); } // Deep health check for debugging @Get('deep') @HealthCheck() deepCheck() { return this.health.check([ () => this.db.pingCheck('database'), () => this.memory.checkHeap('memory_heap', 200 * 1024 * 1024), () => this.memory.checkRSS('memory_rss', 300 * 1024 * 1024), () => this.disk.checkStorage('disk', { path: '/', thresholdPercent: 0.9 }), () => this.http.pingCheck('external-api', 'https://api.example.com/health'), ]); } } // Custom indicator for business-specific health @Injectable() export class QueueHealthIndicator extends HealthIndicator { constructor(private queueService: QueueService) { super(); } async isHealthy(key: string): Promise { const queueStats = await this.queueService.getStats(); const isHealthy = queueStats.failedCount < 100; const result = this.getStatus(key, isHealthy, { waiting: queueStats.waitingCount, active: queueStats.activeCount, failed: queueStats.failedCount, }); if (!isHealthy) { throw new HealthCheckError('Queue unhealthy', result); } return result; } } // Redis health indicator @Injectable() export class RedisHealthIndicator extends HealthIndicator { constructor(@InjectRedis() private redis: Redis) { super(); } async isHealthy(key: string): Promise { try { const pong = await this.redis.ping(); return this.getStatus(key, pong === 'PONG'); } catch (error) { throw new HealthCheckError('Redis check failed', this.getStatus(key, false)); } } } // Use custom indicators @Get('ready') @HealthCheck() readiness() { return this.health.check([ () => this.db.pingCheck('database'), () => this.redis.isHealthy('redis'), () => this.queue.isHealthy('job-queue'), ]); } // Graceful shutdown handling @Injectable() export class GracefulShutdownService implements OnApplicationShutdown { private isShuttingDown = false; isShutdown(): boolean { return this.isShuttingDown; } async onApplicationShutdown(signal: string): Promise { this.isShuttingDown = true; console.log(`Shutting down on ${signal}`); // Wait for in-flight requests await new Promise((resolve) => setTimeout(resolve, 5000)); } } // Health check respects shutdown state @Get('ready') @HealthCheck() readiness() { if (this.shutdownService.isShutdown()) { throw new ServiceUnavailableException('Shutting down'); } return this.health.check([ () => this.db.pingCheck('database'), ]); } ``` ### Kubernetes Configuration ```yaml # Kubernetes deployment with probes apiVersion: apps/v1 kind: Deployment metadata: name: api-service spec: template: spec: containers: - name: api image: api-service:latest ports: - containerPort: 3000 livenessProbe: httpGet: path: /health/live port: 3000 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 readinessProbe: httpGet: path: /health/ready port: 3000 initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 3 startupProbe: httpGet: path: /health/live port: 3000 initialDelaySeconds: 0 periodSeconds: 5 failureThreshold: 30 ``` Reference: [NestJS Terminus](https://docs.nestjs.com/recipes/terminus) --- ### 9.2 Use Message and Event Patterns Correctly **Impact: MEDIUM** — Proper patterns ensure reliable microservice communication NestJS microservices support two communication patterns: request-response (MessagePattern) and event-based (EventPattern). Use MessagePattern when you need a response, and EventPattern for fire-and-forget notifications. Understanding the difference prevents communication bugs. **Incorrect (using wrong pattern for use case):** ```typescript // Use @MessagePattern for fire-and-forget @Controller() export class NotificationsController { @MessagePattern('user.created') async handleUserCreated(data: UserCreatedEvent) { // This WAITS for response, blocking the sender await this.emailService.sendWelcome(data.email); // If email fails, sender gets an error (coupling!) } } // Use @EventPattern expecting a response @Controller() export class OrdersController { @EventPattern('inventory.check') async checkInventory(data: CheckInventoryDto) { const available = await this.inventory.check(data); return available; // This return value is IGNORED with @EventPattern! } } // Tight coupling in client @Injectable() export class UsersService { async createUser(dto: CreateUserDto): Promise { const user = await this.repo.save(dto); // Blocks until notification service responds await this.client.send('user.created', user).toPromise(); // If notification service is down, user creation fails! return user; } } ``` **Correct (use MessagePattern for request-response, EventPattern for fire-and-forget):** ```typescript // MessagePattern: Request-Response (when you NEED a response) @Controller() export class InventoryController { @MessagePattern({ cmd: 'check_inventory' }) async checkInventory(data: CheckInventoryDto): Promise { const result = await this.inventoryService.check(data.productId, data.quantity); return result; // Response sent back to caller } } // Client expects response @Injectable() export class OrdersService { async createOrder(dto: CreateOrderDto): Promise { // Check inventory - we NEED this response to proceed const inventory = await firstValueFrom( this.inventoryClient.send( { cmd: 'check_inventory' }, { productId: dto.productId, quantity: dto.quantity }, ), ); if (!inventory.available) { throw new BadRequestException('Insufficient inventory'); } return this.repo.save(dto); } } // EventPattern: Fire-and-Forget (for notifications, side effects) @Controller() export class NotificationsController { @EventPattern('user.created') async handleUserCreated(data: UserCreatedEvent): Promise { // No return value needed - just process the event await this.emailService.sendWelcome(data.email); await this.analyticsService.track('user_signup', data); // If this fails, it doesn't affect the sender } } // Client emits event without waiting @Injectable() export class UsersService { async createUser(dto: CreateUserDto): Promise { const user = await this.repo.save(dto); // Fire and forget - doesn't block, doesn't wait this.eventClient.emit('user.created', { userId: user.id, email: user.email, timestamp: new Date(), }); return user; // User creation succeeds regardless of event handling } } // Hybrid pattern for critical events @Injectable() export class OrdersService { async createOrder(dto: CreateOrderDto): Promise { const order = await this.repo.save(dto); // Critical: inventory reservation (use MessagePattern) const reserved = await firstValueFrom( this.inventoryClient.send({ cmd: 'reserve_inventory' }, { orderId: order.id, items: dto.items, }), ); if (!reserved.success) { await this.repo.delete(order.id); throw new BadRequestException('Could not reserve inventory'); } // Non-critical: notifications (use EventPattern) this.eventClient.emit('order.created', { orderId: order.id, userId: dto.userId, total: dto.total, }); return order; } } // Error handling patterns // MessagePattern errors propagate to caller @MessagePattern({ cmd: 'get_user' }) async getUser(userId: string): Promise { const user = await this.repo.findOne({ where: { id: userId } }); if (!user) { throw new RpcException('User not found'); // Received by caller } return user; } // EventPattern errors should be handled locally @EventPattern('order.created') async handleOrderCreated(data: OrderCreatedEvent): Promise { try { await this.processOrder(data); } catch (error) { // Log and potentially retry - don't throw this.logger.error('Failed to process order event', error); await this.deadLetterQueue.add(data); } } ``` Reference: [NestJS Microservices](https://docs.nestjs.com/microservices/basics) --- ### 9.3 Use Message Queues for Background Jobs **Impact: MEDIUM-HIGH** — Queues enable reliable background processing Use `@nestjs/bullmq` for background job processing. Queues decouple long-running tasks from HTTP requests, enable retry logic, and distribute workload across workers. Use them for emails, file processing, notifications, and any task that shouldn't block user requests. **Incorrect (long-running tasks in HTTP handlers):** ```typescript // Long-running tasks in HTTP handlers @Controller('reports') export class ReportsController { @Post() async generate(@Body() dto: GenerateReportDto): Promise { // This blocks the request for potentially minutes const data = await this.fetchLargeDataset(dto); const report = await this.processData(data); // Slow! await this.sendEmail(dto.email, report); // Can fail! return report; // Client times out } } // Fire-and-forget without retry @Injectable() export class EmailService { async sendWelcome(email: string): Promise { // If this fails, email is never sent await this.mailer.send({ to: email, template: 'welcome' }); // No retry, no tracking, no visibility } } // Use setInterval for scheduled tasks setInterval(async () => { await cleanupOldRecords(); }, 60000); // No error handling, memory leaks ``` **Correct (use BullMQ for background processing):** ```typescript // Configure BullMQ import { BullModule } from '@nestjs/bullmq'; @Module({ imports: [ BullModule.forRoot({ connection: { host: 'localhost', port: 6379, }, defaultJobOptions: { removeOnComplete: 1000, removeOnFail: 5000, attempts: 3, backoff: { type: 'exponential', delay: 1000, }, }, }), BullModule.registerQueue({ name: 'email' }, { name: 'reports' }, { name: 'notifications' }), ], }) export class QueueModule {} // Producer: Add jobs to queue @Injectable() export class ReportsService { constructor(@InjectQueue('reports') private reportsQueue: Queue) {} async requestReport(dto: GenerateReportDto): Promise<{ jobId: string }> { // Return immediately, process in background const job = await this.reportsQueue.add('generate', dto, { priority: dto.urgent ? 1 : 10, delay: dto.scheduledFor ? Date.parse(dto.scheduledFor) - Date.now() : 0, }); return { jobId: job.id }; } async getJobStatus(jobId: string): Promise { const job = await this.reportsQueue.getJob(jobId); return { status: await job.getState(), progress: job.progress, result: job.returnvalue, }; } } // Consumer: Process jobs @Processor('reports') export class ReportsProcessor { private readonly logger = new Logger(ReportsProcessor.name); @Process('generate') async generateReport(job: Job): Promise { this.logger.log(`Processing report job ${job.id}`); // Update progress await job.updateProgress(10); const data = await this.fetchData(job.data); await job.updateProgress(50); const report = await this.processData(data); await job.updateProgress(90); await this.saveReport(report); await job.updateProgress(100); return report; } @OnQueueActive() onActive(job: Job) { this.logger.log(`Processing job ${job.id}`); } @OnQueueCompleted() onCompleted(job: Job, result: any) { this.logger.log(`Job ${job.id} completed`); } @OnQueueFailed() onFailed(job: Job, error: Error) { this.logger.error(`Job ${job.id} failed: ${error.message}`); } } // Email queue with retry @Processor('email') export class EmailProcessor { @Process('send') async sendEmail(job: Job): Promise { const { to, template, data } = job.data; try { await this.mailer.send({ to, template, context: data, }); } catch (error) { // BullMQ will retry based on job options throw error; } } } // Usage @Injectable() export class NotificationService { constructor(@InjectQueue('email') private emailQueue: Queue) {} async sendWelcome(user: User): Promise { await this.emailQueue.add( 'send', { to: user.email, template: 'welcome', data: { name: user.name }, }, { attempts: 5, backoff: { type: 'exponential', delay: 5000 }, }, ); } } // Scheduled jobs @Injectable() export class ScheduledJobsService implements OnModuleInit { constructor(@InjectQueue('maintenance') private queue: Queue) {} async onModuleInit(): Promise { // Clean up old reports daily at midnight await this.queue.add( 'cleanup', {}, { repeat: { cron: '0 0 * * *' }, jobId: 'daily-cleanup', // Prevent duplicates }, ); // Send digest every hour await this.queue.add( 'digest', {}, { repeat: { every: 60 * 60 * 1000 }, jobId: 'hourly-digest', }, ); } } @Processor('maintenance') export class MaintenanceProcessor { @Process('cleanup') async cleanup(): Promise { await this.cleanupOldReports(); await this.cleanupExpiredSessions(); } @Process('digest') async sendDigest(): Promise { const users = await this.getUsersForDigest(); for (const user of users) { await this.emailQueue.add('send', { to: user.email, template: 'digest' }); } } } // Queue monitoring with Bull Board import { BullBoardModule } from '@bull-board/nestjs'; import { BullMQAdapter } from '@bull-board/api/bullMQAdapter'; @Module({ imports: [ BullBoardModule.forRoot({ route: '/admin/queues', adapter: ExpressAdapter, }), BullBoardModule.forFeature({ name: 'email', adapter: BullMQAdapter, }), BullBoardModule.forFeature({ name: 'reports', adapter: BullMQAdapter, }), ], }) export class AdminModule {} ``` Reference: [NestJS Queues](https://docs.nestjs.com/techniques/queues) --- ## 10. DevOps & Deployment **Section Impact: LOW-MEDIUM** ### 10.1 Implement Graceful Shutdown **Impact: MEDIUM-HIGH** — Proper shutdown handling ensures zero-downtime deployments Handle SIGTERM and SIGINT signals to gracefully shutdown your NestJS application. Stop accepting new requests, wait for in-flight requests to complete, close database connections, and clean up resources. This prevents data loss and connection errors during deployments. **Incorrect (ignoring shutdown signals):** ```typescript // Ignore shutdown signals async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); // App crashes immediately on SIGTERM // In-flight requests fail // Database connections are abruptly closed } // Long-running tasks without cancellation @Injectable() export class ProcessingService { async processLargeFile(file: File): Promise { // No way to interrupt this during shutdown for (let i = 0; i < file.chunks.length; i++) { await this.processChunk(file.chunks[i]); // May run for minutes, blocking shutdown } } } ``` **Correct (enable shutdown hooks and handle cleanup):** ```typescript // Enable shutdown hooks in main.ts async function bootstrap() { const app = await NestFactory.create(AppModule); // Enable shutdown hooks app.enableShutdownHooks(); // Optional: Add timeout for forced shutdown const server = await app.listen(3000); server.setTimeout(30000); // 30 second timeout // Handle graceful shutdown const signals = ['SIGTERM', 'SIGINT']; signals.forEach(signal => { process.on(signal, async () => { console.log(`Received ${signal}, starting graceful shutdown...`); // Stop accepting new connections server.close(async () => { console.log('HTTP server closed'); await app.close(); process.exit(0); }); // Force exit after timeout setTimeout(() => { console.error('Forced shutdown after timeout'); process.exit(1); }, 30000); }); }); } // Lifecycle hooks for cleanup @Injectable() export class DatabaseService implements OnApplicationShutdown { private readonly connections: Connection[] = []; async onApplicationShutdown(signal?: string): Promise { console.log(`Database service shutting down on ${signal}`); // Close all connections gracefully await Promise.all(this.connections.map(conn => conn.close())); console.log('All database connections closed'); } } // Queue processor with graceful shutdown @Injectable() export class QueueService implements OnApplicationShutdown, OnModuleDestroy { private isShuttingDown = false; onModuleDestroy(): void { this.isShuttingDown = true; } async onApplicationShutdown(): Promise { // Wait for current jobs to complete await this.queue.close(); } async processJob(job: Job): Promise { if (this.isShuttingDown) { throw new Error('Service is shutting down'); } await this.doWork(job); } } // WebSocket gateway cleanup @WebSocketGateway() export class EventsGateway implements OnApplicationShutdown { @WebSocketServer() server: Server; async onApplicationShutdown(): Promise { // Notify all connected clients this.server.emit('shutdown', { message: 'Server is shutting down' }); // Close all connections this.server.disconnectSockets(); } } // Health check integration @Injectable() export class ShutdownService { private isShuttingDown = false; startShutdown(): void { this.isShuttingDown = true; } isShutdown(): boolean { return this.isShuttingDown; } } @Controller('health') export class HealthController { constructor(private shutdownService: ShutdownService) {} @Get('ready') @HealthCheck() readiness(): Promise { // Return 503 during shutdown - k8s stops sending traffic if (this.shutdownService.isShutdown()) { throw new ServiceUnavailableException('Shutting down'); } return this.health.check([() => this.db.pingCheck('database')]); } } // Integrate with shutdown @Injectable() export class AppShutdownService implements OnApplicationShutdown { constructor(private shutdownService: ShutdownService) {} async onApplicationShutdown(): Promise { // Mark as unhealthy first this.shutdownService.startShutdown(); // Wait for k8s to update endpoints await this.sleep(5000); // Then proceed with cleanup } } // Request tracking for in-flight requests @Injectable() export class RequestTracker implements NestMiddleware, OnApplicationShutdown { private activeRequests = 0; private isShuttingDown = false; private shutdownPromise: Promise | null = null; private resolveShutdown: (() => void) | null = null; use(req: Request, res: Response, next: NextFunction): void { if (this.isShuttingDown) { res.status(503).send('Service Unavailable'); return; } this.activeRequests++; res.on('finish', () => { this.activeRequests--; if (this.isShuttingDown && this.activeRequests === 0 && this.resolveShutdown) { this.resolveShutdown(); } }); next(); } async onApplicationShutdown(): Promise { this.isShuttingDown = true; if (this.activeRequests > 0) { console.log(`Waiting for ${this.activeRequests} requests to complete`); this.shutdownPromise = new Promise(resolve => { this.resolveShutdown = resolve; }); // Wait with timeout await Promise.race([this.shutdownPromise, new Promise(resolve => setTimeout(resolve, 30000))]); } console.log('All requests completed'); } } ``` Reference: [NestJS Lifecycle Events](https://docs.nestjs.com/fundamentals/lifecycle-events) --- ### 10.2 Use ConfigModule for Environment Configuration **Impact: LOW-MEDIUM** — Proper configuration prevents deployment failures Use `@nestjs/config` for environment-based configuration. Validate configuration at startup to fail fast on misconfigurations. Use namespaced configuration for organization and type safety. **Incorrect (accessing process.env directly):** ```typescript // Access process.env directly @Injectable() export class DatabaseService { constructor() { // No validation, can fail at runtime this.connection = new Pool({ host: process.env.DB_HOST, port: parseInt(process.env.DB_PORT), // NaN if missing password: process.env.DB_PASSWORD, // undefined if missing }); } } // Scattered env access @Injectable() export class EmailService { sendEmail() { // Different services access env differently const apiKey = process.env.SENDGRID_API_KEY || 'default'; // Typos go unnoticed: process.env.SENDGRID_API_KY } } ``` **Correct (use @nestjs/config with validation):** ```typescript // Setup validated configuration import { ConfigModule, ConfigService, registerAs } from '@nestjs/config'; import * as Joi from 'joi'; // config/database.config.ts export const databaseConfig = registerAs('database', () => ({ host: process.env.DB_HOST, port: parseInt(process.env.DB_PORT, 10), username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, })); // config/app.config.ts export const appConfig = registerAs('app', () => ({ port: parseInt(process.env.PORT, 10) || 3000, environment: process.env.NODE_ENV || 'development', apiPrefix: process.env.API_PREFIX || 'api', })); // config/validation.schema.ts export const validationSchema = Joi.object({ NODE_ENV: Joi.string().valid('development', 'production', 'test').default('development'), PORT: Joi.number().default(3000), DB_HOST: Joi.string().required(), DB_PORT: Joi.number().default(5432), DB_USERNAME: Joi.string().required(), DB_PASSWORD: Joi.string().required(), DB_NAME: Joi.string().required(), JWT_SECRET: Joi.string().min(32).required(), REDIS_URL: Joi.string().uri().required(), }); // app.module.ts @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, // Available everywhere without importing load: [databaseConfig, appConfig], validationSchema, validationOptions: { abortEarly: true, // Stop on first error allowUnknown: true, // Allow other env vars }, }), TypeOrmModule.forRootAsync({ inject: [ConfigService], useFactory: (config: ConfigService) => ({ type: 'postgres', host: config.get('database.host'), port: config.get('database.port'), username: config.get('database.username'), password: config.get('database.password'), database: config.get('database.database'), autoLoadEntities: true, }), }), ], }) export class AppModule {} // Type-safe configuration access export interface AppConfig { port: number; environment: 'development' | 'production' | 'test'; apiPrefix: string; } export interface DatabaseConfig { host: string; port: number; username: string; password: string; database: string; } // Type-safe access @Injectable() export class AppService { constructor(private config: ConfigService) {} getPort(): number { // Type-safe with generic return this.config.get('app.port'); } getDatabaseConfig(): DatabaseConfig { return this.config.get('database'); } } // Inject namespaced config directly @Injectable() export class DatabaseService { constructor( @Inject(databaseConfig.KEY) private dbConfig: ConfigType, ) { // Full type inference! const host = this.dbConfig.host; // string const port = this.dbConfig.port; // number } } // Environment files support ConfigModule.forRoot({ envFilePath: [`.env.${process.env.NODE_ENV}.local`, `.env.${process.env.NODE_ENV}`, '.env.local', '.env'], }); // .env.development // DB_HOST=localhost // DB_PORT=5432 // .env.production // DB_HOST=prod-db.example.com // DB_PORT=5432 ``` Reference: [NestJS Configuration](https://docs.nestjs.com/techniques/configuration) --- ### 10.3 Use Structured Logging **Impact: MEDIUM-HIGH** — Structured logging enables effective debugging and monitoring Use NestJS Logger with structured JSON output in production. Include contextual information (request ID, user ID, operation) to trace requests across services. Avoid console.log and implement proper log levels. **Incorrect (using console.log in production):** ```typescript // Use console.log in production @Injectable() export class UsersService { async createUser(dto: CreateUserDto): Promise { console.log('Creating user:', dto); // Not structured, no levels, lost in production logs try { const user = await this.repo.save(dto); console.log('User created:', user.id); return user; } catch (error) { console.log('Error:', error); // Using log for errors throw error; } } } // Log sensitive data console.log('Login attempt:', { email, password }); // SECURITY RISK! // Inconsistent log format logger.log('User ' + userId + ' created at ' + new Date()); // Hard to parse, no structure ``` **Correct (use structured logging with context):** ```typescript // Configure logger in main.ts async function bootstrap() { const app = await NestFactory.create(AppModule, { logger: process.env.NODE_ENV === 'production' ? ['error', 'warn', 'log'] : ['error', 'warn', 'log', 'debug', 'verbose'], }); } // Use NestJS Logger with context @Injectable() export class UsersService { private readonly logger = new Logger(UsersService.name); async createUser(dto: CreateUserDto): Promise { this.logger.log('Creating user', { email: dto.email }); try { const user = await this.repo.save(dto); this.logger.log('User created', { userId: user.id }); return user; } catch (error) { this.logger.error('Failed to create user', error.stack, { email: dto.email, }); throw error; } } } // Custom logger for JSON output @Injectable() export class JsonLogger implements LoggerService { log(message: string, context?: object): void { console.log( JSON.stringify({ level: 'info', timestamp: new Date().toISOString(), message, ...context, }), ); } error(message: string, trace?: string, context?: object): void { console.error( JSON.stringify({ level: 'error', timestamp: new Date().toISOString(), message, trace, ...context, }), ); } warn(message: string, context?: object): void { console.warn( JSON.stringify({ level: 'warn', timestamp: new Date().toISOString(), message, ...context, }), ); } debug(message: string, context?: object): void { console.debug( JSON.stringify({ level: 'debug', timestamp: new Date().toISOString(), message, ...context, }), ); } } // Request context logging with ClsModule import { ClsModule, ClsService } from 'nestjs-cls'; @Module({ imports: [ ClsModule.forRoot({ global: true, middleware: { mount: true, generateId: true, }, }), ], }) export class AppModule {} // Middleware to set request context @Injectable() export class RequestContextMiddleware implements NestMiddleware { constructor(private cls: ClsService) {} use(req: Request, res: Response, next: NextFunction): void { const requestId = req.headers['x-request-id'] || randomUUID(); this.cls.set('requestId', requestId); this.cls.set('userId', req.user?.id); res.setHeader('x-request-id', requestId); next(); } } // Logger that includes request context @Injectable() export class ContextLogger { constructor(private cls: ClsService) {} log(message: string, data?: object): void { console.log( JSON.stringify({ level: 'info', timestamp: new Date().toISOString(), requestId: this.cls.get('requestId'), userId: this.cls.get('userId'), message, ...data, }), ); } error(message: string, error: Error, data?: object): void { console.error( JSON.stringify({ level: 'error', timestamp: new Date().toISOString(), requestId: this.cls.get('requestId'), userId: this.cls.get('userId'), message, error: error.message, stack: error.stack, ...data, }), ); } } // Pino integration for high-performance logging import { LoggerModule } from 'nestjs-pino'; @Module({ imports: [ LoggerModule.forRoot({ pinoHttp: { level: process.env.NODE_ENV === 'production' ? 'info' : 'debug', transport: process.env.NODE_ENV !== 'production' ? { target: 'pino-pretty' } : undefined, redact: ['req.headers.authorization', 'req.body.password'], serializers: { req: req => ({ method: req.method, url: req.url, query: req.query, }), res: res => ({ statusCode: res.statusCode, }), }, }, }), ], }) export class AppModule {} // Usage with Pino @Injectable() export class UsersService { constructor(private logger: PinoLogger) { this.logger.setContext(UsersService.name); } async findOne(id: string): Promise { this.logger.info({ userId: id }, 'Finding user'); // Pino uses first arg for data, second for message } } ``` Reference: [NestJS Logger](https://docs.nestjs.com/techniques/logger) --- ## References - https://docs.nestjs.com - https://github.com/nestjs/nest - https://typeorm.io - https://github.com/typestack/class-validator - https://github.com/goldbergyoni/nodebestpractices --- _Generated by build-agents.ts on 2026-01-16_ ================================================ FILE: .agents/skills/nestjs-best-practices/README.md ================================================ # NestJS Best Practices 📖 [For Humans <3](https://kadajett.github.io/agent-nestjs-skills/) A structured repository for creating and maintaining NestJS Best Practices optimized for agents and LLMs. ## Installation Install this skill using [skills](https://github.com/vercel-labs/skills): ```bash # GitHub shorthand npx skills add Kadajett/agent-nestjs-skills # Install globally (available across all projects) npx skills add Kadajett/agent-nestjs-skills --global # Install for specific agents npx skills add Kadajett/agent-nestjs-skills -a claude-code -a cursor ``` ### Supported Agents - Claude Code - OpenCode - Codex - Cursor - Antigravity - Roo Code ## Structure - `rules/` - Individual rule files (one per rule) - `_sections.md` - Section metadata (titles, impacts, descriptions) - `_template.md` - Template for creating new rules - `area-description.md` - Individual rule files - `scripts/` - Build scripts and utilities - `metadata.json` - Document metadata (version, organization, abstract) - **`AGENTS.md`** - Compiled output (generated) ## Getting Started 1. Install dependencies: ```bash cd scripts && npm install ``` 2. Build AGENTS.md from rules: ```bash npm run build # or ./scripts/build.sh ``` ## Creating a New Rule 1. Copy `rules/_template.md` to `rules/area-description.md` 2. Choose the appropriate area prefix: - `arch-` for Architecture (Section 1) - `di-` for Dependency Injection (Section 2) - `error-` for Error Handling (Section 3) - `security-` for Security (Section 4) - `perf-` for Performance (Section 5) - `test-` for Testing (Section 6) - `db-` for Database & ORM (Section 7) - `api-` for API Design (Section 8) - `micro-` for Microservices (Section 9) - `devops-` for DevOps & Deployment (Section 10) 3. Fill in the frontmatter and content 4. Ensure you have clear examples with explanations 5. Run the build script to regenerate AGENTS.md ## Rule File Structure Each rule file should follow this structure: ````markdown --- title: Rule Title Here impact: MEDIUM impactDescription: Optional description tags: tag1, tag2, tag3 --- ## Rule Title Here Brief explanation of the rule and why it matters. **Incorrect (description of what's wrong):** ```typescript // Bad code example ``` ```` **Correct (description of what's right):** ```typescript // Good code example ``` Optional explanatory text after examples. Reference: [NestJS Documentation](https://docs.nestjs.com) ## File Naming Convention - Files starting with `_` are special (excluded from build) - Rule files: `area-description.md` (e.g., `arch-avoid-circular-deps.md`) - Section is automatically inferred from filename prefix - Rules are sorted alphabetically by title within each section - IDs (e.g., 1.1, 1.2) are auto-generated during build ## Impact Levels | Level | Description | | ----------- | ------------------------------------------------------------------------------------- | | CRITICAL | Violations cause runtime errors, security vulnerabilities, or architectural breakdown | | HIGH | Significant impact on reliability, security, or maintainability | | MEDIUM-HIGH | Notable impact on quality and developer experience | | MEDIUM | Moderate impact on code quality and best practices | | LOW-MEDIUM | Minor improvements for consistency and maintainability | ## Scripts - `npm run build` (in scripts/) - Compile rules into AGENTS.md ## Contributing When adding or modifying rules: 1. Use the correct filename prefix for your section 2. Follow the `_template.md` structure 3. Include clear bad/good examples with explanations 4. Add appropriate tags 5. Run the build script to regenerate AGENTS.md 6. Rules are automatically sorted by title - no need to manage numbers! ## Documentation Website The documentation website source code lives on the [`docs` branch](https://github.com/Kadajett/agent-nestjs-skills/tree/docs/website). This separation keeps the skill installation lightweight while maintaining the full documentation site. To contribute to the website: ```bash git checkout docs cd website npm install npm run dev ``` ## Acknowledgments - Inspired by the [Vercel React Best Practices](https://github.com/vercel-labs/agent-skills) skill structure - Compatible with [skills](https://github.com/vercel-labs/skills) for easy installation across coding agents ## Compatible Agents These NestJS skills work with: - [Claude Code](https://claude.ai/code) - Anthropic's official CLI - [AdaL](https://sylph.ai/adal) - Self-evolving AI coding agent with MCP support ================================================ FILE: .agents/skills/nestjs-best-practices/SKILL.md ================================================ --- name: nestjs-best-practices description: NestJS best practices and architecture patterns for building production-ready applications. This skill should be used when writing, reviewing, or refactoring NestJS code to ensure proper patterns for modules, dependency injection, security, and performance. license: MIT metadata: author: Kadajett version: '1.1.0' --- # NestJS Best Practices Comprehensive best practices guide for NestJS applications. Contains 40 rules across 10 categories, prioritized by impact to guide automated refactoring and code generation. ## When to Apply Reference these guidelines when: - Writing new NestJS modules, controllers, or services - Implementing authentication and authorization - Reviewing code for architecture and security issues - Refactoring existing NestJS codebases - Optimizing performance or database queries - Building microservices architectures ## Rule Categories by Priority | Priority | Category | Impact | Prefix | | -------- | -------------------- | ----------- | ----------- | | 1 | Architecture | CRITICAL | `arch-` | | 2 | Dependency Injection | CRITICAL | `di-` | | 3 | Error Handling | HIGH | `error-` | | 4 | Security | HIGH | `security-` | | 5 | Performance | HIGH | `perf-` | | 6 | Testing | MEDIUM-HIGH | `test-` | | 7 | Database & ORM | MEDIUM-HIGH | `db-` | | 8 | API Design | MEDIUM | `api-` | | 9 | Microservices | MEDIUM | `micro-` | | 10 | DevOps & Deployment | LOW-MEDIUM | `devops-` | ## Quick Reference ### 1. Architecture (CRITICAL) - `arch-avoid-circular-deps` - Avoid circular module dependencies - `arch-feature-modules` - Organize by feature, not technical layer - `arch-module-sharing` - Proper module exports/imports, avoid duplicate providers - `arch-single-responsibility` - Focused services over "god services" - `arch-use-repository-pattern` - Abstract database logic for testability - `arch-use-events` - Event-driven architecture for decoupling ### 2. Dependency Injection (CRITICAL) - `di-avoid-service-locator` - Avoid service locator anti-pattern - `di-interface-segregation` - Interface Segregation Principle (ISP) - `di-liskov-substitution` - Liskov Substitution Principle (LSP) - `di-prefer-constructor-injection` - Constructor over property injection - `di-scope-awareness` - Understand singleton/request/transient scopes - `di-use-interfaces-tokens` - Use injection tokens for interfaces ### 3. Error Handling (HIGH) - `error-use-exception-filters` - Centralized exception handling - `error-throw-http-exceptions` - Use NestJS HTTP exceptions - `error-handle-async-errors` - Handle async errors properly ### 4. Security (HIGH) - `security-auth-jwt` - Secure JWT authentication - `security-validate-all-input` - Validate with class-validator - `security-use-guards` - Authentication and authorization guards - `security-sanitize-output` - Prevent XSS attacks - `security-rate-limiting` - Implement rate limiting ### 5. Performance (HIGH) - `perf-async-hooks` - Proper async lifecycle hooks - `perf-use-caching` - Implement caching strategies - `perf-optimize-database` - Optimize database queries - `perf-lazy-loading` - Lazy load modules for faster startup ### 6. Testing (MEDIUM-HIGH) - `test-use-testing-module` - Use NestJS testing utilities - `test-e2e-supertest` - E2E testing with Supertest - `test-mock-external-services` - Mock external dependencies ### 7. Database & ORM (MEDIUM-HIGH) - `db-use-transactions` - Transaction management - `db-avoid-n-plus-one` - Avoid N+1 query problems - `db-use-migrations` - Use migrations for schema changes ### 8. API Design (MEDIUM) - `api-use-dto-serialization` - DTO and response serialization - `api-use-interceptors` - Cross-cutting concerns - `api-versioning` - API versioning strategies - `api-use-pipes` - Input transformation with pipes ### 9. Microservices (MEDIUM) - `micro-use-patterns` - Message and event patterns - `micro-use-health-checks` - Health checks for orchestration - `micro-use-queues` - Background job processing ### 10. DevOps & Deployment (LOW-MEDIUM) - `devops-use-config-module` - Environment configuration - `devops-use-logging` - Structured logging - `devops-graceful-shutdown` - Zero-downtime deployments ## How to Use Read individual rule files for detailed explanations and code examples: ``` rules/arch-avoid-circular-deps.md rules/security-validate-all-input.md rules/_sections.md ``` Each rule file contains: - Brief explanation of why it matters - Incorrect code example with explanation - Correct code example with explanation - Additional context and references ## Full Compiled Document For the complete guide with all rules expanded: `AGENTS.md` ================================================ FILE: .agents/skills/nestjs-best-practices/rules/api-use-dto-serialization.md ================================================ --- title: Use DTOs and Serialization for API Responses impact: MEDIUM impactDescription: Response DTOs prevent accidental data exposure and ensure consistency tags: api, dto, serialization, class-transformer --- ## Use DTOs and Serialization for API Responses Never return entity objects directly from controllers. Use response DTOs with class-transformer's `@Exclude()` and `@Expose()` decorators to control exactly what data is sent to clients. This prevents accidental exposure of sensitive fields and provides a stable API contract. **Incorrect (returning entities directly or manual spreading):** ```typescript // Return entities directly @Controller('users') export class UsersController { @Get(':id') async findOne(@Param('id') id: string): Promise { return this.usersService.findById(id); // Returns: { id, email, passwordHash, ssn, internalNotes, ... } // Exposes sensitive data! } } // Manual object spreading (error-prone) @Get(':id') async findOne(@Param('id') id: string) { const user = await this.usersService.findById(id); return { id: user.id, email: user.email, name: user.name, // Easy to forget to exclude sensitive fields // Hard to maintain across endpoints }; } ``` **Correct (use class-transformer with @Exclude and response DTOs):** ```typescript // Enable class-transformer globally async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector))); await app.listen(3000); } // Entity with serialization control @Entity() export class User { @PrimaryGeneratedColumn('uuid') id: string; @Column() email: string; @Column() name: string; @Column() @Exclude() // Never include in responses passwordHash: string; @Column({ nullable: true }) @Exclude() ssn: string; @Column({ default: false }) @Exclude({ toPlainOnly: true }) // Exclude from response, allow in requests isAdmin: boolean; @CreateDateColumn() createdAt: Date; @Column() @Exclude() internalNotes: string; } // Now returning entity is safe @Controller('users') export class UsersController { @Get(':id') async findOne(@Param('id') id: string): Promise { return this.usersService.findById(id); // Returns: { id, email, name, createdAt } // Sensitive fields excluded automatically } } // For different response shapes, use explicit DTOs export class UserResponseDto { @Expose() id: string; @Expose() email: string; @Expose() name: string; @Expose() @Transform(({ obj }) => obj.posts?.length || 0) postCount: number; constructor(partial: Partial) { Object.assign(this, partial); } } export class UserDetailResponseDto extends UserResponseDto { @Expose() createdAt: Date; @Expose() @Type(() => PostResponseDto) posts: PostResponseDto[]; } // Controller with explicit DTOs @Controller('users') export class UsersController { @Get() @SerializeOptions({ type: UserResponseDto }) async findAll(): Promise { const users = await this.usersService.findAll(); return users.map(u => plainToInstance(UserResponseDto, u)); } @Get(':id') async findOne(@Param('id') id: string): Promise { const user = await this.usersService.findByIdWithPosts(id); return plainToInstance(UserDetailResponseDto, user, { excludeExtraneousValues: true, }); } } // Groups for conditional serialization export class UserDto { @Expose() id: string; @Expose() name: string; @Expose({ groups: ['admin'] }) email: string; @Expose({ groups: ['admin'] }) createdAt: Date; @Expose({ groups: ['admin', 'owner'] }) settings: UserSettings; } @Controller('users') export class UsersController { @Get() @SerializeOptions({ groups: ['public'] }) async findAllPublic(): Promise { // Returns: { id, name } } @Get('admin') @UseGuards(AdminGuard) @SerializeOptions({ groups: ['admin'] }) async findAllAdmin(): Promise { // Returns: { id, name, email, createdAt } } @Get('me') @SerializeOptions({ groups: ['owner'] }) async getProfile(@CurrentUser() user: User): Promise { // Returns: { id, name, settings } } } ``` Reference: [NestJS Serialization](https://docs.nestjs.com/techniques/serialization) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/api-use-interceptors.md ================================================ --- title: Use Interceptors for Cross-Cutting Concerns impact: MEDIUM-HIGH impactDescription: Interceptors provide clean separation for cross-cutting logic tags: api, interceptors, logging, caching --- ## Use Interceptors for Cross-Cutting Concerns Interceptors can transform responses, add logging, handle caching, and measure performance without polluting your business logic. They wrap the route handler execution, giving you access to both the request and response streams. **Incorrect (logging and transformation in every method):** ```typescript // Logging in every controller method @Controller('users') export class UsersController { @Get() async findAll(): Promise { const start = Date.now(); this.logger.log('findAll called'); const users = await this.usersService.findAll(); this.logger.log(`findAll completed in ${Date.now() - start}ms`); return users; } @Get(':id') async findOne(@Param('id') id: string): Promise { const start = Date.now(); this.logger.log(`findOne called with id: ${id}`); const user = await this.usersService.findOne(id); this.logger.log(`findOne completed in ${Date.now() - start}ms`); return user; } // Repeated in every method! } // Manual response wrapping @Get() async findAll(): Promise<{ data: User[]; meta: Meta }> { const users = await this.usersService.findAll(); return { data: users, meta: { timestamp: new Date(), count: users.length }, }; } ``` **Correct (use interceptors for cross-cutting concerns):** ```typescript // Logging interceptor @Injectable() export class LoggingInterceptor implements NestInterceptor { private readonly logger = new Logger('HTTP'); intercept(context: ExecutionContext, next: CallHandler): Observable { const request = context.switchToHttp().getRequest(); const { method, url, body } = request; const now = Date.now(); return next.handle().pipe( tap({ next: (data) => { const response = context.switchToHttp().getResponse(); this.logger.log( `${method} ${url} ${response.statusCode} - ${Date.now() - now}ms`, ); }, error: (error) => { this.logger.error( `${method} ${url} ${error.status || 500} - ${Date.now() - now}ms`, error.stack, ); }, }), ); } } // Response transformation interceptor @Injectable() export class TransformInterceptor implements NestInterceptor> { intercept(context: ExecutionContext, next: CallHandler): Observable> { return next.handle().pipe( map((data) => ({ data, meta: { timestamp: new Date().toISOString(), path: context.switchToHttp().getRequest().url, }, })), ); } } // Timeout interceptor @Injectable() export class TimeoutInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable { return next.handle().pipe( timeout(5000), catchError((err) => { if (err instanceof TimeoutError) { throw new RequestTimeoutException('Request timed out'); } throw err; }), ); } } // Apply globally or per-controller @Module({ providers: [ { provide: APP_INTERCEPTOR, useClass: LoggingInterceptor }, { provide: APP_INTERCEPTOR, useClass: TransformInterceptor }, ], }) export class AppModule {} // Or per-controller @Controller('users') @UseInterceptors(LoggingInterceptor) export class UsersController { @Get() async findAll(): Promise { // Clean business logic only return this.usersService.findAll(); } } // Custom cache interceptor with TTL @Injectable() export class HttpCacheInterceptor implements NestInterceptor { constructor( private cacheManager: Cache, private reflector: Reflector, ) {} async intercept(context: ExecutionContext, next: CallHandler): Promise> { const request = context.switchToHttp().getRequest(); // Only cache GET requests if (request.method !== 'GET') { return next.handle(); } const cacheKey = this.generateKey(request); const ttl = this.reflector.get('cacheTTL', context.getHandler()) || 300; const cached = await this.cacheManager.get(cacheKey); if (cached) { return of(cached); } return next.handle().pipe( tap((response) => { this.cacheManager.set(cacheKey, response, ttl); }), ); } private generateKey(request: Request): string { return `cache:${request.url}:${JSON.stringify(request.query)}`; } } // Usage with custom TTL @Get() @SetMetadata('cacheTTL', 600) @UseInterceptors(HttpCacheInterceptor) async findAll(): Promise { return this.usersService.findAll(); } // Error mapping interceptor @Injectable() export class ErrorMappingInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable { return next.handle().pipe( catchError((error) => { if (error instanceof EntityNotFoundError) { throw new NotFoundException(error.message); } if (error instanceof QueryFailedError) { if (error.message.includes('duplicate')) { throw new ConflictException('Resource already exists'); } } throw error; }), ); } } ``` Reference: [NestJS Interceptors](https://docs.nestjs.com/interceptors) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/api-use-pipes.md ================================================ --- title: Use Pipes for Input Transformation impact: MEDIUM impactDescription: Pipes ensure clean, validated data reaches your handlers tags: api, pipes, validation, transformation --- ## Use Pipes for Input Transformation Use built-in pipes like `ParseIntPipe`, `ParseUUIDPipe`, and `DefaultValuePipe` for common transformations. Create custom pipes for business-specific transformations. Pipes separate validation/transformation logic from controllers. **Incorrect (manual type parsing in handlers):** ```typescript // Manual type parsing in handlers @Controller('users') export class UsersController { @Get(':id') async findOne(@Param('id') id: string): Promise { // Manual validation in every handler const uuid = id.trim(); if (!isUUID(uuid)) { throw new BadRequestException('Invalid UUID'); } return this.usersService.findOne(uuid); } @Get() async findAll( @Query('page') page: string, @Query('limit') limit: string, ): Promise { // Manual parsing and defaults const pageNum = parseInt(page) || 1; const limitNum = parseInt(limit) || 10; return this.usersService.findAll(pageNum, limitNum); } } // Type coercion without validation @Get() async search(@Query('price') price: string): Promise { const priceNum = +price; // NaN if invalid, no error return this.productsService.findByPrice(priceNum); } ``` **Correct (use built-in and custom pipes):** ```typescript // Use built-in pipes for common transformations @Controller('users') export class UsersController { @Get(':id') async findOne(@Param('id', ParseUUIDPipe) id: string): Promise { // id is guaranteed to be a valid UUID return this.usersService.findOne(id); } @Get() async findAll( @Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number, @Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number, ): Promise { // Automatic defaults and type conversion return this.usersService.findAll(page, limit); } @Get('by-status/:status') async findByStatus( @Param('status', new ParseEnumPipe(UserStatus)) status: UserStatus, ): Promise { return this.usersService.findByStatus(status); } } // Custom pipe for business logic @Injectable() export class ParseDatePipe implements PipeTransform { transform(value: string): Date { const date = new Date(value); if (isNaN(date.getTime())) { throw new BadRequestException('Invalid date format'); } return date; } } @Get('reports') async getReports( @Query('from', ParseDatePipe) from: Date, @Query('to', ParseDatePipe) to: Date, ): Promise { return this.reportsService.findBetween(from, to); } // Custom transformation pipes @Injectable() export class NormalizeEmailPipe implements PipeTransform { transform(value: string): string { if (!value) return value; return value.trim().toLowerCase(); } } // Parse comma-separated values @Injectable() export class ParseArrayPipe implements PipeTransform { transform(value: string): string[] { if (!value) return []; return value.split(',').map((v) => v.trim()).filter(Boolean); } } @Get('products') async findProducts( @Query('ids', ParseArrayPipe) ids: string[], @Query('email', NormalizeEmailPipe) email: string, ): Promise { // ids is already an array, email is normalized return this.productsService.findByIds(ids); } // Sanitize HTML input @Injectable() export class SanitizeHtmlPipe implements PipeTransform { transform(value: string): string { if (!value) return value; return sanitizeHtml(value, { allowedTags: [] }); } } // Global validation pipe with transformation app.useGlobalPipes( new ValidationPipe({ whitelist: true, // Strip non-DTO properties transform: true, // Auto-transform to DTO types transformOptions: { enableImplicitConversion: true, // Convert query strings to numbers }, forbidNonWhitelisted: true, // Throw on extra properties }), ); // DTO with transformation decorators export class FindProductsDto { @IsOptional() @Type(() => Number) @IsInt() @Min(1) page?: number = 1; @IsOptional() @Type(() => Number) @IsInt() @Min(1) @Max(100) limit?: number = 10; @IsOptional() @Transform(({ value }) => value?.toLowerCase()) @IsString() search?: string; @IsOptional() @Transform(({ value }) => value?.split(',')) @IsArray() @IsString({ each: true }) categories?: string[]; } @Get() async findAll(@Query() dto: FindProductsDto): Promise { // dto is already transformed and validated return this.productsService.findAll(dto); } // Pipe error customization @Injectable() export class CustomParseIntPipe extends ParseIntPipe { constructor() { super({ exceptionFactory: (error) => new BadRequestException(`${error} must be a valid integer`), }); } } // Or use options on built-in pipes @Get(':id') async findOne( @Param( 'id', new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE, exceptionFactory: () => new NotAcceptableException('ID must be numeric'), }), ) id: number, ): Promise { return this.itemsService.findOne(id); } ``` Reference: [NestJS Pipes](https://docs.nestjs.com/pipes) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/api-versioning.md ================================================ --- title: Use API Versioning for Breaking Changes impact: MEDIUM impactDescription: Versioning allows you to evolve APIs without breaking existing clients tags: api, versioning, breaking-changes, compatibility --- ## Use API Versioning for Breaking Changes Use NestJS built-in versioning when making breaking changes to your API. Choose a versioning strategy (URI, header, or media type) and apply it consistently. This allows old clients to continue working while new clients use updated endpoints. **Incorrect (breaking changes without versioning):** ```typescript // Breaking changes without versioning @Controller('users') export class UsersController { @Get(':id') async findOne(@Param('id') id: string): Promise { // Original response: { id, name, email } // Later changed to: { id, firstName, lastName, emailAddress } // Old clients break! return this.usersService.findOne(id); } } // Manual versioning in routes @Controller('v1/users') export class UsersV1Controller {} @Controller('v2/users') export class UsersV2Controller {} // Inconsistent, error-prone, hard to maintain ``` **Correct (use NestJS built-in versioning):** ```typescript // Enable versioning in main.ts async function bootstrap() { const app = await NestFactory.create(AppModule); // URI versioning: /v1/users, /v2/users app.enableVersioning({ type: VersioningType.URI, defaultVersion: '1', }); // Or header versioning: X-API-Version: 1 app.enableVersioning({ type: VersioningType.HEADER, header: 'X-API-Version', defaultVersion: '1', }); // Or media type: Accept: application/json;v=1 app.enableVersioning({ type: VersioningType.MEDIA_TYPE, key: 'v=', defaultVersion: '1', }); await app.listen(3000); } // Version-specific controllers @Controller('users') @Version('1') export class UsersV1Controller { @Get(':id') async findOne(@Param('id') id: string): Promise { const user = await this.usersService.findOne(id); // V1 response format return { id: user.id, name: user.name, email: user.email, }; } } @Controller('users') @Version('2') export class UsersV2Controller { @Get(':id') async findOne(@Param('id') id: string): Promise { const user = await this.usersService.findOne(id); // V2 response format with breaking changes return { id: user.id, firstName: user.firstName, lastName: user.lastName, emailAddress: user.email, createdAt: user.createdAt, }; } } // Per-route versioning - different versions for different routes @Controller('users') export class UsersController { @Get() @Version('1') findAllV1(): Promise { return this.usersService.findAllV1(); } @Get() @Version('2') findAllV2(): Promise { return this.usersService.findAllV2(); } @Get(':id') @Version(['1', '2']) // Same handler for multiple versions findOne(@Param('id') id: string): Promise { return this.usersService.findOne(id); } @Post() @Version(VERSION_NEUTRAL) // Available in all versions create(@Body() dto: CreateUserDto): Promise { return this.usersService.create(dto); } } // Shared service with version-specific logic @Injectable() export class UsersService { async findOne(id: string, version: string): Promise { const user = await this.repo.findOne({ where: { id } }); if (version === '1') { return this.toV1Response(user); } return this.toV2Response(user); } private toV1Response(user: User): UserV1Response { return { id: user.id, name: `${user.firstName} ${user.lastName}`, email: user.email, }; } private toV2Response(user: User): UserV2Response { return { id: user.id, firstName: user.firstName, lastName: user.lastName, emailAddress: user.email, createdAt: user.createdAt, }; } } // Controller extracts version @Controller('users') export class UsersController { @Get(':id') async findOne(@Param('id') id: string, @Headers('X-API-Version') version: string = '1'): Promise { return this.usersService.findOne(id, version); } } // Deprecation strategy - mark old versions as deprecated @Controller('users') @Version('1') @UseInterceptors(DeprecationInterceptor) export class UsersV1Controller { // All V1 routes will include deprecation warning } @Injectable() export class DeprecationInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable { const response = context.switchToHttp().getResponse(); response.setHeader('Deprecation', 'true'); response.setHeader('Sunset', 'Sat, 1 Jan 2025 00:00:00 GMT'); response.setHeader('Link', '; rel="successor-version"'); return next.handle(); } } ``` Reference: [NestJS Versioning](https://docs.nestjs.com/techniques/versioning) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/arch-avoid-circular-deps.md ================================================ --- title: Avoid Circular Dependencies impact: CRITICAL impactDescription: '#1 cause of runtime crashes' tags: architecture, modules, dependencies --- ## Avoid Circular Dependencies Circular dependencies occur when Module A imports Module B, and Module B imports Module A (directly or transitively). NestJS can sometimes resolve these through forward references, but they indicate architectural problems and should be avoided. This is the #1 cause of runtime crashes in NestJS applications. **Incorrect (circular module imports):** ```typescript // users.module.ts @Module({ imports: [OrdersModule], // Orders needs Users, Users needs Orders = circular providers: [UsersService], exports: [UsersService], }) export class UsersModule {} // orders.module.ts @Module({ imports: [UsersModule], // Circular dependency! providers: [OrdersService], exports: [OrdersService], }) export class OrdersModule {} ``` **Correct (extract shared logic or use events):** ```typescript // Option 1: Extract shared logic to a third module // shared.module.ts @Module({ providers: [SharedService], exports: [SharedService], }) export class SharedModule {} // users.module.ts @Module({ imports: [SharedModule], providers: [UsersService], }) export class UsersModule {} // orders.module.ts @Module({ imports: [SharedModule], providers: [OrdersService], }) export class OrdersModule {} // Option 2: Use events for decoupled communication // users.service.ts @Injectable() export class UsersService { constructor(private eventEmitter: EventEmitter2) {} async createUser(data: CreateUserDto) { const user = await this.userRepo.save(data); this.eventEmitter.emit('user.created', user); return user; } } // orders.service.ts @Injectable() export class OrdersService { @OnEvent('user.created') handleUserCreated(user: User) { // React to user creation without direct dependency } } ``` Reference: [NestJS Circular Dependency](https://docs.nestjs.com/fundamentals/circular-dependency) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/arch-feature-modules.md ================================================ --- title: Organize by Feature Modules impact: CRITICAL impactDescription: '3-5x faster onboarding and development' tags: architecture, modules, organization --- ## Organize by Feature Modules Organize your application into feature modules that encapsulate related functionality. Each feature module should be self-contained with its own controllers, services, entities, and DTOs. Avoid organizing by technical layer (all controllers together, all services together). This enables 3-5x faster onboarding and feature development. **Incorrect (technical layer organization):** ```typescript // Technical layer organization (anti-pattern) src/ ├── controllers/ │ ├── users.controller.ts │ ├── orders.controller.ts │ └── products.controller.ts ├── services/ │ ├── users.service.ts │ ├── orders.service.ts │ └── products.service.ts ├── entities/ │ ├── user.entity.ts │ ├── order.entity.ts │ └── product.entity.ts └── app.module.ts // Imports everything directly ``` **Correct (feature module organization):** ```typescript // Feature module organization src/ ├── users/ │ ├── dto/ │ │ ├── create-user.dto.ts │ │ └── update-user.dto.ts │ ├── entities/ │ │ └── user.entity.ts │ ├── users.controller.ts │ ├── users.service.ts │ ├── users.repository.ts │ └── users.module.ts ├── orders/ │ ├── dto/ │ ├── entities/ │ ├── orders.controller.ts │ ├── orders.service.ts │ └── orders.module.ts ├── shared/ │ ├── guards/ │ ├── interceptors/ │ ├── filters/ │ └── shared.module.ts └── app.module.ts // users.module.ts @Module({ imports: [TypeOrmModule.forFeature([User])], controllers: [UsersController], providers: [UsersService, UsersRepository], exports: [UsersService], // Only export what others need }) export class UsersModule {} // app.module.ts @Module({ imports: [ ConfigModule.forRoot(), TypeOrmModule.forRoot(), UsersModule, OrdersModule, SharedModule, ], }) export class AppModule {} ``` Reference: [NestJS Modules](https://docs.nestjs.com/modules) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/arch-module-sharing.md ================================================ --- title: Use Proper Module Sharing Patterns impact: CRITICAL impactDescription: Prevents duplicate instances, memory leaks, and state inconsistency tags: architecture, modules, sharing, exports --- ## Use Proper Module Sharing Patterns NestJS modules are singletons by default. When a service is properly exported from a module and that module is imported elsewhere, the same instance is shared. However, providing a service in multiple modules creates separate instances, leading to memory waste, state inconsistency, and confusing behavior. Always encapsulate services in dedicated modules, export them explicitly, and import the module where needed. **Incorrect (service provided in multiple modules):** ```typescript // StorageService provided directly in multiple modules - WRONG // storage.service.ts @Injectable() export class StorageService { private cache = new Map(); // Each instance has separate state! store(key: string, value: any) { this.cache.set(key, value); } } // app.module.ts @Module({ providers: [StorageService], // Instance #1 controllers: [AppController], }) export class AppModule {} // videos.module.ts @Module({ providers: [StorageService], // Instance #2 - different from AppModule! controllers: [VideosController], }) export class VideosModule {} // Problems: // 1. Two separate StorageService instances exist // 2. cache.set() in VideosModule doesn't affect AppModule's cache // 3. Memory wasted on duplicate instances // 4. Debugging nightmares when state doesn't sync ``` **Correct (dedicated module with exports):** ```typescript // storage/storage.module.ts @Module({ providers: [StorageService], exports: [StorageService], // Make available to importers }) export class StorageModule {} // videos/videos.module.ts @Module({ imports: [StorageModule], // Import the module, not the service controllers: [VideosController], providers: [VideosService], }) export class VideosModule {} // channels/channels.module.ts @Module({ imports: [StorageModule], // Same instance shared controllers: [ChannelsController], providers: [ChannelsService], }) export class ChannelsModule {} // app.module.ts @Module({ imports: [ StorageModule, // Only if AppModule itself needs StorageService VideosModule, ChannelsModule, ], }) export class AppModule {} // Now all modules share the SAME StorageService instance ``` **When to use @Global() (sparingly):** ```typescript // ONLY for truly cross-cutting concerns @Global() @Module({ providers: [ConfigService, LoggerService], exports: [ConfigService, LoggerService], }) export class CoreModule {} // Import once in AppModule @Module({ imports: [CoreModule], // Registered globally, available everywhere }) export class AppModule {} // Other modules don't need to import CoreModule @Module({ controllers: [UsersController], providers: [UsersService], // Can inject ConfigService without importing }) export class UsersModule {} // WARNING: Don't make everything global! // - Hides dependencies (can't see what a module needs from imports) // - Makes testing harder // - Reserve for: config, logging, database connections ``` **Module re-exporting pattern:** ```typescript // common.module.ts - shared utilities @Module({ providers: [DateService, ValidationService], exports: [DateService, ValidationService], }) export class CommonModule {} // core.module.ts - re-exports common for convenience @Module({ imports: [CommonModule, DatabaseModule], exports: [CommonModule, DatabaseModule], // Re-export for consumers }) export class CoreModule {} // feature.module.ts - imports CoreModule, gets both @Module({ imports: [CoreModule], // Gets CommonModule + DatabaseModule controllers: [FeatureController], }) export class FeatureModule {} ``` Reference: [NestJS Modules](https://docs.nestjs.com/modules#shared-modules) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/arch-single-responsibility.md ================================================ --- title: Single Responsibility for Services impact: CRITICAL impactDescription: '40%+ improvement in testability' tags: architecture, services, single-responsibility --- ## Single Responsibility for Services Each service should have a single, well-defined responsibility. Avoid "god services" that handle multiple unrelated concerns. If a service name includes "And" or handles more than one domain concept, it likely violates single responsibility. This reduces complexity and improves testability by 40%+. **Incorrect (god service anti-pattern):** ```typescript // God service anti-pattern @Injectable() export class UserAndOrderService { constructor( private userRepo: UserRepository, private orderRepo: OrderRepository, private mailer: MailService, private payment: PaymentService, ) {} async createUser(dto: CreateUserDto) { const user = await this.userRepo.save(dto); await this.mailer.sendWelcome(user); return user; } async createOrder(userId: string, dto: CreateOrderDto) { const order = await this.orderRepo.save({ userId, ...dto }); await this.payment.charge(order); await this.mailer.sendOrderConfirmation(order); return order; } async calculateOrderStats(userId: string) { // Stats logic mixed in } async validatePayment(orderId: string) { // Payment logic mixed in } } ``` **Correct (focused services with single responsibility):** ```typescript // Focused services with single responsibility @Injectable() export class UsersService { constructor(private userRepo: UserRepository) {} async create(dto: CreateUserDto): Promise { return this.userRepo.save(dto); } async findById(id: string): Promise { return this.userRepo.findOneOrFail({ where: { id } }); } } @Injectable() export class OrdersService { constructor(private orderRepo: OrderRepository) {} async create(userId: string, dto: CreateOrderDto): Promise { return this.orderRepo.save({ userId, ...dto }); } async findByUser(userId: string): Promise { return this.orderRepo.find({ where: { userId } }); } } @Injectable() export class OrderStatsService { constructor(private orderRepo: OrderRepository) {} async calculateForUser(userId: string): Promise { // Focused stats calculation } } // Orchestration in controller or dedicated orchestrator @Controller('orders') export class OrdersController { constructor( private orders: OrdersService, private payment: PaymentService, private notifications: NotificationService, ) {} @Post() async create(@CurrentUser() user: User, @Body() dto: CreateOrderDto) { const order = await this.orders.create(user.id, dto); await this.payment.charge(order); await this.notifications.sendOrderConfirmation(order); return order; } } ``` Reference: [NestJS Providers](https://docs.nestjs.com/providers) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/arch-use-events.md ================================================ --- title: Use Event-Driven Architecture for Decoupling impact: MEDIUM-HIGH impactDescription: Enables async processing and modularity tags: architecture, events, decoupling --- ## Use Event-Driven Architecture for Decoupling Use `@nestjs/event-emitter` for intra-service events and message brokers for inter-service communication. Events allow modules to react to changes without direct dependencies, improving modularity and enabling async processing. **Incorrect (direct service coupling):** ```typescript // Direct service coupling @Injectable() export class OrdersService { constructor( private inventoryService: InventoryService, private emailService: EmailService, private analyticsService: AnalyticsService, private notificationService: NotificationService, private loyaltyService: LoyaltyService, ) {} async createOrder(dto: CreateOrderDto): Promise { const order = await this.repo.save(dto); // Tight coupling - OrdersService knows about all consumers await this.inventoryService.reserve(order.items); await this.emailService.sendConfirmation(order); await this.analyticsService.track('order_created', order); await this.notificationService.push(order.userId, 'Order placed'); await this.loyaltyService.addPoints(order.userId, order.total); // Adding new behavior requires modifying this service return order; } } ``` **Correct (event-driven decoupling):** ```typescript // Use EventEmitter for decoupling import { EventEmitter2 } from '@nestjs/event-emitter'; // Define event export class OrderCreatedEvent { constructor( public readonly orderId: string, public readonly userId: string, public readonly items: OrderItem[], public readonly total: number, ) {} } // Service emits events @Injectable() export class OrdersService { constructor( private eventEmitter: EventEmitter2, private repo: Repository, ) {} async createOrder(dto: CreateOrderDto): Promise { const order = await this.repo.save(dto); // Emit event - no knowledge of consumers this.eventEmitter.emit('order.created', new OrderCreatedEvent(order.id, order.userId, order.items, order.total)); return order; } } // Listeners in separate modules @Injectable() export class InventoryListener { @OnEvent('order.created') async handleOrderCreated(event: OrderCreatedEvent): Promise { await this.inventoryService.reserve(event.items); } } @Injectable() export class EmailListener { @OnEvent('order.created') async handleOrderCreated(event: OrderCreatedEvent): Promise { await this.emailService.sendConfirmation(event.orderId); } } @Injectable() export class AnalyticsListener { @OnEvent('order.created') async handleOrderCreated(event: OrderCreatedEvent): Promise { await this.analyticsService.track('order_created', { orderId: event.orderId, total: event.total, }); } } ``` Reference: [NestJS Events](https://docs.nestjs.com/techniques/events) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/arch-use-repository-pattern.md ================================================ --- title: Use Repository Pattern for Data Access impact: HIGH impactDescription: Decouples business logic from database tags: architecture, repository, data-access --- ## Use Repository Pattern for Data Access Create custom repositories to encapsulate complex queries and database logic. This keeps services focused on business logic, makes testing easier with mock repositories, and allows changing database implementations without affecting business code. **Incorrect (complex queries in services):** ```typescript // Complex queries in services @Injectable() export class UsersService { constructor(@InjectRepository(User) private repo: Repository) {} async findActiveWithOrders(minOrders: number): Promise { // Complex query logic mixed with business logic return this.repo .createQueryBuilder('user') .leftJoinAndSelect('user.orders', 'order') .where('user.isActive = :active', { active: true }) .andWhere('user.deletedAt IS NULL') .groupBy('user.id') .having('COUNT(order.id) >= :min', { min: minOrders }) .orderBy('user.createdAt', 'DESC') .getMany(); } // Service becomes bloated with query logic } ``` **Correct (custom repository with encapsulated queries):** ```typescript // Custom repository with encapsulated queries @Injectable() export class UsersRepository { constructor(@InjectRepository(User) private repo: Repository) {} async findById(id: string): Promise { return this.repo.findOne({ where: { id } }); } async findByEmail(email: string): Promise { return this.repo.findOne({ where: { email } }); } async findActiveWithMinOrders(minOrders: number): Promise { return this.repo .createQueryBuilder('user') .leftJoinAndSelect('user.orders', 'order') .where('user.isActive = :active', { active: true }) .andWhere('user.deletedAt IS NULL') .groupBy('user.id') .having('COUNT(order.id) >= :min', { min: minOrders }) .orderBy('user.createdAt', 'DESC') .getMany(); } async save(user: User): Promise { return this.repo.save(user); } } // Clean service with business logic only @Injectable() export class UsersService { constructor(private usersRepo: UsersRepository) {} async getActiveUsersWithOrders(): Promise { return this.usersRepo.findActiveWithMinOrders(1); } async create(dto: CreateUserDto): Promise { const existing = await this.usersRepo.findByEmail(dto.email); if (existing) { throw new ConflictException('Email already registered'); } const user = new User(); user.email = dto.email; user.name = dto.name; return this.usersRepo.save(user); } } ``` Reference: [Repository Pattern](https://martinfowler.com/eaaCatalog/repository.html) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/db-avoid-n-plus-one.md ================================================ --- title: Avoid N+1 Query Problems impact: HIGH impactDescription: N+1 queries are one of the most common performance killers tags: database, n-plus-one, queries, performance --- ## Avoid N+1 Query Problems N+1 queries occur when you fetch a list of entities, then make an additional query for each entity to load related data. Use eager loading with `relations`, query builder joins, or DataLoader to batch queries efficiently. **Incorrect (lazy loading in loops causes N+1):** ```typescript // Lazy loading in loops causes N+1 @Injectable() export class OrdersService { async getOrdersWithItems(userId: string): Promise { const orders = await this.orderRepo.find({ where: { userId } }); // 1 query for orders for (const order of orders) { // N additional queries - one per order! order.items = await this.itemRepo.find({ where: { orderId: order.id } }); } return orders; } } // Accessing lazy relations without loading @Controller('users') export class UsersController { @Get() async findAll(): Promise { const users = await this.userRepo.find(); // If User.posts is lazy-loaded, serializing triggers N queries return users; // Each user.posts access = 1 query } } ``` **Correct (use relations for eager loading):** ```typescript // Use relations option for eager loading @Injectable() export class OrdersService { async getOrdersWithItems(userId: string): Promise { // Single query with JOIN return this.orderRepo.find({ where: { userId }, relations: ['items', 'items.product'], }); } } // Use QueryBuilder for complex joins @Injectable() export class UsersService { async getUsersWithPostCounts(): Promise { return this.userRepo .createQueryBuilder('user') .leftJoin('user.posts', 'post') .select('user.id', 'id') .addSelect('user.name', 'name') .addSelect('COUNT(post.id)', 'postCount') .groupBy('user.id') .getRawMany(); } async getActiveUsersWithPosts(): Promise { return this.userRepo .createQueryBuilder('user') .leftJoinAndSelect('user.posts', 'post') .leftJoinAndSelect('post.comments', 'comment') .where('user.isActive = :active', { active: true }) .andWhere('post.status = :status', { status: 'published' }) .getMany(); } } // Use find options for specific fields async getOrderSummaries(userId: string): Promise { return this.orderRepo.find({ where: { userId }, relations: ['items'], select: { id: true, total: true, status: true, items: { id: true, quantity: true, price: true, }, }, }); } // Use DataLoader for GraphQL to batch and cache queries import DataLoader from 'dataloader'; @Injectable({ scope: Scope.REQUEST }) export class PostsLoader { constructor(private postsService: PostsService) {} readonly batchPosts = new DataLoader(async (userIds) => { // Single query for all users' posts const posts = await this.postsService.findByUserIds([...userIds]); // Group by userId const postsMap = new Map(); for (const post of posts) { const userPosts = postsMap.get(post.userId) || []; userPosts.push(post); postsMap.set(post.userId, userPosts); } // Return in same order as input return userIds.map((id) => postsMap.get(id) || []); }); } // In resolver @ResolveField() async posts(@Parent() user: User): Promise { // DataLoader batches multiple calls into single query return this.postsLoader.batchPosts.load(user.id); } // Enable query logging in development to detect N+1 TypeOrmModule.forRoot({ logging: ['query', 'error'], logger: 'advanced-console', }); ``` Reference: [TypeORM Relations](https://typeorm.io/relations) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/db-use-migrations.md ================================================ --- title: Use Database Migrations impact: HIGH impactDescription: Enables safe, repeatable database schema changes tags: database, migrations, typeorm, schema --- ## Use Database Migrations Never use `synchronize: true` in production. Use migrations for all schema changes. Migrations provide version control for your database, enable safe rollbacks, and ensure consistency across all environments. **Incorrect (using synchronize or manual SQL):** ```typescript // Use synchronize in production TypeOrmModule.forRoot({ type: 'postgres', synchronize: true, // DANGEROUS in production! // Can drop columns, tables, or data }); // Manual SQL in production @Injectable() export class DatabaseService { async addColumn(): Promise { await this.dataSource.query('ALTER TABLE users ADD COLUMN age INT'); // No version control, no rollback, inconsistent across envs } } // Modify entities without migration @Entity() export class User { @Column() email: string; @Column() // Added without migration newField: string; // Will crash in production if synchronize is false } ``` **Correct (use migrations for all schema changes):** ```typescript // Configure TypeORM for migrations // data-source.ts export const dataSource = new DataSource({ type: 'postgres', host: process.env.DB_HOST, port: parseInt(process.env.DB_PORT), username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, entities: ['dist/**/*.entity.js'], migrations: ['dist/migrations/*.js'], synchronize: false, // Always false in production migrationsRun: true, // Run migrations on startup }); // app.module.ts TypeOrmModule.forRootAsync({ inject: [ConfigService], useFactory: (config: ConfigService) => ({ type: 'postgres', host: config.get('DB_HOST'), synchronize: config.get('NODE_ENV') === 'development', // Only in dev migrations: ['dist/migrations/*.js'], migrationsRun: true, }), }); // migrations/1705312800000-AddUserAge.ts import { MigrationInterface, QueryRunner } from 'typeorm'; export class AddUserAge1705312800000 implements MigrationInterface { name = 'AddUserAge1705312800000'; public async up(queryRunner: QueryRunner): Promise { // Add column with default to handle existing rows await queryRunner.query(` ALTER TABLE "users" ADD "age" integer DEFAULT 0 `); // Add index for frequently queried columns await queryRunner.query(` CREATE INDEX "IDX_users_age" ON "users" ("age") `); } public async down(queryRunner: QueryRunner): Promise { // Always implement down for rollback await queryRunner.query(`DROP INDEX "IDX_users_age"`); await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "age"`); } } // Safe column rename (two-step) export class RenameNameToFullName1705312900000 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { // Step 1: Add new column await queryRunner.query(` ALTER TABLE "users" ADD "full_name" varchar(255) `); // Step 2: Copy data await queryRunner.query(` UPDATE "users" SET "full_name" = "name" `); // Step 3: Add NOT NULL constraint await queryRunner.query(` ALTER TABLE "users" ALTER COLUMN "full_name" SET NOT NULL `); // Step 4: Drop old column (after verifying app works) await queryRunner.query(` ALTER TABLE "users" DROP COLUMN "name" `); } public async down(queryRunner: QueryRunner): Promise { await queryRunner.query(`ALTER TABLE "users" ADD "name" varchar(255)`); await queryRunner.query(`UPDATE "users" SET "name" = "full_name"`); await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "full_name"`); } } ``` Reference: [TypeORM Migrations](https://typeorm.io/migrations) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/db-use-transactions.md ================================================ --- title: Use Transactions for Multi-Step Operations impact: HIGH impactDescription: Ensures data consistency in multi-step operations tags: database, transactions, typeorm, consistency --- ## Use Transactions for Multi-Step Operations When multiple database operations must succeed or fail together, wrap them in a transaction. This prevents partial updates that leave your data in an inconsistent state. Use TypeORM's transaction APIs or the DataSource query runner for complex scenarios. **Incorrect (multiple saves without transaction):** ```typescript // Multiple saves without transaction @Injectable() export class OrdersService { async createOrder(userId: string, items: OrderItem[]): Promise { // If any step fails, data is inconsistent const order = await this.orderRepo.save({ userId, status: 'pending' }); for (const item of items) { await this.orderItemRepo.save({ orderId: order.id, ...item }); await this.inventoryRepo.decrement({ productId: item.productId }, 'stock', item.quantity); } await this.paymentService.charge(order.id); // If payment fails, order and inventory are already modified! return order; } } ``` **Correct (use DataSource.transaction for automatic rollback):** ```typescript // Use DataSource.transaction() for automatic rollback @Injectable() export class OrdersService { constructor(private dataSource: DataSource) {} async createOrder(userId: string, items: OrderItem[]): Promise { return this.dataSource.transaction(async manager => { // All operations use the same transactional manager const order = await manager.save(Order, { userId, status: 'pending' }); for (const item of items) { await manager.save(OrderItem, { orderId: order.id, ...item }); await manager.decrement(Inventory, { productId: item.productId }, 'stock', item.quantity); } // If this throws, everything rolls back await this.paymentService.chargeWithManager(manager, order.id); return order; }); } } // QueryRunner for manual transaction control @Injectable() export class TransferService { constructor(private dataSource: DataSource) {} async transfer(fromId: string, toId: string, amount: number): Promise { const queryRunner = this.dataSource.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); try { // Debit source account await queryRunner.manager.decrement(Account, { id: fromId }, 'balance', amount); // Verify sufficient funds const source = await queryRunner.manager.findOne(Account, { where: { id: fromId }, }); if (source.balance < 0) { throw new BadRequestException('Insufficient funds'); } // Credit destination account await queryRunner.manager.increment(Account, { id: toId }, 'balance', amount); // Log the transaction await queryRunner.manager.save(TransactionLog, { fromId, toId, amount, timestamp: new Date(), }); await queryRunner.commitTransaction(); } catch (error) { await queryRunner.rollbackTransaction(); throw error; } finally { await queryRunner.release(); } } } // Repository method with transaction support @Injectable() export class UsersRepository { constructor( @InjectRepository(User) private repo: Repository, private dataSource: DataSource, ) {} async createWithProfile(userData: CreateUserDto, profileData: CreateProfileDto): Promise { return this.dataSource.transaction(async manager => { const user = await manager.save(User, userData); await manager.save(Profile, { ...profileData, userId: user.id }); return user; }); } } ``` Reference: [TypeORM Transactions](https://typeorm.io/transactions) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/devops-graceful-shutdown.md ================================================ --- title: Implement Graceful Shutdown impact: MEDIUM-HIGH impactDescription: Proper shutdown handling ensures zero-downtime deployments tags: devops, graceful-shutdown, lifecycle, kubernetes --- ## Implement Graceful Shutdown Handle SIGTERM and SIGINT signals to gracefully shutdown your NestJS application. Stop accepting new requests, wait for in-flight requests to complete, close database connections, and clean up resources. This prevents data loss and connection errors during deployments. **Incorrect (ignoring shutdown signals):** ```typescript // Ignore shutdown signals async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); // App crashes immediately on SIGTERM // In-flight requests fail // Database connections are abruptly closed } // Long-running tasks without cancellation @Injectable() export class ProcessingService { async processLargeFile(file: File): Promise { // No way to interrupt this during shutdown for (let i = 0; i < file.chunks.length; i++) { await this.processChunk(file.chunks[i]); // May run for minutes, blocking shutdown } } } ``` **Correct (enable shutdown hooks and handle cleanup):** ```typescript // Enable shutdown hooks in main.ts async function bootstrap() { const app = await NestFactory.create(AppModule); // Enable shutdown hooks app.enableShutdownHooks(); // Optional: Add timeout for forced shutdown const server = await app.listen(3000); server.setTimeout(30000); // 30 second timeout // Handle graceful shutdown const signals = ['SIGTERM', 'SIGINT']; signals.forEach(signal => { process.on(signal, async () => { console.log(`Received ${signal}, starting graceful shutdown...`); // Stop accepting new connections server.close(async () => { console.log('HTTP server closed'); await app.close(); process.exit(0); }); // Force exit after timeout setTimeout(() => { console.error('Forced shutdown after timeout'); process.exit(1); }, 30000); }); }); } // Lifecycle hooks for cleanup @Injectable() export class DatabaseService implements OnApplicationShutdown { private readonly connections: Connection[] = []; async onApplicationShutdown(signal?: string): Promise { console.log(`Database service shutting down on ${signal}`); // Close all connections gracefully await Promise.all(this.connections.map(conn => conn.close())); console.log('All database connections closed'); } } // Queue processor with graceful shutdown @Injectable() export class QueueService implements OnApplicationShutdown, OnModuleDestroy { private isShuttingDown = false; onModuleDestroy(): void { this.isShuttingDown = true; } async onApplicationShutdown(): Promise { // Wait for current jobs to complete await this.queue.close(); } async processJob(job: Job): Promise { if (this.isShuttingDown) { throw new Error('Service is shutting down'); } await this.doWork(job); } } // WebSocket gateway cleanup @WebSocketGateway() export class EventsGateway implements OnApplicationShutdown { @WebSocketServer() server: Server; async onApplicationShutdown(): Promise { // Notify all connected clients this.server.emit('shutdown', { message: 'Server is shutting down' }); // Close all connections this.server.disconnectSockets(); } } // Health check integration @Injectable() export class ShutdownService { private isShuttingDown = false; startShutdown(): void { this.isShuttingDown = true; } isShutdown(): boolean { return this.isShuttingDown; } } @Controller('health') export class HealthController { constructor(private shutdownService: ShutdownService) {} @Get('ready') @HealthCheck() readiness(): Promise { // Return 503 during shutdown - k8s stops sending traffic if (this.shutdownService.isShutdown()) { throw new ServiceUnavailableException('Shutting down'); } return this.health.check([() => this.db.pingCheck('database')]); } } // Integrate with shutdown @Injectable() export class AppShutdownService implements OnApplicationShutdown { constructor(private shutdownService: ShutdownService) {} async onApplicationShutdown(): Promise { // Mark as unhealthy first this.shutdownService.startShutdown(); // Wait for k8s to update endpoints await this.sleep(5000); // Then proceed with cleanup } } // Request tracking for in-flight requests @Injectable() export class RequestTracker implements NestMiddleware, OnApplicationShutdown { private activeRequests = 0; private isShuttingDown = false; private shutdownPromise: Promise | null = null; private resolveShutdown: (() => void) | null = null; use(req: Request, res: Response, next: NextFunction): void { if (this.isShuttingDown) { res.status(503).send('Service Unavailable'); return; } this.activeRequests++; res.on('finish', () => { this.activeRequests--; if (this.isShuttingDown && this.activeRequests === 0 && this.resolveShutdown) { this.resolveShutdown(); } }); next(); } async onApplicationShutdown(): Promise { this.isShuttingDown = true; if (this.activeRequests > 0) { console.log(`Waiting for ${this.activeRequests} requests to complete`); this.shutdownPromise = new Promise(resolve => { this.resolveShutdown = resolve; }); // Wait with timeout await Promise.race([this.shutdownPromise, new Promise(resolve => setTimeout(resolve, 30000))]); } console.log('All requests completed'); } } ``` Reference: [NestJS Lifecycle Events](https://docs.nestjs.com/fundamentals/lifecycle-events) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/devops-use-config-module.md ================================================ --- title: Use ConfigModule for Environment Configuration impact: LOW-MEDIUM impactDescription: Proper configuration prevents deployment failures tags: devops, configuration, environment, validation --- ## Use ConfigModule for Environment Configuration Use `@nestjs/config` for environment-based configuration. Validate configuration at startup to fail fast on misconfigurations. Use namespaced configuration for organization and type safety. **Incorrect (accessing process.env directly):** ```typescript // Access process.env directly @Injectable() export class DatabaseService { constructor() { // No validation, can fail at runtime this.connection = new Pool({ host: process.env.DB_HOST, port: parseInt(process.env.DB_PORT), // NaN if missing password: process.env.DB_PASSWORD, // undefined if missing }); } } // Scattered env access @Injectable() export class EmailService { sendEmail() { // Different services access env differently const apiKey = process.env.SENDGRID_API_KEY || 'default'; // Typos go unnoticed: process.env.SENDGRID_API_KY } } ``` **Correct (use @nestjs/config with validation):** ```typescript // Setup validated configuration import { ConfigModule, ConfigService, registerAs } from '@nestjs/config'; import * as Joi from 'joi'; // config/database.config.ts export const databaseConfig = registerAs('database', () => ({ host: process.env.DB_HOST, port: parseInt(process.env.DB_PORT, 10), username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, })); // config/app.config.ts export const appConfig = registerAs('app', () => ({ port: parseInt(process.env.PORT, 10) || 3000, environment: process.env.NODE_ENV || 'development', apiPrefix: process.env.API_PREFIX || 'api', })); // config/validation.schema.ts export const validationSchema = Joi.object({ NODE_ENV: Joi.string().valid('development', 'production', 'test').default('development'), PORT: Joi.number().default(3000), DB_HOST: Joi.string().required(), DB_PORT: Joi.number().default(5432), DB_USERNAME: Joi.string().required(), DB_PASSWORD: Joi.string().required(), DB_NAME: Joi.string().required(), JWT_SECRET: Joi.string().min(32).required(), REDIS_URL: Joi.string().uri().required(), }); // app.module.ts @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, // Available everywhere without importing load: [databaseConfig, appConfig], validationSchema, validationOptions: { abortEarly: true, // Stop on first error allowUnknown: true, // Allow other env vars }, }), TypeOrmModule.forRootAsync({ inject: [ConfigService], useFactory: (config: ConfigService) => ({ type: 'postgres', host: config.get('database.host'), port: config.get('database.port'), username: config.get('database.username'), password: config.get('database.password'), database: config.get('database.database'), autoLoadEntities: true, }), }), ], }) export class AppModule {} // Type-safe configuration access export interface AppConfig { port: number; environment: 'development' | 'production' | 'test'; apiPrefix: string; } export interface DatabaseConfig { host: string; port: number; username: string; password: string; database: string; } // Type-safe access @Injectable() export class AppService { constructor(private config: ConfigService) {} getPort(): number { // Type-safe with generic return this.config.get('app.port'); } getDatabaseConfig(): DatabaseConfig { return this.config.get('database'); } } // Inject namespaced config directly @Injectable() export class DatabaseService { constructor( @Inject(databaseConfig.KEY) private dbConfig: ConfigType, ) { // Full type inference! const host = this.dbConfig.host; // string const port = this.dbConfig.port; // number } } // Environment files support ConfigModule.forRoot({ envFilePath: [`.env.${process.env.NODE_ENV}.local`, `.env.${process.env.NODE_ENV}`, '.env.local', '.env'], }); // .env.development // DB_HOST=localhost // DB_PORT=5432 // .env.production // DB_HOST=prod-db.example.com // DB_PORT=5432 ``` Reference: [NestJS Configuration](https://docs.nestjs.com/techniques/configuration) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/devops-use-logging.md ================================================ --- title: Use Structured Logging impact: MEDIUM-HIGH impactDescription: Structured logging enables effective debugging and monitoring tags: devops, logging, structured-logs, pino --- ## Use Structured Logging Use NestJS Logger with structured JSON output in production. Include contextual information (request ID, user ID, operation) to trace requests across services. Avoid console.log and implement proper log levels. **Incorrect (using console.log in production):** ```typescript // Use console.log in production @Injectable() export class UsersService { async createUser(dto: CreateUserDto): Promise { console.log('Creating user:', dto); // Not structured, no levels, lost in production logs try { const user = await this.repo.save(dto); console.log('User created:', user.id); return user; } catch (error) { console.log('Error:', error); // Using log for errors throw error; } } } // Log sensitive data console.log('Login attempt:', { email, password }); // SECURITY RISK! // Inconsistent log format logger.log('User ' + userId + ' created at ' + new Date()); // Hard to parse, no structure ``` **Correct (use structured logging with context):** ```typescript // Configure logger in main.ts async function bootstrap() { const app = await NestFactory.create(AppModule, { logger: process.env.NODE_ENV === 'production' ? ['error', 'warn', 'log'] : ['error', 'warn', 'log', 'debug', 'verbose'], }); } // Use NestJS Logger with context @Injectable() export class UsersService { private readonly logger = new Logger(UsersService.name); async createUser(dto: CreateUserDto): Promise { this.logger.log('Creating user', { email: dto.email }); try { const user = await this.repo.save(dto); this.logger.log('User created', { userId: user.id }); return user; } catch (error) { this.logger.error('Failed to create user', error.stack, { email: dto.email, }); throw error; } } } // Custom logger for JSON output @Injectable() export class JsonLogger implements LoggerService { log(message: string, context?: object): void { console.log( JSON.stringify({ level: 'info', timestamp: new Date().toISOString(), message, ...context, }), ); } error(message: string, trace?: string, context?: object): void { console.error( JSON.stringify({ level: 'error', timestamp: new Date().toISOString(), message, trace, ...context, }), ); } warn(message: string, context?: object): void { console.warn( JSON.stringify({ level: 'warn', timestamp: new Date().toISOString(), message, ...context, }), ); } debug(message: string, context?: object): void { console.debug( JSON.stringify({ level: 'debug', timestamp: new Date().toISOString(), message, ...context, }), ); } } // Request context logging with ClsModule import { ClsModule, ClsService } from 'nestjs-cls'; @Module({ imports: [ ClsModule.forRoot({ global: true, middleware: { mount: true, generateId: true, }, }), ], }) export class AppModule {} // Middleware to set request context @Injectable() export class RequestContextMiddleware implements NestMiddleware { constructor(private cls: ClsService) {} use(req: Request, res: Response, next: NextFunction): void { const requestId = req.headers['x-request-id'] || randomUUID(); this.cls.set('requestId', requestId); this.cls.set('userId', req.user?.id); res.setHeader('x-request-id', requestId); next(); } } // Logger that includes request context @Injectable() export class ContextLogger { constructor(private cls: ClsService) {} log(message: string, data?: object): void { console.log( JSON.stringify({ level: 'info', timestamp: new Date().toISOString(), requestId: this.cls.get('requestId'), userId: this.cls.get('userId'), message, ...data, }), ); } error(message: string, error: Error, data?: object): void { console.error( JSON.stringify({ level: 'error', timestamp: new Date().toISOString(), requestId: this.cls.get('requestId'), userId: this.cls.get('userId'), message, error: error.message, stack: error.stack, ...data, }), ); } } // Pino integration for high-performance logging import { LoggerModule } from 'nestjs-pino'; @Module({ imports: [ LoggerModule.forRoot({ pinoHttp: { level: process.env.NODE_ENV === 'production' ? 'info' : 'debug', transport: process.env.NODE_ENV !== 'production' ? { target: 'pino-pretty' } : undefined, redact: ['req.headers.authorization', 'req.body.password'], serializers: { req: req => ({ method: req.method, url: req.url, query: req.query, }), res: res => ({ statusCode: res.statusCode, }), }, }, }), ], }) export class AppModule {} // Usage with Pino @Injectable() export class UsersService { constructor(private logger: PinoLogger) { this.logger.setContext(UsersService.name); } async findOne(id: string): Promise { this.logger.info({ userId: id }, 'Finding user'); // Pino uses first arg for data, second for message } } ``` Reference: [NestJS Logger](https://docs.nestjs.com/techniques/logger) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/di-avoid-service-locator.md ================================================ --- title: Avoid Service Locator Anti-Pattern impact: HIGH impactDescription: Hides dependencies and breaks testability tags: dependency-injection, anti-patterns, testing --- ## Avoid Service Locator Anti-Pattern Avoid using `ModuleRef.get()` or global containers to resolve dependencies at runtime. This hides dependencies, makes code harder to test, and breaks the benefits of dependency injection. Use constructor injection instead. **Incorrect (service locator anti-pattern):** ```typescript // Use ModuleRef to get dependencies dynamically @Injectable() export class OrdersService { constructor(private moduleRef: ModuleRef) {} async createOrder(dto: CreateOrderDto): Promise { // Dependencies are hidden - not visible in constructor const usersService = this.moduleRef.get(UsersService); const inventoryService = this.moduleRef.get(InventoryService); const paymentService = this.moduleRef.get(PaymentService); const user = await usersService.findOne(dto.userId); // ... rest of logic } } // Global singleton container class ServiceContainer { private static instance: ServiceContainer; private services = new Map(); static getInstance(): ServiceContainer { if (!this.instance) { this.instance = new ServiceContainer(); } return this.instance; } get(key: string): T { return this.services.get(key); } } ``` **Correct (constructor injection with explicit dependencies):** ```typescript // Use constructor injection - dependencies are explicit @Injectable() export class OrdersService { constructor( private usersService: UsersService, private inventoryService: InventoryService, private paymentService: PaymentService, ) {} async createOrder(dto: CreateOrderDto): Promise { const user = await this.usersService.findOne(dto.userId); const inventory = await this.inventoryService.check(dto.items); // Dependencies are clear and testable } } // Easy to test with mocks describe('OrdersService', () => { let service: OrdersService; beforeEach(async () => { const module = await Test.createTestingModule({ providers: [ OrdersService, { provide: UsersService, useValue: mockUsersService }, { provide: InventoryService, useValue: mockInventoryService }, { provide: PaymentService, useValue: mockPaymentService }, ], }).compile(); service = module.get(OrdersService); }); }); // VALID: Factory pattern for dynamic instantiation @Injectable() export class HandlerFactory { constructor(private moduleRef: ModuleRef) {} getHandler(type: string): Handler { switch (type) { case 'email': return this.moduleRef.get(EmailHandler); case 'sms': return this.moduleRef.get(SmsHandler); default: return this.moduleRef.get(DefaultHandler); } } } ``` Reference: [NestJS Module Reference](https://docs.nestjs.com/fundamentals/module-ref) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/di-interface-segregation.md ================================================ --- title: Apply Interface Segregation Principle impact: HIGH impactDescription: Reduces coupling and improves testability by 30-50% tags: dependency-injection, interfaces, solid, isp --- ## Apply Interface Segregation Principle Clients should not be forced to depend on interfaces they don't use. In NestJS, this means keeping interfaces small and focused on specific capabilities rather than creating "fat" interfaces that bundle unrelated methods. When a service only needs to send emails, it shouldn't depend on an interface that also includes SMS, push notifications, and logging. Split large interfaces into role-based ones. **Incorrect (fat interface forcing unused dependencies):** ```typescript // Fat interface - forces all consumers to depend on everything interface NotificationService { sendEmail(to: string, subject: string, body: string): Promise; sendSms(phone: string, message: string): Promise; sendPush(userId: string, notification: PushPayload): Promise; sendSlack(channel: string, message: string): Promise; logNotification(type: string, payload: any): Promise; getDeliveryStatus(id: string): Promise; retryFailed(id: string): Promise; scheduleNotification(dto: ScheduleDto): Promise; } // Consumer only needs email, but must mock everything for tests @Injectable() export class OrdersService { constructor( private notifications: NotificationService, // Depends on 8 methods, uses 1 ) {} async confirmOrder(order: Order): Promise { await this.notifications.sendEmail( order.customer.email, 'Order Confirmed', `Your order ${order.id} has been confirmed.`, ); } } // Testing is painful - must mock unused methods const mockNotificationService = { sendEmail: jest.fn(), sendSms: jest.fn(), // Never used, but required sendPush: jest.fn(), // Never used, but required sendSlack: jest.fn(), // Never used, but required logNotification: jest.fn(), // Never used, but required getDeliveryStatus: jest.fn(), // Never used, but required retryFailed: jest.fn(), // Never used, but required scheduleNotification: jest.fn(), // Never used, but required }; ``` **Correct (segregated interfaces by capability):** ```typescript // Segregated interfaces - each focused on one capability interface EmailSender { sendEmail(to: string, subject: string, body: string): Promise; } interface SmsSender { sendSms(phone: string, message: string): Promise; } interface PushSender { sendPush(userId: string, notification: PushPayload): Promise; } interface NotificationLogger { logNotification(type: string, payload: any): Promise; } interface NotificationScheduler { scheduleNotification(dto: ScheduleDto): Promise; } // Implementation can implement multiple interfaces @Injectable() export class NotificationService implements EmailSender, SmsSender, PushSender { async sendEmail(to: string, subject: string, body: string): Promise { // Email implementation } async sendSms(phone: string, message: string): Promise { // SMS implementation } async sendPush(userId: string, notification: PushPayload): Promise { // Push implementation } } // Or separate implementations @Injectable() export class SendGridEmailService implements EmailSender { async sendEmail(to: string, subject: string, body: string): Promise { // SendGrid-specific implementation } } // Consumer depends only on what it needs @Injectable() export class OrdersService { constructor( @Inject(EMAIL_SENDER) private emailSender: EmailSender, // Minimal dependency ) {} async confirmOrder(order: Order): Promise { await this.emailSender.sendEmail( order.customer.email, 'Order Confirmed', `Your order ${order.id} has been confirmed.`, ); } } // Testing is simple - only mock what's used const mockEmailSender: EmailSender = { sendEmail: jest.fn(), }; // Module registration with tokens export const EMAIL_SENDER = Symbol('EMAIL_SENDER'); export const SMS_SENDER = Symbol('SMS_SENDER'); @Module({ providers: [ { provide: EMAIL_SENDER, useClass: SendGridEmailService }, { provide: SMS_SENDER, useClass: TwilioSmsService }, ], exports: [EMAIL_SENDER, SMS_SENDER], }) export class NotificationModule {} ``` **Combining interfaces when needed:** ```typescript // Sometimes a consumer legitimately needs multiple capabilities interface EmailAndSmsSender extends EmailSender, SmsSender {} // Or use intersection types type MultiChannelSender = EmailSender & SmsSender & PushSender; // Consumer that genuinely needs multiple channels @Injectable() export class AlertService { constructor( @Inject(MULTI_CHANNEL_SENDER) private sender: EmailSender & SmsSender, ) {} async sendCriticalAlert(user: User, message: string): Promise { await Promise.all([ this.sender.sendEmail(user.email, 'Critical Alert', message), this.sender.sendSms(user.phone, message), ]); } } ``` Reference: [Interface Segregation Principle](https://en.wikipedia.org/wiki/Interface_segregation_principle) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/di-liskov-substitution.md ================================================ --- title: Honor Liskov Substitution Principle impact: HIGH impactDescription: Ensures implementations are truly interchangeable without breaking callers tags: dependency-injection, inheritance, solid, lsp --- ## Honor Liskov Substitution Principle Subtypes must be substitutable for their base types without altering program correctness. In NestJS with dependency injection, this means any implementation of an interface or abstract class must honor the contract completely. A mock payment service used in tests must behave like a real payment service (return similar shapes, handle errors the same way). Violating LSP causes subtle bugs when swapping implementations. **Incorrect (implementation violates the contract):** ```typescript // Base interface with clear contract interface PaymentGateway { /** * Charges the specified amount. * @returns PaymentResult on success * @throws PaymentFailedException on payment failure */ charge(amount: number, currency: string): Promise; } // Production implementation - follows the contract @Injectable() export class StripeService implements PaymentGateway { async charge(amount: number, currency: string): Promise { const response = await this.stripe.charges.create({ amount, currency }); return { success: true, transactionId: response.id, amount }; } } // Mock that violates LSP - different behavior! @Injectable() export class MockPaymentService implements PaymentGateway { async charge(amount: number, currency: string): Promise { // VIOLATION 1: Throws for valid input (contract says return PaymentResult) if (amount > 1000) { throw new Error('Mock does not support large amounts'); } // VIOLATION 2: Returns null instead of PaymentResult if (currency !== 'USD') { return null as any; // Real service would convert or reject properly } // VIOLATION 3: Missing required field return { success: true } as PaymentResult; // Missing transactionId! } } // Consumer trusts the contract @Injectable() export class OrdersService { constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {} async checkout(order: Order): Promise { const result = await this.payment.charge(order.total, order.currency); // These fail with MockPaymentService: await this.saveTransaction(result.transactionId); // undefined! await this.sendReceipt(result); // might be null! } } ``` **Correct (implementations honor the contract):** ```typescript // Well-defined interface with documented behavior interface PaymentGateway { /** * Charges the specified amount. * @param amount - Amount in smallest currency unit (cents) * @param currency - ISO 4217 currency code * @returns PaymentResult with transactionId, success status, and amount * @throws PaymentFailedException if charge is declined * @throws InvalidCurrencyException if currency is not supported */ charge(amount: number, currency: string): Promise; /** * Refunds a previous charge. * @throws TransactionNotFoundException if transactionId is invalid */ refund(transactionId: string, amount?: number): Promise; } // Production implementation @Injectable() export class StripeService implements PaymentGateway { async charge(amount: number, currency: string): Promise { try { const response = await this.stripe.charges.create({ amount, currency }); return { success: true, transactionId: response.id, amount: response.amount, }; } catch (error) { if (error.type === 'card_error') { throw new PaymentFailedException(error.message); } throw error; } } async refund(transactionId: string, amount?: number): Promise { // Implementation... } } // Mock that honors LSP - same contract, same behavior shape @Injectable() export class MockPaymentService implements PaymentGateway { private transactions = new Map(); async charge(amount: number, currency: string): Promise { // Honor the contract: validate currency like real service would if (!['USD', 'EUR', 'GBP'].includes(currency)) { throw new InvalidCurrencyException(`Unsupported currency: ${currency}`); } // Simulate decline for specific test scenarios if (amount === 99999) { throw new PaymentFailedException('Card declined (test scenario)'); } // Return same shape as production const result: PaymentResult = { success: true, transactionId: `mock_${Date.now()}_${Math.random().toString(36)}`, amount, }; this.transactions.set(result.transactionId, result); return result; } async refund(transactionId: string, amount?: number): Promise { // Honor the contract: throw if transaction not found if (!this.transactions.has(transactionId)) { throw new TransactionNotFoundException(transactionId); } return { success: true, refundId: `refund_${transactionId}`, amount: amount ?? this.transactions.get(transactionId)!.amount, }; } } // Consumer can swap implementations safely @Injectable() export class OrdersService { constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {} async checkout(order: Order): Promise { try { const result = await this.payment.charge(order.total, order.currency); // Works with both StripeService and MockPaymentService order.transactionId = result.transactionId; order.status = 'paid'; return order; } catch (error) { if (error instanceof PaymentFailedException) { order.status = 'payment_failed'; return order; } throw error; } } } ``` **Testing LSP compliance:** ```typescript // Shared test suite that any implementation must pass function testPaymentGatewayContract(createGateway: () => PaymentGateway) { describe('PaymentGateway contract', () => { let gateway: PaymentGateway; beforeEach(() => { gateway = createGateway(); }); it('returns PaymentResult with all required fields', async () => { const result = await gateway.charge(1000, 'USD'); expect(result).toHaveProperty('success'); expect(result).toHaveProperty('transactionId'); expect(result).toHaveProperty('amount'); expect(typeof result.transactionId).toBe('string'); }); it('throws InvalidCurrencyException for unsupported currency', async () => { await expect(gateway.charge(1000, 'INVALID')).rejects.toThrow(InvalidCurrencyException); }); it('throws TransactionNotFoundException for invalid refund', async () => { await expect(gateway.refund('nonexistent')).rejects.toThrow(TransactionNotFoundException); }); }); } // Run against all implementations describe('StripeService', () => { testPaymentGatewayContract(() => new StripeService(mockStripeClient)); }); describe('MockPaymentService', () => { testPaymentGatewayContract(() => new MockPaymentService()); }); ``` Reference: [Liskov Substitution Principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/di-prefer-constructor-injection.md ================================================ --- title: Prefer Constructor Injection impact: CRITICAL impactDescription: Required for proper DI and testing tags: dependency-injection, constructor, testing --- ## Prefer Constructor Injection Always use constructor injection over property injection. Constructor injection makes dependencies explicit, enables TypeScript type checking, ensures dependencies are available when the class is instantiated, and improves testability. This is required for proper DI, testing, and TypeScript support. **Incorrect (property injection with hidden dependencies):** ```typescript // Property injection - avoid unless necessary @Injectable() export class UsersService { @Inject() private userRepo: UserRepository; // Hidden dependency @Inject('CONFIG') private config: ConfigType; // Also hidden async findAll() { return this.userRepo.find(); } } // Problems: // 1. Dependencies not visible in constructor // 2. Service can be instantiated without dependencies in tests // 3. TypeScript can't enforce dependency types at instantiation ``` **Correct (constructor injection with explicit dependencies):** ```typescript // Constructor injection - explicit and testable @Injectable() export class UsersService { constructor( private readonly userRepo: UserRepository, @Inject('CONFIG') private readonly config: ConfigType, ) {} async findAll(): Promise { return this.userRepo.find(); } } // Testing is straightforward describe('UsersService', () => { let service: UsersService; let mockRepo: jest.Mocked; beforeEach(() => { mockRepo = { find: jest.fn(), save: jest.fn(), } as any; service = new UsersService(mockRepo, { dbUrl: 'test' }); }); it('should find all users', async () => { mockRepo.find.mockResolvedValue([{ id: '1', name: 'Test' }]); const result = await service.findAll(); expect(result).toHaveLength(1); }); }); // Only use property injection for optional dependencies @Injectable() export class LoggingService { @Optional() @Inject('ANALYTICS') private analytics?: AnalyticsService; log(message: string) { console.log(message); this.analytics?.track('log', message); // Optional enhancement } } ``` Reference: [NestJS Providers](https://docs.nestjs.com/providers) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/di-scope-awareness.md ================================================ --- title: Understand Provider Scopes impact: CRITICAL impactDescription: Prevents data leaks and performance issues tags: dependency-injection, scopes, request-context --- ## Understand Provider Scopes NestJS has three provider scopes: DEFAULT (singleton), REQUEST (per-request instance), and TRANSIENT (new instance for each injection). Most providers should be singletons. Request-scoped providers have performance implications as they bubble up through the dependency tree. Understanding scopes prevents memory leaks and incorrect data sharing. **Incorrect (wrong scope usage):** ```typescript // Request-scoped when not needed (performance hit) @Injectable({ scope: Scope.REQUEST }) export class UsersService { // This creates a new instance for EVERY request // All dependencies also become request-scoped async findAll() { return this.userRepo.find(); } } // Singleton with mutable request state @Injectable() // Default: singleton export class RequestContextService { private userId: string; // DANGER: Shared across all requests! setUser(userId: string) { this.userId = userId; // Overwrites for all concurrent requests } getUser() { return this.userId; // Returns wrong user! } } ``` **Correct (appropriate scope for each use case):** ```typescript // Singleton for stateless services (default, most common) @Injectable() export class UsersService { constructor(private readonly userRepo: UserRepository) {} async findById(id: string): Promise { return this.userRepo.findOne({ where: { id } }); } } // Request-scoped ONLY when you need request context @Injectable({ scope: Scope.REQUEST }) export class RequestContextService { private userId: string; setUser(userId: string) { this.userId = userId; } getUser(): string { return this.userId; } } // Better: Use NestJS built-in request context import { REQUEST } from '@nestjs/core'; import { Request } from 'express'; @Injectable({ scope: Scope.REQUEST }) export class AuditService { constructor(@Inject(REQUEST) private request: Request) {} log(action: string) { console.log(`User ${this.request.user?.id} performed ${action}`); } } // Best: Use ClsModule for async context (no scope bubble-up) import { ClsService } from 'nestjs-cls'; @Injectable() // Stays singleton! export class AuditService { constructor(private cls: ClsService) {} log(action: string) { const userId = this.cls.get('userId'); console.log(`User ${userId} performed ${action}`); } } ``` Reference: [NestJS Injection Scopes](https://docs.nestjs.com/fundamentals/injection-scopes) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/di-use-interfaces-tokens.md ================================================ --- title: Use Injection Tokens for Interfaces impact: HIGH impactDescription: Enables interface-based DI at runtime tags: dependency-injection, tokens, interfaces --- ## Use Injection Tokens for Interfaces TypeScript interfaces are erased at compile time and can't be used as injection tokens. Use string tokens, symbols, or abstract classes when you want to inject implementations of interfaces. This enables swapping implementations for testing or different environments. **Incorrect (interface can't be used as token):** ```typescript // Interface can't be used as injection token interface PaymentGateway { charge(amount: number): Promise; } @Injectable() export class StripeService implements PaymentGateway { charge(amount: number) { /* ... */ } } @Injectable() export class OrdersService { // This WON'T work - PaymentGateway doesn't exist at runtime constructor(private payment: PaymentGateway) {} } ``` **Correct (symbol tokens or abstract classes):** ```typescript // Option 1: String/Symbol tokens (most flexible) export const PAYMENT_GATEWAY = Symbol('PAYMENT_GATEWAY'); export interface PaymentGateway { charge(amount: number): Promise; } @Injectable() export class StripeService implements PaymentGateway { async charge(amount: number): Promise { // Stripe implementation } } @Injectable() export class MockPaymentService implements PaymentGateway { async charge(amount: number): Promise { return { success: true, id: 'mock-id' }; } } // Module registration @Module({ providers: [ { provide: PAYMENT_GATEWAY, useClass: process.env.NODE_ENV === 'test' ? MockPaymentService : StripeService, }, ], exports: [PAYMENT_GATEWAY], }) export class PaymentModule {} // Injection @Injectable() export class OrdersService { constructor(@Inject(PAYMENT_GATEWAY) private payment: PaymentGateway) {} async createOrder(dto: CreateOrderDto) { await this.payment.charge(dto.amount); } } // Option 2: Abstract class (carries runtime type info) export abstract class PaymentGateway { abstract charge(amount: number): Promise; } @Injectable() export class StripeService extends PaymentGateway { async charge(amount: number): Promise { // Implementation } } // No @Inject needed with abstract class @Injectable() export class OrdersService { constructor(private payment: PaymentGateway) {} } ``` Reference: [NestJS Custom Providers](https://docs.nestjs.com/fundamentals/custom-providers) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/error-handle-async-errors.md ================================================ --- title: Handle Async Errors Properly impact: HIGH impactDescription: Prevents process crashes from unhandled rejections tags: error-handling, async, promises --- ## Handle Async Errors Properly NestJS automatically catches errors from async route handlers, but errors from background tasks, event handlers, and manually created promises can crash your application. Always handle async errors explicitly and use global handlers as a safety net. **Incorrect (fire-and-forget without error handling):** ```typescript // Fire-and-forget without error handling @Injectable() export class UsersService { async createUser(dto: CreateUserDto): Promise { const user = await this.repo.save(dto); // Fire and forget - if this fails, error is unhandled! this.emailService.sendWelcome(user.email); return user; } } // Unhandled promise in event handler @Injectable() export class OrdersService { @OnEvent('order.created') handleOrderCreated(event: OrderCreatedEvent) { // This returns a promise but it's not awaited! this.processOrder(event); // Errors will crash the process } private async processOrder(event: OrderCreatedEvent): Promise { await this.inventoryService.reserve(event.items); await this.notificationService.send(event.userId); } } // Missing try-catch in scheduled tasks @Cron('0 0 * * *') async dailyCleanup(): Promise { await this.cleanupService.run(); // If this throws, no error handling } ``` **Correct (explicit async error handling):** ```typescript // Handle fire-and-forget with explicit catch @Injectable() export class UsersService { private readonly logger = new Logger(UsersService.name); async createUser(dto: CreateUserDto): Promise { const user = await this.repo.save(dto); // Explicitly catch and log errors this.emailService.sendWelcome(user.email).catch(error => { this.logger.error('Failed to send welcome email', error.stack); // Optionally queue for retry }); return user; } } // Properly handle async event handlers @Injectable() export class OrdersService { private readonly logger = new Logger(OrdersService.name); @OnEvent('order.created') async handleOrderCreated(event: OrderCreatedEvent): Promise { try { await this.processOrder(event); } catch (error) { this.logger.error('Failed to process order', { event, error }); // Don't rethrow - would crash the process await this.deadLetterQueue.add('order.created', event); } } } // Safe scheduled tasks @Injectable() export class CleanupService { private readonly logger = new Logger(CleanupService.name); @Cron('0 0 * * *') async dailyCleanup(): Promise { try { await this.cleanupService.run(); this.logger.log('Daily cleanup completed'); } catch (error) { this.logger.error('Daily cleanup failed', error.stack); // Alert or retry logic } } } // Global unhandled rejection handler in main.ts async function bootstrap() { const app = await NestFactory.create(AppModule); const logger = new Logger('Bootstrap'); process.on('unhandledRejection', (reason, promise) => { logger.error('Unhandled Rejection at:', promise, 'reason:', reason); }); process.on('uncaughtException', error => { logger.error('Uncaught Exception:', error); process.exit(1); }); await app.listen(3000); } ``` Reference: [Node.js Unhandled Rejections](https://nodejs.org/api/process.html#event-unhandledrejection) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/error-throw-http-exceptions.md ================================================ --- title: Throw HTTP Exceptions from Services impact: HIGH impactDescription: Keeps controllers thin and simplifies error handling tags: error-handling, exceptions, services --- ## Throw HTTP Exceptions from Services It's acceptable (and often preferable) to throw `HttpException` subclasses from services in HTTP applications. This keeps controllers thin and allows services to communicate appropriate error states. For truly layer-agnostic services, use domain exceptions that map to HTTP status codes. **Incorrect (return error objects instead of throwing):** ```typescript // Return error objects instead of throwing @Injectable() export class UsersService { async findById(id: string): Promise<{ user?: User; error?: string }> { const user = await this.repo.findOne({ where: { id } }); if (!user) { return { error: 'User not found' }; // Controller must check this } return { user }; } } @Controller('users') export class UsersController { @Get(':id') async findOne(@Param('id') id: string) { const result = await this.usersService.findById(id); if (result.error) { throw new NotFoundException(result.error); } return result.user; } } ``` **Correct (throw exceptions directly from service):** ```typescript // Throw exceptions directly from service @Injectable() export class UsersService { constructor(private readonly repo: UserRepository) {} async findById(id: string): Promise { const user = await this.repo.findOne({ where: { id } }); if (!user) { throw new NotFoundException(`User #${id} not found`); } return user; } async create(dto: CreateUserDto): Promise { const existing = await this.repo.findOne({ where: { email: dto.email }, }); if (existing) { throw new ConflictException('Email already registered'); } return this.repo.save(dto); } async update(id: string, dto: UpdateUserDto): Promise { const user = await this.findById(id); // Throws if not found Object.assign(user, dto); return this.repo.save(user); } } // Controller stays thin @Controller('users') export class UsersController { @Get(':id') findOne(@Param('id') id: string): Promise { return this.usersService.findById(id); } @Post() create(@Body() dto: CreateUserDto): Promise { return this.usersService.create(dto); } } // For layer-agnostic services, use domain exceptions export class EntityNotFoundException extends Error { constructor( public readonly entity: string, public readonly id: string, ) { super(`${entity} with ID "${id}" not found`); } } // Map to HTTP in exception filter @Catch(EntityNotFoundException) export class EntityNotFoundFilter implements ExceptionFilter { catch(exception: EntityNotFoundException, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); response.status(404).json({ statusCode: 404, message: exception.message, entity: exception.entity, id: exception.id, }); } } ``` Reference: [NestJS Exception Filters](https://docs.nestjs.com/exception-filters) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/error-use-exception-filters.md ================================================ --- title: Use Exception Filters for Error Handling impact: HIGH impactDescription: Consistent, centralized error handling tags: error-handling, exception-filters, consistency --- ## Use Exception Filters for Error Handling Never catch exceptions and manually format error responses in controllers. Use NestJS exception filters to handle errors consistently across your application. Create custom exception filters for specific error types and a global filter for unhandled exceptions. **Incorrect (manual error handling in controllers):** ```typescript // Manual error handling in controllers @Controller('users') export class UsersController { @Get(':id') async findOne(@Param('id') id: string, @Res() res: Response) { try { const user = await this.usersService.findById(id); if (!user) { return res.status(404).json({ statusCode: 404, message: 'User not found', }); } return res.json(user); } catch (error) { console.error(error); return res.status(500).json({ statusCode: 500, message: 'Internal server error', }); } } } ``` **Correct (exception filters with consistent handling):** ```typescript // Use built-in and custom exceptions @Controller('users') export class UsersController { @Get(':id') async findOne(@Param('id') id: string): Promise { const user = await this.usersService.findById(id); if (!user) { throw new NotFoundException(`User #${id} not found`); } return user; } } // Custom domain exception export class UserNotFoundException extends NotFoundException { constructor(userId: string) { super({ statusCode: 404, error: 'Not Found', message: `User with ID "${userId}" not found`, code: 'USER_NOT_FOUND', }); } } // Custom exception filter for domain errors @Catch(DomainException) export class DomainExceptionFilter implements ExceptionFilter { catch(exception: DomainException, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); const request = ctx.getRequest(); const status = exception.getStatus?.() || 400; response.status(status).json({ statusCode: status, code: exception.code, message: exception.message, timestamp: new Date().toISOString(), path: request.url, }); } } // Global exception filter for unhandled errors @Catch() export class AllExceptionsFilter implements ExceptionFilter { constructor(private readonly logger: Logger) {} catch(exception: unknown, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); const request = ctx.getRequest(); const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR; const message = exception instanceof HttpException ? exception.message : 'Internal server error'; this.logger.error(`${request.method} ${request.url}`, exception instanceof Error ? exception.stack : exception); response.status(status).json({ statusCode: status, message, timestamp: new Date().toISOString(), path: request.url, }); } } // Register globally in main.ts app.useGlobalFilters(new AllExceptionsFilter(app.get(Logger)), new DomainExceptionFilter()); // Or via module @Module({ providers: [ { provide: APP_FILTER, useClass: AllExceptionsFilter, }, ], }) export class AppModule {} ``` Reference: [NestJS Exception Filters](https://docs.nestjs.com/exception-filters) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/micro-use-health-checks.md ================================================ --- title: Implement Health Checks for Microservices impact: MEDIUM-HIGH impactDescription: Health checks enable orchestrators to manage service lifecycle tags: microservices, health-checks, terminus, kubernetes --- ## Implement Health Checks for Microservices Implement liveness and readiness probes using `@nestjs/terminus`. Liveness checks determine if the service should be restarted. Readiness checks determine if the service can accept traffic. Proper health checks enable Kubernetes and load balancers to route traffic correctly. **Incorrect (simple ping that doesn't check dependencies):** ```typescript // Simple ping that doesn't check dependencies @Controller('health') export class HealthController { @Get() check(): string { return 'OK'; // Service might be unhealthy but returns OK } } // Health check that blocks on slow dependencies @Controller('health') export class HealthController { @Get() async check(): Promise { // If database is slow, health check times out await this.userRepo.findOne({ where: { id: '1' } }); await this.redis.ping(); await this.externalApi.healthCheck(); return 'OK'; } } ``` **Correct (use @nestjs/terminus for comprehensive health checks):** ```typescript // Use @nestjs/terminus for comprehensive health checks import { HealthCheckService, HttpHealthIndicator, TypeOrmHealthIndicator, HealthCheck, DiskHealthIndicator, MemoryHealthIndicator, } from '@nestjs/terminus'; @Controller('health') export class HealthController { constructor( private health: HealthCheckService, private http: HttpHealthIndicator, private db: TypeOrmHealthIndicator, private disk: DiskHealthIndicator, private memory: MemoryHealthIndicator, ) {} // Liveness probe - is the service alive? @Get('live') @HealthCheck() liveness() { return this.health.check([ // Basic checks only () => this.memory.checkHeap('memory_heap', 200 * 1024 * 1024), // 200MB ]); } // Readiness probe - can the service handle traffic? @Get('ready') @HealthCheck() readiness() { return this.health.check([ () => this.db.pingCheck('database'), () => this.http.pingCheck('redis', 'http://redis:6379', { timeout: 1000 }), () => this.disk.checkStorage('disk', { path: '/', thresholdPercent: 0.9 }), ]); } // Deep health check for debugging @Get('deep') @HealthCheck() deepCheck() { return this.health.check([ () => this.db.pingCheck('database'), () => this.memory.checkHeap('memory_heap', 200 * 1024 * 1024), () => this.memory.checkRSS('memory_rss', 300 * 1024 * 1024), () => this.disk.checkStorage('disk', { path: '/', thresholdPercent: 0.9 }), () => this.http.pingCheck('external-api', 'https://api.example.com/health'), ]); } } // Custom indicator for business-specific health @Injectable() export class QueueHealthIndicator extends HealthIndicator { constructor(private queueService: QueueService) { super(); } async isHealthy(key: string): Promise { const queueStats = await this.queueService.getStats(); const isHealthy = queueStats.failedCount < 100; const result = this.getStatus(key, isHealthy, { waiting: queueStats.waitingCount, active: queueStats.activeCount, failed: queueStats.failedCount, }); if (!isHealthy) { throw new HealthCheckError('Queue unhealthy', result); } return result; } } // Redis health indicator @Injectable() export class RedisHealthIndicator extends HealthIndicator { constructor(@InjectRedis() private redis: Redis) { super(); } async isHealthy(key: string): Promise { try { const pong = await this.redis.ping(); return this.getStatus(key, pong === 'PONG'); } catch (error) { throw new HealthCheckError('Redis check failed', this.getStatus(key, false)); } } } // Use custom indicators @Get('ready') @HealthCheck() readiness() { return this.health.check([ () => this.db.pingCheck('database'), () => this.redis.isHealthy('redis'), () => this.queue.isHealthy('job-queue'), ]); } // Graceful shutdown handling @Injectable() export class GracefulShutdownService implements OnApplicationShutdown { private isShuttingDown = false; isShutdown(): boolean { return this.isShuttingDown; } async onApplicationShutdown(signal: string): Promise { this.isShuttingDown = true; console.log(`Shutting down on ${signal}`); // Wait for in-flight requests await new Promise((resolve) => setTimeout(resolve, 5000)); } } // Health check respects shutdown state @Get('ready') @HealthCheck() readiness() { if (this.shutdownService.isShutdown()) { throw new ServiceUnavailableException('Shutting down'); } return this.health.check([ () => this.db.pingCheck('database'), ]); } ``` ### Kubernetes Configuration ```yaml # Kubernetes deployment with probes apiVersion: apps/v1 kind: Deployment metadata: name: api-service spec: template: spec: containers: - name: api image: api-service:latest ports: - containerPort: 3000 livenessProbe: httpGet: path: /health/live port: 3000 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 readinessProbe: httpGet: path: /health/ready port: 3000 initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 3 startupProbe: httpGet: path: /health/live port: 3000 initialDelaySeconds: 0 periodSeconds: 5 failureThreshold: 30 ``` Reference: [NestJS Terminus](https://docs.nestjs.com/recipes/terminus) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/micro-use-patterns.md ================================================ --- title: Use Message and Event Patterns Correctly impact: MEDIUM impactDescription: Proper patterns ensure reliable microservice communication tags: microservices, message-pattern, event-pattern, communication --- ## Use Message and Event Patterns Correctly NestJS microservices support two communication patterns: request-response (MessagePattern) and event-based (EventPattern). Use MessagePattern when you need a response, and EventPattern for fire-and-forget notifications. Understanding the difference prevents communication bugs. **Incorrect (using wrong pattern for use case):** ```typescript // Use @MessagePattern for fire-and-forget @Controller() export class NotificationsController { @MessagePattern('user.created') async handleUserCreated(data: UserCreatedEvent) { // This WAITS for response, blocking the sender await this.emailService.sendWelcome(data.email); // If email fails, sender gets an error (coupling!) } } // Use @EventPattern expecting a response @Controller() export class OrdersController { @EventPattern('inventory.check') async checkInventory(data: CheckInventoryDto) { const available = await this.inventory.check(data); return available; // This return value is IGNORED with @EventPattern! } } // Tight coupling in client @Injectable() export class UsersService { async createUser(dto: CreateUserDto): Promise { const user = await this.repo.save(dto); // Blocks until notification service responds await this.client.send('user.created', user).toPromise(); // If notification service is down, user creation fails! return user; } } ``` **Correct (use MessagePattern for request-response, EventPattern for fire-and-forget):** ```typescript // MessagePattern: Request-Response (when you NEED a response) @Controller() export class InventoryController { @MessagePattern({ cmd: 'check_inventory' }) async checkInventory(data: CheckInventoryDto): Promise { const result = await this.inventoryService.check(data.productId, data.quantity); return result; // Response sent back to caller } } // Client expects response @Injectable() export class OrdersService { async createOrder(dto: CreateOrderDto): Promise { // Check inventory - we NEED this response to proceed const inventory = await firstValueFrom( this.inventoryClient.send( { cmd: 'check_inventory' }, { productId: dto.productId, quantity: dto.quantity }, ), ); if (!inventory.available) { throw new BadRequestException('Insufficient inventory'); } return this.repo.save(dto); } } // EventPattern: Fire-and-Forget (for notifications, side effects) @Controller() export class NotificationsController { @EventPattern('user.created') async handleUserCreated(data: UserCreatedEvent): Promise { // No return value needed - just process the event await this.emailService.sendWelcome(data.email); await this.analyticsService.track('user_signup', data); // If this fails, it doesn't affect the sender } } // Client emits event without waiting @Injectable() export class UsersService { async createUser(dto: CreateUserDto): Promise { const user = await this.repo.save(dto); // Fire and forget - doesn't block, doesn't wait this.eventClient.emit('user.created', { userId: user.id, email: user.email, timestamp: new Date(), }); return user; // User creation succeeds regardless of event handling } } // Hybrid pattern for critical events @Injectable() export class OrdersService { async createOrder(dto: CreateOrderDto): Promise { const order = await this.repo.save(dto); // Critical: inventory reservation (use MessagePattern) const reserved = await firstValueFrom( this.inventoryClient.send({ cmd: 'reserve_inventory' }, { orderId: order.id, items: dto.items, }), ); if (!reserved.success) { await this.repo.delete(order.id); throw new BadRequestException('Could not reserve inventory'); } // Non-critical: notifications (use EventPattern) this.eventClient.emit('order.created', { orderId: order.id, userId: dto.userId, total: dto.total, }); return order; } } // Error handling patterns // MessagePattern errors propagate to caller @MessagePattern({ cmd: 'get_user' }) async getUser(userId: string): Promise { const user = await this.repo.findOne({ where: { id: userId } }); if (!user) { throw new RpcException('User not found'); // Received by caller } return user; } // EventPattern errors should be handled locally @EventPattern('order.created') async handleOrderCreated(data: OrderCreatedEvent): Promise { try { await this.processOrder(data); } catch (error) { // Log and potentially retry - don't throw this.logger.error('Failed to process order event', error); await this.deadLetterQueue.add(data); } } ``` Reference: [NestJS Microservices](https://docs.nestjs.com/microservices/basics) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/micro-use-queues.md ================================================ --- title: Use Message Queues for Background Jobs impact: MEDIUM-HIGH impactDescription: Queues enable reliable background processing tags: microservices, queues, bullmq, background-jobs --- ## Use Message Queues for Background Jobs Use `@nestjs/bullmq` for background job processing. Queues decouple long-running tasks from HTTP requests, enable retry logic, and distribute workload across workers. Use them for emails, file processing, notifications, and any task that shouldn't block user requests. **Incorrect (long-running tasks in HTTP handlers):** ```typescript // Long-running tasks in HTTP handlers @Controller('reports') export class ReportsController { @Post() async generate(@Body() dto: GenerateReportDto): Promise { // This blocks the request for potentially minutes const data = await this.fetchLargeDataset(dto); const report = await this.processData(data); // Slow! await this.sendEmail(dto.email, report); // Can fail! return report; // Client times out } } // Fire-and-forget without retry @Injectable() export class EmailService { async sendWelcome(email: string): Promise { // If this fails, email is never sent await this.mailer.send({ to: email, template: 'welcome' }); // No retry, no tracking, no visibility } } // Use setInterval for scheduled tasks setInterval(async () => { await cleanupOldRecords(); }, 60000); // No error handling, memory leaks ``` **Correct (use BullMQ for background processing):** ```typescript // Configure BullMQ import { BullModule } from '@nestjs/bullmq'; @Module({ imports: [ BullModule.forRoot({ connection: { host: 'localhost', port: 6379, }, defaultJobOptions: { removeOnComplete: 1000, removeOnFail: 5000, attempts: 3, backoff: { type: 'exponential', delay: 1000, }, }, }), BullModule.registerQueue({ name: 'email' }, { name: 'reports' }, { name: 'notifications' }), ], }) export class QueueModule {} // Producer: Add jobs to queue @Injectable() export class ReportsService { constructor(@InjectQueue('reports') private reportsQueue: Queue) {} async requestReport(dto: GenerateReportDto): Promise<{ jobId: string }> { // Return immediately, process in background const job = await this.reportsQueue.add('generate', dto, { priority: dto.urgent ? 1 : 10, delay: dto.scheduledFor ? Date.parse(dto.scheduledFor) - Date.now() : 0, }); return { jobId: job.id }; } async getJobStatus(jobId: string): Promise { const job = await this.reportsQueue.getJob(jobId); return { status: await job.getState(), progress: job.progress, result: job.returnvalue, }; } } // Consumer: Process jobs @Processor('reports') export class ReportsProcessor { private readonly logger = new Logger(ReportsProcessor.name); @Process('generate') async generateReport(job: Job): Promise { this.logger.log(`Processing report job ${job.id}`); // Update progress await job.updateProgress(10); const data = await this.fetchData(job.data); await job.updateProgress(50); const report = await this.processData(data); await job.updateProgress(90); await this.saveReport(report); await job.updateProgress(100); return report; } @OnQueueActive() onActive(job: Job) { this.logger.log(`Processing job ${job.id}`); } @OnQueueCompleted() onCompleted(job: Job, result: any) { this.logger.log(`Job ${job.id} completed`); } @OnQueueFailed() onFailed(job: Job, error: Error) { this.logger.error(`Job ${job.id} failed: ${error.message}`); } } // Email queue with retry @Processor('email') export class EmailProcessor { @Process('send') async sendEmail(job: Job): Promise { const { to, template, data } = job.data; try { await this.mailer.send({ to, template, context: data, }); } catch (error) { // BullMQ will retry based on job options throw error; } } } // Usage @Injectable() export class NotificationService { constructor(@InjectQueue('email') private emailQueue: Queue) {} async sendWelcome(user: User): Promise { await this.emailQueue.add( 'send', { to: user.email, template: 'welcome', data: { name: user.name }, }, { attempts: 5, backoff: { type: 'exponential', delay: 5000 }, }, ); } } // Scheduled jobs @Injectable() export class ScheduledJobsService implements OnModuleInit { constructor(@InjectQueue('maintenance') private queue: Queue) {} async onModuleInit(): Promise { // Clean up old reports daily at midnight await this.queue.add( 'cleanup', {}, { repeat: { cron: '0 0 * * *' }, jobId: 'daily-cleanup', // Prevent duplicates }, ); // Send digest every hour await this.queue.add( 'digest', {}, { repeat: { every: 60 * 60 * 1000 }, jobId: 'hourly-digest', }, ); } } @Processor('maintenance') export class MaintenanceProcessor { @Process('cleanup') async cleanup(): Promise { await this.cleanupOldReports(); await this.cleanupExpiredSessions(); } @Process('digest') async sendDigest(): Promise { const users = await this.getUsersForDigest(); for (const user of users) { await this.emailQueue.add('send', { to: user.email, template: 'digest' }); } } } // Queue monitoring with Bull Board import { BullBoardModule } from '@bull-board/nestjs'; import { BullMQAdapter } from '@bull-board/api/bullMQAdapter'; @Module({ imports: [ BullBoardModule.forRoot({ route: '/admin/queues', adapter: ExpressAdapter, }), BullBoardModule.forFeature({ name: 'email', adapter: BullMQAdapter, }), BullBoardModule.forFeature({ name: 'reports', adapter: BullMQAdapter, }), ], }) export class AdminModule {} ``` Reference: [NestJS Queues](https://docs.nestjs.com/techniques/queues) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/perf-async-hooks.md ================================================ --- title: Use Async Lifecycle Hooks Correctly impact: HIGH impactDescription: Improper async handling blocks application startup tags: performance, lifecycle, async, hooks --- ## Use Async Lifecycle Hooks Correctly NestJS lifecycle hooks (`onModuleInit`, `onApplicationBootstrap`, etc.) support async operations. However, misusing them can block application startup or cause race conditions. Understand the lifecycle order and use hooks appropriately. **Incorrect (fire-and-forget async without await):** ```typescript // Fire-and-forget async without await @Injectable() export class DatabaseService implements OnModuleInit { onModuleInit() { // This runs but doesn't block - app starts before DB is ready! this.connect(); } private async connect() { await this.pool.connect(); console.log('Database connected'); } } // Heavy blocking operations in constructor @Injectable() export class ConfigService { private config: Config; constructor() { // BLOCKS entire module instantiation synchronously this.config = fs.readFileSync('config.json'); } } ``` **Correct (return promises from async hooks):** ```typescript // Return promise from async hooks @Injectable() export class DatabaseService implements OnModuleInit { private pool: Pool; async onModuleInit(): Promise { // NestJS waits for this to complete before continuing await this.pool.connect(); console.log('Database connected'); } async onModuleDestroy(): Promise { // Clean up resources on shutdown await this.pool.end(); console.log('Database disconnected'); } } // Use onApplicationBootstrap for cross-module dependencies @Injectable() export class CacheWarmerService implements OnApplicationBootstrap { constructor( private cache: CacheService, private products: ProductsService, ) {} async onApplicationBootstrap(): Promise { // All modules are initialized, safe to warm cache const products = await this.products.findPopular(); await this.cache.warmup(products); } } // Heavy init in async hooks, not constructor @Injectable() export class ConfigService implements OnModuleInit { private config: Config; constructor() { // Keep constructor synchronous and fast } async onModuleInit(): Promise { // Async loading in lifecycle hook this.config = await this.loadConfig(); } private async loadConfig(): Promise { const file = await fs.promises.readFile('config.json'); return JSON.parse(file.toString()); } get(key: string): T { return this.config[key]; } } // Enable shutdown hooks in main.ts async function bootstrap() { const app = await NestFactory.create(AppModule); app.enableShutdownHooks(); // Enable SIGTERM/SIGINT handling await app.listen(3000); } ``` Reference: [NestJS Lifecycle Events](https://docs.nestjs.com/fundamentals/lifecycle-events) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/perf-lazy-loading.md ================================================ --- title: Use Lazy Loading for Large Modules impact: MEDIUM impactDescription: Improves startup time for large applications tags: performance, lazy-loading, modules, optimization --- ## Use Lazy Loading for Large Modules NestJS supports lazy-loading modules, which defers initialization until first use. This is valuable for large applications where some features are rarely used, serverless deployments where cold start time matters, or when certain modules have heavy initialization costs. **Incorrect (loading everything eagerly):** ```typescript // Load everything eagerly in a large app @Module({ imports: [ UsersModule, OrdersModule, PaymentsModule, ReportsModule, // Heavy, rarely used AnalyticsModule, // Heavy, rarely used AdminModule, // Only admins use this LegacyModule, // Migration module, rarely used BulkImportModule, // Used once a month ], }) export class AppModule {} // All modules initialize at startup, even if never used // Slow cold starts in serverless // Memory wasted on unused modules ``` **Correct (lazy load rarely-used modules):** ```typescript // Use LazyModuleLoader for optional modules import { LazyModuleLoader } from '@nestjs/core'; @Injectable() export class ReportsService { constructor(private lazyModuleLoader: LazyModuleLoader) {} async generateReport(type: string): Promise { // Load module only when needed const { ReportsModule } = await import('./reports/reports.module'); const moduleRef = await this.lazyModuleLoader.load(() => ReportsModule); const reportsService = moduleRef.get(ReportsGeneratorService); return reportsService.generate(type); } } // Lazy load admin features with caching @Injectable() export class AdminService { private adminModule: ModuleRef | null = null; constructor(private lazyModuleLoader: LazyModuleLoader) {} private async getAdminModule(): Promise { if (!this.adminModule) { const { AdminModule } = await import('./admin/admin.module'); this.adminModule = await this.lazyModuleLoader.load(() => AdminModule); } return this.adminModule; } async runAdminTask(task: string): Promise { const moduleRef = await this.getAdminModule(); const taskRunner = moduleRef.get(AdminTaskRunner); await taskRunner.run(task); } } // Reusable lazy loader service @Injectable() export class ModuleLoaderService { private loadedModules = new Map(); constructor(private lazyModuleLoader: LazyModuleLoader) {} async load(key: string, importFn: () => Promise<{ default: Type } | Type>): Promise { if (!this.loadedModules.has(key)) { const module = await importFn(); const moduleType = 'default' in module ? module.default : module; const moduleRef = await this.lazyModuleLoader.load(() => moduleType); this.loadedModules.set(key, moduleRef); } return this.loadedModules.get(key)!; } } // Preload modules in background after startup @Injectable() export class ModulePreloader implements OnApplicationBootstrap { constructor(private lazyModuleLoader: LazyModuleLoader) {} async onApplicationBootstrap(): Promise { setTimeout(async () => { await this.preloadModule(() => import('./reports/reports.module')); }, 5000); // 5 seconds after startup } private async preloadModule(importFn: () => Promise): Promise { try { const module = await importFn(); const moduleType = module.default || Object.values(module)[0]; await this.lazyModuleLoader.load(() => moduleType); } catch (error) { console.warn('Failed to preload module', error); } } } ``` Reference: [NestJS Lazy Loading Modules](https://docs.nestjs.com/fundamentals/lazy-loading-modules) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/perf-optimize-database.md ================================================ --- title: Optimize Database Queries impact: HIGH impactDescription: Database queries are typically the largest source of latency tags: performance, database, queries, optimization --- ## Optimize Database Queries Select only needed columns, use proper indexes, avoid over-fetching relations, and consider query performance when designing your data access. Most API slowness traces back to inefficient database queries. **Incorrect (over-fetching data and missing indexes):** ```typescript // Select everything when you need few fields @Injectable() export class UsersService { async findAllEmails(): Promise { const users = await this.repo.find(); // Fetches ALL columns for ALL users return users.map(u => u.email); } async getUserSummary(id: string): Promise { const user = await this.repo.findOne({ where: { id }, relations: ['posts', 'posts.comments', 'posts.comments.author', 'followers'], }); // Over-fetches massive relation tree return { name: user.name, postCount: user.posts.length }; } } // No indexes on frequently queried columns @Entity() export class Order { @Column() userId: string; // No index - full table scan on every lookup @Column() status: string; // No index - slow status filtering } ``` **Correct (select only needed data with proper indexes):** ```typescript // Select only needed columns @Injectable() export class UsersService { async findAllEmails(): Promise { const users = await this.repo.find({ select: ['email'], // Only fetch email column }); return users.map(u => u.email); } // Use QueryBuilder for complex selections async getUserSummary(id: string): Promise { return this.repo .createQueryBuilder('user') .select('user.name', 'name') .addSelect('COUNT(post.id)', 'postCount') .leftJoin('user.posts', 'post') .where('user.id = :id', { id }) .groupBy('user.id') .getRawOne(); } // Fetch relations only when needed async getFullProfile(id: string): Promise { return this.repo.findOne({ where: { id }, relations: ['posts'], // Only immediate relation select: { id: true, name: true, email: true, posts: { id: true, title: true, }, }, }); } } // Add indexes on frequently queried columns @Entity() @Index(['userId']) @Index(['status']) @Index(['createdAt']) @Index(['userId', 'status']) // Composite index for common query pattern export class Order { @PrimaryGeneratedColumn('uuid') id: string; @Column() userId: string; @Column() status: string; @CreateDateColumn() createdAt: Date; } // Always paginate large datasets @Injectable() export class OrdersService { async findAll(page = 1, limit = 20): Promise> { const [items, total] = await this.repo.findAndCount({ skip: (page - 1) * limit, take: limit, order: { createdAt: 'DESC' }, }); return { items, meta: { page, limit, total, totalPages: Math.ceil(total / limit), }, }; } } ``` Reference: [TypeORM Query Builder](https://typeorm.io/select-query-builder) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/perf-use-caching.md ================================================ --- title: Use Caching Strategically impact: HIGH impactDescription: Dramatically reduces database load and response times tags: performance, caching, redis, optimization --- ## Use Caching Strategically Implement caching for expensive operations, frequently accessed data, and external API calls. Use NestJS CacheModule with appropriate TTLs and cache invalidation strategies. Don't cache everything - focus on high-impact areas. **Incorrect (no caching or caching everything):** ```typescript // No caching for expensive, repeated queries @Injectable() export class ProductsService { async getPopular(): Promise { // Runs complex aggregation query EVERY request return this.productsRepo .createQueryBuilder('p') .leftJoin('p.orders', 'o') .select('p.*, COUNT(o.id) as orderCount') .groupBy('p.id') .orderBy('orderCount', 'DESC') .limit(20) .getMany(); } } // Cache everything without thought @Injectable() export class UsersService { @CacheKey('users') @CacheTTL(3600) @UseInterceptors(CacheInterceptor) async findAll(): Promise { // Caching user list for 1 hour is wrong if data changes frequently return this.usersRepo.find(); } } ``` **Correct (strategic caching with proper invalidation):** ```typescript // Setup caching module @Module({ imports: [ CacheModule.registerAsync({ imports: [ConfigModule], inject: [ConfigService], useFactory: (config: ConfigService) => ({ stores: [new KeyvRedis(config.get('REDIS_URL'))], ttl: 60 * 1000, // Default 60s }), }), ], }) export class AppModule {} // Manual caching for granular control @Injectable() export class ProductsService { constructor( @Inject(CACHE_MANAGER) private cache: Cache, private productsRepo: ProductRepository, ) {} async getPopular(): Promise { const cacheKey = 'products:popular'; // Try cache first const cached = await this.cache.get(cacheKey); if (cached) return cached; // Cache miss - fetch and cache const products = await this.fetchPopularProducts(); await this.cache.set(cacheKey, products, 5 * 60 * 1000); // 5 min TTL return products; } // Invalidate cache on changes async updateProduct(id: string, dto: UpdateProductDto): Promise { const product = await this.productsRepo.save({ id, ...dto }); await this.cache.del('products:popular'); // Invalidate return product; } } // Decorator-based caching with auto-interceptor @Controller('categories') @UseInterceptors(CacheInterceptor) export class CategoriesController { @Get() @CacheTTL(30 * 60 * 1000) // 30 minutes - categories rarely change findAll(): Promise { return this.categoriesService.findAll(); } @Get(':id') @CacheTTL(60 * 1000) // 1 minute @CacheKey('category') findOne(@Param('id') id: string): Promise { return this.categoriesService.findOne(id); } } // Event-based cache invalidation @Injectable() export class CacheInvalidationService { constructor(@Inject(CACHE_MANAGER) private cache: Cache) {} @OnEvent('product.created') @OnEvent('product.updated') @OnEvent('product.deleted') async invalidateProductCaches(event: ProductEvent) { await Promise.all([this.cache.del('products:popular'), this.cache.del(`product:${event.productId}`)]); } } ``` Reference: [NestJS Caching](https://docs.nestjs.com/techniques/caching) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/security-auth-jwt.md ================================================ --- title: Implement Secure JWT Authentication impact: CRITICAL impactDescription: Essential for secure APIs tags: security, jwt, authentication, tokens --- ## Implement Secure JWT Authentication Use `@nestjs/jwt` with `@nestjs/passport` for authentication. Store secrets securely, use appropriate token lifetimes, implement refresh tokens, and validate tokens properly. Never expose sensitive data in JWT payloads. **Incorrect (insecure JWT implementation):** ```typescript // Hardcode secrets @Module({ imports: [ JwtModule.register({ secret: 'my-secret-key', // Exposed in code signOptions: { expiresIn: '7d' }, // Too long }), ], }) export class AuthModule {} // Store sensitive data in JWT async login(user: User): Promise<{ accessToken: string }> { const payload = { sub: user.id, email: user.email, password: user.password, // NEVER include password! ssn: user.ssn, // NEVER include sensitive data! isAdmin: user.isAdmin, // Can be tampered if not verified }; return { accessToken: this.jwtService.sign(payload) }; } // Skip token validation @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor() { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), secretOrKey: 'my-secret', }); } async validate(payload: any): Promise { return payload; // No validation of user existence } } ``` **Correct (secure JWT with refresh tokens):** ```typescript // Secure JWT configuration @Module({ imports: [ JwtModule.registerAsync({ imports: [ConfigModule], inject: [ConfigService], useFactory: (config: ConfigService) => ({ secret: config.get('JWT_SECRET'), signOptions: { expiresIn: '15m', // Short-lived access tokens issuer: config.get('JWT_ISSUER'), audience: config.get('JWT_AUDIENCE'), }, }), }), PassportModule.register({ defaultStrategy: 'jwt' }), ], }) export class AuthModule {} // Minimal JWT payload @Injectable() export class AuthService { async login(user: User): Promise { // Only include necessary, non-sensitive data const payload: JwtPayload = { sub: user.id, email: user.email, roles: user.roles, iat: Math.floor(Date.now() / 1000), }; const accessToken = this.jwtService.sign(payload); const refreshToken = await this.createRefreshToken(user.id); return { accessToken, refreshToken, expiresIn: 900 }; } private async createRefreshToken(userId: string): Promise { const token = randomBytes(32).toString('hex'); const hashedToken = await bcrypt.hash(token, 10); await this.refreshTokenRepo.save({ userId, token: hashedToken, expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days }); return token; } } // Proper JWT strategy with validation @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor( private config: ConfigService, private usersService: UsersService, ) { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), secretOrKey: config.get('JWT_SECRET'), ignoreExpiration: false, issuer: config.get('JWT_ISSUER'), audience: config.get('JWT_AUDIENCE'), }); } async validate(payload: JwtPayload): Promise { // Verify user still exists and is active const user = await this.usersService.findById(payload.sub); if (!user || !user.isActive) { throw new UnauthorizedException('User not found or inactive'); } // Verify token wasn't issued before password change if (user.passwordChangedAt) { const tokenIssuedAt = new Date(payload.iat * 1000); if (tokenIssuedAt < user.passwordChangedAt) { throw new UnauthorizedException('Token invalidated by password change'); } } return user; } } ``` Reference: [NestJS Authentication](https://docs.nestjs.com/security/authentication) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/security-rate-limiting.md ================================================ --- title: Implement Rate Limiting impact: HIGH impactDescription: Protects against abuse and ensures fair resource usage tags: security, rate-limiting, throttler, protection --- ## Implement Rate Limiting Use `@nestjs/throttler` to limit request rates per client. Apply different limits for different endpoints - stricter for auth endpoints, more relaxed for read operations. Consider using Redis for distributed rate limiting in clustered deployments. **Incorrect (no rate limiting on sensitive endpoints):** ```typescript // No rate limiting on sensitive endpoints @Controller('auth') export class AuthController { @Post('login') async login(@Body() dto: LoginDto): Promise { // Attackers can brute-force credentials return this.authService.login(dto); } @Post('forgot-password') async forgotPassword(@Body() dto: ForgotPasswordDto): Promise { // Can be abused to spam users with emails return this.authService.sendResetEmail(dto.email); } } // Same limits for all endpoints @UseGuards(ThrottlerGuard) @Controller('api') export class ApiController { @Get('public-data') async getPublic() {} // Should allow more requests @Post('process-payment') async payment() {} // Should be more restrictive } ``` **Correct (configured throttler with endpoint-specific limits):** ```typescript // Configure throttler globally with multiple limits import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler'; @Module({ imports: [ ThrottlerModule.forRoot([ { name: 'short', ttl: 1000, // 1 second limit: 3, // 3 requests per second }, { name: 'medium', ttl: 10000, // 10 seconds limit: 20, // 20 requests per 10 seconds }, { name: 'long', ttl: 60000, // 1 minute limit: 100, // 100 requests per minute }, ]), ], providers: [ { provide: APP_GUARD, useClass: ThrottlerGuard, }, ], }) export class AppModule {} // Override limits per endpoint @Controller('auth') export class AuthController { @Post('login') @Throttle({ short: { limit: 5, ttl: 60000 } }) // 5 attempts per minute async login(@Body() dto: LoginDto): Promise { return this.authService.login(dto); } @Post('forgot-password') @Throttle({ short: { limit: 3, ttl: 3600000 } }) // 3 per hour async forgotPassword(@Body() dto: ForgotPasswordDto): Promise { return this.authService.sendResetEmail(dto.email); } } // Skip throttling for certain routes @Controller('health') export class HealthController { @Get() @SkipThrottle() check(): string { return 'OK'; } } // Custom throttle per user type @Injectable() export class CustomThrottlerGuard extends ThrottlerGuard { protected async getTracker(req: Request): Promise { // Use user ID if authenticated, IP otherwise return req.user?.id || req.ip; } protected async getLimit(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest(); // Higher limits for authenticated users if (request.user) { return request.user.isPremium ? 1000 : 200; } return 50; // Anonymous users } } ``` Reference: [NestJS Throttler](https://docs.nestjs.com/security/rate-limiting) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/security-sanitize-output.md ================================================ --- title: Sanitize Output to Prevent XSS impact: HIGH impactDescription: XSS vulnerabilities can compromise user sessions and data tags: security, xss, sanitization, html --- ## Sanitize Output to Prevent XSS While NestJS APIs typically return JSON (which browsers don't execute), XSS risks exist when rendering HTML, storing user content, or when frontend frameworks improperly handle API responses. Sanitize user-generated content before storage and use proper Content-Type headers. **Incorrect (storing raw HTML without sanitization):** ```typescript // Store raw HTML from users @Injectable() export class CommentsService { async create(dto: CreateCommentDto): Promise { // User can inject: return this.repo.save({ content: dto.content, // Raw, unsanitized authorId: dto.authorId, }); } } // Return HTML without sanitization @Controller('pages') export class PagesController { @Get(':slug') @Header('Content-Type', 'text/html') async getPage(@Param('slug') slug: string): Promise { const page = await this.pagesService.findBySlug(slug); // If page.content contains user input, XSS is possible return `${page.content}`; } } // Reflect user input in errors @Get(':id') async findOne(@Param('id') id: string): Promise { const user = await this.repo.findOne({ where: { id } }); if (!user) { // XSS if id contains malicious content and error is rendered throw new NotFoundException(`User ${id} not found`); } return user; } ``` **Correct (sanitize content and use proper headers):** ```typescript // Sanitize HTML content before storage import * as sanitizeHtml from 'sanitize-html'; @Injectable() export class CommentsService { private readonly sanitizeOptions: sanitizeHtml.IOptions = { allowedTags: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'], allowedAttributes: { a: ['href', 'title'], }, allowedSchemes: ['http', 'https', 'mailto'], }; async create(dto: CreateCommentDto): Promise { return this.repo.save({ content: sanitizeHtml(dto.content, this.sanitizeOptions), authorId: dto.authorId, }); } } // Use validation pipe to strip HTML import { Transform } from 'class-transformer'; export class CreatePostDto { @IsString() @MaxLength(1000) @Transform(({ value }) => sanitizeHtml(value, { allowedTags: [] })) title: string; @IsString() @Transform(({ value }) => sanitizeHtml(value, { allowedTags: ['p', 'br', 'b', 'i', 'a'], allowedAttributes: { a: ['href'] }, }), ) content: string; } // Set proper Content-Type headers @Controller('api') export class ApiController { @Get('data') @Header('Content-Type', 'application/json') async getData(): Promise { // JSON response - browser won't execute scripts return this.service.getData(); } } // Sanitize error messages @Get(':id') async findOne(@Param('id', ParseUUIDPipe) id: string): Promise { const user = await this.repo.findOne({ where: { id } }); if (!user) { // UUID validation ensures safe format throw new NotFoundException('User not found'); } return user; } // Use Helmet for CSP headers import helmet from 'helmet'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.use( helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], scriptSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], imgSrc: ["'self'", 'data:', 'https:'], }, }, }), ); await app.listen(3000); } ``` Reference: [OWASP XSS Prevention](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/security-use-guards.md ================================================ --- title: Use Guards for Authentication and Authorization impact: HIGH impactDescription: Enforces access control before handlers execute tags: security, guards, authentication, authorization --- ## Use Guards for Authentication and Authorization Guards determine whether a request should be handled based on authentication state, roles, permissions, or other conditions. They run after middleware but before pipes and interceptors, making them ideal for access control. Use guards instead of manual checks in controllers. **Incorrect (manual auth checks in every handler):** ```typescript // Manual auth checks in every handler @Controller('admin') export class AdminController { @Get('users') async getUsers(@Request() req) { if (!req.user) { throw new UnauthorizedException(); } if (!req.user.roles.includes('admin')) { throw new ForbiddenException(); } return this.adminService.getUsers(); } @Delete('users/:id') async deleteUser(@Request() req, @Param('id') id: string) { if (!req.user) { throw new UnauthorizedException(); } if (!req.user.roles.includes('admin')) { throw new ForbiddenException(); } return this.adminService.deleteUser(id); } } ``` **Correct (guards with declarative decorators):** ```typescript // JWT Auth Guard @Injectable() export class JwtAuthGuard implements CanActivate { constructor( private jwtService: JwtService, private reflector: Reflector, ) {} async canActivate(context: ExecutionContext): Promise { // Check for @Public() decorator const isPublic = this.reflector.getAllAndOverride('isPublic', [context.getHandler(), context.getClass()]); if (isPublic) return true; const request = context.switchToHttp().getRequest(); const token = this.extractToken(request); if (!token) { throw new UnauthorizedException('No token provided'); } try { request.user = await this.jwtService.verifyAsync(token); return true; } catch { throw new UnauthorizedException('Invalid token'); } } private extractToken(request: Request): string | undefined { const [type, token] = request.headers.authorization?.split(' ') ?? []; return type === 'Bearer' ? token : undefined; } } // Roles Guard @Injectable() export class RolesGuard implements CanActivate { constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { const requiredRoles = this.reflector.getAllAndOverride('roles', [context.getHandler(), context.getClass()]); if (!requiredRoles) return true; const { user } = context.switchToHttp().getRequest(); return requiredRoles.some(role => user.roles?.includes(role)); } } // Decorators export const Public = () => SetMetadata('isPublic', true); export const Roles = (...roles: Role[]) => SetMetadata('roles', roles); // Register guards globally @Module({ providers: [ { provide: APP_GUARD, useClass: JwtAuthGuard }, { provide: APP_GUARD, useClass: RolesGuard }, ], }) export class AppModule {} // Clean controller @Controller('admin') @Roles(Role.Admin) // Applied to all routes export class AdminController { @Get('users') getUsers(): Promise { return this.adminService.getUsers(); } @Delete('users/:id') deleteUser(@Param('id') id: string): Promise { return this.adminService.deleteUser(id); } @Public() // Override: no auth required @Get('health') health() { return { status: 'ok' }; } } ``` Reference: [NestJS Guards](https://docs.nestjs.com/guards) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/security-validate-all-input.md ================================================ --- title: Validate All Input with DTOs and Pipes impact: HIGH impactDescription: First line of defense against attacks tags: security, validation, dto, pipes --- ## Validate All Input with DTOs and Pipes Always validate incoming data using class-validator decorators on DTOs and the global ValidationPipe. Never trust user input. Validate all request bodies, query parameters, and route parameters before processing. **Incorrect (trust raw input without validation):** ```typescript // Trust raw input without validation @Controller('users') export class UsersController { @Post() create(@Body() body: any) { // body could contain anything - SQL injection, XSS, etc. return this.usersService.create(body); } @Get() findAll(@Query() query: any) { // query.limit could be "'; DROP TABLE users; --" return this.usersService.findAll(query.limit); } } // DTOs without validation decorators export class CreateUserDto { name: string; // No validation email: string; // Could be "not-an-email" age: number; // Could be "abc" or -999 } ``` **Correct (validated DTOs with global ValidationPipe):** ```typescript // Enable ValidationPipe globally in main.ts async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes( new ValidationPipe({ whitelist: true, // Strip unknown properties forbidNonWhitelisted: true, // Throw on unknown properties transform: true, // Auto-transform to DTO types transformOptions: { enableImplicitConversion: true, }, }), ); await app.listen(3000); } // Create well-validated DTOs import { IsString, IsEmail, IsInt, Min, Max, IsOptional, MinLength, MaxLength, Matches, IsNotEmpty, } from 'class-validator'; import { Transform, Type } from 'class-transformer'; export class CreateUserDto { @IsString() @IsNotEmpty() @MinLength(2) @MaxLength(100) @Transform(({ value }) => value?.trim()) name: string; @IsEmail() @Transform(({ value }) => value?.toLowerCase().trim()) email: string; @IsInt() @Min(0) @Max(150) age: number; @IsString() @MinLength(8) @MaxLength(100) @Matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/, { message: 'Password must contain uppercase, lowercase, and number', }) password: string; } // Query DTO with defaults and transformation export class FindUsersQueryDto { @IsOptional() @IsString() @MaxLength(100) search?: string; @IsOptional() @Type(() => Number) @IsInt() @Min(1) @Max(100) limit: number = 20; @IsOptional() @Type(() => Number) @IsInt() @Min(0) offset: number = 0; } // Param validation export class UserIdParamDto { @IsUUID('4') id: string; } @Controller('users') export class UsersController { @Post() create(@Body() dto: CreateUserDto): Promise { // dto is guaranteed to be valid return this.usersService.create(dto); } @Get() findAll(@Query() query: FindUsersQueryDto): Promise { // query.limit is a number, query.search is sanitized return this.usersService.findAll(query); } @Get(':id') findOne(@Param() params: UserIdParamDto): Promise { // params.id is a valid UUID return this.usersService.findById(params.id); } } ``` Reference: [NestJS Validation](https://docs.nestjs.com/techniques/validation) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/test-e2e-supertest.md ================================================ --- title: Use Supertest for E2E Testing impact: HIGH impactDescription: Validates the full request/response cycle tags: testing, e2e, supertest, integration --- ## Use Supertest for E2E Testing End-to-end tests use Supertest to make real HTTP requests against your NestJS application. They test the full stack including middleware, guards, pipes, and interceptors. E2E tests catch integration issues that unit tests miss. **Incorrect (no proper E2E setup or teardown):** ```typescript // Only unit test controllers describe('UsersController', () => { it('should return users', async () => { const service = { findAll: jest.fn().mockResolvedValue([]) }; const controller = new UsersController(service as any); const result = await controller.findAll(); expect(result).toEqual([]); // Doesn't test: routes, guards, pipes, serialization }); }); // E2E tests without proper setup/teardown describe('Users API', () => { it('should create user', async () => { const app = await NestFactory.create(AppModule); // No proper initialization // No cleanup after test // Hits real database }); }); ``` **Correct (proper E2E setup with Supertest):** ```typescript // Proper E2E test setup import { Test, TestingModule } from '@nestjs/testing'; import { INestApplication, ValidationPipe } from '@nestjs/common'; import * as request from 'supertest'; import { AppModule } from '../src/app.module'; describe('UsersController (e2e)', () => { let app: INestApplication; beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [AppModule], }).compile(); app = moduleFixture.createNestApplication(); // Apply same config as production app.useGlobalPipes( new ValidationPipe({ whitelist: true, transform: true, forbidNonWhitelisted: true, }), ); await app.init(); }); afterAll(async () => { await app.close(); }); describe('/users (POST)', () => { it('should create a user', () => { return request(app.getHttpServer()) .post('/users') .send({ name: 'John', email: 'john@test.com' }) .expect(201) .expect(res => { expect(res.body).toHaveProperty('id'); expect(res.body.name).toBe('John'); expect(res.body.email).toBe('john@test.com'); }); }); it('should return 400 for invalid email', () => { return request(app.getHttpServer()) .post('/users') .send({ name: 'John', email: 'invalid-email' }) .expect(400) .expect(res => { expect(res.body.message).toContain('email'); }); }); }); describe('/users/:id (GET)', () => { it('should return 404 for non-existent user', () => { return request(app.getHttpServer()).get('/users/non-existent-id').expect(404); }); }); }); // Testing with authentication describe('Protected Routes (e2e)', () => { let app: INestApplication; let authToken: string; beforeAll(async () => { const moduleFixture = await Test.createTestingModule({ imports: [AppModule], }).compile(); app = moduleFixture.createNestApplication(); app.useGlobalPipes(new ValidationPipe({ whitelist: true })); await app.init(); // Get auth token const loginResponse = await request(app.getHttpServer()) .post('/auth/login') .send({ email: 'test@test.com', password: 'password' }); authToken = loginResponse.body.accessToken; }); it('should return 401 without token', () => { return request(app.getHttpServer()).get('/users/me').expect(401); }); it('should return user profile with valid token', () => { return request(app.getHttpServer()) .get('/users/me') .set('Authorization', `Bearer ${authToken}`) .expect(200) .expect(res => { expect(res.body.email).toBe('test@test.com'); }); }); }); // Database isolation for E2E tests describe('Orders API (e2e)', () => { let app: INestApplication; let dataSource: DataSource; beforeAll(async () => { const moduleFixture = await Test.createTestingModule({ imports: [ ConfigModule.forRoot({ envFilePath: '.env.test', // Test database config }), AppModule, ], }).compile(); app = moduleFixture.createNestApplication(); dataSource = moduleFixture.get(DataSource); await app.init(); }); beforeEach(async () => { // Clean database between tests await dataSource.synchronize(true); }); afterAll(async () => { await dataSource.destroy(); await app.close(); }); }); ``` Reference: [NestJS E2E Testing](https://docs.nestjs.com/fundamentals/testing#end-to-end-testing) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/test-mock-external-services.md ================================================ --- title: Mock External Services in Tests impact: HIGH impactDescription: Ensures fast, reliable, deterministic tests tags: testing, mocking, external-services, jest --- ## Mock External Services in Tests Never call real external services (APIs, databases, message queues) in unit tests. Mock them to ensure tests are fast, deterministic, and don't incur costs. Use realistic mock data and test edge cases like timeouts and errors. **Incorrect (calling real APIs and databases):** ```typescript // Call real APIs in tests describe('PaymentService', () => { it('should process payment', async () => { const service = new PaymentService(new StripeClient(realApiKey)); // Hits real Stripe API! const result = await service.charge('tok_visa', 1000); // Slow, costs money, flaky }); }); // Use real database describe('UsersService', () => { beforeEach(async () => { await connection.query('DELETE FROM users'); // Modifies real DB }); it('should create user', async () => { const user = await service.create({ email: 'test@test.com' }); // Side effects on shared database }); }); // Incomplete mocks const mockHttpService = { get: jest.fn().mockResolvedValue({ data: {} }), // Missing error scenarios, missing other methods }; ``` **Correct (mock all external dependencies):** ```typescript // Mock HTTP service properly describe('WeatherService', () => { let service: WeatherService; let httpService: jest.Mocked; beforeEach(async () => { const module = await Test.createTestingModule({ providers: [ WeatherService, { provide: HttpService, useValue: { get: jest.fn(), post: jest.fn(), }, }, ], }).compile(); service = module.get(WeatherService); httpService = module.get(HttpService); }); it('should return weather data', async () => { const mockResponse = { data: { temperature: 72, humidity: 45 }, status: 200, statusText: 'OK', headers: {}, config: {}, }; httpService.get.mockReturnValue(of(mockResponse)); const result = await service.getWeather('NYC'); expect(result).toEqual({ temperature: 72, humidity: 45 }); }); it('should handle API timeout', async () => { httpService.get.mockReturnValue(throwError(() => new Error('ETIMEDOUT'))); await expect(service.getWeather('NYC')).rejects.toThrow('Weather service unavailable'); }); it('should handle rate limiting', async () => { httpService.get.mockReturnValue( throwError(() => ({ response: { status: 429, data: { message: 'Rate limited' } }, })), ); await expect(service.getWeather('NYC')).rejects.toThrow(TooManyRequestsException); }); }); // Mock repository instead of database describe('UsersService', () => { let service: UsersService; let repo: jest.Mocked>; beforeEach(async () => { const mockRepo = { find: jest.fn(), findOne: jest.fn(), save: jest.fn(), delete: jest.fn(), createQueryBuilder: jest.fn(), }; const module = await Test.createTestingModule({ providers: [UsersService, { provide: getRepositoryToken(User), useValue: mockRepo }], }).compile(); service = module.get(UsersService); repo = module.get(getRepositoryToken(User)); }); it('should find user by id', async () => { const mockUser = { id: '1', name: 'John', email: 'john@test.com' }; repo.findOne.mockResolvedValue(mockUser); const result = await service.findById('1'); expect(result).toEqual(mockUser); expect(repo.findOne).toHaveBeenCalledWith({ where: { id: '1' } }); }); }); // Create mock factory for complex SDKs function createMockStripe(): jest.Mocked { return { paymentIntents: { create: jest.fn(), retrieve: jest.fn(), confirm: jest.fn(), cancel: jest.fn(), }, customers: { create: jest.fn(), retrieve: jest.fn(), }, } as any; } // Mock time for time-dependent tests describe('TokenService', () => { beforeEach(() => { jest.useFakeTimers(); jest.setSystemTime(new Date('2024-01-15')); }); afterEach(() => { jest.useRealTimers(); }); it('should expire token after 1 hour', async () => { const token = await service.createToken(); // Fast-forward time jest.advanceTimersByTime(61 * 60 * 1000); expect(await service.isValid(token)).toBe(false); }); }); ``` Reference: [Jest Mocking](https://jestjs.io/docs/mock-functions) ================================================ FILE: .agents/skills/nestjs-best-practices/rules/test-use-testing-module.md ================================================ --- title: Use Testing Module for Unit Tests impact: HIGH impactDescription: Enables proper isolated testing with mocked dependencies tags: testing, unit-tests, mocking, jest --- ## Use Testing Module for Unit Tests Use `@nestjs/testing` module to create isolated test environments with mocked dependencies. This ensures your tests run fast, don't depend on external services, and properly test your business logic in isolation. **Incorrect (manual instantiation bypassing DI):** ```typescript // Instantiate services manually without DI describe('UsersService', () => { it('should create user', async () => { // Manual instantiation bypasses DI const repo = new UserRepository(); // Real repo! const service = new UsersService(repo); const user = await service.create({ name: 'Test' }); // This hits the real database! }); }); // Test implementation details describe('UsersController', () => { it('should call service', async () => { const service = { create: jest.fn() }; const controller = new UsersController(service as any); await controller.create({ name: 'Test' }); expect(service.create).toHaveBeenCalled(); // Tests implementation, not behavior }); }); ``` **Correct (use Test.createTestingModule with mocked dependencies):** ```typescript // Use Test.createTestingModule for proper DI import { Test, TestingModule } from '@nestjs/testing'; describe('UsersService', () => { let service: UsersService; let repo: jest.Mocked; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ UsersService, { provide: UserRepository, useValue: { save: jest.fn(), findOne: jest.fn(), find: jest.fn(), }, }, ], }).compile(); service = module.get(UsersService); repo = module.get(UserRepository); }); afterEach(() => { jest.clearAllMocks(); }); describe('create', () => { it('should save and return user', async () => { const dto = { name: 'John', email: 'john@test.com' }; const expectedUser = { id: '1', ...dto }; repo.save.mockResolvedValue(expectedUser); const result = await service.create(dto); expect(result).toEqual(expectedUser); expect(repo.save).toHaveBeenCalledWith(dto); }); it('should throw on duplicate email', async () => { repo.findOne.mockResolvedValue({ id: '1', email: 'test@test.com' }); await expect(service.create({ name: 'Test', email: 'test@test.com' })).rejects.toThrow(ConflictException); }); }); describe('findById', () => { it('should return user when found', async () => { const user = { id: '1', name: 'John' }; repo.findOne.mockResolvedValue(user); const result = await service.findById('1'); expect(result).toEqual(user); }); it('should throw NotFoundException when not found', async () => { repo.findOne.mockResolvedValue(null); await expect(service.findById('999')).rejects.toThrow(NotFoundException); }); }); }); // Testing guards and interceptors describe('RolesGuard', () => { let guard: RolesGuard; let reflector: Reflector; beforeEach(async () => { const module = await Test.createTestingModule({ providers: [RolesGuard, Reflector], }).compile(); guard = module.get(RolesGuard); reflector = module.get(Reflector); }); it('should allow when no roles required', () => { const context = createMockExecutionContext({ user: { roles: [] } }); jest.spyOn(reflector, 'getAllAndOverride').mockReturnValue(undefined); expect(guard.canActivate(context)).toBe(true); }); it('should allow admin for admin-only route', () => { const context = createMockExecutionContext({ user: { roles: ['admin'] } }); jest.spyOn(reflector, 'getAllAndOverride').mockReturnValue(['admin']); expect(guard.canActivate(context)).toBe(true); }); }); function createMockExecutionContext(request: Partial): ExecutionContext { return { switchToHttp: () => ({ getRequest: () => request, }), getHandler: () => jest.fn(), getClass: () => jest.fn(), } as ExecutionContext; } ``` Reference: [NestJS Testing](https://docs.nestjs.com/fundamentals/testing) ================================================ FILE: .agents/skills/nestjs-best-practices/scripts/build-agents.ts ================================================ #!/usr/bin/env npx ts-node /** * Build script for generating AGENTS.md from individual rule files * * Usage: npx ts-node scripts/build-agents.ts * * This script: * 1. Reads all rule files from the rules/ directory * 2. Parses YAML frontmatter for metadata * 3. Groups rules by category based on filename prefix * 4. Generates a consolidated AGENTS.md file */ import * as fs from 'fs'; import * as path from 'path'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Category definitions with ordering and metadata const CATEGORIES = [ { prefix: 'arch-', name: 'Architecture', impact: 'CRITICAL', section: 1 }, { prefix: 'di-', name: 'Dependency Injection', impact: 'CRITICAL', section: 2 }, { prefix: 'error-', name: 'Error Handling', impact: 'HIGH', section: 3 }, { prefix: 'security-', name: 'Security', impact: 'HIGH', section: 4 }, { prefix: 'perf-', name: 'Performance', impact: 'HIGH', section: 5 }, { prefix: 'test-', name: 'Testing', impact: 'MEDIUM-HIGH', section: 6 }, { prefix: 'db-', name: 'Database & ORM', impact: 'MEDIUM-HIGH', section: 7 }, { prefix: 'api-', name: 'API Design', impact: 'MEDIUM', section: 8 }, { prefix: 'micro-', name: 'Microservices', impact: 'MEDIUM', section: 9 }, { prefix: 'devops-', name: 'DevOps & Deployment', impact: 'LOW-MEDIUM', section: 10 }, ]; interface RuleFrontmatter { title: string; impact: string; impactDescription: string; tags: string[]; } interface Rule { filename: string; frontmatter: RuleFrontmatter; content: string; category: string; categorySection: number; } function parseFrontmatter(content: string): { frontmatter: RuleFrontmatter | null; body: string } { const frontmatterRegex = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/; const match = content.match(frontmatterRegex); if (!match) { return { frontmatter: null, body: content }; } const frontmatterStr = match[1]; const body = match[2]; // Simple YAML parsing for our expected format const frontmatter: Partial = {}; const lines = frontmatterStr.split('\n'); let currentKey = ''; let inArray = false; const arrayItems: string[] = []; for (const line of lines) { if (line.match(/^[a-zA-Z]+:/)) { // Save previous array if we were collecting one if (inArray && currentKey === 'tags') { frontmatter.tags = arrayItems; } inArray = false; arrayItems.length = 0; const [key, ...valueParts] = line.split(':'); const value = valueParts.join(':').trim(); currentKey = key.trim(); if (value === '') { // Might be start of array inArray = true; } else { (frontmatter as any)[currentKey] = value; } } else if (inArray && line.trim().startsWith('-')) { arrayItems.push(line.trim().replace(/^-\s*/, '')); } } // Save final array if needed if (inArray && currentKey === 'tags') { frontmatter.tags = arrayItems; } return { frontmatter: frontmatter as RuleFrontmatter, body: body.trim(), }; } function getCategoryForFile(filename: string): { name: string; section: number } | null { for (const cat of CATEGORIES) { if (filename.startsWith(cat.prefix)) { return { name: cat.name, section: cat.section }; } } return null; } function readMetadata(): any { const metadataPath = path.join(__dirname, '..', 'metadata.json'); return JSON.parse(fs.readFileSync(metadataPath, 'utf-8')); } function readRules(): Rule[] { const rulesDir = path.join(__dirname, '..', 'rules'); const files = fs.readdirSync(rulesDir).filter(f => f.endsWith('.md') && !f.startsWith('_')); const rules: Rule[] = []; for (const file of files) { const filePath = path.join(rulesDir, file); const content = fs.readFileSync(filePath, 'utf-8'); const { frontmatter, body } = parseFrontmatter(content); if (!frontmatter) { console.warn(`Warning: No frontmatter found in ${file}`); continue; } const category = getCategoryForFile(file); if (!category) { console.warn(`Warning: Unknown category for ${file}`); continue; } rules.push({ filename: file, frontmatter, content: body, category: category.name, categorySection: category.section, }); } return rules; } function generateTableOfContents(rulesByCategory: Map): string { let toc = '## Table of Contents\n\n'; for (const cat of CATEGORIES) { const rules = rulesByCategory.get(cat.name); if (!rules || rules.length === 0) continue; // Section anchor format: #1-architecture const sectionAnchor = `${cat.section}-${cat.name.toLowerCase().replace(/[^a-z0-9]+/g, '-')}`; toc += `${cat.section}. [${cat.name}](#${sectionAnchor}) — **${cat.impact}**\n`; for (let i = 0; i < rules.length; i++) { const rule = rules[i]; // Rule anchor format: #11-rule-title const ruleNum = `${cat.section}${i + 1}`; const anchor = `${ruleNum}-${rule.frontmatter.title.toLowerCase().replace(/[^a-z0-9]+/g, '-')}`; toc += ` - ${cat.section}.${i + 1} [${rule.frontmatter.title}](#${anchor})\n`; } } return toc; } function generateAgentsMd(rules: Rule[], metadata: any): string { // Group rules by category const rulesByCategory = new Map(); for (const rule of rules) { if (!rulesByCategory.has(rule.category)) { rulesByCategory.set(rule.category, []); } rulesByCategory.get(rule.category)!.push(rule); } // Sort rules within each category alphabetically for (const [category, categoryRules] of rulesByCategory) { categoryRules.sort((a, b) => a.filename.localeCompare(b.filename)); } // Build document let doc = `# NestJS Best Practices **Version ${metadata.version}** ${metadata.organization} ${metadata.date} > **Note:** > This document is mainly for agents and LLMs to follow when maintaining, > generating, or refactoring NestJS codebases. Humans may also find it > useful, but guidance here is optimized for automation and consistency > by AI-assisted workflows. --- ## Abstract ${metadata.abstract} --- `; // Add table of contents doc += generateTableOfContents(rulesByCategory); doc += '\n---\n\n'; // Add rules by category for (const cat of CATEGORIES) { const categoryRules = rulesByCategory.get(cat.name); if (!categoryRules || categoryRules.length === 0) continue; doc += `## ${cat.section}. ${cat.name}\n\n`; doc += `**Section Impact: ${cat.impact}**\n\n`; for (let i = 0; i < categoryRules.length; i++) { const rule = categoryRules[i]; const ruleNumber = `${cat.section}.${i + 1}`; // Add rule header with number (anchor will be auto-generated as #11-title) doc += `### ${ruleNumber} ${rule.frontmatter.title}\n\n`; doc += `**Impact: ${rule.frontmatter.impact}** — ${rule.frontmatter.impactDescription}\n\n`; // Add rule content (skip the first header since we already added it) let ruleContent = rule.content; // Remove the first h1 or h2 header if it matches the title ruleContent = ruleContent.replace(/^#{1,2}\s+.*\n+/, ''); // Remove the impact line if present (we already added it) ruleContent = ruleContent.replace(/^\*\*Impact:.*\*\*.*\n+/, ''); doc += ruleContent; doc += '\n\n---\n\n'; } } // Add references footer doc += `## References `; for (const ref of metadata.references) { doc += `- ${ref}\n`; } doc += ` --- *Generated by build-agents.ts on ${new Date().toISOString().split('T')[0]}* `; return doc; } function main() { console.log('Building AGENTS.md...\n'); const metadata = readMetadata(); console.log(`Version: ${metadata.version}`); console.log(`Organization: ${metadata.organization}\n`); const rules = readRules(); console.log(`Found ${rules.length} rules\n`); // Count by category const counts = new Map(); for (const rule of rules) { counts.set(rule.category, (counts.get(rule.category) || 0) + 1); } console.log('Rules by category:'); for (const cat of CATEGORIES) { const count = counts.get(cat.name) || 0; if (count > 0) { console.log(` ${cat.name}: ${count}`); } } console.log(''); const agentsMd = generateAgentsMd(rules, metadata); const outputPath = path.join(__dirname, '..', 'AGENTS.md'); fs.writeFileSync(outputPath, agentsMd); console.log(`Generated AGENTS.md (${agentsMd.length} bytes)`); console.log(`Output: ${outputPath}`); } main(); ================================================ FILE: .agents/skills/nestjs-best-practices/scripts/build.sh ================================================ #!/bin/bash # Build script for generating AGENTS.md # Usage: ./build.sh SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" # Check if ts-node is available if command -v npx &> /dev/null; then echo "Running build with ts-node..." npx ts-node build-agents.ts else echo "Error: npx not found. Please install Node.js." exit 1 fi ================================================ FILE: .agents/skills/nestjs-best-practices/scripts/package.json ================================================ { "name": "nestjs-best-practices-scripts", "version": "1.0.0", "type": "module", "description": "Build scripts for NestJS Best Practices skillset", "scripts": { "build": "npx ts-node build-agents.ts", "build:watch": "npx nodemon --watch ../rules --ext md --exec 'npx ts-node build-agents.ts'" }, "devDependencies": { "typescript": "^5.0.0", "ts-node": "^10.9.0", "@types/node": "^20.0.0" } } ================================================ FILE: .agents/skills/typeorm/SKILL.md ================================================ --- name: typeorm description: Guidelines for developing with TypeORM, a full-featured ORM for TypeScript and JavaScript supporting multiple databases --- # TypeORM Development Guidelines You are an expert in TypeORM, TypeScript, and database design with a focus on the Data Mapper pattern and enterprise application architecture. ## Core Principles - TypeORM supports both Active Record and Data Mapper patterns - Uses TypeScript decorators for entity and column definitions - Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, and more - Works in Node.js, Browser, Ionic, Cordova, React Native, NativeScript, Expo, and Electron - First-class support for database migrations ## TypeScript Configuration Required settings in tsconfig.json: ```json { "compilerOptions": { "experimentalDecorators": true, "emitDecoratorMetadata": true, "strict": true, "target": "ES2020", "module": "commonjs", "moduleResolution": "node" } } ``` ## Entity Definition ### Basic Entity ```typescript import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm'; @Entity('users') export class User { @PrimaryGeneratedColumn() id: number; @Column({ type: 'varchar', length: 255, unique: true }) email: string; @Column({ type: 'varchar', length: 255, nullable: true }) name: string | null; @Column({ type: 'boolean', default: true }) isActive: boolean; @CreateDateColumn() createdAt: Date; @UpdateDateColumn() updatedAt: Date; } ``` ### Primary Key Options ```typescript // Auto-increment @PrimaryGeneratedColumn() id: number; // UUID @PrimaryGeneratedColumn("uuid") id: string; // Custom primary key @PrimaryColumn() id: string; // Composite primary key @Entity() export class OrderItem { @PrimaryColumn() orderId: number; @PrimaryColumn() productId: number; } ``` ### Column Decorators ```typescript @Entity() export class Product { @PrimaryGeneratedColumn() id: number; // String columns @Column({ type: 'varchar', length: 255 }) name: string; @Column({ type: 'text', nullable: true }) description: string | null; // Numeric columns @Column({ type: 'decimal', precision: 10, scale: 2 }) price: number; @Column({ type: 'int', default: 0 }) stock: number; // Boolean @Column({ type: 'boolean', default: true }) isAvailable: boolean; // JSON @Column({ type: 'jsonb', nullable: true }) metadata: Record | null; // Enum @Column({ type: 'enum', enum: ['active', 'inactive', 'pending'], default: 'pending', }) status: 'active' | 'inactive' | 'pending'; // Timestamps @CreateDateColumn() createdAt: Date; @UpdateDateColumn() updatedAt: Date; @DeleteDateColumn() deletedAt: Date | null; // For soft deletes // Version column for optimistic locking @VersionColumn() version: number; } ``` ## Relationships ### One-to-One ```typescript @Entity() export class User { @PrimaryGeneratedColumn() id: number; @OneToOne(() => Profile, profile => profile.user, { cascade: true }) @JoinColumn() profile: Profile; } @Entity() export class Profile { @PrimaryGeneratedColumn() id: number; @Column() bio: string; @OneToOne(() => User, user => user.profile) user: User; } ``` ### One-to-Many / Many-to-One ```typescript @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column() name: string; @OneToMany(() => Post, post => post.author) posts: Post[]; } @Entity() export class Post { @PrimaryGeneratedColumn() id: number; @Column() title: string; @ManyToOne(() => User, user => user.posts, { onDelete: 'CASCADE' }) @JoinColumn({ name: 'author_id' }) author: User; @Column() authorId: number; // Explicit foreign key column } ``` ### Many-to-Many ```typescript @Entity() export class Post { @PrimaryGeneratedColumn() id: number; @Column() title: string; @ManyToMany(() => Tag, tag => tag.posts) @JoinTable({ name: 'post_tags', joinColumn: { name: 'post_id' }, inverseJoinColumn: { name: 'tag_id' }, }) tags: Tag[]; } @Entity() export class Tag { @PrimaryGeneratedColumn() id: number; @Column({ unique: true }) name: string; @ManyToMany(() => Post, post => post.tags) posts: Post[]; } ``` ## Repository Pattern ### Basic Repository Usage ```typescript import { AppDataSource } from './data-source'; import { User } from './entities/User'; const userRepository = AppDataSource.getRepository(User); // Find all const users = await userRepository.find(); // Find with conditions const activeUsers = await userRepository.find({ where: { isActive: true }, }); // Find one const user = await userRepository.findOne({ where: { id: 1 }, }); // Find or fail const user = await userRepository.findOneOrFail({ where: { id: 1 }, }); // Save const newUser = userRepository.create({ email: 'user@example.com', name: 'John Doe', }); await userRepository.save(newUser); // Update await userRepository.update({ id: 1 }, { name: 'Jane Doe' }); // Delete await userRepository.delete({ id: 1 }); // Soft delete (requires @DeleteDateColumn) await userRepository.softDelete({ id: 1 }); ``` ### Custom Repository ```typescript import { Repository, DataSource } from 'typeorm'; import { User } from './entities/User'; export class UserRepository extends Repository { constructor(private dataSource: DataSource) { super(User, dataSource.createEntityManager()); } async findByEmail(email: string): Promise { return this.findOne({ where: { email } }); } async findActiveUsers(): Promise { return this.find({ where: { isActive: true }, order: { createdAt: 'DESC' }, }); } async findWithPosts(userId: number): Promise { return this.findOne({ where: { id: userId }, relations: ['posts'], }); } } ``` ### Query Builder ```typescript const users = await userRepository .createQueryBuilder('user') .leftJoinAndSelect('user.posts', 'post') .where('user.isActive = :isActive', { isActive: true }) .andWhere('post.publishedAt IS NOT NULL') .orderBy('user.createdAt', 'DESC') .skip(0) .take(10) .getMany(); // With raw results const result = await userRepository .createQueryBuilder('user') .select('COUNT(*)', 'count') .where('user.isActive = :isActive', { isActive: true }) .getRawOne(); // Insert with query builder await userRepository .createQueryBuilder() .insert() .into(User) .values([ { email: 'user1@example.com', name: 'User 1' }, { email: 'user2@example.com', name: 'User 2' }, ]) .execute(); ``` ## Data Source Configuration ```typescript // data-source.ts import { DataSource } from 'typeorm'; import { User } from './entities/User'; import { Post } from './entities/Post'; export const AppDataSource = new DataSource({ type: 'postgres', host: process.env.DB_HOST || 'localhost', port: parseInt(process.env.DB_PORT || '5432'), username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, // Entity configuration entities: [User, Post], // Or use glob pattern: entities: ["src/entities/**/*.ts"] // Migrations migrations: ['src/migrations/**/*.ts'], // Synchronize - NEVER use in production synchronize: false, // Logging logging: process.env.NODE_ENV === 'development', // Connection pool poolSize: 10, // SSL (for production) ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false, }); // Initialize connection AppDataSource.initialize() .then(() => console.log('Data Source initialized')) .catch(error => console.error('Error initializing Data Source:', error)); ``` ## Migrations ### Creating Migrations ```bash # Generate migration from entity changes npx typeorm migration:generate src/migrations/CreateUsers -d src/data-source.ts # Create empty migration npx typeorm migration:create src/migrations/SeedUsers # Run migrations npx typeorm migration:run -d src/data-source.ts # Revert last migration npx typeorm migration:revert -d src/data-source.ts ``` ### Migration File Structure ```typescript import { MigrationInterface, QueryRunner, Table, TableIndex } from 'typeorm'; export class CreateUsers1234567890 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { await queryRunner.createTable( new Table({ name: 'users', columns: [ { name: 'id', type: 'int', isPrimary: true, isGenerated: true, generationStrategy: 'increment', }, { name: 'email', type: 'varchar', length: '255', isUnique: true, }, { name: 'name', type: 'varchar', length: '255', isNullable: true, }, { name: 'is_active', type: 'boolean', default: true, }, { name: 'created_at', type: 'timestamp', default: 'CURRENT_TIMESTAMP', }, { name: 'updated_at', type: 'timestamp', default: 'CURRENT_TIMESTAMP', onUpdate: 'CURRENT_TIMESTAMP', }, ], }), true, ); await queryRunner.createIndex( 'users', new TableIndex({ name: 'IDX_USERS_EMAIL', columnNames: ['email'], }), ); } public async down(queryRunner: QueryRunner): Promise { await queryRunner.dropIndex('users', 'IDX_USERS_EMAIL'); await queryRunner.dropTable('users'); } } ``` ## Transactions ```typescript // Using QueryRunner const queryRunner = AppDataSource.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); try { const user = queryRunner.manager.create(User, { email: 'user@example.com', name: 'User', }); await queryRunner.manager.save(user); const post = queryRunner.manager.create(Post, { title: 'First Post', author: user, }); await queryRunner.manager.save(post); await queryRunner.commitTransaction(); } catch (error) { await queryRunner.rollbackTransaction(); throw error; } finally { await queryRunner.release(); } // Using transaction method await AppDataSource.transaction(async manager => { const user = manager.create(User, { email: 'user@example.com', name: 'User', }); await manager.save(user); const post = manager.create(Post, { title: 'First Post', author: user, }); await manager.save(post); }); ``` ## NestJS Integration ```typescript // app.module.ts import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { User } from './entities/user.entity'; import { UsersModule } from './users/users.module'; @Module({ imports: [ TypeOrmModule.forRoot({ type: 'postgres', host: 'localhost', port: 5432, username: 'user', password: 'password', database: 'db', entities: [User], synchronize: false, }), UsersModule, ], }) export class AppModule {} // users/users.module.ts @Module({ imports: [TypeOrmModule.forFeature([User])], providers: [UsersService], controllers: [UsersController], }) export class UsersModule {} // users/users.service.ts @Injectable() export class UsersService { constructor( @InjectRepository(User) private usersRepository: Repository, ) {} findAll(): Promise { return this.usersRepository.find(); } findOne(id: number): Promise { return this.usersRepository.findOneBy({ id }); } } ``` ## Best Practices ### Use Migrations in Production Never use `synchronize: true` in production. Always use migrations: ```typescript // Development: Use migrations, not sync synchronize: false, ``` ### Eager vs Lazy Loading ```typescript // Eager loading - loads relations automatically @OneToMany(() => Post, (post) => post.author, { eager: true }) posts: Post[]; // Lazy loading - loads relations on access @OneToMany(() => Post, (post) => post.author) posts: Promise; // Explicit loading (recommended) const user = await userRepository.findOne({ where: { id: 1 }, relations: ["posts"], }); ``` ### Avoid N+1 Queries ```typescript // Bad: N+1 queries const users = await userRepository.find(); for (const user of users) { console.log(user.posts); // Separate query for each user } // Good: Eager load relations const users = await userRepository.find({ relations: ['posts'], }); ``` ### Use Indexes ```typescript @Entity() @Index(['email']) @Index(['firstName', 'lastName']) export class User { @Column() @Index() email: string; @Column() firstName: string; @Column() lastName: string; } ``` ### Cascade Operations ```typescript @OneToMany(() => Post, (post) => post.author, { cascade: true, // Saves/removes related posts onDelete: "CASCADE", // Database-level cascade }) posts: Post[]; ``` ### Naming Strategies For consistent naming between TypeScript and database: ```typescript import { DefaultNamingStrategy, NamingStrategyInterface } from "typeorm"; import { snakeCase } from "typeorm/util/StringUtils"; export class SnakeNamingStrategy extends DefaultNamingStrategy implements NamingStrategyInterface { tableName(targetName: string, userSpecifiedName: string | undefined): string { return userSpecifiedName ? userSpecifiedName : snakeCase(targetName); } columnName(propertyName: string, customName: string, embeddedPrefixes: string[]): string { return snakeCase(embeddedPrefixes.join("_")) + (customName ? customName : snakeCase(propertyName)); } } // Use in data source config namingStrategy: new SnakeNamingStrategy(), ``` ================================================ FILE: .agents/skills/typescript-advanced-types/SKILL.md ================================================ --- name: typescript-advanced-types description: Master TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript projects. --- # TypeScript Advanced Types Comprehensive guidance for mastering TypeScript's advanced type system including generics, conditional types, mapped types, template literal types, and utility types for building robust, type-safe applications. ## When to Use This Skill - Building type-safe libraries or frameworks - Creating reusable generic components - Implementing complex type inference logic - Designing type-safe API clients - Building form validation systems - Creating strongly-typed configuration objects - Implementing type-safe state management - Migrating JavaScript codebases to TypeScript ## Core Concepts ### 1. Generics **Purpose:** Create reusable, type-flexible components while maintaining type safety. **Basic Generic Function:** ```typescript function identity(value: T): T { return value; } const num = identity(42); // Type: number const str = identity('hello'); // Type: string const auto = identity(true); // Type inferred: boolean ``` **Generic Constraints:** ```typescript interface HasLength { length: number; } function logLength(item: T): T { console.log(item.length); return item; } logLength('hello'); // OK: string has length logLength([1, 2, 3]); // OK: array has length logLength({ length: 10 }); // OK: object has length // logLength(42); // Error: number has no length ``` **Multiple Type Parameters:** ```typescript function merge(obj1: T, obj2: U): T & U { return { ...obj1, ...obj2 }; } const merged = merge({ name: 'John' }, { age: 30 }); // Type: { name: string } & { age: number } ``` ### 2. Conditional Types **Purpose:** Create types that depend on conditions, enabling sophisticated type logic. **Basic Conditional Type:** ```typescript type IsString = T extends string ? true : false; type A = IsString; // true type B = IsString; // false ``` **Extracting Return Types:** ```typescript type ReturnType = T extends (...args: any[]) => infer R ? R : never; function getUser() { return { id: 1, name: 'John' }; } type User = ReturnType; // Type: { id: number; name: string; } ``` **Distributive Conditional Types:** ```typescript type ToArray = T extends any ? T[] : never; type StrOrNumArray = ToArray; // Type: string[] | number[] ``` **Nested Conditions:** ```typescript type TypeName = T extends string ? 'string' : T extends number ? 'number' : T extends boolean ? 'boolean' : T extends undefined ? 'undefined' : T extends Function ? 'function' : 'object'; type T1 = TypeName; // "string" type T2 = TypeName<() => void>; // "function" ``` ### 3. Mapped Types **Purpose:** Transform existing types by iterating over their properties. **Basic Mapped Type:** ```typescript type Readonly = { readonly [P in keyof T]: T[P]; }; interface User { id: number; name: string; } type ReadonlyUser = Readonly; // Type: { readonly id: number; readonly name: string; } ``` **Optional Properties:** ```typescript type Partial = { [P in keyof T]?: T[P]; }; type PartialUser = Partial; // Type: { id?: number; name?: string; } ``` **Key Remapping:** ```typescript type Getters = { [K in keyof T as `get${Capitalize}`]: () => T[K]; }; interface Person { name: string; age: number; } type PersonGetters = Getters; // Type: { getName: () => string; getAge: () => number; } ``` **Filtering Properties:** ```typescript type PickByType = { [K in keyof T as T[K] extends U ? K : never]: T[K]; }; interface Mixed { id: number; name: string; age: number; active: boolean; } type OnlyNumbers = PickByType; // Type: { id: number; age: number; } ``` ### 4. Template Literal Types **Purpose:** Create string-based types with pattern matching and transformation. **Basic Template Literal:** ```typescript type EventName = 'click' | 'focus' | 'blur'; type EventHandler = `on${Capitalize}`; // Type: "onClick" | "onFocus" | "onBlur" ``` **String Manipulation:** ```typescript type UppercaseGreeting = Uppercase<'hello'>; // "HELLO" type LowercaseGreeting = Lowercase<'HELLO'>; // "hello" type CapitalizedName = Capitalize<'john'>; // "John" type UncapitalizedName = Uncapitalize<'John'>; // "john" ``` **Path Building:** ```typescript type Path = T extends object ? { [K in keyof T]: K extends string ? `${K}` | `${K}.${Path}` : never; }[keyof T] : never; interface Config { server: { host: string; port: number; }; database: { url: string; }; } type ConfigPath = Path; // Type: "server" | "database" | "server.host" | "server.port" | "database.url" ``` ### 5. Utility Types **Built-in Utility Types:** ```typescript // Partial - Make all properties optional type PartialUser = Partial; // Required - Make all properties required type RequiredUser = Required; // Readonly - Make all properties readonly type ReadonlyUser = Readonly; // Pick - Select specific properties type UserName = Pick; // Omit - Remove specific properties type UserWithoutPassword = Omit; // Exclude - Exclude types from union type T1 = Exclude<'a' | 'b' | 'c', 'a'>; // "b" | "c" // Extract - Extract types from union type T2 = Extract<'a' | 'b' | 'c', 'a' | 'b'>; // "a" | "b" // NonNullable - Exclude null and undefined type T3 = NonNullable; // string // Record - Create object type with keys K and values T type PageInfo = Record<'home' | 'about', { title: string }>; ``` ## Advanced Patterns ### Pattern 1: Type-Safe Event Emitter ```typescript type EventMap = { 'user:created': { id: string; name: string }; 'user:updated': { id: string }; 'user:deleted': { id: string }; }; class TypedEventEmitter> { private listeners: { [K in keyof T]?: Array<(data: T[K]) => void>; } = {}; on(event: K, callback: (data: T[K]) => void): void { if (!this.listeners[event]) { this.listeners[event] = []; } this.listeners[event]!.push(callback); } emit(event: K, data: T[K]): void { const callbacks = this.listeners[event]; if (callbacks) { callbacks.forEach(callback => callback(data)); } } } const emitter = new TypedEventEmitter(); emitter.on('user:created', data => { console.log(data.id, data.name); // Type-safe! }); emitter.emit('user:created', { id: '1', name: 'John' }); // emitter.emit("user:created", { id: "1" }); // Error: missing 'name' ``` ### Pattern 2: Type-Safe API Client ```typescript type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'; type EndpointConfig = { '/users': { GET: { response: User[] }; POST: { body: { name: string; email: string }; response: User }; }; '/users/:id': { GET: { params: { id: string }; response: User }; PUT: { params: { id: string }; body: Partial; response: User }; DELETE: { params: { id: string }; response: void }; }; }; type ExtractParams = T extends { params: infer P } ? P : never; type ExtractBody = T extends { body: infer B } ? B : never; type ExtractResponse = T extends { response: infer R } ? R : never; class APIClient>> { async request( path: Path, method: Method, ...[options]: ExtractParams extends never ? ExtractBody extends never ? [] : [{ body: ExtractBody }] : [ { params: ExtractParams; body?: ExtractBody; }, ] ): Promise> { // Implementation here return {} as any; } } const api = new APIClient(); // Type-safe API calls const users = await api.request('/users', 'GET'); // Type: User[] const newUser = await api.request('/users', 'POST', { body: { name: 'John', email: 'john@example.com' }, }); // Type: User const user = await api.request('/users/:id', 'GET', { params: { id: '123' }, }); // Type: User ``` ### Pattern 3: Builder Pattern with Type Safety ```typescript type BuilderState = { [K in keyof T]: T[K] | undefined; }; type RequiredKeys = { [K in keyof T]-?: {} extends Pick ? never : K; }[keyof T]; type OptionalKeys = { [K in keyof T]-?: {} extends Pick ? K : never; }[keyof T]; type IsComplete = RequiredKeys extends keyof S ? (S[RequiredKeys] extends undefined ? false : true) : false; class Builder = {}> { private state: S = {} as S; set(key: K, value: T[K]): Builder> { this.state[key] = value; return this as any; } build(this: IsComplete extends true ? this : never): T { return this.state as T; } } interface User { id: string; name: string; email: string; age?: number; } const builder = new Builder(); const user = builder.set('id', '1').set('name', 'John').set('email', 'john@example.com').build(); // OK: all required fields set // const incomplete = builder // .set("id", "1") // .build(); // Error: missing required fields ``` ### Pattern 4: Deep Readonly/Partial ```typescript type DeepReadonly = { readonly [P in keyof T]: T[P] extends object ? (T[P] extends Function ? T[P] : DeepReadonly) : T[P]; }; type DeepPartial = { [P in keyof T]?: T[P] extends object ? T[P] extends Array ? Array> : DeepPartial : T[P]; }; interface Config { server: { host: string; port: number; ssl: { enabled: boolean; cert: string; }; }; database: { url: string; pool: { min: number; max: number; }; }; } type ReadonlyConfig = DeepReadonly; // All nested properties are readonly type PartialConfig = DeepPartial; // All nested properties are optional ``` ### Pattern 5: Type-Safe Form Validation ```typescript type ValidationRule = { validate: (value: T) => boolean; message: string; }; type FieldValidation = { [K in keyof T]?: ValidationRule[]; }; type ValidationErrors = { [K in keyof T]?: string[]; }; class FormValidator> { constructor(private rules: FieldValidation) {} validate(data: T): ValidationErrors | null { const errors: ValidationErrors = {}; let hasErrors = false; for (const key in this.rules) { const fieldRules = this.rules[key]; const value = data[key]; if (fieldRules) { const fieldErrors: string[] = []; for (const rule of fieldRules) { if (!rule.validate(value)) { fieldErrors.push(rule.message); } } if (fieldErrors.length > 0) { errors[key] = fieldErrors; hasErrors = true; } } } return hasErrors ? errors : null; } } interface LoginForm { email: string; password: string; } const validator = new FormValidator({ email: [ { validate: v => v.includes('@'), message: 'Email must contain @', }, { validate: v => v.length > 0, message: 'Email is required', }, ], password: [ { validate: v => v.length >= 8, message: 'Password must be at least 8 characters', }, ], }); const errors = validator.validate({ email: 'invalid', password: 'short', }); // Type: { email?: string[]; password?: string[]; } | null ``` ### Pattern 6: Discriminated Unions ```typescript type Success = { status: 'success'; data: T; }; type Error = { status: 'error'; error: string; }; type Loading = { status: 'loading'; }; type AsyncState = Success | Error | Loading; function handleState(state: AsyncState): void { switch (state.status) { case 'success': console.log(state.data); // Type: T break; case 'error': console.log(state.error); // Type: string break; case 'loading': console.log('Loading...'); break; } } // Type-safe state machine type State = | { type: 'idle' } | { type: 'fetching'; requestId: string } | { type: 'success'; data: any } | { type: 'error'; error: Error }; type Event = | { type: 'FETCH'; requestId: string } | { type: 'SUCCESS'; data: any } | { type: 'ERROR'; error: Error } | { type: 'RESET' }; function reducer(state: State, event: Event): State { switch (state.type) { case 'idle': return event.type === 'FETCH' ? { type: 'fetching', requestId: event.requestId } : state; case 'fetching': if (event.type === 'SUCCESS') { return { type: 'success', data: event.data }; } if (event.type === 'ERROR') { return { type: 'error', error: event.error }; } return state; case 'success': case 'error': return event.type === 'RESET' ? { type: 'idle' } : state; } } ``` ## Type Inference Techniques ### 1. Infer Keyword ```typescript // Extract array element type type ElementType = T extends (infer U)[] ? U : never; type NumArray = number[]; type Num = ElementType; // number // Extract promise type type PromiseType = T extends Promise ? U : never; type AsyncNum = PromiseType>; // number // Extract function parameters type Parameters = T extends (...args: infer P) => any ? P : never; function foo(a: string, b: number) {} type FooParams = Parameters; // [string, number] ``` ### 2. Type Guards ```typescript function isString(value: unknown): value is string { return typeof value === 'string'; } function isArrayOf(value: unknown, guard: (item: unknown) => item is T): value is T[] { return Array.isArray(value) && value.every(guard); } const data: unknown = ['a', 'b', 'c']; if (isArrayOf(data, isString)) { data.forEach(s => s.toUpperCase()); // Type: string[] } ``` ### 3. Assertion Functions ```typescript function assertIsString(value: unknown): asserts value is string { if (typeof value !== 'string') { throw new Error('Not a string'); } } function processValue(value: unknown) { assertIsString(value); // value is now typed as string console.log(value.toUpperCase()); } ``` ## Best Practices 1. **Use `unknown` over `any`**: Enforce type checking 2. **Prefer `interface` for object shapes**: Better error messages 3. **Use `type` for unions and complex types**: More flexible 4. **Leverage type inference**: Let TypeScript infer when possible 5. **Create helper types**: Build reusable type utilities 6. **Use const assertions**: Preserve literal types 7. **Avoid type assertions**: Use type guards instead 8. **Document complex types**: Add JSDoc comments 9. **Use strict mode**: Enable all strict compiler options 10. **Test your types**: Use type tests to verify type behavior ## Type Testing ```typescript // Type assertion tests type AssertEqual = [T] extends [U] ? ([U] extends [T] ? true : false) : false; type Test1 = AssertEqual; // true type Test2 = AssertEqual; // false type Test3 = AssertEqual; // false // Expect error helper type ExpectError = T; // Example usage type ShouldError = ExpectError>; ``` ## Common Pitfalls 1. **Over-using `any`**: Defeats the purpose of TypeScript 2. **Ignoring strict null checks**: Can lead to runtime errors 3. **Too complex types**: Can slow down compilation 4. **Not using discriminated unions**: Misses type narrowing opportunities 5. **Forgetting readonly modifiers**: Allows unintended mutations 6. **Circular type references**: Can cause compiler errors 7. **Not handling edge cases**: Like empty arrays or null values ## Performance Considerations - Avoid deeply nested conditional types - Use simple types when possible - Cache complex type computations - Limit recursion depth in recursive types - Use build tools to skip type checking in production ## Resources - **TypeScript Handbook**: https://www.typescriptlang.org/docs/handbook/ - **Type Challenges**: https://github.com/type-challenges/type-challenges - **TypeScript Deep Dive**: https://basarat.gitbook.io/typescript/ - **Effective TypeScript**: Book by Dan Vanderkam ================================================ FILE: .dockerignore ================================================ client/.next/cache # Keep empty dir. Next.js scans it on startup even with CDN assets client/.next/static/** !client/.next/static/ client/.turbo node_modules npm-debug.log .turbo ================================================ FILE: .editorconfig ================================================ # http://editorconfig.org root = true [*] charset = utf-8 indent_style = space indent_size = 2 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true [*.md] insert_final_newline = false trim_trailing_whitespace = false ================================================ FILE: .eslintignore ================================================ # don't ever lint node_modules node_modules # don't lint build output (make sure it's set to your correct build folder name) dist ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: [dzmitry-varabei] patreon: # Replace with a single Patreon username open_collective: rsschool ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] ================================================ FILE: .github/ISSUE_TEMPLATE/bug-report.md ================================================ --- name: 🐞 Bug report about: Create a report to help us improve title: '' labels: bug --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Device (please complete the following information):** - OS: [e.g. iOS] - Browser [e.g. chrome, safari] - Version [e.g. 22] **Additional context** Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/data-issue-report.md ================================================ --- name: 🗃 Data issue about: Create a issue to fix data title: '' labels: data issue --- **Course** Specify your course (e.g. rs-2019-q1) **What is wrong** Describe data issue ================================================ FILE: .github/ISSUE_TEMPLATE/feature-request.md ================================================ --- name: 🚀 Feature request about: Request a feature to implement title: '' labels: feature --- **Describe the feature** Please describe the requested feature in details. **Additional context** Add any other context about the feature here. ================================================ FILE: .github/auto-label.json ================================================ { "rules": { "code:client": ["client/"], "code:server": ["server/"], "⛓ dependencies": "**/package.json" } } ================================================ FILE: .github/copilot-instructions.md ================================================ Use instructions from `AGENTS.md` to guide your work. ================================================ FILE: .github/pull_request_template.md ================================================ [Pull Request Guidelines](https://github.com/rolling-scopes/rsschool-app/blob/master/CONTRIBUTING.md#pull-requests) **Issue**: _Link to the relevant GitHub Issue (e.g., `#123` for issue number 123)._ **Description**: _Please provide a description of the changes in this Pull Request. Include screenshots or GIFs if applicable. The description should clearly explain the purpose of this PR._ **Self-Check**: - [ ] Database migration added (if required) - [ ] Changes tested locally ================================================ FILE: .github/workflows/deploy-sloths.yaml ================================================ name: Deploy sloths.rs.school on: push: branches: [master] paths: - 'tools/sloths/**' - '.github/workflows/deploy-sloths.yaml' concurrency: group: deploy-sloths cancel-in-progress: true jobs: build_deploy: name: Build and Deploy runs-on: ubuntu-latest defaults: run: working-directory: ./tools/sloths steps: - name: Setup Node.js environment uses: actions/setup-node@v4 with: node-version: '24' - name: Checkout uses: actions/checkout@v4 - name: Restore npm cache uses: actions/cache@v4 env: cache-name: cache-npm with: path: ~/.npm key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('tools/sloths/package-lock.json') }} restore-keys: | ${{ runner.os }}-${{ env.cache-name }}- - name: Install dependencies run: npm ci - name: Build env: VITE_CDN_URL: https://cdn.rs.school/sloths run: npm run build-only - name: Upload to S3 env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: eu-central-1 run: | aws s3 cp dist s3://sloths.rs.school/ --recursive --cache-control "public,max-age=3600,immutable" ================================================ FILE: .github/workflows/deploy.yaml ================================================ name: Deploy RS School App on: push: branches: [master] env: DO_NOT_TRACK: '1' NODE_OPTIONS: '--no-warnings' TURBO_VERSION: '2.8.12' jobs: checks: name: Test, Lint concurrency: group: deploy-checks cancel-in-progress: true runs-on: ubuntu-latest timeout-minutes: 30 steps: - name: Setup Node.js environment uses: actions/setup-node@v4 with: node-version: '24' - name: Checkout uses: actions/checkout@v4 - name: Restore npm cache uses: actions/cache@v4 env: cache-name: cache-npm with: path: ~/.npm key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('package-lock.json') }} restore-keys: | ${{ runner.os }}-${{ env.cache-name }}- - name: Install dependencies run: npm ci - name: Test run: npm run test - name: Lint run: npm run lint build_client: name: Build (Client) concurrency: group: deploy-build-client cancel-in-progress: true needs: [checks] runs-on: ubuntu-latest timeout-minutes: 20 steps: - name: Setup Node.js environment uses: actions/setup-node@v4 with: node-version: '24' - name: Checkout uses: actions/checkout@v4 - name: Restore npm cache uses: actions/cache@v4 env: cache-name: cache-npm with: path: ~/.npm key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('package-lock.json') }} restore-keys: | ${{ runner.os }}-${{ env.cache-name }}- - name: Generate isolated subworkspace for Client run: npx turbo@${{ env.TURBO_VERSION }} prune --scope=client --docker - name: Add lockfile and package.json's and source of isolated subworkspace run: | mkdir -p app cp -r out/json/. app cp -r out/full/. app cp -r out/package-lock.json app cp -r .dockerignore app cp -r common app/common - name: Install dependencies run: npm ci working-directory: ./app - name: Build (npm run build) env: NODE_ENV: production RSSHCOOL_UI_GCP_MAPS_API_KEY: ${{ secrets.RSSHCOOL_UI_GCP_MAPS_API_KEY }} CDN_HOST: 'https://cdn.rs.school' SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} working-directory: ./app run: npm run build - name: Clean modules working-directory: ./app run: npm prune --omit=dev - name: Upload to CDN env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: eu-central-1 run: aws s3 cp ./app/client/.next/static s3://cdn.rs.school/_next/static/ --recursive --cache-control "public,max-age=15552000,immutable" - name: Build container run: docker build -t ghcr.io/rolling-scopes/rsschool-app-client:master -f client/Dockerfile ./app - name: Login to GitHub Container Registry uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Publish "client" container run: docker push ghcr.io/rolling-scopes/rsschool-app-client:master build_server: name: Build (Server) concurrency: group: deploy-build-server cancel-in-progress: true needs: [checks] runs-on: ubuntu-latest timeout-minutes: 20 steps: - name: Setup Node.js environment uses: actions/setup-node@v4 with: node-version: '24' - name: Checkout uses: actions/checkout@v4 - name: Restore npm cache uses: actions/cache@v4 env: cache-name: cache-npm with: path: ~/.npm key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('package-lock.json') }} restore-keys: | ${{ runner.os }}-${{ env.cache-name }}- - name: Generate isolated subworkspace for Server run: npx turbo@${{ env.TURBO_VERSION }} prune --scope=server --docker - name: Add lockfile and package.json's and source of isolated subworkspace run: | mkdir -p app cp -r out/json/. app cp -r out/full/. app cp -r out/package-lock.json app cp -r common app/common - name: Install dependencies run: npm ci working-directory: ./app - name: Build (npm run build) env: NODE_ENV: production run: npm run build working-directory: ./app - name: Build container run: docker build -t ghcr.io/rolling-scopes/rsschool-app-server:master -f server/Dockerfile ./app - name: Login to GitHub Container Registry uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Publish "server" container run: docker push ghcr.io/rolling-scopes/rsschool-app-server:master build_nestjs: name: Build (Nest.js) concurrency: group: deploy-build-nestjs cancel-in-progress: true needs: [checks] runs-on: ubuntu-latest timeout-minutes: 20 steps: - name: Setup Node.js environment uses: actions/setup-node@v4 with: node-version: '24' - name: Checkout uses: actions/checkout@v4 - name: Restore npm cache uses: actions/cache@v4 env: cache-name: cache-npm with: path: ~/.npm key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('package-lock.json') }} restore-keys: | ${{ runner.os }}-${{ env.cache-name }}- - name: Generate isolated subworkspace for Nest.js run: npx turbo@${{ env.TURBO_VERSION }} prune --scope=server --scope=nestjs --docker - name: Add lockfile and package.json's and source of isolated subworkspace run: | mkdir -p app cp -r out/json/. app cp -r out/full/. app cp -r out/package-lock.json app cp -r common app/common - name: Install dependencies run: npm ci working-directory: ./app - name: Build (npm run build) env: NODE_ENV: production run: npx turbo run build --filter=nestjs working-directory: ./app - name: Build container run: docker build -t ghcr.io/rolling-scopes/rsschool-app-nestjs:master -f nestjs/Dockerfile ./app - name: Login to GitHub Container Registry uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Publish "nestjs" container run: docker push ghcr.io/rolling-scopes/rsschool-app-nestjs:master deploy: name: deploy concurrency: group: deploy-aws cancel-in-progress: false needs: [build_client, build_server, build_nestjs] runs-on: ubuntu-latest timeout-minutes: 30 env: PRIVATE_KEY: ${{ secrets.AWS_PRIVATE_KEY }} HOSTNAME: ${{ secrets.EC2_HOSTNAME }} USERNAME: ${{ secrets.EC2_USERNAME }} environment: name: production url: https://app.rs.school steps: - name: Checkout uses: actions/checkout@v4 - name: Pull AWS SSM Params uses: deptno/action-aws-ssm-to-dotenv@v1.3.2 env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: eu-central-1 with: ssm-path: /prod/app format: dotenv output: .env - name: Setup SSH key run: echo "$PRIVATE_KEY" > private_key && chmod 600 private_key - name: Pull the latest images run: | scp -o StrictHostKeyChecking=no -i private_key ./.env ${USERNAME}@${HOSTNAME}:~/.env scp -o StrictHostKeyChecking=no -i private_key ./docker-compose.yml ${USERNAME}@${HOSTNAME}:~/docker-compose.yml scp -o StrictHostKeyChecking=no -i private_key ./setup/nginx/nginx.conf ${USERNAME}@${HOSTNAME}:~/nginx/nginx.conf ssh -o StrictHostKeyChecking=no -i private_key ${USERNAME}@${HOSTNAME} ' sleep 10 docker-compose pull ' - name: Restart run: | ssh -o StrictHostKeyChecking=no -i private_key ${USERNAME}@${HOSTNAME} ' docker-compose up -d docker-compose restart nginx ' - name: Clean up run: | ssh -o StrictHostKeyChecking=no -i private_key ${USERNAME}@${HOSTNAME} ' docker system prune -f ' rm -f private_key ================================================ FILE: .github/workflows/pull_request.yml ================================================ name: Pull Request on: pull_request: branches: [master] types: [opened, synchronize, reopened, labeled, unlabeled] env: DO_NOT_TRACK: '1' NODE_OPTIONS: '--no-warnings' jobs: pr_checks: name: Lint, Format, Test concurrency: group: pr-checks_${{ github.head_ref }} cancel-in-progress: true runs-on: ubuntu-latest timeout-minutes: 20 steps: - name: Setup Node.js environment uses: actions/setup-node@v4 with: node-version: '24' - name: Checkout uses: actions/checkout@v4 - name: Restore npm cache uses: actions/cache@v4 env: cache-name: cache-npm with: path: ~/.npm key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('package-lock.json') }} restore-keys: | ${{ runner.os }}-${{ env.cache-name }}- - name: Install dependencies run: npm ci - name: Format run: npm run ci:format - name: Lint run: npm run lint - name: Test (client) run: npm run test:ci if: success() || failure() working-directory: client - name: Test (server) run: npm run test:ci if: success() || failure() working-directory: server - name: Test (nestjs) run: npm run test:ci if: success() || failure() working-directory: nestjs pr_build: name: Build concurrency: group: pr-build_${{ github.head_ref }} cancel-in-progress: true runs-on: ubuntu-latest timeout-minutes: 20 steps: - name: Setup Node.js environment uses: actions/setup-node@v4 with: node-version: '24' - name: Checkout uses: actions/checkout@v4 - name: Restore next cache uses: actions/cache@v4 env: cache-name: cache-next with: path: | client/.next/cache key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.ref_name }}-${{ github.sha }} restore-keys: | ${{ runner.os }}-${{ env.cache-name }}-${{ github.ref_name }} - name: Restore Turborepo cache uses: actions/cache@v4 env: cache-name: cache-turbo with: path: .turbo key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('package-lock.json', 'turbo.json') }} restore-keys: | ${{ runner.os }}-${{ env.cache-name }}- - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 if: ${{ contains(github.event.pull_request.labels.*.name, 'deploy') }} with: platforms: linux/amd64 - name: Login to ECR uses: docker/login-action@v3 if: ${{ contains(github.event.pull_request.labels.*.name, 'deploy') }} with: registry: 511361162520.dkr.ecr.eu-central-1.amazonaws.com username: ${{ secrets.AWS_ACCESS_KEY_ID }} password: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - name: Restore npm cache uses: actions/cache@v4 env: cache-name: cache-npm with: path: ~/.npm key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('package-lock.json') }} restore-keys: | ${{ runner.os }}-${{ env.cache-name }}- - name: Install dependencies env: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 'true' run: npm ci - name: Build (npm run build) env: AWS_LAMBDA: 'true' NODE_ENV: production RSSCHOOL_DEV_TOOLS: 'true' RSSHCOOL_UI_GCP_MAPS_API_KEY: ${{ secrets.RSSHCOOL_UI_GCP_MAPS_API_KEY }} run: npm run build - name: Upload to CDN if: ${{ contains(github.event.pull_request.labels.*.name, 'deploy') }} working-directory: ./client env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: eu-central-1 run: aws s3 cp .next/static s3://cdn.rs.school/_next/static/ --recursive --cache-control "public,max-age=15552000,immutable" - name: Package Client uses: docker/build-push-action@v6 if: ${{ contains(github.event.pull_request.labels.*.name, 'deploy') }} with: platforms: linux/amd64 provenance: false context: . file: ./client/Dockerfile.lambda push: true tags: 511361162520.dkr.ecr.eu-central-1.amazonaws.com/rsschool-ui:pr${{ github.event.pull_request.number }} cache-from: type=gha,scope=rsschool-ui-lambda cache-to: type=gha,mode=max,scope=rsschool-ui-lambda - name: Pull AWS SSM Params uses: deptno/action-aws-ssm-to-dotenv@v1.3.2 if: ${{ contains(github.event.pull_request.labels.*.name, 'deploy') }} env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: eu-central-1 with: ssm-path: /staging/app format: dotenv output: server/.env - name: Package Server uses: docker/build-push-action@v6 if: ${{ contains(github.event.pull_request.labels.*.name, 'deploy') }} with: platforms: linux/amd64 provenance: false context: . file: ./server/Dockerfile.lambda push: true tags: 511361162520.dkr.ecr.eu-central-1.amazonaws.com/rsschool-server:pr${{ github.event.pull_request.number }} cache-from: type=gha,scope=rsschool-server-lambda cache-to: type=gha,mode=max,scope=rsschool-server-lambda - name: Pull AWS SSM Params uses: deptno/action-aws-ssm-to-dotenv@v1.3.2 if: ${{ contains(github.event.pull_request.labels.*.name, 'deploy') }} env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: eu-central-1 with: ssm-path: /staging/app format: dotenv output: nestjs/.env - name: Package Nestjs uses: docker/build-push-action@v6 if: ${{ contains(github.event.pull_request.labels.*.name, 'deploy') }} with: platforms: linux/amd64 provenance: false context: . file: ./nestjs/Dockerfile.lambda push: true tags: 511361162520.dkr.ecr.eu-central-1.amazonaws.com/rsschool-nestjs:pr${{ github.event.pull_request.number }} cache-from: type=gha,scope=rsschool-nestjs-lambda cache-to: type=gha,mode=max,scope=rsschool-nestjs-lambda pr_deploy: needs: [pr_build] name: Deploy concurrency: group: pr-deploy_${{ github.head_ref }} cancel-in-progress: false timeout-minutes: 30 runs-on: ubuntu-latest if: ${{ contains(github.event.pull_request.labels.*.name, 'deploy') }} steps: - name: Setup Node.js environment uses: actions/setup-node@v4 with: node-version: '24' - name: Checkout uses: actions/checkout@v4 - name: Restore npm cache uses: actions/cache@v4 env: cache-name: cache-npm with: path: ~/.npm key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('setup/cdk/package-lock.json') }} restore-keys: | ${{ runner.os }}-${{ env.cache-name }}- - name: Install dependencies working-directory: ./setup/cdk run: npm ci - name: Deploy env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: eu-central-1 working-directory: ./setup/cdk run: | npx cdk deploy --require-approval never -c feature=pr${{ github.event.pull_request.number }} -c deployId=${GITHUB_RUN_ID} - name: Create GitHub deployment uses: apalchys/deployment-action@v1.2.6 id: deployment with: ref: ${{ github.event.pull_request.head.ref }} pr: true pr_id: ${{ github.event.pull_request.number }} token: ${{ secrets.GITHUB_TOKEN }} target_url: https://pr${{ github.event.pull_request.number }}.app.rs.school environment: pr${{ github.event.pull_request.number }} initial_status: success pr_e2e: needs: [pr_deploy] name: End-to-End Tests concurrency: group: pr-e2e_${{ github.head_ref }} cancel-in-progress: true timeout-minutes: 10 runs-on: ubuntu-latest if: ${{ contains(github.event.pull_request.labels.*.name, 'deploy') }} steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node.js environment uses: actions/setup-node@v4 with: node-version: '24' - name: Restore npm cache uses: actions/cache@v4 env: cache-name: cache-npm with: path: ~/.npm key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('package-lock.json') }} restore-keys: | ${{ runner.os }}-${{ env.cache-name }}- - name: Restore browsers cache uses: actions/cache@v4 with: path: '~/.cache/ms-playwright/' key: ${{ runner.os }}-browsers-${{ hashFiles('package-lock.json') }} - name: Install Dependencies working-directory: ./client run: npm ci - name: Install Playwright working-directory: ./client run: npx playwright install --with-deps - name: Run Playwright tests working-directory: ./client env: CI: 'true' BASE_URL: https://pr${{ github.event.pull_request.number }}.app.rs.school run: npx playwright test - name: Save Report uses: actions/upload-artifact@v4 if: always() with: name: playwright-report path: client/playwright-report/ retention-days: 15 ================================================ FILE: .github/workflows/pull_request_close.yml ================================================ name: Pull Request Delete on: pull_request: types: [closed] branches: [master] jobs: pr_delete: runs-on: ubuntu-latest if: ${{ contains(github.event.pull_request.labels.*.name, 'deploy') }} steps: - name: Setup Node.js environment uses: actions/setup-node@v4 with: node-version: '24' - name: Checkout uses: actions/checkout@v4 - name: Restore npm cache uses: actions/cache@v4 env: cache-name: cache-npm with: path: ~/.npm key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('setup/cdk/package-lock.json') }} restore-keys: | ${{ runner.os }}-${{ env.cache-name }}- - name: Install Dependencies working-directory: setup/cdk run: npm ci - name: Delete Stack env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_DEFAULT_REGION: eu-central-1 working-directory: setup/cdk run: | npx cdk destroy --force --all -c feature=pr${{ github.event.pull_request.number }} -c deployId=${GITHUB_RUN_ID} - name: Delete Deployment uses: strumwolf/delete-deployment-environment@v2 with: token: ${{ secrets.DELETE_ENV_TOKEN }} environment: pr${{ github.event.pull_request.number }} ================================================ FILE: .github/workflows/renovate.yml ================================================ name: Renovate on: schedule: # once a month - cron: '0 0 1 * *' workflow_dispatch: jobs: renovate: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Self-hosted Renovate uses: renovatebot/github-action@v34.82.0 with: configurationFile: renovate.json token: ${{ secrets.RENOVATE_TOKEN }} ================================================ FILE: .gitignore ================================================ .DS_Store .env .history/ .idea/ .next/ .sentryclirc .swc/ .turbo .vscode app/ coverage dist/ node_modules/ out playwright-report/ reports/*.xml setup/backup.sql setup/cdk/cdk.out test-results/ client/tsconfig.tsbuildinfo /tmp ================================================ FILE: .oxfmtrc.json ================================================ { "trailingComma": "all", "tabWidth": 2, "semi": true, "singleQuote": true, "printWidth": 120, "arrowParens": "avoid", "sortPackageJson": false, "ignorePatterns": ["client/src/api/", "client/playwright-report/", "package-lock.json"] } ================================================ FILE: AGENTS.md ================================================ # LLM Agents Instructions ## General - Read `README.md` first for project overview and architecture - Read `CONTRIBUTING.md` for local development setup, commands, commit messages, and PR conventions - Read `GUIDELINES.md` for code style and testing patterns - Read `DOMAIN.md` for application domain and data models - Read all `*/README.md` files for workspace specific architecture and development guidelines ## Instructions - Use the `tmp` folder in the repository to store temporary files during execution and planning if needed - Never run deploy commands - Run `npm run lint`, `npm run test`, `npm run compile`, and `npm run format` at the end of each completed task if you modified any files. If any of these steps fail, fix the issues and run the commands again. - Prefer telegraph style for communications and reasoning. Sacrifice grammar and punctuation for clarity and conciseness. Save tokens. ## Documentation Style Use the following writing style for documentation, comments and README files. Audience: Software Engineers. Expect half to be native speakers and half non-native speakers. - Keep the reading level at or below 9th grade (Flesch-Kincaid) - Write in clear, concise, active voice; don't assume passive voice reads well - Keep noun phrases short and avoid stacked modifiers - Limit embedded clauses to one level - Use conditionals sparingly and stick to simple if/then forms; avoid mixed or inverted versions - Use transition words (however, therefore, because) to show logic instead of expecting readers to infer it - Spread new ideas across sentences; avoid packing many concepts into one - Do not end list items with a period - Do not use emojis - Do not write obvious comments; explain why, not how or what ## Commands - `npm run lint`: Run linting - `npm run test`: Run tests - `npm run compile`: Compile the project - `npm run format`: Format the code ================================================ FILE: CONTRIBUTING.md ================================================ # How To Contribute ## Local development ### Prerequisites - [Git 2.10+](https://git-scm.com/downloads) - [NodeJS LTS](https://nodejs.org/en/) - [Podman](https://podman.io/docs/installation) - [podman-compose](https://github.com/containers/podman-compose) ### Steps 1. Fork the repository () 2. Clone the repository to your local machine () ```command-line git clone git@github.com:[username]/rsschool-app.git ``` 3. Navigate into the directory where you've cloned the source code and install NPM dependencies ```command-line cd rsschool-app npm install ``` 4. Create a branch for your feature. Prefix the branch with your GitHub Username. Example: `git checkout -b AlreadyBored/feature-x master` ```command-line git checkout -b /feature-x master ``` 5. The application requires a connection to a Postgres database. Here is how to get test database running locally: Run a Postgres Database locally using Podman & podman-compose ```command-line npm run db:up ``` Restore a test database snapshot ```command-line npm run db:restore ``` If you are done with development, stop the database; ```command-line npm run db:down ``` 6. Run the application in development mode with live reload: ```command-line npm start ``` 7. Do hacking 👩‍💻👨‍💻 8. You could specify any environment variable during development using `.env` file. Make a copy of `server/.env.example` and `nestjs/.env.example` and rename it to `server/.env` or `nestjs/.env` respectively. We support it via `dotenv` package. More information about usage here: 9. By default locally, you will be logged with `admin` access. If you want to change it, need to set `RSSCHOOL_AUTH_DEV_ADMIN` to `false` in `nestjs/.env` file **IMPORTANT:** Never commit changes to `.env` file 10. Do not forget to write unit-tests for your feature following [Unit-Tests Style Guide](#unit-tests-style-guide). We use [Jest](https://facebook.github.io/jest/) for unit-tests. 11. Write end-to-end tests for your feature if applicable. Please see `client/specs` directory for more information. We use [Playwright](https://playwright.dev/) for end-to-end tests. You can run them using `npm run test:e2e` command and they supposed to work against test database snapshot. 12. Make sure tests, lints pass and code formatted properly (they'll be run on a git pre-commit hook too) ```command-line npm test npm run lint npm run pretty ``` 13. Commit your changes using a descriptive commit message that follows our [Commit Message Conventions](#git-commit-messages) ```command-line git commit -m "feat: implement feature X" ``` 14. Push your branch to GitHub: ```command-line git push origin /feature-x ``` 15. Create a pull request. We support "feature branch" deployments. If you want to deploy your pull request, please add `deploy` label during creation. ## API client generation We use [OpenAPI Generator](https://openapi-generator.tech/) to generate API client for `NestJS` API. Here are steps how to do it: - Make sure database is running locally - Navigate to `./nestjs` - Run ```sh npm run openapi ``` - Commit updated files (`/client/src/api/*` and `./nestjs/src/spec.json`) _NOTE: in case of problems with running `openapi` you might need to install [Java](https://www.java.com/) or use some other way from [OpenAPI Generator Installation docs](https://openapi-generator.tech/docs/installation/)_ ## Database Migrations If you made changes to DB models, you need to create a DB migration. Here are steps how to do it 1. Go to `/server` 2. Run `npm run typeorm:migration:generate src/migrations/{MigrationName}` where `{MigrationName}` is your migration name. 3. Import your migration to `migrations` array at `./server/src/migrations/index.ts` 4. Commit and push your changes See more about TypeORM migrations at official docs [Migrations](https://github.com/typeorm/typeorm/blob/master/docs/migrations.md) ## Pull Requests - Branch name must be prefixed with your GitHub Username. Example: `AlreadyBored/feature-x`. For LLM agents, use `agent/` prefix. Example: `agent/feature-x`. - Check how to create a [Pull Request](https://help.github.com/articles/creating-a-pull-request/) (PR). - PR titles must follow [Conventional Commits](#git-commit-messages). - Do not include issue IDs in the PR title. - Use GitHub's [Draft PR](https://github.blog/2019-02-14-introducing-draft-pull-requests/) feature instead of using "WIP" (Work In Progress) in the title. - Consider adding relevant `area:*` label(s) to your PR. - Add the `deploy` label if you want the PR deployed to the staging environment. **NOTE**: This feature does not work for PRs opened from forks due to security limitations. - Write a clear and meaningful description for your PR. - Include screenshots and animated GIFs in your PR description whenever possible to demonstrate changes. ## Style Guides ### Git Commit Messages - Use [Conventional Commits](https://conventionalcommits.org/) format - Allowed Types: - build: - _changes that affect the build system or external dependencies (example scopes: npm, webpack)_ - ci: - _changes to our CI configuration files and scripts (example scopes: drone)_ - docs: - _documentation only changes_ - feat: - _a new feature_ - fix: - _a bug fix_ - perf: - _a code change that improves performance_ - refactor: - _a code change that neither fixes a bug nor adds a feature_ - style: - _changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)_ - test: - _adding missing tests or correcting existing tests_ - Use the present tense ("add feature" not "added feature") - Use the imperative mood ("move cursor to..." not "moves cursor to...") - Limit the first line to 72 characters or less - Reference issues and pull requests liberally after the first line ================================================ FILE: DOMAIN.md ================================================ # Domain Model This document describes the core domain model implemented in `server/src/models` and how the entities relate. It is meant as a quick map for agents working on the platform. ## Core identity and profile - `User` is the primary identity (GitHub-based) with contact info, profile details, and visibility settings. - `ProfilePermissions` stores per-user visibility rules for profile fields (public vs student vs mentor). - `ExternalAccount` records auxiliary learning platforms (codewars, etc) on `User`. - `LoginState` stores short-lived state for auth or channel linking flows. - `Session` defines derived course roles used by authorization (`CourseRole`). ## Courses, roles, and enrollment - `Course` defines a cohort with dates, discipline, location, flags (planned/completed/invite-only), mentoring settings, and certificate rules. - `Discipline` is a taxonomy item used by courses, tasks, and events. - `DiscordServer` holds course communication endpoints (gratitude URL, mentors chat, etc). - `Student` is a per-course role for a `User` with progress flags (failed/expelled/completed), scores, rank, mentoring, and certificate. - `Mentor` is a per-course role for a `User` with mentor settings and assigned students. - `CourseUser` stores course-scoped role flags (manager, supervisor, activist, task owner, etc). - `CourseManager` is a simple join for course managers (legacy or specific flows). - `Registry` is a course registration request for a user (student or mentor) with status. - `MentorRegistry` stores mentor preferences and capacity for course assignment. - `UserGroup` is a static group of users tied to course roles. ## Tasks and assessment - `Task` is the reusable definition of a task (type, description, skills, tags, criteria). - `CourseTask` is a `Task` scheduled for a specific course with deadlines, scoring, checker type, and cross-check status. - `TaskCriteria` stores per-task cross-check criteria definitions. - `TaskResult` is the authoritative score and submission links for a student on a course task. - `TaskChecker` links a mentor to a student for a course task review. - `TaskArtefact` stores optional artefacts (video, presentation, notes) per student and course task. - `TaskVerification` stores auto-check results for tests (status, score, answers, metadata). - `TaskInterviewStudent` enrolls a student into a task-based interview flow. - `TaskInterviewResult` stores mentor interview results and form answers for a task. ## Cross-check (peer review) Used when `CourseTask.checker = crossCheck`. - `TaskSolution` is a student's submitted solution URL plus review metadata. - `TaskSolutionChecker` assigns a reviewer (student) to a submitted solution. - `TaskSolutionResult` stores a review score, criteria-based review, and discussion messages. - `CourseTask.crossCheckStatus` tracks lifecycle: `initial` -> `distributed` -> `completed`. ## Interviews and stage assessments - `StageInterview` records a mentor-student interview tied to a course and optional task/stage. - `StageInterviewStudent` registers a student into stage interview flow. - `StageInterviewFeedback` stores structured JSON feedback for a stage interview. ## Teams and distributions - `TeamDistribution` defines a team formation window and constraints (size, score thresholds). - `TeamDistributionStudent` tracks who participates in distribution and whether they are assigned. - `Team` is a concrete team with members, lead, and chat metadata. ## Schedule and events - `Event` is a reusable schedule entity (lecture, workshop, etc) with description and type. - `CourseEvent` schedules an `Event` in a course with date/time, organizer, and details. ## Feedback, reputation, and certificates - `Feedback` is a public gratitude/badge from one user to another (course-scoped). - `PrivateFeedback` is internal feedback not visible publicly. - `StudentFeedback` is mentor feedback with structured soft-skill ratings and recommendations. - `Certificate` is an issued course certificate linked to a student. - `Contributor` represents platform contributors with profile descriptions. ## CV and hiring - `Resume` stores CV data for a user and visibility to employers. ## Notifications - `Notification` defines notification types and parent/child relationships. - `NotificationChannel` defines delivery channels (`email`, `telegram`, `discord`). - `NotificationChannelSettings` provides per-notification templates per channel. - `NotificationUserSettings` stores per-user subscription preferences per notification+channel. - `NotificationUserConnection` stores channel identifiers linked to users (e.g. Telegram id). - `Alert` is a global or course-scoped banner message. ## Audit and integrations - `History` stores audit records for inserts/updates/removals with before/after snapshots. - `RepositoryEvent` stores webhook-like activity from GitHub repositories. - `Prompt` stores configurable text prompts for AI-assisted features. ## Relationship map (high level) - `User` -> `Student` / `Mentor` (per course) - `Course` -> `Student`, `Mentor`, `CourseTask`, `CourseEvent`, `Registry` - `Task` -> `CourseTask` -> `TaskResult` / `TaskSolution` / `TaskVerification` - `TaskSolution` -> `TaskSolutionChecker` -> `TaskSolutionResult` (cross-check) - `CourseTask` -> `TaskInterviewStudent` / `TaskInterviewResult` - `Course` -> `TeamDistribution` -> `Team` / `TeamDistributionStudent` - `Course` -> `Certificate` (via `Student`) - `User` -> `Resume` / `ProfilePermissions` / `NotificationUserSettings` ================================================ FILE: GUIDELINES.md ================================================ # Guidelines ## Project Structure - `client` - Next.js frontend (active) - `nestjs` - NestJS backend (active) - `server` - Old Koa.js backend. Still used for legacy endpoints and TypeORM entities New features should be implemented in `nestjs`. If changes need to be made to functionality in `server/`, migrate it to `nestjs` first (exception: small critical hotfixes). Only allowed to modify TypeORM entities in `server` ### Path Aliases | Alias | Resolves To | Used In | | ------------- | --------------------- | ------- | | `@entities/*` | `server/src/models/*` | nestjs | | `@client/*` | `client/src/*` | client | ### File Naming | Type | Pattern | Example | | ---------- | -------------------- | ---------------------- | | Component | PascalCase.tsx | `CourseCard.tsx` | | Hook | camelCase.ts | `useExpelledStats.ts` | | Service | kebab-case.ts | `courses.service.ts` | | DTO | kebab-case.dto.ts | `create-course.dto.ts` | | Test | \*.test.ts(x) | `auth.service.test.ts` | | Module | kebab-case.module.ts | `courses.module.ts` | | Entity | camelCase.ts | `course.ts` | | CSS Module | \*.module.css | `Card.module.css` | ## TypeScript Conventions - Prefer `unknown` over `any` for truly unknown types - Use utility types (`Pick`, `Omit`, `Partial`) instead of manual type construction - Specify return types for public functions explicitly - Use `readonly` modifier for data that should not be mutated - Treat function arguments as immutable - Use `const` by default, `let` only when reassignment is needed - Prefix unused variables with underscore: `_unused` - Return early to reduce nesting depth - Use `async/await` over `.then()` chains - Use `Promise.all()` for parallel async operations - Order imports: external packages, internal aliases, relative imports - Use path aliases instead of deep relative imports ## Client (React/Next.js) ### Module Structure ``` modules// components/ # Feature-specific UI hooks/ # Feature-specific hooks pages/ # Page components services/ # API wrappers types.ts # Feature types index.ts # Barrel exports ``` ### Components - Use functional components with hooks exclusively - Export as named exports (not default) - One component per file as primary export - Use `React.memo()` for components with expensive renders - Keep components pure when possible - Define props with `type`, not `interface` ### Pages - Keep `pages/*.tsx` as thin wrappers only - Compose providers and render single module page component - Delegate all logic to module components ### State Management - Minimize `useEffect` - use only when truly necessary - Derive computed values instead of storing in state - Lift state up rather than prop drilling - Use context for cross-cutting concerns ### Hooks - Use hooks from `react-use` or `ahooks` before writing custom - Use `useRequest` from `ahooks` for API calls - Keep custom hooks focused and simple ### Services - Initialize clients at module level (singleton pattern) - Use OpenAPI client for `/api/v2/*`, axios services for legacy `/api/*` ### Ant Design - Use `Form.useForm()` with typed form values - Use `Form.Item` with `rules` for validation - Use `theme.useToken()` for theme colors - Use `clsx` for conditional class composition ### Styling - Use CSS modules exclusively: `*.module.css` - Co-locate styles with components - Do not use styled-jsx (deprecated) ## Backend (NestJS) ### Module Structure ``` nestjs/src// .module.ts # Module definition .controller.ts # HTTP endpoints .service.ts # Business logic dto/ index.ts # Barrel exports .dto.ts # Response DTO create-.dto.ts # Input DTO with validation update-.dto.ts # Partial input (extends create) ``` ### Controllers - Add `@ApiTags('domain')` to controller class - Add `@ApiOperation({ operationId: 'verbNoun' })` to every endpoint - Add `@ApiOkResponse({ type: DtoClass })` for type safety - Use `ParseIntPipe` for numeric params - Transform entities to DTOs before returning - Keep controllers thin - delegate business logic to services ### Guards and Roles - `@UseGuards(DefaultGuard)` - authenticated users - `@UseGuards(DefaultGuard, RoleGuard)` + `@RequiredRoles([...])` - role-based - Use `Role.Admin`, `CourseRole.Manager` enums, not strings ### Services - Inject repositories: `@InjectRepository(Entity)` - Use `findOneOrFail` / `findOneByOrFail` when entity must exist - Keep services focused on single domain ### DTOs - Response DTO: constructor takes entity, maps to properties - Input DTO: validation decorators (`@IsNotEmpty`, `@IsString`, etc.) - Update DTO: `extends PartialType(CreateDto)` - All properties need `@ApiProperty()` for OpenAPI ### Error Handling - Include context in error messages: `Entity with id ${id} not found` ## TypeORM Entities All entities live in `server/src/models/`. NestJS imports via `@entities/*` alias. ### Relations - Keep both relation property and `fieldId` column ### Migrations - Register migrations in `server/src/migrations/index.ts` - Export all entities from `server/src/models/index.ts` ## Testing ### File Organization - Unit tests: `{source}.test.ts(x)` next to source file - E2E tests: `client/specs/*.spec.ts` (Playwright) - Never use separate `__tests__` directories for new code ### Test Structure - `describe` as noun/situation: `describe('AuthService')` - `it` should describe behavior: `it('should return null when not found')` - Group related tests with nested `describe` blocks - Extract shared setup to `beforeEach` - Extract shared mock data to reusable constants ### Test Independence - Each test must be independent and not rely on others - Reset mocks and state in `beforeEach` - Ensure tests are deterministic (no random values) ### Assertions - Assert full object shapes over field-by-field checks - Use `expect.objectContaining()` for partial matching - Test both success and error paths - Test edge cases: empty arrays, null values, boundaries ### Async Testing - Use `await expect().rejects.toThrow()` for error cases - In React: use `findBy*` for first async query, then `getBy*` for rest - Keep only ONE assertion inside `waitFor` callback ### NestJS Tests - Use `Test.createTestingModule()` for setup - Mock repositories via `getRepositoryToken(Entity)` - Mock services with `jest.fn()` methods ### React Tests - Use `@testing-library/react` utilities - Query by role/text over test IDs - Test user behavior, not implementation ### Mock Data Typing ```typescript // Correct const mockData = { id: 1, name: 'Test' } as User; // Avoid const mockData = { ... } as unknown as User; ``` ================================================ FILE: LICENSE ================================================ Mozilla Public License Version 2.0 ================================== 1. Definitions -------------- 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 1.3. "Contribution" means Covered Software of a particular Contributor. 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. "Incompatible With Secondary Licenses" means (a) that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or (b) that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. "Executable Form" means any form of the work other than Source Code Form. 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" means this document. 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. "Modifications" means any of the following: (a) any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or (b) any new file in Source Code Form that contains any Covered Software. 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. "Source Code Form" means the form of the work preferred for making modifications. 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions -------------------------------- 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and (b) under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: (a) for any code that a Contributor has removed from Covered Software; or (b) for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or (c) under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities ------------------- 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: (a) such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and (b) You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation --------------------------------------------------- If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination -------------- 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. ************************************************************************ * * * 6. Disclaimer of Warranty * * ------------------------- * * * * Covered Software is provided under this License on an "as is" * * basis, without warranty of any kind, either expressed, implied, or * * statutory, including, without limitation, warranties that the * * Covered Software is free of defects, merchantable, fit for a * * particular purpose or non-infringing. The entire risk as to the * * quality and performance of the Covered Software is with You. * * Should any Covered Software prove defective in any respect, You * * (not any Contributor) assume the cost of any necessary servicing, * * repair, or correction. This disclaimer of warranty constitutes an * * essential part of this License. No use of any Covered Software is * * authorized under this License except under this disclaimer. * * * ************************************************************************ ************************************************************************ * * * 7. Limitation of Liability * * -------------------------- * * * * Under no circumstances and under no legal theory, whether tort * * (including negligence), contract, or otherwise, shall any * * Contributor, or anyone who distributes Covered Software as * * permitted above, be liable to You for any direct, indirect, * * special, incidental, or consequential damages of any character * * including, without limitation, damages for lost profits, loss of * * goodwill, work stoppage, computer failure or malfunction, or any * * and all other commercial damages or losses, even if such party * * shall have been informed of the possibility of such damages. This * * limitation of liability shall not apply to liability for death or * * personal injury resulting from such party's negligence to the * * extent applicable law prohibits such limitation. Some * * jurisdictions do not allow the exclusion or limitation of * * incidental or consequential damages, so this exclusion and * * limitation may not apply to You. * * * ************************************************************************ 8. Litigation ------------- Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 9. Miscellaneous ---------------- This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License --------------------------- 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice ------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. ================================================ FILE: README.md ================================================ [![Deploy](https://github.com/rolling-scopes/rsschool-app/actions/workflows/deploy.yaml/badge.svg?branch=master)](https://github.com/rolling-scopes/rsschool-app/actions/workflows/deploy.yaml) # RS School App [app.rs.school](https://app.rs.school) is an application for the [RS School](https://rs.school) educational process. It helps thousands of students become front-end, back-end, mobile, and data science engineers. ## Monitoring https://status.rs.school/ ## Technology Overview ### Stack - Language: [TypeScript](https://www.typescriptlang.org/) - Front-end: [Next.js](https://nextjs.org/) / [React](https://reactjs.org/) - Back-end: [NestJS](https://nestjs.com/) and [Koa.js](https://koajs.com/) (deprecated backend) / [Node.js](https://nodejs.org/en/) - End-to-end: [Playwright](https://playwright.dev/) - Database: [PostgreSQL](https://www.postgresql.org/) - Deployment: [Podman](https://podman.io/) ### Infrastructure - Cloud: [AWS EC2](https://aws.amazon.com/ec2/), [AWS RDS](https://aws.amazon.com/rds/postgresql/), [AWS S3](https://aws.amazon.com/s3/), [AWS CloudWatch](https://aws.amazon.com/cloudwatch/) - Monitoring: [Sentry](https://rs-school.sentry.io/), [Checkly](https://app.checklyhq.com/) - CI/CD: [GitHub Actions](https://github.com/rolling-scopes/rsschool-app/tree/master/.github/workflows) ## Getting Started Repository is organized into 3 workspaces: `server`, `nestjs`, `client`. Each workspace has its own package.json and npm scripts. We use [Turbo](https://turbo.build/) to run scripts across workspaces. ### Prerequisites Please install the following software before starting development: - [Git 2.10+](https://git-scm.com/downloads) - [Node.js LTS](https://nodejs.org/en/download/) - [Podman](https://podman.io/docs/installation) - [podman-compose](https://github.com/containers/podman-compose) ### Steps - Clone [repository](https://github.com/rolling-scopes/rsschool-app) - Run `npm install` (installs dependencies in the root folder and `client` / `server` folders.) - Run `npm run db:up` (starts local database) - Run `npm run db:restore` (restores a test DB snapshot) - Make copies of `server/.env.example` and `nestjs/.env.example`, then rename them to `server/.env` and `nestjs/.env` respectively - Run `npm start` (starts application by running Next.js and REST API server) - Open `http://localhost:3000` in a browser - See more in [CONTRIBUTING](https://github.com/rolling-scopes/rsschool-app/blob/master/CONTRIBUTING.md) guide ### Running docs locally - Install docsify globally: `npm i -g docsify-cli` - Run `docsify serve -p 4000 docs` ## Contributing See [CONTRIBUTING](https://github.com/rolling-scopes/rsschool-app/blob/master/CONTRIBUTING.md) guide ## Contributors ### Code Contributors This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. _Made with [contrib.rocks](https://contrib.rocks)_ ### Financial Contributors Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/rsschool/contribute)] #### Individuals #### Organizations Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/rsschool/contribute)] ## License [Mozilla Public License 2.0](https://github.com/rolling-scopes/rsschool-app/blob/master/LICENSE) ================================================ FILE: VITEST.md ================================================ # Vitest Migration Plan ## Goal Migrate all automated tests in this repository from Jest to latest Vitest with no feature regressions, stable CI reports, and same or better local developer experience ## Current Baseline - Monorepo with three active workspaces: `client`, `server`, `nestjs` - Jest is used in all three workspaces through local scripts and dedicated config files - Current usage footprint is large (`421` Jest-related references in source and test files, across `97` files) - Current CI uploads `jest-junit-*.xml` artifacts and relies on per-workspace `test:ci` - Root ESLint setup loads `eslint-plugin-jest` test recommendations ## Migration Strategy - Use phased migration with strict checkpoints - Keep repository green after each phase - Prefer adapter-compatible migration first (test runtime switch), then API cleanup - Migrate workspace by workspace, not all at once - Keep rollback path simple by limiting each pull request scope ## Work Breakdown ### Phase 0 - Planning and guardrails 1. Create tracking issue and checklist for all steps in this document 2. Freeze non-critical test refactors during migration window 3. Define acceptance criteria - all current test scripts have Vitest equivalents - CI test jobs pass in all workspaces - XML reports still published and consumed by report workflow - no Jest runtime package remains in active dependencies 4. Define branch strategy - one umbrella branch for the migration - optional child branches by workspace if team parallelizes 5. Define rollback strategy - each phase merged only after green CI - keep isolated commits for dependency changes, config changes, and test API updates ### Phase 1 - Repository inventory and mapping 1. Build complete inventory of Jest touchpoints - dependency graph (`jest`, `ts-jest`, `@types/jest`, `jest-environment-jsdom`, `jest-junit`, `jest-mock-axios`) - config files (`client/jest.config.mjs`, `server/jest.config.mjs`, `nestjs/jest.config.mjs`, `nestjs/test/jest-e2e.json`) - scripts in `package.json` files - ESLint test plugin configuration - CI workflow references to Jest output names 2. Build test API usage inventory - global APIs (`jest.fn`, `jest.spyOn`, `jest.mock`, fake timers, reset helpers) - type usages (`jest.Mock`, `jest.mocked`) - setup files and global matchers 3. Build workspace-specific complexity score - `client`: Next.js + jsdom + module mocks + `jest-mock-axios` - `server`: Node + TypeScript transform + legacy backend - `nestjs`: Node + Nest testing utilities + e2e config 4. Lock expected command matrix for final validation - root: `npm run lint`, `npm run test`, `npm run compile`, `npm run format` - plus workspace-level targeted test runs during migration ### Phase 2 - Target architecture decisions 1. Choose Vitest version and lockfile policy 2. Decide transform strategy for TypeScript in each workspace - native Vitest + Vite transform where possible - avoid keeping Jest-specific transform chain 3. Decide coverage provider and reporter format parity 4. Decide XML reporter replacement strategy for `jest-junit` - select Vitest-compatible junit reporter - preserve artifact naming or update workflows accordingly 5. Decide lint strategy - replace `eslint-plugin-jest` with Vitest-aware ruleset, or mixed mode during transition 6. Decide test file naming policy - keep current `.test`/`.spec` names for minimal churn 7. Decide future approach for Nest e2e tests - keep in Vitest as integration tests - or keep separate runner temporarily with clear deprecation date ### Phase 3 - Foundation changes at root level 1. Update root dependencies - add Vitest core and shared helpers - remove root Jest packages when no longer needed 2. Add shared base Vitest config pattern - either reusable root config module or per-workspace local configs extending shared defaults 3. Update root lint config for test files - switch from Jest plugin defaults to Vitest-aware defaults 4. Update root scripts only if needed for monorepo ergonomics - keep `turbo run test` behavior unchanged for contributors 5. Regenerate lockfile and confirm deterministic install ### Phase 4 - Migrate client workspace 1. Replace Jest config with Vitest config compatible with Next.js client tests 2. Recreate jsdom environment setup and test globals 3. Migrate `setupJest` file naming and imports to Vitest-compatible setup 4. Replace Jest scripts in `client/package.json` - `test`, `test:ci`, `test:watch`, `coverage` 5. Replace or adapt `jest-mock-axios` usage - evaluate native module mocking and spies first - if replacement package is needed, add it explicitly 6. Update test files in batches - convert `jest.*` runtime calls to `vi.*` - convert typed mocks from Jest types to Vitest types - fix hoisting-sensitive mocks (`jest.mock` patterns) 7. Run client tests repeatedly until green 8. Ensure junit XML output generated for CI artifact upload ### Phase 5 - Migrate server workspace 1. Replace `server/jest.config.mjs` with Vitest config for Node environment 2. Replace Jest scripts in `server/package.json` 3. Convert server test runtime APIs from `jest.*` to `vi.*` 4. Validate module resolution parity (`moduleDirectories`, aliases, TS paths) 5. Validate timers and mocks behavior in legacy service tests 6. Run server test suite and coverage commands 7. Ensure CI XML report is emitted at expected path ### Phase 6 - Migrate nestjs workspace 1. Replace `nestjs/jest.config.mjs` with Vitest config 2. Replace `nestjs/test/jest-e2e.json` strategy - either merge into Vitest project config - or split unit/integration projects in a single Vitest config 3. Replace Jest scripts in `nestjs/package.json` 4. Convert Nest unit tests from `jest.*` to `vi.*` 5. Validate compatibility with Nest testing module patterns 6. Rework debug script strategy (`test:debug`) for Vitest runtime 7. Migrate e2e command to Vitest-equivalent workflow 8. Ensure junit XML report is emitted at expected path ### Phase 7 - CI and reporting migration 1. Update `.github/workflows/pull_request.yml` - keep per-workspace test jobs - update artifact paths and names if reporter file names changed 2. Update `.github/workflows/test_report.yml` - replace any Jest-specific reporter assumptions - ensure parsed reports still annotate PRs 3. Update deploy workflow test command assumptions where relevant 4. Validate that all workflows still run under current Node version policy ### Phase 8 - TypeScript and tooling cleanup 1. Remove Jest-only types from tsconfig include/types if present 2. Ensure Vitest globals typing configured per workspace or explicit imports used 3. Remove obsolete Jest dependencies and config files 4. Remove stale test setup filenames referencing Jest 5. Remove dead helper code or mocks created only for Jest behavior ### Phase 9 - Verification and hardening 1. Full local validation - `npm run lint` - `npm run test` - `npm run compile` - `npm run format` 2. Spot-check changed tests for deterministic behavior - fake timers - async expectations - module mocks 3. Run CI on migration branch and verify all jobs green 4. Compare test duration before/after and capture regression notes 5. Compare coverage before/after and capture gaps ### Phase 10 - Documentation and handoff 1. Update contributor docs - root `README.md` testing section if needed - `CONTRIBUTING.md` test commands and local workflow - workspace READMEs if they mention Jest 2. Add migration notes - known incompatibilities - new mocking patterns - timer usage guidance 3. Create follow-up backlog for non-blocking improvements - optimize slow tests - improve test isolation - enforce new lint rules for Vitest patterns ## Execution Model for LLM Worker ### Operating rules 1. Work in small PRs with one phase or one workspace per PR 2. Keep PRs reviewable (target under ~400 changed lines unless mechanical rename) 3. Do not mix runtime migration with unrelated refactors 4. After each PR, run full required checks and attach outputs 5. If migration blocks on one workspace, ship completed workspace migrations first behind stable CI ### Suggested PR sequence 1. PR 1: root dependencies, lint plugin transition, shared Vitest base 2. PR 2: client migration 3. PR 3: server migration 4. PR 4: nestjs migration including e2e strategy 5. PR 5: CI/reporting cleanup and docs 6. PR 6: final Jest removal and dead-code cleanup ### Definition of done - No active workspace uses Jest runtime commands - No active workspace depends on Jest-only packages - All local required commands pass at root - CI test and reporting workflows pass with Vitest outputs - Documentation reflects Vitest-based workflow ## Risk Register and Mitigations 1. Mock hoisting differences break tests - mitigate with batch conversion and per-file verification 2. Timer behavior differences create flaky tests - mitigate by standardizing fake timer lifecycle in setup/teardown 3. Next.js client transform differences cause import failures - mitigate by validating module transform and alias mapping early in client phase 4. Nest e2e behavior differs from Jest config assumptions - mitigate by isolating e2e migration and validating with dedicated command before merge 5. CI report parser mismatch after reporter switch - mitigate by updating report workflow and testing artifact parsing in draft PR 6. Hidden Jest references survive in tooling - mitigate with final repository-wide scan for `jest` tokens and config filenames ## Final Migration Checklist - [ ] Root dependencies switched to Vitest stack - [ ] ESLint test rules updated for Vitest - [ ] Client tests green on Vitest - [ ] Server tests green on Vitest - [ ] NestJS tests green on Vitest - [ ] NestJS e2e path migrated or formally isolated with deadline - [ ] CI test jobs green - [ ] CI test reports parsed and published - [ ] Jest configs removed - [ ] Jest packages removed from lockfile and package manifests - [ ] Root validation commands pass - [ ] Documentation updated ================================================ FILE: client/.dockerignore ================================================ .next/cache # Keep empty dir. Next.js scans it on startup even with CDN assets .next/static/** !.next/static/ node_modules .turbo ================================================ FILE: client/Dockerfile ================================================ FROM node:24-alpine EXPOSE 8080 ENV NODE_ENV=production ENV NODE_PORT=8080 WORKDIR /app COPY client/next.config.mjs /app/client/next.config.mjs COPY client/next.config.prod.mjs /app/client/next.config.prod.mjs COPY client/package.json /app/client/package.json COPY client/public /app/client/public COPY package.json /app COPY package-lock.json /app RUN npm ci --production --no-optional COPY client/.next /app/client/.next CMD cd /app/client && npm run prod ================================================ FILE: client/Dockerfile.lambda ================================================ FROM node:24-bullseye-slim AS builder ENV NEXT_RUNTIME=nodejs WORKDIR /container_out COPY package.json package.json COPY package-lock.json package-lock.json COPY client/public client/public COPY client/package.json client/package.json COPY client/next.config.prod.mjs client/next.config.mjs RUN npm ci --production --no-optional # .next build folder COPY client/.next client/.next # Lambda Container with AWS Lambda Web Adapter FROM node:24-bullseye-slim ENV NODE_ENV=production ENV TZ=utc ENV NEXT_RUNTIME=nodejs ENV PORT=8080 ENV AWS_LWA_PORT=8080 WORKDIR /var/task COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:1.0.0-rc1 /lambda-adapter /opt/extensions/lambda-adapter COPY --from=builder /container_out /var/task/ CMD [ "npm", "run", "prod", "--prefix", "/var/task/client" ] ================================================ FILE: client/README.md ================================================ # Client This document defines the unified client architecture for the project. ## Core Principles - Feature ownership lives in `modules/`. - Shared code lives in `shared/`. - Domain rules live in `entities/`. - Pages are thin route wrappers. ## Folder Structure ``` client/src/ pages/ # Next.js routes (thin wrappers only) modules/ # Feature modules (vertical slices) shared/ # Cross-feature components, hooks, utils, services entities/ # Domain models and pure logic api/ # OpenAPI generated client (read-only) providers/ # App-level providers styles/ # Global CSS (minimal) configs/ # Global config ``` ## Module Template ``` modules// components/ hooks/ pages/ services/ data/ utils/ styles/ # CSS modules only types.ts index.ts ``` ## Architecture Rules ### Pages - `pages/**` should only: - Compose providers. - Render a module page component. - No business logic in pages. ### Modules - Feature-specific UI, hooks, and services remain in the module. - Modules can import from `shared/`, `entities/`, or `api/`. - Modules should not import from other modules directly. ### Shared - `shared/` holds cross-feature utilities and UI. - Only move to `shared/` if used in at least 2 modules. - `shared/` must not import from `modules/`. ### Entities - `entities/` contains domain types and pure logic. - No React or side effects in `entities/`. ### Components - Single file components should be placed into a file with the same name as the component but with the `.tsx` extension. - If the component is complex and requires multiple files (many internal only components), it should be placed in a folder with the same name as the component. The folder should contain an `index.ts` file that exports the final component. ### API Usage - `/api/v2/*` -> OpenAPI client only (`client/src/api`). - `/api/*` -> Axios services only. - Wrap API access inside module services for testability. ### Styling - CSS modules only: `*.module.css`. - styled-jsx is deprecated and must be removed when files are touched. - Keep `styles/` minimal for global resets and theme-level CSS. ### Naming & Tests - Consistent folder naming within modules. - Tests live next to the source file and have the same name as the source file but with the `.test.ts(x)` extension. ### Imports - Use path aliases (`@client/modules`, `@client/shared`, `@client/api`). - Avoid deep relative imports across module boundaries. ## Migration Policy - Refactor only when a file is touched. - Convert styled-jsx to CSS modules during touch-based changes. - Do not initiate large-scale rewrites without approval. ## Enforcement (Planned) - ESLint boundary rules to prevent cross-module imports. - ESLint rule to ban styled-jsx usage (error level). - Lint rule for page thinness. ## Example Page Wrapper ```tsx import { SessionProvider } from '@client/modules/Course/contexts'; import { ScorePage } from '@client/modules/Score/pages/ScorePage'; export default function Page() { return ( ); } ``` ================================================ FILE: client/eslint.config.mjs ================================================ import path from 'node:path'; import { fileURLToPath } from 'node:url'; import boundaries from 'eslint-plugin-boundaries'; import testingLibrary from 'eslint-plugin-testing-library'; import defaultConfig from '../eslint.config.mjs'; export default [ ...defaultConfig, { ...testingLibrary.configs['flat/react'], files: ['**/__tests__/**/*.ts?(x)', '**/?(*.)+(spec|test).ts?(x)'], }, { files: ['src/**/*.{ts,tsx}'], plugins: { boundaries, }, settings: { 'import/resolver': { typescript: { project: path.join(path.dirname(fileURLToPath(import.meta.url)), 'tsconfig.json'), }, }, 'boundaries/include': ['src/**/*.{ts,tsx}'], 'boundaries/ignore': ['src/**/*.css'], 'boundaries/elements': [ { type: 'pages', pattern: 'src/pages/**/*.{ts,tsx}', mode: 'file' }, { type: 'modules', pattern: 'src/modules/*', capture: ['moduleName'] }, { type: 'shared', pattern: 'src/shared/**/*.{ts,tsx}', mode: 'file' }, ], }, rules: { 'boundaries/element-types': [ 'warn', { default: 'allow', rules: [ { from: 'modules', disallow: 'modules', message: 'Modules must not import other modules directly.', }, { from: 'shared', disallow: 'modules', message: 'Shared code must not import from modules.', }, ], }, ], 'no-restricted-imports': [ 'error', { paths: [ { name: 'styled-jsx/css', message: "The 'jsx' attribute from styled-jsx is deprecated. Use CSS modules instead.", }, { name: 'styled-jsx', message: "The 'jsx' attribute from styled-jsx is deprecated. Use CSS modules instead.", }, ], }, ], 'no-restricted-syntax': [ 'error', { selector: "JSXAttribute[name.name='jsx']", message: "The 'jsx' attribute from styled-jsx is deprecated. Use CSS modules instead.", }, ], }, }, ]; ================================================ FILE: client/next-env.d.ts ================================================ /// /// import './.next/types/routes.d.ts'; // NOTE: This file should not be edited // see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. ================================================ FILE: client/next.config.mjs ================================================ import prodConfig from './next.config.prod.mjs'; const isProd = process.env.NODE_ENV === 'production'; const server = process.env.SERVER_HOST || 'http://localhost:3001'; const nestjs = process.env.NESTJS_HOST || 'http://localhost:3002'; const nextConfig = { ...prodConfig, rewrites: () => isProd ? [] : [ { source: '/certificate/:path*', destination: `${nestjs}/certificate/:path*` }, { source: '/swagger', destination: `${nestjs}/swagger/` }, { source: '/swagger-json', destination: `${nestjs}/swagger-json` }, { source: '/swagger:path', destination: `${nestjs}/swagger/swagger:path` }, { source: '/api/v2/:path*', destination: `${nestjs}/:path*` }, { source: '/api/:path*', destination: `${server}/:path*` }, ], }; export default nextConfig; ================================================ FILE: client/next.config.prod.mjs ================================================ const isProd = process.env.NODE_ENV === 'production'; const nextConfig = { assetPrefix: isProd ? 'https://cdn.rs.school' : '', env: { BUILD_VERSION: process.env.BUILD_VERSION || '0.0.0.0.0', APP_VERSION: process.env.APP_VERSION, RSSHCOOL_UI_GCP_MAPS_API_KEY: process.env.RSSHCOOL_UI_GCP_MAPS_API_KEY, CDN_HOST: process.env.CDN_HOST || '', RSSCHOOL_DEV_TOOLS: process.env.RSSCHOOL_DEV_TOOLS || 'false', }, }; export default nextConfig; ================================================ FILE: client/package.json ================================================ { "name": "client", "private": true, "version": "1.0.0", "license": "Mozilla Public License 2.0", "browserslist": [ "> 1%" ], "scripts": { "start": "next dev", "build": "next build", "compile": "tsc -p tsconfig.json --noEmit", "prod": "next start -p 8080", "lint": "eslint src", "test": "vitest run", "test:ci": "vitest run", "test:watch": "vitest", "test:e2e": "npx playwright test", "coverage": "vitest run --coverage" }, "dependencies": { "@ant-design/cssinjs": "^2.1.0", "@ant-design/icons": "6.1.0", "@ant-design/plots": "2.6.8", "@dnd-kit/core": "^6.3.1", "@dnd-kit/modifiers": "^9.0.0", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", "ahooks": "3.8.4", "antd": "6.3.1", "clsx": "2.1.1", "cookie": "0.7.0", "csvtojson": "2.0.10", "next": "^16.1.6", "react": "18.3.1", "react-dom": "18.3.1", "react-markdown": "8.0.7", "react-masonry-css": "1.0.16", "react-quill": "2.0.0", "react-use": "17.4.0", "remark-gfm": "3.0.1", "use-places-autocomplete": "4.0.1" }, "devDependencies": { "@playwright/test": "^1.51.1", "@testing-library/jest-dom": "6.1.4", "@testing-library/react": "14.1.2", "@testing-library/user-event": "14.5.1", "@types/aws-lambda": "8.10.126", "@types/cookie": "0.5.4", "@types/react": "18.3.18", "@types/react-dom": "18.3.5", "eslint-plugin-testing-library": "7.16.0", "mq-polyfill": "1.1.8" }, "nextBundleAnalysis": { "budget": 512000, "budgetPercentIncreaseRed": 5, "showDetails": true } } ================================================ FILE: client/playwright.config.ts ================================================ import type { PlaywrightTestConfig } from '@playwright/test'; import { devices } from '@playwright/test'; /** * Read environment variables from file. * https://github.com/motdotla/dotenv */ // require('dotenv').config(); /** * See https://playwright.dev/docs/test-configuration. */ const config: PlaywrightTestConfig = { testDir: './specs', /* Maximum time one test can run for. */ timeout: 30 * 1000, expect: { /** * Maximum time expect() should wait for the condition to be met. * For example in `await expect(locator).toHaveText();` */ timeout: 3000, }, /* Fail the build on CI if you accidentally left test.only in the source code. */ forbidOnly: !!process.env.CI, /* Retry on CI only */ retries: process.env.CI ? 1 : 0, /* Opt out of parallel tests on CI. */ workers: process.env.CI ? 1 : undefined, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: 'html', /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ actionTimeout: 0, /* Base URL to use in actions like `await page.goto('/')`. */ // baseURL: 'http://localhost:3000', /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ trace: 'on-first-retry', screenshot: 'on', }, /* Configure projects for major browsers */ projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'], }, }, ], }; export default config; ================================================ FILE: client/public/static/empty.txt ================================================ ================================================ FILE: client/specs/smoke.spec.ts ================================================ import { test, expect } from '@playwright/test'; const url = process.env.BASE_URL || 'http://localhost:3000'; test.beforeEach(async ({ page }) => { await page.context().clearCookies(); await page.goto(url); await page.getByRole('button', { name: /sign up with gitHub/i }).click(); await page.waitForSelector('.page-header'); }); test.describe('Home', () => { test('should have link to Score page', async ({ page }) => { expect(await page.locator('css=a >> text="Score"').isVisible()).toBe(true); const href = await page.locator('css=a >> text="Score"').getAttribute('href'); expect(href?.includes('/course/score')).toBeTruthy(); }); test('should navigate to Courses page and verify threshold', async ({ page }) => { await page.click('span[role="img"][aria-label="menu-unfold"]'); await page.click('text=Admin Area'); const coursesLink = page.locator('text=Courses'); await expect(coursesLink).toBeVisible(); await coursesLink.click(); await page.getByRole('button', { name: /add course/i }).click(); const certificateThreshold = page.locator('input#certificateThreshold'); await expect(certificateThreshold).toHaveValue('70'); }); }); ================================================ FILE: client/src/__mocks__/axios.js ================================================ import { vi } from 'vitest'; const mockAxios = { get: vi.fn(), post: vi.fn(), put: vi.fn(), patch: vi.fn(), delete: vi.fn(), create: vi.fn(() => mockAxios), defaults: { headers: { common: {} } }, interceptors: { request: { use: vi.fn(), eject: vi.fn() }, response: { use: vi.fn(), eject: vi.fn() }, }, reset() { this.get.mockReset(); this.post.mockReset(); this.put.mockReset(); this.patch.mockReset(); this.delete.mockReset(); }, }; export default mockAxios; ================================================ FILE: client/src/__mocks__/hooks/index.ts ================================================ export { useMessage } from './useMessage'; export { useTheme } from './useTheme'; ================================================ FILE: client/src/__mocks__/hooks/useMessage.tsx ================================================ import { vi } from 'vitest'; export const useMessage = () => ({ message: { success: vi.fn(), error: vi.fn(), info: vi.fn(), warning: vi.fn(), }, notification: { success: vi.fn(), error: vi.fn(), info: vi.fn(), warning: vi.fn(), open: vi.fn(), close: vi.fn(), }, }); ================================================ FILE: client/src/__mocks__/hooks/useTheme.tsx ================================================ import { vi } from 'vitest'; export const useTheme = () => ({ theme: 'light', themeChange: vi.fn(), autoTheme: true, changeAutoTheme: vi.fn(), }); ================================================ FILE: client/src/__mocks__/next/config.ts ================================================ export default function getConfig() { return {}; } ================================================ FILE: client/src/__tests__/ProfilePage.test.tsx ================================================ import { Session } from '@client/components/withSession'; import ProfilePage from '@client/pages/profile'; import { render } from '@testing-library/react'; import { SessionApi } from '@client/api'; import { NextRouter, useRouter } from 'next/router'; vi.mock('next/config', () => () => ({})); vi.mock('next/router', async () => ({ ...(await vi.importActual('next/router')), useRouter: vi.fn(), })); vi.mock('@client/api', async () => ({ ...(await vi.importActual('@client/api')), ProfileApi: vi.fn(), UsersNotificationsApi: vi.fn(), NotificationsApi: vi.fn(), CoursesApi: vi.fn(), CoursesTasksApi: vi.fn(), StudentsScoreApi: vi.fn(), UpdateUserDtoLanguagesEnum: {}, })); const session = { id: 2020, githubId: 'mikhama', isAdmin: true, isHirer: false, isActivist: false, courses: { 13: { roles: ['manager'], }, 1: { roles: ['mentor'], }, 2: { roles: ['student'], }, }, } as Session; SessionApi.prototype.getSession = vi.fn().mockResolvedValue({ data: session }); const router = { query: { githubId: 'petrov', }, } as unknown as NextRouter; describe('ProfilePage', () => { describe('Should render correctly', () => { it('if full profile info is in the state', () => { vi.mocked(useRouter).mockReturnValue(router); const { container } = render(); expect(container).toMatchSnapshot(); }); }); }); ================================================ FILE: client/src/__tests__/__snapshots__/ProfilePage.test.tsx.snap ================================================ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`ProfilePage > Should render correctly > if full profile info is in the state 1`] = `
Loading...
`; ================================================ FILE: client/src/api/.gitignore ================================================ wwwroot/*.js node_modules typings dist ================================================ FILE: client/src/api/.npmignore ================================================ # empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm ================================================ FILE: client/src/api/.openapi-generator/FILES ================================================ .gitignore .npmignore api.ts base.ts common.ts configuration.ts git_push.sh index.ts ================================================ FILE: client/src/api/.openapi-generator/VERSION ================================================ 5.4.0 ================================================ FILE: client/src/api/.openapi-generator-ignore ================================================ # OpenAPI Generator Ignore # Generated by openapi-generator https://github.com/openapitools/openapi-generator # Use this file to prevent files from being overwritten by the generator. # The patterns follow closely to .gitignore or .dockerignore. # As an example, the C# client generator defines ApiClient.cs. # You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: #ApiClient.cs # You can match any string of characters against a directory, file or extension with a single asterisk (*): #foo/*/qux # The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux # You can recursively match patterns against a directory, file or extension with a double asterisk (**): #foo/**/qux # This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux # You can also negate patterns with an exclamation (!). # For example, you can ignore all files in a docs folder with the file extension .md: #docs/*.md # Then explicitly reverse the ignore rule for a single file: #!docs/README.md ================================================ FILE: client/src/api/api.ts ================================================ /* tslint:disable */ /* eslint-disable */ /** * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * * The version of the OpenAPI document: 1.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech * Do not edit the class manually. */ import { Configuration } from './configuration'; import globalAxios, { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios'; // Some imports not used depending on template conditions // @ts-ignore import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from './common'; // @ts-ignore import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError } from './base'; /** * * @export * @interface ActivityDto */ export interface ActivityDto { /** * * @type {number} * @memberof ActivityDto */ 'lastActivityTime': number; /** * * @type {boolean} * @memberof ActivityDto */ 'isActive': boolean; } /** * * @export * @interface AlertDto */ export interface AlertDto { /** * * @type {number} * @memberof AlertDto */ 'id': number; /** * * @type {string} * @memberof AlertDto */ 'type': string; /** * * @type {string} * @memberof AlertDto */ 'text': string; /** * * @type {boolean} * @memberof AlertDto */ 'enabled': boolean; /** * * @type {number} * @memberof AlertDto */ 'courseId': number | null; /** * * @type {string} * @memberof AlertDto */ 'updatedDate': string; /** * * @type {string} * @memberof AlertDto */ 'createdDate': string; } /** * * @export * @interface ApplicantResumeDto */ export interface ApplicantResumeDto { /** * * @type {string} * @memberof ApplicantResumeDto */ 'uuid': string; /** * * @type {string} * @memberof ApplicantResumeDto */ 'avatarLink': string | null; /** * * @type {Array} * @memberof ApplicantResumeDto */ 'visibleCourses': Array; /** * * @type {string} * @memberof ApplicantResumeDto */ 'desiredPosition': string | null; /** * * @type {string} * @memberof ApplicantResumeDto */ 'email': string | null; /** * * @type {string} * @memberof ApplicantResumeDto */ 'englishLevel': ApplicantResumeDtoEnglishLevelEnum; /** * * @type {number} * @memberof ApplicantResumeDto */ 'expires': number | null; /** * * @type {boolean} * @memberof ApplicantResumeDto */ 'fullTime': boolean; /** * * @type {string} * @memberof ApplicantResumeDto */ 'githubId': string; /** * * @type {number} * @memberof ApplicantResumeDto */ 'id': number; /** * * @type {string} * @memberof ApplicantResumeDto */ 'linkedin': string | null; /** * * @type {string} * @memberof ApplicantResumeDto */ 'locations': string | null; /** * * @type {string} * @memberof ApplicantResumeDto */ 'militaryService': ApplicantResumeDtoMilitaryServiceEnum; /** * * @type {string} * @memberof ApplicantResumeDto */ 'name': string | null; /** * * @type {string} * @memberof ApplicantResumeDto */ 'notes': string | null; /** * * @type {string} * @memberof ApplicantResumeDto */ 'phone': string | null; /** * * @type {string} * @memberof ApplicantResumeDto */ 'selfIntroLink': string | null; /** * * @type {string} * @memberof ApplicantResumeDto */ 'skype': string | null; /** * * @type {string} * @memberof ApplicantResumeDto */ 'startFrom': string | null; /** * * @type {string} * @memberof ApplicantResumeDto */ 'telegram': string | null; /** * * @type {string} * @memberof ApplicantResumeDto */ 'website': string | null; } export const ApplicantResumeDtoEnglishLevelEnum = { Unknown: 'unknown', A0: 'a0', A1: 'a1', A2: 'a2', B1: 'b1', B2: 'b2', C1: 'c1', C2: 'c2' } as const; export type ApplicantResumeDtoEnglishLevelEnum = typeof ApplicantResumeDtoEnglishLevelEnum[keyof typeof ApplicantResumeDtoEnglishLevelEnum]; export const ApplicantResumeDtoMilitaryServiceEnum = { Served: 'served', Liable: 'liable', NotLiable: 'notLiable' } as const; export type ApplicantResumeDtoMilitaryServiceEnum = typeof ApplicantResumeDtoMilitaryServiceEnum[keyof typeof ApplicantResumeDtoMilitaryServiceEnum]; /** * * @export * @interface ApproveMentorDto */ export interface ApproveMentorDto { /** * * @type {Array} * @memberof ApproveMentorDto */ 'preselectedCourses': Array; } /** * * @export * @interface Attributes */ export interface Attributes { /** * * @type {string} * @memberof Attributes */ 'template'?: string | null; } /** * * @export * @interface AuthConnectionDto */ export interface AuthConnectionDto { /** * * @type {string} * @memberof AuthConnectionDto */ 'channelId': string; /** * * @type {string} * @memberof AuthConnectionDto */ 'externalId': string; } /** * * @export * @interface AuthUserDto */ export interface AuthUserDto { /** * * @type {number} * @memberof AuthUserDto */ 'id': number; /** * * @type {string} * @memberof AuthUserDto */ 'githubId': string; /** * * @type {{ [key: string]: string; }} * @memberof AuthUserDto */ 'roles': { [key: string]: string; }; /** * * @type {boolean} * @memberof AuthUserDto */ 'isAdmin': boolean; /** * * @type {boolean} * @memberof AuthUserDto */ 'isHirer': boolean; /** * * @type {Array} * @memberof AuthUserDto */ 'appRoles': Array; /** * * @type {{ [key: string]: AuthUserDtoCourses; }} * @memberof AuthUserDto */ 'courses': { [key: string]: AuthUserDtoCourses; }; } export const AuthUserDtoRolesEnum = { Mentor: 'mentor', Student: 'student' } as const; export type AuthUserDtoRolesEnum = typeof AuthUserDtoRolesEnum[keyof typeof AuthUserDtoRolesEnum]; /** * * @export * @interface AuthUserDtoCourses */ export interface AuthUserDtoCourses { /** * * @type {Array} * @memberof AuthUserDtoCourses */ 'roles': Array; } export const AuthUserDtoCoursesRolesEnum = { Manager: 'manager', Supervisor: 'supervisor', Student: 'student', Mentor: 'mentor', Dementor: 'dementor', Activist: 'activist' } as const; export type AuthUserDtoCoursesRolesEnum = typeof AuthUserDtoCoursesRolesEnum[keyof typeof AuthUserDtoCoursesRolesEnum]; /** * * @export * @interface AutoTestAttributesDto */ export interface AutoTestAttributesDto { /** * * @type {PublicAttributesDto} * @memberof AutoTestAttributesDto */ 'public': PublicAttributesDto; /** * * @type {Array>} * @memberof AutoTestAttributesDto */ 'answers': Array>; } /** * * @export * @interface AutoTestTaskDto */ export interface AutoTestTaskDto { /** * * @type {string} * @memberof AutoTestTaskDto */ 'type': AutoTestTaskDtoTypeEnum; /** * * @type {string} * @memberof AutoTestTaskDto */ 'name': string; /** * * @type {number} * @memberof AutoTestTaskDto */ 'id': number; /** * * @type {string} * @memberof AutoTestTaskDto */ 'descriptionUrl': string; /** * * @type {string} * @memberof AutoTestTaskDto */ 'description': string; /** * * @type {string} * @memberof AutoTestTaskDto */ 'githubRepoName': string; /** * * @type {string} * @memberof AutoTestTaskDto */ 'sourceGithubRepoUrl': string; /** * * @type {IdNameDto} * @memberof AutoTestTaskDto */ 'discipline': IdNameDto; /** * * @type {boolean} * @memberof AutoTestTaskDto */ 'githubPrRequired': boolean; /** * * @type {string} * @memberof AutoTestTaskDto */ 'createdDate': string; /** * * @type {string} * @memberof AutoTestTaskDto */ 'updatedDate': string; /** * * @type {Array} * @memberof AutoTestTaskDto */ 'tags': Array; /** * * @type {Array} * @memberof AutoTestTaskDto */ 'skills': Array; /** * * @type {AutoTestAttributesDto} * @memberof AutoTestTaskDto */ 'attributes': AutoTestAttributesDto; /** * * @type {Array} * @memberof AutoTestTaskDto */ 'courses': Array; } export const AutoTestTaskDtoTypeEnum = { Jstask: 'jstask', Kotlintask: 'kotlintask', Objctask: 'objctask', Htmltask: 'htmltask', Ipynb: 'ipynb', Selfeducation: 'selfeducation', Codewars: 'codewars', Test: 'test', Codejam: 'codejam', Interview: 'interview', StageInterview: 'stage-interview', Cvhtml: 'cv:html', Cvmarkdown: 'cv:markdown' } as const; export type AutoTestTaskDtoTypeEnum = typeof AutoTestTaskDtoTypeEnum[keyof typeof AutoTestTaskDtoTypeEnum]; /** * * @export * @interface AvailableReviewStatsDto */ export interface AvailableReviewStatsDto { /** * * @type {string} * @memberof AvailableReviewStatsDto */ 'name': string; /** * * @type {number} * @memberof AvailableReviewStatsDto */ 'id': number; /** * * @type {number} * @memberof AvailableReviewStatsDto */ 'checksCount': number; /** * * @type {number} * @memberof AvailableReviewStatsDto */ 'completedChecksCount': number; } /** * * @export * @interface AvailableStudentDto */ export interface AvailableStudentDto { /** * * @type {number} * @memberof AvailableStudentDto */ 'id': number; /** * * @type {string} * @memberof AvailableStudentDto */ 'name': string; /** * * @type {string} * @memberof AvailableStudentDto */ 'githubId': string; /** * * @type {string} * @memberof AvailableStudentDto */ 'cityName': string | null; /** * * @type {string} * @memberof AvailableStudentDto */ 'countryName': string | null; /** * * @type {boolean} * @memberof AvailableStudentDto */ 'isGoodCandidate': boolean; /** * * @type {string} * @memberof AvailableStudentDto */ 'rating': string | null; /** * * @type {number} * @memberof AvailableStudentDto */ 'totalScore': number; /** * * @type {string} * @memberof AvailableStudentDto */ 'registeredDate': string; /** * * @type {number} * @memberof AvailableStudentDto */ 'maxScore': number; /** * * @type {number} * @memberof AvailableStudentDto */ 'feedbackVersion': number; } /** * * @export * @interface BadgeDto */ export interface BadgeDto { /** * * @type {string} * @memberof BadgeDto */ 'name': string; /** * * @type {BadgeEnum} * @memberof BadgeDto */ 'id': BadgeEnum; } /** * * @export * @enum {string} */ export const BadgeEnum = { Congratulations: 'Congratulations', ExpertHelp: 'Expert_help', GreatSpeaker: 'Great_speaker', GoodJob: 'Good_job', HelpingHand: 'Helping_hand', Hero: 'Hero', ThankYou: 'Thank_you', OutstandingWork: 'Outstanding_work', TopPerformer: 'Top_performer', JobOffer: 'Job_Offer', RsActivist: 'RS_activist', JuryTeam: 'Jury_Team', Mentor: 'Mentor', Contributor: 'Contributor', Coordinator: 'Coordinator', Thanks: 'Thanks' } as const; export type BadgeEnum = typeof BadgeEnum[keyof typeof BadgeEnum]; /** * * @export * @interface BasicAutoTestTaskDto */ export interface BasicAutoTestTaskDto { /** * * @type {number} * @memberof BasicAutoTestTaskDto */ 'id': number; /** * * @type {string} * @memberof BasicAutoTestTaskDto */ 'name': string; /** * * @type {number} * @memberof BasicAutoTestTaskDto */ 'maxAttemptsNumber': number | null; /** * * @type {number} * @memberof BasicAutoTestTaskDto */ 'numberOfQuestions': number | null; /** * * @type {number} * @memberof BasicAutoTestTaskDto */ 'strictAttemptsMode': number | null; /** * * @type {number} * @memberof BasicAutoTestTaskDto */ 'thresholdPercentage': number | null; } /** * * @export * @interface ChannelSettings */ export interface ChannelSettings { /** * * @type {string} * @memberof ChannelSettings */ 'channelId': string; /** * * @type {object} * @memberof ChannelSettings */ 'template': object; } /** * * @export * @interface CheckScheduleChangesDto */ export interface CheckScheduleChangesDto { /** * * @type {number} * @memberof CheckScheduleChangesDto */ 'lastHours': number; } /** * * @export * @interface CheckTasksDeadlineDto */ export interface CheckTasksDeadlineDto { /** * * @type {number} * @memberof CheckTasksDeadlineDto */ 'deadlineInHours': number; } /** * * @export * @enum {string} */ export const CheckerEnum = { AutoTest: 'auto-test', Assigned: 'assigned', Mentor: 'mentor', TaskOwner: 'taskOwner', CrossCheck: 'crossCheck' } as const; export type CheckerEnum = typeof CheckerEnum[keyof typeof CheckerEnum]; /** * * @export * @interface CommentMentorRegistryDto */ export interface CommentMentorRegistryDto { /** * * @type {string} * @memberof CommentMentorRegistryDto */ 'comment': string | null; } /** * * @export * @interface ConsentDto */ export interface ConsentDto { /** * * @type {boolean} * @memberof ConsentDto */ 'consent': boolean; } /** * * @export * @interface ContactsDto */ export interface ContactsDto { /** * * @type {string} * @memberof ContactsDto */ 'phone'?: string | null; /** * * @type {string} * @memberof ContactsDto */ 'email'?: string | null; /** * * @type {string} * @memberof ContactsDto */ 'epamEmail'?: string | null; /** * * @type {string} * @memberof ContactsDto */ 'skype'?: string | null; /** * * @type {string} * @memberof ContactsDto */ 'whatsApp'?: string | null; /** * * @type {string} * @memberof ContactsDto */ 'telegram'?: string | null; /** * * @type {string} * @memberof ContactsDto */ 'notes'?: string | null; /** * * @type {string} * @memberof ContactsDto */ 'linkedIn'?: string | null; /** * * @type {string} * @memberof ContactsDto */ 'discord'?: string | null; } /** * * @export * @interface ContributorDto */ export interface ContributorDto { /** * * @type {string} * @memberof ContributorDto */ 'description': string; /** * * @type {ContributorUserDto} * @memberof ContributorDto */ 'user': ContributorUserDto; /** * * @type {number} * @memberof ContributorDto */ 'id': number; /** * * @type {string} * @memberof ContributorDto */ 'createdDate': string; /** * * @type {string} * @memberof ContributorDto */ 'updatedDate': string; } /** * * @export * @interface ContributorUserDto */ export interface ContributorUserDto { /** * * @type {number} * @memberof ContributorUserDto */ 'id': number; /** * * @type {string} * @memberof ContributorUserDto */ 'githubId': string; /** * * @type {string} * @memberof ContributorUserDto */ 'firstName': string; /** * * @type {string} * @memberof ContributorUserDto */ 'lastName': string; } /** * * @export * @interface CountriesStatsDto */ export interface CountriesStatsDto { /** * * @type {Array} * @memberof CountriesStatsDto */ 'countries': Array; } /** * * @export * @interface CountryDto */ export interface CountryDto { /** * * @type {string} * @memberof CountryDto */ 'countryName': string; } /** * * @export * @interface CountryStatDto */ export interface CountryStatDto { /** * * @type {string} * @memberof CountryStatDto */ 'countryName': string; /** * * @type {number} * @memberof CountryStatDto */ 'count': number; } /** * * @export * @interface CourseAggregateStatsDto */ export interface CourseAggregateStatsDto { /** * * @type {CountriesStatsDto} * @memberof CourseAggregateStatsDto */ 'studentsCountries': CountriesStatsDto; /** * * @type {CourseStatsDto} * @memberof CourseAggregateStatsDto */ 'studentsStats': CourseStatsDto; /** * * @type {CountriesStatsDto} * @memberof CourseAggregateStatsDto */ 'mentorsCountries': CountriesStatsDto; /** * * @type {CourseMentorsStatsDto} * @memberof CourseAggregateStatsDto */ 'mentorsStats': CourseMentorsStatsDto; /** * * @type {Array} * @memberof CourseAggregateStatsDto */ 'courseTasks': Array; /** * * @type {CountriesStatsDto} * @memberof CourseAggregateStatsDto */ 'studentsCertificatesCountries': CountriesStatsDto; } /** * * @export * @interface CourseCopyFromDto */ export interface CourseCopyFromDto { /** * * @type {number} * @memberof CourseCopyFromDto */ 'copyFromCourseId': number; } /** * * @export * @interface CourseDto */ export interface CourseDto { /** * * @type {number} * @memberof CourseDto */ 'id': number; /** * * @type {string} * @memberof CourseDto */ 'createdDate': string; /** * * @type {string} * @memberof CourseDto */ 'updatedDate': string; /** * * @type {string} * @memberof CourseDto */ 'name': string; /** * * @type {string} * @memberof CourseDto */ 'fullName': string; /** * * @type {string} * @memberof CourseDto */ 'alias': string; /** * * @type {string} * @memberof CourseDto */ 'description': string; /** * * @type {string} * @memberof CourseDto */ 'descriptionUrl': string; /** * * @type {number} * @memberof CourseDto */ 'year': number; /** * * @type {string} * @memberof CourseDto */ 'startDate': string; /** * * @type {string} * @memberof CourseDto */ 'endDate': string; /** * * @type {string} * @memberof CourseDto */ 'registrationEndDate': string | null; /** * * @type {string} * @memberof CourseDto */ 'primarySkillId': string; /** * * @type {string} * @memberof CourseDto */ 'primarySkillName': string; /** * * @type {string} * @memberof CourseDto */ 'locationName': string; /** * * @type {number} * @memberof CourseDto */ 'discordServerId': number; /** * * @type {boolean} * @memberof CourseDto */ 'completed': boolean; /** * * @type {boolean} * @memberof CourseDto */ 'planned': boolean; /** * * @type {boolean} * @memberof CourseDto */ 'inviteOnly': boolean; /** * * @type {string} * @memberof CourseDto */ 'certificateIssuer': string; /** * * @type {boolean} * @memberof CourseDto */ 'usePrivateRepositories': boolean; /** * * @type {boolean} * @memberof CourseDto */ 'personalMentoring': boolean; /** * * @type {string} * @memberof CourseDto */ 'personalMentoringStartDate': string | null; /** * * @type {string} * @memberof CourseDto */ 'personalMentoringEndDate': string | null; /** * * @type {string} * @memberof CourseDto */ 'logo': string; /** * * @type {IdNameDto} * @memberof CourseDto */ 'discipline': IdNameDto | null; /** * * @type {number} * @memberof CourseDto */ 'minStudentsPerMentor': number; /** * * @type {number} * @memberof CourseDto */ 'certificateThreshold': number; /** * * @type {string} * @memberof CourseDto */ 'wearecommunityUrl': string | null; /** * * @type {Array} * @memberof CourseDto */ 'certificateDisciplines': Array | null; } /** * * @export * @interface CourseEventDto */ export interface CourseEventDto { /** * * @type {number} * @memberof CourseEventDto */ 'id': number; /** * * @type {string} * @memberof CourseEventDto */ 'name': string; /** * * @type {string} * @memberof CourseEventDto */ 'type': CourseEventDtoTypeEnum; /** * * @type {string} * @memberof CourseEventDto */ 'description': string; /** * * @type {string} * @memberof CourseEventDto */ 'descriptionUrl': string; /** * * @type {string} * @memberof CourseEventDto */ 'dateTime': string; /** * * @type {string} * @memberof CourseEventDto */ 'endTime': string; /** * * @type {PersonDto} * @memberof CourseEventDto */ 'organizer': PersonDto; } export const CourseEventDtoTypeEnum = { LectureOnline: 'lecture_online', LectureOffline: 'lecture_offline', LectureMixed: 'lecture_mixed', LectureSelfStudy: 'lecture_self_study', Warmup: 'warmup', Info: 'info', Workshop: 'workshop', Meetup: 'meetup', CrossCheckDeadline: 'cross_check_deadline', Webinar: 'webinar', Special: 'special' } as const; export type CourseEventDtoTypeEnum = typeof CourseEventDtoTypeEnum[keyof typeof CourseEventDtoTypeEnum]; /** * * @export * @interface CourseMentorsStatsDto */ export interface CourseMentorsStatsDto { /** * * @type {number} * @memberof CourseMentorsStatsDto */ 'mentorsActiveCount': number; /** * * @type {number} * @memberof CourseMentorsStatsDto */ 'mentorsTotalCount': number; /** * * @type {number} * @memberof CourseMentorsStatsDto */ 'epamMentorsCount': number; } /** * * @export * @interface CourseRecord */ export interface CourseRecord { /** * * @type {string} * @memberof CourseRecord */ 'courseName': string; /** * * @type {number} * @memberof CourseRecord */ 'id': number; } /** * * @export * @interface CourseRolesDto */ export interface CourseRolesDto { /** * * @type {boolean} * @memberof CourseRolesDto */ 'isManager': boolean; /** * * @type {boolean} * @memberof CourseRolesDto */ 'isSupervisor': boolean; /** * * @type {boolean} * @memberof CourseRolesDto */ 'isDementor': boolean; /** * * @type {boolean} * @memberof CourseRolesDto */ 'isActivist': boolean; } /** * * @export * @interface CourseScheduleItemDto */ export interface CourseScheduleItemDto { /** * * @type {number} * @memberof CourseScheduleItemDto */ 'score': number | null; /** * * @type {string} * @memberof CourseScheduleItemDto */ 'name': string; /** * * @type {number} * @memberof CourseScheduleItemDto */ 'id': number; /** * * @type {string} * @memberof CourseScheduleItemDto */ 'status': CourseScheduleItemDtoStatusEnum; /** * * @type {string} * @memberof CourseScheduleItemDto */ 'startDate': string; /** * * @type {string} * @memberof CourseScheduleItemDto */ 'endDate': string; /** * * @type {string} * @memberof CourseScheduleItemDto */ 'crossCheckEndDate': string; /** * * @type {PersonDto} * @memberof CourseScheduleItemDto */ 'organizer': PersonDto | null; /** * * @type {number} * @memberof CourseScheduleItemDto */ 'maxScore': number | null; /** * * @type {number} * @memberof CourseScheduleItemDto */ 'scoreWeight': number | null; /** * * @type {string} * @memberof CourseScheduleItemDto */ 'descriptionUrl': string | null; /** * * @type {string} * @memberof CourseScheduleItemDto */ 'tag': CourseScheduleItemDtoTagEnum; /** * * @type {string} * @memberof CourseScheduleItemDto */ 'type': CourseScheduleItemDtoTypeEnum; } export const CourseScheduleItemDtoStatusEnum = { Done: 'done', Available: 'available', Archived: 'archived', Future: 'future', Missed: 'missed', Review: 'review', Registered: 'registered', Unavailable: 'unavailable' } as const; export type CourseScheduleItemDtoStatusEnum = typeof CourseScheduleItemDtoStatusEnum[keyof typeof CourseScheduleItemDtoStatusEnum]; export const CourseScheduleItemDtoTagEnum = { Lecture: 'lecture', Coding: 'coding', SelfStudy: 'self-study', Interview: 'interview', CrossCheckSubmit: 'cross-check-submit', CrossCheckReview: 'cross-check-review', Test: 'test', TeamDistribution: 'team-distribution' } as const; export type CourseScheduleItemDtoTagEnum = typeof CourseScheduleItemDtoTagEnum[keyof typeof CourseScheduleItemDtoTagEnum]; export const CourseScheduleItemDtoTypeEnum = { CourseTask: 'courseTask', CourseEvent: 'courseEvent', CourseTeamDistribution: 'courseTeamDistribution' } as const; export type CourseScheduleItemDtoTypeEnum = typeof CourseScheduleItemDtoTypeEnum[keyof typeof CourseScheduleItemDtoTypeEnum]; /** * * @export * @interface CourseScheduleTokenDto */ export interface CourseScheduleTokenDto { /** * * @type {string} * @memberof CourseScheduleTokenDto */ 'token': string; } /** * * @export * @interface CourseStatsDto */ export interface CourseStatsDto { /** * * @type {number} * @memberof CourseStatsDto */ 'activeStudentsCount': number; /** * * @type {number} * @memberof CourseStatsDto */ 'totalStudents': number; /** * * @type {number} * @memberof CourseStatsDto */ 'studentsWithMentorCount': number; /** * * @type {number} * @memberof CourseStatsDto */ 'certifiedStudentsCount': number; /** * * @type {number} * @memberof CourseStatsDto */ 'eligibleForCertificationCount': number; } /** * * @export * @interface CourseTaskDetailedDto */ export interface CourseTaskDetailedDto { /** * * @type {number} * @memberof CourseTaskDetailedDto */ 'id': number; /** * * @type {number} * @memberof CourseTaskDetailedDto */ 'taskId': number; /** * * @type {string} * @memberof CourseTaskDetailedDto */ 'type': CourseTaskDetailedDtoTypeEnum; /** * * @type {string} * @memberof CourseTaskDetailedDto */ 'name': string; /** * * @type {string} * @memberof CourseTaskDetailedDto */ 'checker': CourseTaskDetailedDtoCheckerEnum; /** * * @type {string} * @memberof CourseTaskDetailedDto */ 'studentStartDate': string; /** * * @type {string} * @memberof CourseTaskDetailedDto */ 'studentEndDate': string; /** * * @type {string} * @memberof CourseTaskDetailedDto */ 'studentRegistrationStartDate': string | null; /** * * @type {string} * @memberof CourseTaskDetailedDto */ 'crossCheckEndDate': string | null; /** * * @type {string} * @memberof CourseTaskDetailedDto */ 'descriptionUrl': string; /** * * @type {PersonDto} * @memberof CourseTaskDetailedDto */ 'taskOwner': PersonDto | null; /** * * @type {object} * @memberof CourseTaskDetailedDto */ 'taskSolutions': object | null; /** * * @type {number} * @memberof CourseTaskDetailedDto */ 'maxScore': number; /** * * @type {number} * @memberof CourseTaskDetailedDto */ 'scoreWeight': number; /** * * @type {number} * @memberof CourseTaskDetailedDto */ 'pairsCount': number | null; /** * * @type {CrossCheckStatusEnum} * @memberof CourseTaskDetailedDto */ 'crossCheckStatus': CrossCheckStatusEnum; /** * * @type {string} * @memberof CourseTaskDetailedDto */ 'submitText': string | null; /** * * @type {Validations} * @memberof CourseTaskDetailedDto */ 'validations': Validations | null; /** * * @type {object} * @memberof CourseTaskDetailedDto */ 'publicAttributes': object; /** * * @type {string} * @memberof CourseTaskDetailedDto */ 'githubRepoName': string; /** * * @type {string} * @memberof CourseTaskDetailedDto */ 'sourceGithubRepoUrl': string; /** * * @type {number} * @memberof CourseTaskDetailedDto */ 'resultsCount': number; } export const CourseTaskDetailedDtoTypeEnum = { Jstask: 'jstask', Kotlintask: 'kotlintask', Objctask: 'objctask', Htmltask: 'htmltask', Ipynb: 'ipynb', Selfeducation: 'selfeducation', Codewars: 'codewars', Test: 'test', Codejam: 'codejam', Interview: 'interview', StageInterview: 'stage-interview', Cvhtml: 'cv:html', Cvmarkdown: 'cv:markdown' } as const; export type CourseTaskDetailedDtoTypeEnum = typeof CourseTaskDetailedDtoTypeEnum[keyof typeof CourseTaskDetailedDtoTypeEnum]; export const CourseTaskDetailedDtoCheckerEnum = { AutoTest: 'auto-test', Assigned: 'assigned', Mentor: 'mentor', TaskOwner: 'taskOwner', CrossCheck: 'crossCheck' } as const; export type CourseTaskDetailedDtoCheckerEnum = typeof CourseTaskDetailedDtoCheckerEnum[keyof typeof CourseTaskDetailedDtoCheckerEnum]; /** * * @export * @interface CourseTaskDto */ export interface CourseTaskDto { /** * * @type {number} * @memberof CourseTaskDto */ 'id': number; /** * * @type {number} * @memberof CourseTaskDto */ 'taskId': number; /** * * @type {string} * @memberof CourseTaskDto */ 'type': CourseTaskDtoTypeEnum; /** * * @type {string} * @memberof CourseTaskDto */ 'name': string; /** * * @type {string} * @memberof CourseTaskDto */ 'checker': CourseTaskDtoCheckerEnum; /** * * @type {string} * @memberof CourseTaskDto */ 'studentStartDate': string; /** * * @type {string} * @memberof CourseTaskDto */ 'studentEndDate': string; /** * * @type {string} * @memberof CourseTaskDto */ 'studentRegistrationStartDate': string | null; /** * * @type {string} * @memberof CourseTaskDto */ 'crossCheckEndDate': string | null; /** * * @type {string} * @memberof CourseTaskDto */ 'descriptionUrl': string; /** * * @type {PersonDto} * @memberof CourseTaskDto */ 'taskOwner': PersonDto | null; /** * * @type {object} * @memberof CourseTaskDto */ 'taskSolutions': object | null; /** * * @type {number} * @memberof CourseTaskDto */ 'maxScore': number; /** * * @type {number} * @memberof CourseTaskDto */ 'scoreWeight': number; /** * * @type {number} * @memberof CourseTaskDto */ 'pairsCount': number | null; /** * * @type {CrossCheckStatusEnum} * @memberof CourseTaskDto */ 'crossCheckStatus': CrossCheckStatusEnum; /** * * @type {string} * @memberof CourseTaskDto */ 'submitText': string | null; /** * * @type {Validations} * @memberof CourseTaskDto */ 'validations': Validations | null; } export const CourseTaskDtoTypeEnum = { Jstask: 'jstask', Kotlintask: 'kotlintask', Objctask: 'objctask', Htmltask: 'htmltask', Ipynb: 'ipynb', Selfeducation: 'selfeducation', Codewars: 'codewars', Test: 'test', Codejam: 'codejam', Interview: 'interview', StageInterview: 'stage-interview', Cvhtml: 'cv:html', Cvmarkdown: 'cv:markdown' } as const; export type CourseTaskDtoTypeEnum = typeof CourseTaskDtoTypeEnum[keyof typeof CourseTaskDtoTypeEnum]; export const CourseTaskDtoCheckerEnum = { AutoTest: 'auto-test', Assigned: 'assigned', Mentor: 'mentor', TaskOwner: 'taskOwner', CrossCheck: 'crossCheck' } as const; export type CourseTaskDtoCheckerEnum = typeof CourseTaskDtoCheckerEnum[keyof typeof CourseTaskDtoCheckerEnum]; /** * * @export * @interface CourseUserDto */ export interface CourseUserDto { /** * * @type {number} * @memberof CourseUserDto */ 'id': number; /** * * @type {number} * @memberof CourseUserDto */ 'courseId': number; /** * * @type {string} * @memberof CourseUserDto */ 'name': string; /** * * @type {string} * @memberof CourseUserDto */ 'githubId': string; /** * * @type {boolean} * @memberof CourseUserDto */ 'isManager': boolean; /** * * @type {boolean} * @memberof CourseUserDto */ 'isSupervisor': boolean; /** * * @type {boolean} * @memberof CourseUserDto */ 'isJuryActivist': boolean; /** * * @type {boolean} * @memberof CourseUserDto */ 'isDementor': boolean; /** * * @type {boolean} * @memberof CourseUserDto */ 'isActivist': boolean; } /** * * @export * @interface CreateActivityDto */ export interface CreateActivityDto { /** * * @type {boolean} * @memberof CreateActivityDto */ 'isActive': boolean; } /** * * @export * @interface CreateActivityWebhookDto */ export interface CreateActivityWebhookDto { /** * * @type {SenderDto} * @memberof CreateActivityWebhookDto */ 'sender': SenderDto; } /** * * @export * @interface CreateAlertDto */ export interface CreateAlertDto { /** * * @type {string} * @memberof CreateAlertDto */ 'type': string; /** * * @type {string} * @memberof CreateAlertDto */ 'text': string; /** * * @type {boolean} * @memberof CreateAlertDto */ 'enabled'?: boolean; /** * * @type {number} * @memberof CreateAlertDto */ 'courseId'?: number; } /** * * @export * @interface CreateContributorDto */ export interface CreateContributorDto { /** * * @type {string} * @memberof CreateContributorDto */ 'description': string; /** * * @type {number} * @memberof CreateContributorDto */ 'userId': number; } /** * * @export * @interface CreateCourseDto */ export interface CreateCourseDto { /** * * @type {string} * @memberof CreateCourseDto */ 'name': string; /** * * @type {string} * @memberof CreateCourseDto */ 'startDate': string; /** * * @type {string} * @memberof CreateCourseDto */ 'endDate': string; /** * * @type {string} * @memberof CreateCourseDto */ 'fullName': string; /** * * @type {string} * @memberof CreateCourseDto */ 'alias': string; /** * * @type {string} * @memberof CreateCourseDto */ 'registrationEndDate'?: string; /** * * @type {boolean} * @memberof CreateCourseDto */ 'completed'?: boolean; /** * * @type {boolean} * @memberof CreateCourseDto */ 'planned'?: boolean; /** * * @type {boolean} * @memberof CreateCourseDto */ 'inviteOnly'?: boolean; /** * * @type {string} * @memberof CreateCourseDto */ 'description'?: string; /** * * @type {string} * @memberof CreateCourseDto */ 'descriptionUrl'?: string; /** * * @type {number} * @memberof CreateCourseDto */ 'disciplineId'?: number; /** * * @type {number} * @memberof CreateCourseDto */ 'discordServerId'?: number; /** * * @type {boolean} * @memberof CreateCourseDto */ 'usePrivateRepositories'?: boolean; /** * * @type {string} * @memberof CreateCourseDto */ 'certificateIssuer'?: string; /** * * @type {boolean} * @memberof CreateCourseDto */ 'personalMentoring'?: boolean; /** * * @type {string} * @memberof CreateCourseDto */ 'personalMentoringStartDate'?: string; /** * * @type {string} * @memberof CreateCourseDto */ 'personalMentoringEndDate'?: string; /** * * @type {string} * @memberof CreateCourseDto */ 'logo'?: string; /** * * @type {number} * @memberof CreateCourseDto */ 'minStudentsPerMentor'?: number; /** * * @type {number} * @memberof CreateCourseDto */ 'certificateThreshold': number; /** * * @type {string} * @memberof CreateCourseDto */ 'wearecommunityUrl': string; /** * * @type {Array} * @memberof CreateCourseDto */ 'certificateDisciplines': Array | null; } /** * * @export * @interface CreateCourseEventDto */ export interface CreateCourseEventDto { /** * * @type {number} * @memberof CreateCourseEventDto */ 'eventId': number; /** * * @type {string} * @memberof CreateCourseEventDto */ 'special'?: string; /** * * @type {string} * @memberof CreateCourseEventDto */ 'dateTime'?: string; /** * * @type {string} * @memberof CreateCourseEventDto */ 'endTime'?: string; /** * * @type {number} * @memberof CreateCourseEventDto */ 'duration'?: number; /** * * @type {string} * @memberof CreateCourseEventDto */ 'place'?: string; /** * * @type {Organizer} * @memberof CreateCourseEventDto */ 'organizer'?: Organizer; /** * * @type {number} * @memberof CreateCourseEventDto */ 'organizerId'?: number; /** * * @type {string} * @memberof CreateCourseEventDto */ 'broadcastUrl'?: string; /** * * @type {string} * @memberof CreateCourseEventDto */ 'coordinator'?: string; /** * * @type {string} * @memberof CreateCourseEventDto */ 'comment'?: string; } /** * * @export * @interface CreateCourseTaskDto */ export interface CreateCourseTaskDto { /** * * @type {number} * @memberof CreateCourseTaskDto */ 'taskId': number; /** * * @type {number} * @memberof CreateCourseTaskDto */ 'maxScore'?: number; /** * * @type {number} * @memberof CreateCourseTaskDto */ 'scoreWeight'?: number; /** * * @type {CheckerEnum} * @memberof CreateCourseTaskDto */ 'checker': CheckerEnum; /** * * @type {string} * @memberof CreateCourseTaskDto */ 'studentStartDate': string; /** * * @type {string} * @memberof CreateCourseTaskDto */ 'studentEndDate': string; /** * * @type {string} * @memberof CreateCourseTaskDto */ 'studentRegistrationStartDate'?: string; /** * * @type {string} * @memberof CreateCourseTaskDto */ 'crossCheckEndDate'?: string; /** * * @type {number} * @memberof CreateCourseTaskDto */ 'taskOwnerId'?: number; /** * * @type {number} * @memberof CreateCourseTaskDto */ 'pairsCount'?: number; /** * * @type {string} * @memberof CreateCourseTaskDto */ 'type': CreateCourseTaskDtoTypeEnum; /** * * @type {string} * @memberof CreateCourseTaskDto */ 'submitText': string; /** * * @type {object} * @memberof CreateCourseTaskDto */ 'validations': object; } export const CreateCourseTaskDtoTypeEnum = { Jstask: 'jstask', Kotlintask: 'kotlintask', Objctask: 'objctask', Htmltask: 'htmltask', Ipynb: 'ipynb', Selfeducation: 'selfeducation', Codewars: 'codewars', Test: 'test', Codejam: 'codejam', Interview: 'interview', StageInterview: 'stage-interview', Cvhtml: 'cv:html', Cvmarkdown: 'cv:markdown' } as const; export type CreateCourseTaskDtoTypeEnum = typeof CreateCourseTaskDtoTypeEnum[keyof typeof CreateCourseTaskDtoTypeEnum]; /** * * @export * @interface CreateDisciplineDto */ export interface CreateDisciplineDto { /** * * @type {string} * @memberof CreateDisciplineDto */ 'name': string; } /** * * @export * @interface CreateDiscordServerDto */ export interface CreateDiscordServerDto { /** * * @type {string} * @memberof CreateDiscordServerDto */ 'name': string; /** * * @type {string} * @memberof CreateDiscordServerDto */ 'gratitudeUrl': string; /** * * @type {string} * @memberof CreateDiscordServerDto */ 'mentorsChatUrl': string; } /** * * @export * @interface CreateEventDto */ export interface CreateEventDto { /** * * @type {string} * @memberof CreateEventDto */ 'name': string; /** * * @type {string} * @memberof CreateEventDto */ 'type': string; /** * * @type {number} * @memberof CreateEventDto */ 'disciplineId': number; /** * * @type {string} * @memberof CreateEventDto */ 'descriptionUrl': string; /** * * @type {string} * @memberof CreateEventDto */ 'description': string; } /** * * @export * @interface CreateGratitudeDto */ export interface CreateGratitudeDto { /** * * @type {Array} * @memberof CreateGratitudeDto */ 'userIds': Array; /** * * @type {number} * @memberof CreateGratitudeDto */ 'courseId': number; /** * * @type {string} * @memberof CreateGratitudeDto */ 'comment': string; /** * * @type {string} * @memberof CreateGratitudeDto */ 'badgeId': string; } /** * * @export * @interface CreatePromptDto */ export interface CreatePromptDto { /** * * @type {string} * @memberof CreatePromptDto */ 'type': string; /** * * @type {string} * @memberof CreatePromptDto */ 'text': string; /** * * @type {number} * @memberof CreatePromptDto */ 'temperature': number; } /** * * @export * @interface CreateStudentFeedbackDto */ export interface CreateStudentFeedbackDto { /** * * @type {StudentFeedbackContentDto} * @memberof CreateStudentFeedbackDto */ 'content': StudentFeedbackContentDto; /** * * @type {string} * @memberof CreateStudentFeedbackDto */ 'recommendation': CreateStudentFeedbackDtoRecommendationEnum; /** * * @type {string} * @memberof CreateStudentFeedbackDto */ 'englishLevel': CreateStudentFeedbackDtoEnglishLevelEnum; } export const CreateStudentFeedbackDtoRecommendationEnum = { Hire: 'hire', NotHire: 'not-hire' } as const; export type CreateStudentFeedbackDtoRecommendationEnum = typeof CreateStudentFeedbackDtoRecommendationEnum[keyof typeof CreateStudentFeedbackDtoRecommendationEnum]; export const CreateStudentFeedbackDtoEnglishLevelEnum = { Unknown: 'unknown', A0: 'a0', A1: 'a1', A2: 'a2', B1: 'b1', B2: 'b2', C1: 'c1', C2: 'c2' } as const; export type CreateStudentFeedbackDtoEnglishLevelEnum = typeof CreateStudentFeedbackDtoEnglishLevelEnum[keyof typeof CreateStudentFeedbackDtoEnglishLevelEnum]; /** * * @export * @interface CreateTaskDto */ export interface CreateTaskDto { /** * * @type {string} * @memberof CreateTaskDto */ 'name': string; /** * * @type {object} * @memberof CreateTaskDto */ 'attributes': object; /** * * @type {string} * @memberof CreateTaskDto */ 'descriptionUrl': string; /** * * @type {string} * @memberof CreateTaskDto */ 'description': string; /** * * @type {string} * @memberof CreateTaskDto */ 'githubRepoName': string; /** * * @type {string} * @memberof CreateTaskDto */ 'sourceGithubRepoUrl': string; /** * * @type {number} * @memberof CreateTaskDto */ 'disciplineId': number; /** * * @type {boolean} * @memberof CreateTaskDto */ 'githubPrRequired': boolean; /** * * @type {string} * @memberof CreateTaskDto */ 'type': string; /** * * @type {Array} * @memberof CreateTaskDto */ 'skills': Array; /** * * @type {Array} * @memberof CreateTaskDto */ 'tags': Array; } /** * * @export * @interface CreateTaskVerificationDto */ export interface CreateTaskVerificationDto { /** * * @type {number} * @memberof CreateTaskVerificationDto */ 'id'?: number; } /** * * @export * @interface CreateTeamDistributionDto */ export interface CreateTeamDistributionDto { /** * * @type {string} * @memberof CreateTeamDistributionDto */ 'name': string; /** * * @type {string} * @memberof CreateTeamDistributionDto */ 'startDate': string; /** * * @type {string} * @memberof CreateTeamDistributionDto */ 'endDate': string; /** * * @type {string} * @memberof CreateTeamDistributionDto */ 'description': string; /** * * @type {string} * @memberof CreateTeamDistributionDto */ 'descriptionUrl': string; /** * * @type {number} * @memberof CreateTeamDistributionDto */ 'minTeamSize': number; /** * * @type {number} * @memberof CreateTeamDistributionDto */ 'maxTeamSize': number; /** * * @type {number} * @memberof CreateTeamDistributionDto */ 'strictTeamSize': number; /** * * @type {boolean} * @memberof CreateTeamDistributionDto */ 'strictTeamSizeMode': boolean; /** * * @type {number} * @memberof CreateTeamDistributionDto */ 'minTotalScore': number; } /** * * @export * @interface CreateTeamDto */ export interface CreateTeamDto { /** * * @type {string} * @memberof CreateTeamDto */ 'name': string; /** * * @type {string} * @memberof CreateTeamDto */ 'description': string; /** * * @type {string} * @memberof CreateTeamDto */ 'chatLink': string; /** * * @type {Array} * @memberof CreateTeamDto */ 'studentIds'?: Array; } /** * * @export * @interface CreateUserGroupDto */ export interface CreateUserGroupDto { /** * * @type {string} * @memberof CreateUserGroupDto */ 'name': string; /** * * @type {Array} * @memberof CreateUserGroupDto */ 'users': Array; /** * * @type {Array} * @memberof CreateUserGroupDto */ 'roles': Array; } export const CreateUserGroupDtoRolesEnum = { TaskOwner: 'taskOwner', Manager: 'manager', Supervisor: 'supervisor', Student: 'student', Mentor: 'mentor', Dementor: 'dementor', Activist: 'activist' } as const; export type CreateUserGroupDtoRolesEnum = typeof CreateUserGroupDtoRolesEnum[keyof typeof CreateUserGroupDtoRolesEnum]; /** * * @export * @interface CriteriaDto */ export interface CriteriaDto { /** * * @type {number} * @memberof CriteriaDto */ 'max'?: number; /** * * @type {string} * @memberof CriteriaDto */ 'type': CriteriaDtoTypeEnum; /** * * @type {string} * @memberof CriteriaDto */ 'text': string; /** * * @type {string} * @memberof CriteriaDto */ 'key': string; /** * * @type {number} * @memberof CriteriaDto */ 'index': number; } export const CriteriaDtoTypeEnum = { Title: 'title', Subtask: 'subtask', Penalty: 'penalty' } as const; export type CriteriaDtoTypeEnum = typeof CriteriaDtoTypeEnum[keyof typeof CriteriaDtoTypeEnum]; /** * * @export * @interface CrossCheckAuthorDto */ export interface CrossCheckAuthorDto { /** * * @type {number} * @memberof CrossCheckAuthorDto */ 'id': number; /** * * @type {string} * @memberof CrossCheckAuthorDto */ 'name': string; /** * * @type {string} * @memberof CrossCheckAuthorDto */ 'githubId': string; /** * * @type {Discord} * @memberof CrossCheckAuthorDto */ 'discord': Discord | null; } /** * * @export * @interface CrossCheckCriteriaDataDto */ export interface CrossCheckCriteriaDataDto { /** * * @type {string} * @memberof CrossCheckCriteriaDataDto */ 'key': string; /** * * @type {number} * @memberof CrossCheckCriteriaDataDto */ 'max'?: number; /** * * @type {string} * @memberof CrossCheckCriteriaDataDto */ 'text': string; /** * * @type {string} * @memberof CrossCheckCriteriaDataDto */ 'type': CrossCheckCriteriaDataDtoTypeEnum; /** * * @type {number} * @memberof CrossCheckCriteriaDataDto */ 'point'?: number; /** * * @type {string} * @memberof CrossCheckCriteriaDataDto */ 'textComment'?: string; } export const CrossCheckCriteriaDataDtoTypeEnum = { Title: 'title', Subtask: 'subtask', Penalty: 'penalty' } as const; export type CrossCheckCriteriaDataDtoTypeEnum = typeof CrossCheckCriteriaDataDtoTypeEnum[keyof typeof CrossCheckCriteriaDataDtoTypeEnum]; /** * * @export * @interface CrossCheckFeedbackDto */ export interface CrossCheckFeedbackDto { /** * * @type {string} * @memberof CrossCheckFeedbackDto */ 'url'?: string; /** * * @type {Array} * @memberof CrossCheckFeedbackDto */ 'reviews'?: Array; } /** * * @export * @interface CrossCheckMessageAuthorDto */ export interface CrossCheckMessageAuthorDto { /** * * @type {string} * @memberof CrossCheckMessageAuthorDto */ 'githubId': string; /** * * @type {number} * @memberof CrossCheckMessageAuthorDto */ 'id': number; } /** * * @export * @interface CrossCheckMessageDto */ export interface CrossCheckMessageDto { /** * * @type {CrossCheckMessageAuthorDto} * @memberof CrossCheckMessageDto */ 'author': CrossCheckMessageAuthorDto | null; /** * * @type {string} * @memberof CrossCheckMessageDto */ 'content': string; /** * * @type {string} * @memberof CrossCheckMessageDto */ 'timestamp': string; /** * * @type {boolean} * @memberof CrossCheckMessageDto */ 'isReviewerRead': boolean; /** * * @type {boolean} * @memberof CrossCheckMessageDto */ 'isStudentRead': boolean; /** * * @type {string} * @memberof CrossCheckMessageDto */ 'role': CrossCheckMessageDtoRoleEnum; } export const CrossCheckMessageDtoRoleEnum = { Reviewer: 'reviewer', Student: 'student' } as const; export type CrossCheckMessageDtoRoleEnum = typeof CrossCheckMessageDtoRoleEnum[keyof typeof CrossCheckMessageDtoRoleEnum]; /** * * @export * @interface CrossCheckPairDto */ export interface CrossCheckPairDto { /** * * @type {PersonDto} * @memberof CrossCheckPairDto */ 'student': PersonDto; /** * * @type {PersonDto} * @memberof CrossCheckPairDto */ 'checker': PersonDto; /** * * @type {IdNameDto} * @memberof CrossCheckPairDto */ 'task': IdNameDto; /** * * @type {number} * @memberof CrossCheckPairDto */ 'score': number; /** * * @type {number} * @memberof CrossCheckPairDto */ 'id': number; /** * * @type {string} * @memberof CrossCheckPairDto */ 'comment': string; /** * * @type {string} * @memberof CrossCheckPairDto */ 'url': string; /** * * @type {string} * @memberof CrossCheckPairDto */ 'reviewedDate': string; /** * * @type {string} * @memberof CrossCheckPairDto */ 'privateRepository': string; /** * * @type {string} * @memberof CrossCheckPairDto */ 'submittedDate': string; /** * * @type {Array} * @memberof CrossCheckPairDto */ 'historicalScores': Array; /** * * @type {Array} * @memberof CrossCheckPairDto */ 'messages': Array; } /** * * @export * @interface CrossCheckPairResponseDto */ export interface CrossCheckPairResponseDto { /** * * @type {Array} * @memberof CrossCheckPairResponseDto */ 'items': Array; /** * * @type {PaginationDto} * @memberof CrossCheckPairResponseDto */ 'pagination': PaginationDto; } /** * * @export * @interface CrossCheckSolutionReviewDto */ export interface CrossCheckSolutionReviewDto { /** * * @type {number} * @memberof CrossCheckSolutionReviewDto */ 'id': number; /** * * @type {number} * @memberof CrossCheckSolutionReviewDto */ 'dateTime': number; /** * * @type {string} * @memberof CrossCheckSolutionReviewDto */ 'comment': string; /** * * @type {Array} * @memberof CrossCheckSolutionReviewDto */ 'criteria'?: Array; /** * * @type {CrossCheckAuthorDto} * @memberof CrossCheckSolutionReviewDto */ 'author': CrossCheckAuthorDto | null; /** * * @type {number} * @memberof CrossCheckSolutionReviewDto */ 'score': number; /** * * @type {Array} * @memberof CrossCheckSolutionReviewDto */ 'messages': Array; } /** * * @export * @enum {string} */ export const CrossCheckStatusEnum = { Initial: 'initial', Distributed: 'distributed', Completed: 'completed' } as const; export type CrossCheckStatusEnum = typeof CrossCheckStatusEnum[keyof typeof CrossCheckStatusEnum]; /** * * @export * @interface DevtoolsUserDto */ export interface DevtoolsUserDto { /** * * @type {number} * @memberof DevtoolsUserDto */ 'id': number; /** * * @type {string} * @memberof DevtoolsUserDto */ 'githubId': string; /** * * @type {Array} * @memberof DevtoolsUserDto */ 'mentor': Array; /** * * @type {Array} * @memberof DevtoolsUserDto */ 'student': Array; } /** * * @export * @interface DisciplineDto */ export interface DisciplineDto { /** * * @type {string} * @memberof DisciplineDto */ 'name': string; /** * * @type {number} * @memberof DisciplineDto */ 'id': number; /** * * @type {string} * @memberof DisciplineDto */ 'createdDate': string; /** * * @type {string} * @memberof DisciplineDto */ 'updatedDate': string; } /** * * @export * @interface DisciplineIdsDto */ export interface DisciplineIdsDto { /** * * @type {Array} * @memberof DisciplineIdsDto */ 'ids': Array; } /** * * @export * @interface Discord */ export interface Discord { /** * * @type {string} * @memberof Discord */ 'id': string; /** * * @type {string} * @memberof Discord */ 'username': string; /** * * @type {string} * @memberof Discord */ 'discriminator': string; } /** * * @export * @interface DiscordServerDto */ export interface DiscordServerDto { /** * * @type {number} * @memberof DiscordServerDto */ 'id': number; /** * * @type {number} * @memberof DiscordServerDto */ 'createdDate': number; /** * * @type {number} * @memberof DiscordServerDto */ 'updatedDate': number; /** * * @type {string} * @memberof DiscordServerDto */ 'name': string; /** * * @type {string} * @memberof DiscordServerDto */ 'gratitudeUrl': string; /** * * @type {string} * @memberof DiscordServerDto */ 'mentorsChatUrl': string | null; } /** * * @export * @interface Education */ export interface Education { /** * * @type {string} * @memberof Education */ 'university': string; /** * * @type {string} * @memberof Education */ 'faculty': string; /** * * @type {number} * @memberof Education */ 'graduationYear': number; } /** * * @export * @interface EndorsementDataDto */ export interface EndorsementDataDto { /** * * @type {EndorsementUserDto} * @memberof EndorsementDataDto */ 'user': EndorsementUserDto; /** * User\'s courses * @type {Array} * @memberof EndorsementDataDto */ 'courses': Array; /** * Number of students * @type {number} * @memberof EndorsementDataDto */ 'studentsCount': number; /** * Number of interviews * @type {number} * @memberof EndorsementDataDto */ 'interviewsCount': number; } /** * * @export * @interface EndorsementDto */ export interface EndorsementDto { /** * * @type {string} * @memberof EndorsementDto */ 'summary': string; /** * * @type {object} * @memberof EndorsementDto */ 'data': object | null; } /** * * @export * @interface EndorsementUserDto */ export interface EndorsementUserDto { /** * * @type {number} * @memberof EndorsementUserDto */ 'id': number; /** * * @type {string} * @memberof EndorsementUserDto */ 'githubId': string; /** * * @type {string} * @memberof EndorsementUserDto */ 'firstName': string; /** * * @type {string} * @memberof EndorsementUserDto */ 'lastName': string; } /** * * @export * @interface EventDto */ export interface EventDto { /** * * @type {number} * @memberof EventDto */ 'id': number; /** * * @type {string} * @memberof EventDto */ 'name': string; /** * * @type {string} * @memberof EventDto */ 'descriptionUrl': string | null; /** * * @type {string} * @memberof EventDto */ 'description': string | null; /** * * @type {string} * @memberof EventDto */ 'type': EventDtoTypeEnum; /** * * @type {IdNameDto} * @memberof EventDto */ 'discipline': IdNameDto | null; } export const EventDtoTypeEnum = { LectureOnline: 'lecture_online', LectureOffline: 'lecture_offline', LectureMixed: 'lecture_mixed', LectureSelfStudy: 'lecture_self_study', Warmup: 'warmup', Info: 'info', Workshop: 'workshop', Meetup: 'meetup', CrossCheckDeadline: 'cross_check_deadline', Webinar: 'webinar', Special: 'special' } as const; export type EventDtoTypeEnum = typeof EventDtoTypeEnum[keyof typeof EventDtoTypeEnum]; /** * * @export * @interface ExpelCriteriaDto */ export interface ExpelCriteriaDto { /** * Array of course task IDs * @type {Array} * @memberof ExpelCriteriaDto */ 'courseTaskIds'?: Array; /** * Minimum score threshold * @type {number} * @memberof ExpelCriteriaDto */ 'minScore'?: number; } /** * * @export * @interface ExpelOptionsDto */ export interface ExpelOptionsDto { /** * Whether to keep the student with their mentor * @type {boolean} * @memberof ExpelOptionsDto */ 'keepWithMentor'?: boolean; /** * Save assigning to the mentor (default: false) * @type {boolean} * @memberof ExpelOptionsDto */ 'saveAssigningToMentor'?: boolean; } /** * * @export * @interface ExpelStatusDto */ export interface ExpelStatusDto { /** * Criteria for expelling students * @type {ExpelCriteriaDto} * @memberof ExpelStatusDto */ 'criteria': ExpelCriteriaDto; /** * Additional options for expelling * @type {ExpelOptionsDto} * @memberof ExpelStatusDto */ 'options': ExpelOptionsDto; /** * Reason for expelling the student * @type {string} * @memberof ExpelStatusDto */ 'expellingReason': string; } /** * * @export * @interface ExpelledStatsDto */ export interface ExpelledStatsDto { /** * * @type {string} * @memberof ExpelledStatsDto */ 'id': string; /** * * @type {CourseDto} * @memberof ExpelledStatsDto */ 'course': CourseDto; /** * * @type {UserDto} * @memberof ExpelledStatsDto */ 'user': UserDto; /** * * @type {Array} * @memberof ExpelledStatsDto */ 'reasonForLeaving'?: Array; /** * * @type {string} * @memberof ExpelledStatsDto */ 'otherComment': string; /** * * @type {string} * @memberof ExpelledStatsDto */ 'submittedAt': string; } /** * * @export * @interface FeedbackCourseDto */ export interface FeedbackCourseDto { /** * * @type {string} * @memberof FeedbackCourseDto */ 'name': string; /** * * @type {number} * @memberof FeedbackCourseDto */ 'id': number; } /** * * @export * @interface FeedbackDto */ export interface FeedbackDto { /** * * @type {string} * @memberof FeedbackDto */ 'date': string; /** * * @type {string} * @memberof FeedbackDto */ 'recommendation': string; /** * * @type {string} * @memberof FeedbackDto */ 'englishLevel': string; /** * * @type {string} * @memberof FeedbackDto */ 'recommendationComment': string; /** * * @type {string} * @memberof FeedbackDto */ 'suggestions': string; /** * * @type {Array} * @memberof FeedbackDto */ 'softSkills': Array; /** * * @type {ResumeCourseMentor} * @memberof FeedbackDto */ 'mentor': ResumeCourseMentor; /** * * @type {FeedbackCourseDto} * @memberof FeedbackDto */ 'course': FeedbackCourseDto; } /** * * @export * @interface FeedbackSoftSkill */ export interface FeedbackSoftSkill { /** * * @type {string} * @memberof FeedbackSoftSkill */ 'value': FeedbackSoftSkillValueEnum; /** * * @type {string} * @memberof FeedbackSoftSkill */ 'id': FeedbackSoftSkillIdEnum; } export const FeedbackSoftSkillValueEnum = { None: 'None', Poor: 'Poor', Fair: 'Fair', Good: 'Good', Great: 'Great', Excellent: 'Excellent' } as const; export type FeedbackSoftSkillValueEnum = typeof FeedbackSoftSkillValueEnum[keyof typeof FeedbackSoftSkillValueEnum]; export const FeedbackSoftSkillIdEnum = { Responsible: 'skill.soft.responsible', TeamPlayer: 'skill.soft.team-player', Communicable: 'skill.soft.communicable' } as const; export type FeedbackSoftSkillIdEnum = typeof FeedbackSoftSkillIdEnum[keyof typeof FeedbackSoftSkillIdEnum]; /** * * @export * @interface FilterMentorRegistryResponse */ export interface FilterMentorRegistryResponse { /** * * @type {Array} * @memberof FilterMentorRegistryResponse */ 'mentors': Array; /** * * @type {number} * @memberof FilterMentorRegistryResponse */ 'total': number; } /** * * @export * @interface FormDataDto */ export interface FormDataDto { /** * * @type {string} * @memberof FormDataDto */ 'avatarLink': string | null; /** * * @type {Array} * @memberof FormDataDto */ 'visibleCourses': Array; /** * * @type {string} * @memberof FormDataDto */ 'desiredPosition': string | null; /** * * @type {string} * @memberof FormDataDto */ 'email': string | null; /** * * @type {string} * @memberof FormDataDto */ 'englishLevel': FormDataDtoEnglishLevelEnum; /** * * @type {boolean} * @memberof FormDataDto */ 'fullTime': boolean; /** * * @type {string} * @memberof FormDataDto */ 'githubUsername': string | null; /** * * @type {string} * @memberof FormDataDto */ 'linkedin': string | null; /** * * @type {string} * @memberof FormDataDto */ 'locations': string | null; /** * * @type {string} * @memberof FormDataDto */ 'militaryService': FormDataDtoMilitaryServiceEnum; /** * * @type {string} * @memberof FormDataDto */ 'name': string | null; /** * * @type {string} * @memberof FormDataDto */ 'notes': string | null; /** * * @type {string} * @memberof FormDataDto */ 'phone': string | null; /** * * @type {string} * @memberof FormDataDto */ 'selfIntroLink': string | null; /** * * @type {string} * @memberof FormDataDto */ 'skype': string | null; /** * * @type {string} * @memberof FormDataDto */ 'startFrom': string | null; /** * * @type {string} * @memberof FormDataDto */ 'telegram': string | null; /** * * @type {string} * @memberof FormDataDto */ 'website': string | null; } export const FormDataDtoEnglishLevelEnum = { Unknown: 'unknown', A0: 'a0', A1: 'a1', A2: 'a2', B1: 'b1', B2: 'b2', C1: 'c1', C2: 'c2' } as const; export type FormDataDtoEnglishLevelEnum = typeof FormDataDtoEnglishLevelEnum[keyof typeof FormDataDtoEnglishLevelEnum]; export const FormDataDtoMilitaryServiceEnum = { Served: 'served', Liable: 'liable', NotLiable: 'notLiable' } as const; export type FormDataDtoMilitaryServiceEnum = typeof FormDataDtoMilitaryServiceEnum[keyof typeof FormDataDtoMilitaryServiceEnum]; /** * * @export * @interface GiveConsentDto */ export interface GiveConsentDto { /** * * @type {boolean} * @memberof GiveConsentDto */ 'consent': boolean; /** * * @type {number} * @memberof GiveConsentDto */ 'expires': number; } /** * * @export * @interface GratitudeDto */ export interface GratitudeDto { /** * * @type {PersonDto} * @memberof GratitudeDto */ 'user': PersonDto; /** * * @type {number} * @memberof GratitudeDto */ 'id': number; /** * * @type {BadgeEnum} * @memberof GratitudeDto */ 'badgeId': BadgeEnum; /** * * @type {string} * @memberof GratitudeDto */ 'comment': string; /** * * @type {number} * @memberof GratitudeDto */ 'courseId': number; /** * * @type {string} * @memberof GratitudeDto */ 'date': string; } /** * * @export * @interface HeroRadarDto */ export interface HeroRadarDto { /** * * @type {string} * @memberof HeroRadarDto */ 'githubId': string; /** * * @type {string} * @memberof HeroRadarDto */ 'name': string; /** * * @type {number} * @memberof HeroRadarDto */ 'rank': number; /** * * @type {number} * @memberof HeroRadarDto */ 'total': number; /** * * @type {Array} * @memberof HeroRadarDto */ 'badges': Array; } /** * * @export * @interface HeroesRadarBadgeDto */ export interface HeroesRadarBadgeDto { /** * * @type {string} * @memberof HeroesRadarBadgeDto */ 'id': string; /** * * @type {string} * @memberof HeroesRadarBadgeDto */ 'badgeId': string; /** * * @type {string} * @memberof HeroesRadarBadgeDto */ 'comment': string; /** * * @type {string} * @memberof HeroesRadarBadgeDto */ 'date': string; } /** * * @export * @interface HeroesRadarDto */ export interface HeroesRadarDto { /** * * @type {Array} * @memberof HeroesRadarDto */ 'content': Array; /** * * @type {PaginationMetaDto} * @memberof HeroesRadarDto */ 'pagination': PaginationMetaDto; } /** * * @export * @interface HistoricalScoreDto */ export interface HistoricalScoreDto { /** * * @type {string} * @memberof HistoricalScoreDto */ 'comment': string; /** * * @type {string} * @memberof HistoricalScoreDto */ 'dateTime': string; /** * * @type {Array} * @memberof HistoricalScoreDto */ 'criteria'?: Array; } /** * * @export * @interface IdNameDto */ export interface IdNameDto { /** * * @type {string} * @memberof IdNameDto */ 'name': string; /** * * @type {number} * @memberof IdNameDto */ 'id': number; } /** * * @export * @interface InterviewCommentDto */ export interface InterviewCommentDto { /** * * @type {number} * @memberof InterviewCommentDto */ 'id': number; /** * * @type {string} * @memberof InterviewCommentDto */ 'commentToStudent': string | null; } /** * * @export * @interface InterviewDistributeDto */ export interface InterviewDistributeDto { /** * * @type {boolean} * @memberof InterviewDistributeDto */ 'clean': boolean; /** * * @type {boolean} * @memberof InterviewDistributeDto */ 'registrationEnabled': boolean; } /** * * @export * @interface InterviewDistributeResponseDto */ export interface InterviewDistributeResponseDto { /** * * @type {number} * @memberof InterviewDistributeResponseDto */ 'id': number; /** * * @type {number} * @memberof InterviewDistributeResponseDto */ 'courseTaskId': number; /** * * @type {number} * @memberof InterviewDistributeResponseDto */ 'mentorId': number; /** * * @type {number} * @memberof InterviewDistributeResponseDto */ 'studentId': number; /** * * @type {string} * @memberof InterviewDistributeResponseDto */ 'createdDate': string; /** * * @type {string} * @memberof InterviewDistributeResponseDto */ 'updatedDate': string; } /** * * @export * @interface InterviewDto */ export interface InterviewDto { /** * * @type {number} * @memberof InterviewDto */ 'id': number; /** * * @type {string} * @memberof InterviewDto */ 'type': string; /** * * @type {string} * @memberof InterviewDto */ 'name': string; /** * * @type {string} * @memberof InterviewDto */ 'startDate': string; /** * * @type {string} * @memberof InterviewDto */ 'endDate': string; /** * * @type {string} * @memberof InterviewDto */ 'description': string | null; /** * * @type {string} * @memberof InterviewDto */ 'descriptionUrl': string; /** * * @type {Attributes} * @memberof InterviewDto */ 'attributes': Attributes; /** * Date when student can register for the interview * @type {string} * @memberof InterviewDto */ 'studentRegistrationStartDate': string; } /** * * @export * @interface InterviewFeedbackDto */ export interface InterviewFeedbackDto { /** * * @type {number} * @memberof InterviewFeedbackDto */ 'version'?: number; /** * * @type {object} * @memberof InterviewFeedbackDto */ 'json'?: object; /** * * @type {boolean} * @memberof InterviewFeedbackDto */ 'isCompleted': boolean; /** * * @type {number} * @memberof InterviewFeedbackDto */ 'maxScore': number; } /** * * @export * @interface InterviewPairDto */ export interface InterviewPairDto { /** * * @type {number} * @memberof InterviewPairDto */ 'id': number; /** * * @type {number} * @memberof InterviewPairDto */ 'result': number | null; /** * * @type {InterviewStatus} * @memberof InterviewPairDto */ 'status': InterviewStatus; /** * * @type {PersonDto} * @memberof InterviewPairDto */ 'interviewer': PersonDto; /** * * @type {PersonDto} * @memberof InterviewPairDto */ 'student': PersonDto; } /** * * @export * @enum {string} */ export const InterviewStatus = { NUMBER_1: 1, NUMBER_0: 0 } as const; export type InterviewStatus = typeof InterviewStatus[keyof typeof InterviewStatus]; /** * * @export * @interface InviteMentorsDto */ export interface InviteMentorsDto { /** * * @type {Array} * @memberof InviteMentorsDto */ 'disciplines': Array; /** * * @type {boolean} * @memberof InviteMentorsDto */ 'isMentor': boolean; /** * * @type {string} * @memberof InviteMentorsDto */ 'text': string; } /** * * @export * @interface JoinTeamDto */ export interface JoinTeamDto { /** * * @type {string} * @memberof JoinTeamDto */ 'password': string; } /** * * @export * @interface LeaveCourseRequestDto */ export interface LeaveCourseRequestDto { /** * * @type {Array} * @memberof LeaveCourseRequestDto */ 'reasonForLeaving'?: Array; /** * * @type {string} * @memberof LeaveCourseRequestDto */ 'otherComment'?: string; } /** * * @export * @interface MentorCourseStatsDto */ export interface MentorCourseStatsDto { /** * Name of the course * @type {string} * @memberof MentorCourseStatsDto */ 'courseName': string; /** * Number of certified students mentored in this course * @type {number} * @memberof MentorCourseStatsDto */ 'studentsCount': number; } /** * * @export * @interface MentorDashboardDto */ export interface MentorDashboardDto { /** * * @type {string} * @memberof MentorDashboardDto */ 'studentGithubId': string; /** * * @type {string} * @memberof MentorDashboardDto */ 'studentName': string; /** * * @type {string} * @memberof MentorDashboardDto */ 'taskName': string; /** * * @type {string} * @memberof MentorDashboardDto */ 'taskDescriptionUrl': string; /** * * @type {number} * @memberof MentorDashboardDto */ 'courseTaskId': number; /** * * @type {number} * @memberof MentorDashboardDto */ 'maxScore': number; /** * * @type {number} * @memberof MentorDashboardDto */ 'resultScore': number | null; /** * * @type {string} * @memberof MentorDashboardDto */ 'solutionUrl': string; /** * * @type {SolutionItemStatusEnum} * @memberof MentorDashboardDto */ 'status': SolutionItemStatusEnum; /** * * @type {string} * @memberof MentorDashboardDto */ 'endDate': string; } /** * * @export * @interface MentorDetailsDto */ export interface MentorDetailsDto { /** * * @type {number} * @memberof MentorDetailsDto */ 'id': number; /** * * @type {string} * @memberof MentorDetailsDto */ 'name': string; /** * * @type {string} * @memberof MentorDetailsDto */ 'githubId': string; /** * * @type {boolean} * @memberof MentorDetailsDto */ 'isActive': boolean; /** * * @type {string} * @memberof MentorDetailsDto */ 'cityName': string; /** * * @type {string} * @memberof MentorDetailsDto */ 'countryName': string; /** * * @type {number} * @memberof MentorDetailsDto */ 'maxStudentsLimit': number; /** * * @type {Array} * @memberof MentorDetailsDto */ 'students': Array; /** * * @type {object} * @memberof MentorDetailsDto */ 'interviews'?: object; /** * * @type {object} * @memberof MentorDetailsDto */ 'screenings'?: object; /** * * @type {object} * @memberof MentorDetailsDto */ 'taskResultsStats'?: object; /** * * @type {string} * @memberof MentorDetailsDto */ 'studentsPreference': MentorDetailsDtoStudentsPreferenceEnum; /** * * @type {number} * @memberof MentorDetailsDto */ 'studentsCount'?: number; /** * * @type {string} * @memberof MentorDetailsDto */ 'contactsEpamEmail': string; } export const MentorDetailsDtoStudentsPreferenceEnum = { Any: 'any', Country: 'country', City: 'city' } as const; export type MentorDetailsDtoStudentsPreferenceEnum = typeof MentorDetailsDtoStudentsPreferenceEnum[keyof typeof MentorDetailsDtoStudentsPreferenceEnum]; /** * * @export * @interface MentorDto */ export interface MentorDto { /** * * @type {number} * @memberof MentorDto */ 'id': number; /** * * @type {string} * @memberof MentorDto */ 'githubId': string; /** * * @type {string} * @memberof MentorDto */ 'name': string; } /** * * @export * @interface MentorOptionsDto */ export interface MentorOptionsDto { /** * * @type {number} * @memberof MentorOptionsDto */ 'maxStudentsLimit': number; /** * * @type {string} * @memberof MentorOptionsDto */ 'preferedStudentsLocation': MentorOptionsDtoPreferedStudentsLocationEnum; /** * * @type {Array} * @memberof MentorOptionsDto */ 'students': Array; } export const MentorOptionsDtoPreferedStudentsLocationEnum = { Any: 'any', Country: 'country', City: 'city' } as const; export type MentorOptionsDtoPreferedStudentsLocationEnum = typeof MentorOptionsDtoPreferedStudentsLocationEnum[keyof typeof MentorOptionsDtoPreferedStudentsLocationEnum]; /** * * @export * @interface MentorRegistryDto */ export interface MentorRegistryDto { /** * * @type {number} * @memberof MentorRegistryDto */ 'id': number; /** * * @type {string} * @memberof MentorRegistryDto */ 'githubId': string; /** * * @type {string} * @memberof MentorRegistryDto */ 'cityName': string | null; /** * * @type {Array} * @memberof MentorRegistryDto */ 'preferedCourses': Array; /** * * @type {Array} * @memberof MentorRegistryDto */ 'preselectedCourses': Array; /** * * @type {number} * @memberof MentorRegistryDto */ 'maxStudentsLimit': number; /** * * @type {string} * @memberof MentorRegistryDto */ 'preferedStudentsLocation': string; /** * * @type {string} * @memberof MentorRegistryDto */ 'name': string; /** * * @type {Array} * @memberof MentorRegistryDto */ 'technicalMentoring': Array; /** * * @type {Array} * @memberof MentorRegistryDto */ 'courses': Array; /** * * @type {string} * @memberof MentorRegistryDto */ 'sendDate': string; /** * * @type {string} * @memberof MentorRegistryDto */ 'receivedDate': string; /** * * @type {boolean} * @memberof MentorRegistryDto */ 'hasCertificate': boolean; /** * * @type {boolean} * @memberof MentorRegistryDto */ 'englishMentoring': boolean; /** * * @type {object} * @memberof MentorRegistryDto */ 'primaryEmail': object; /** * * @type {Array} * @memberof MentorRegistryDto */ 'languagesMentoring': Array; /** * * @type {string} * @memberof MentorRegistryDto */ 'contactsEpamEmail': string | null; /** * * @type {string} * @memberof MentorRegistryDto */ 'comment': string | null; } /** * * @export * @interface MentorReviewAssignDto */ export interface MentorReviewAssignDto { /** * * @type {number} * @memberof MentorReviewAssignDto */ 'courseTaskId': number; /** * * @type {number} * @memberof MentorReviewAssignDto */ 'mentorId'?: number; /** * * @type {number} * @memberof MentorReviewAssignDto */ 'studentId': number; } /** * * @export * @interface MentorReviewDto */ export interface MentorReviewDto { /** * Task solution id * @type {number} * @memberof MentorReviewDto */ 'id': number; /** * Course task name * @type {string} * @memberof MentorReviewDto */ 'taskName': string; /** * Course task id * @type {number} * @memberof MentorReviewDto */ 'taskId': number; /** * Task solution url * @type {string} * @memberof MentorReviewDto */ 'solutionUrl': string; /** * Task solution submission date * @type {string} * @memberof MentorReviewDto */ 'submittedAt': string; /** * Checker github id * @type {string} * @memberof MentorReviewDto */ 'checker': string; /** * Task solution score * @type {number} * @memberof MentorReviewDto */ 'score': number; /** * Task max score * @type {number} * @memberof MentorReviewDto */ 'maxScore': number; /** * Student github id * @type {string} * @memberof MentorReviewDto */ 'student': string; /** * Student id * @type {number} * @memberof MentorReviewDto */ 'studentId': number; /** * Task solution review date * @type {string} * @memberof MentorReviewDto */ 'reviewedAt': string; /** * Task description url * @type {string} * @memberof MentorReviewDto */ 'taskDescriptionUrl': string; } /** * * @export * @interface MentorReviewsDto */ export interface MentorReviewsDto { /** * * @type {Array} * @memberof MentorReviewsDto */ 'content': Array; /** * * @type {PaginationMetaDto} * @memberof MentorReviewsDto */ 'pagination': PaginationMetaDto; } /** * * @export * @interface MentorStudentDto */ export interface MentorStudentDto { /** * * @type {string} * @memberof MentorStudentDto */ 'name': string; /** * * @type {string} * @memberof MentorStudentDto */ 'githubId': string; /** * * @type {number} * @memberof MentorStudentDto */ 'id': number; /** * * @type {boolean} * @memberof MentorStudentDto */ 'active': boolean; /** * * @type {string} * @memberof MentorStudentDto */ 'cityName': string | null; /** * * @type {string} * @memberof MentorStudentDto */ 'countryName': string | null; /** * * @type {number} * @memberof MentorStudentDto */ 'totalScore': number; /** * * @type {number} * @memberof MentorStudentDto */ 'rank': number; /** * * @type {Array} * @memberof MentorStudentDto */ 'feedbacks': Array; /** * * @type {string} * @memberof MentorStudentDto */ 'repoUrl': string | null; } /** * * @export * @interface MentorStudentSummaryDto */ export interface MentorStudentSummaryDto { /** * * @type {number} * @memberof MentorStudentSummaryDto */ 'id': number; /** * * @type {string} * @memberof MentorStudentSummaryDto */ 'githubId': string; /** * * @type {string} * @memberof MentorStudentSummaryDto */ 'name': string; /** * * @type {boolean} * @memberof MentorStudentSummaryDto */ 'isActive': boolean; /** * * @type {string} * @memberof MentorStudentSummaryDto */ 'cityName': string; /** * * @type {string} * @memberof MentorStudentSummaryDto */ 'countryName': string; /** * * @type {Array} * @memberof MentorStudentSummaryDto */ 'students': Array; /** * * @type {string} * @memberof MentorStudentSummaryDto */ 'contactsEmail': string | null; /** * * @type {string} * @memberof MentorStudentSummaryDto */ 'contactsPhone': string | null; /** * * @type {string} * @memberof MentorStudentSummaryDto */ 'contactsSkype': string | null; /** * * @type {string} * @memberof MentorStudentSummaryDto */ 'contactsTelegram': string | null; /** * * @type {string} * @memberof MentorStudentSummaryDto */ 'contactsNotes': string | null; /** * * @type {string} * @memberof MentorStudentSummaryDto */ 'contactsWhatsApp': string | null; } /** * * @export * @interface NotificationConnectionDto */ export interface NotificationConnectionDto { /** * * @type {string} * @memberof NotificationConnectionDto */ 'channelId': string; /** * * @type {string} * @memberof NotificationConnectionDto */ 'externalId': string; /** * * @type {number} * @memberof NotificationConnectionDto */ 'userId': number; /** * * @type {boolean} * @memberof NotificationConnectionDto */ 'enabled': boolean; } /** * * @export * @interface NotificationConnectionExistsDto */ export interface NotificationConnectionExistsDto { /** * * @type {string} * @memberof NotificationConnectionExistsDto */ 'channelId': string; /** * * @type {string} * @memberof NotificationConnectionExistsDto */ 'externalId'?: string; /** * * @type {number} * @memberof NotificationConnectionExistsDto */ 'userId'?: number; } /** * * @export * @interface NotificationDto */ export interface NotificationDto { /** * * @type {string} * @memberof NotificationDto */ 'id': string; /** * * @type {string} * @memberof NotificationDto */ 'name': string; /** * * @type {boolean} * @memberof NotificationDto */ 'enabled': boolean; /** * * @type {NotificationType} * @memberof NotificationDto */ 'type': NotificationType; /** * * @type {Array} * @memberof NotificationDto */ 'channels': Array; /** * * @type {string} * @memberof NotificationDto */ 'parentId': string; } /** * * @export * @enum {string} */ export const NotificationType = { Event: 'event', Message: 'message' } as const; export type NotificationType = typeof NotificationType[keyof typeof NotificationType]; /** * * @export * @interface NotificationUserConnectionsDto */ export interface NotificationUserConnectionsDto { /** * * @type {object} * @memberof NotificationUserConnectionsDto */ 'connections': object; } /** * * @export * @interface NotificationUserSettingsDto */ export interface NotificationUserSettingsDto { /** * * @type {string} * @memberof NotificationUserSettingsDto */ 'id': string; /** * * @type {string} * @memberof NotificationUserSettingsDto */ 'name': string; /** * * @type {boolean} * @memberof NotificationUserSettingsDto */ 'enabled': boolean; /** * * @type {object} * @memberof NotificationUserSettingsDto */ 'settings': object; } /** * * @export * @interface Organizer */ export interface Organizer { /** * * @type {number} * @memberof Organizer */ 'id': number; } /** * * @export * @interface PaginationDto */ export interface PaginationDto { /** * * @type {number} * @memberof PaginationDto */ 'pageSize': number; /** * * @type {number} * @memberof PaginationDto */ 'current': number; /** * * @type {number} * @memberof PaginationDto */ 'total': number; /** * * @type {number} * @memberof PaginationDto */ 'totalPages': number; } /** * * @export * @interface PaginationMetaDto */ export interface PaginationMetaDto { /** * * @type {number} * @memberof PaginationMetaDto */ 'itemCount': number; /** * * @type {number} * @memberof PaginationMetaDto */ 'total': number; /** * * @type {number} * @memberof PaginationMetaDto */ 'current': number; /** * * @type {number} * @memberof PaginationMetaDto */ 'pageSize': number; /** * * @type {number} * @memberof PaginationMetaDto */ 'totalPages': number; } /** * * @export * @interface PersonDto */ export interface PersonDto { /** * * @type {string} * @memberof PersonDto */ 'name': string; /** * * @type {string} * @memberof PersonDto */ 'githubId': string; /** * * @type {number} * @memberof PersonDto */ 'id': number; } /** * * @export * @interface PersonalProfileDto */ export interface PersonalProfileDto { /** * * @type {number} * @memberof PersonalProfileDto */ 'userId': number; /** * * @type {string} * @memberof PersonalProfileDto */ 'githubId': string; /** * * @type {string} * @memberof PersonalProfileDto */ 'primaryEmail': string | null; /** * * @type {boolean} * @memberof PersonalProfileDto */ 'isActiveStudent': boolean; } /** * * @export * @interface ProfileCourseDto */ export interface ProfileCourseDto { /** * * @type {number} * @memberof ProfileCourseDto */ 'id': number; /** * * @type {string} * @memberof ProfileCourseDto */ 'createdDate': string; /** * * @type {string} * @memberof ProfileCourseDto */ 'updatedDate': string; /** * * @type {string} * @memberof ProfileCourseDto */ 'name': string; /** * * @type {string} * @memberof ProfileCourseDto */ 'fullName': string; /** * * @type {string} * @memberof ProfileCourseDto */ 'alias': string; /** * * @type {string} * @memberof ProfileCourseDto */ 'description': string; /** * * @type {string} * @memberof ProfileCourseDto */ 'descriptionUrl': string; /** * * @type {number} * @memberof ProfileCourseDto */ 'year': number; /** * * @type {string} * @memberof ProfileCourseDto */ 'startDate': string; /** * * @type {string} * @memberof ProfileCourseDto */ 'endDate': string; /** * * @type {string} * @memberof ProfileCourseDto */ 'registrationEndDate': string | null; /** * * @type {string} * @memberof ProfileCourseDto */ 'primarySkillId': string; /** * * @type {string} * @memberof ProfileCourseDto */ 'primarySkillName': string; /** * * @type {string} * @memberof ProfileCourseDto */ 'locationName': string; /** * * @type {number} * @memberof ProfileCourseDto */ 'discordServerId': number; /** * * @type {boolean} * @memberof ProfileCourseDto */ 'completed': boolean; /** * * @type {boolean} * @memberof ProfileCourseDto */ 'planned': boolean; /** * * @type {boolean} * @memberof ProfileCourseDto */ 'inviteOnly': boolean; /** * * @type {string} * @memberof ProfileCourseDto */ 'certificateIssuer': string; /** * * @type {boolean} * @memberof ProfileCourseDto */ 'usePrivateRepositories': boolean; /** * * @type {boolean} * @memberof ProfileCourseDto */ 'personalMentoring': boolean; /** * * @type {string} * @memberof ProfileCourseDto */ 'personalMentoringStartDate': string | null; /** * * @type {string} * @memberof ProfileCourseDto */ 'personalMentoringEndDate': string | null; /** * * @type {string} * @memberof ProfileCourseDto */ 'logo': string; /** * * @type {IdNameDto} * @memberof ProfileCourseDto */ 'discipline': IdNameDto | null; /** * * @type {number} * @memberof ProfileCourseDto */ 'minStudentsPerMentor': number; /** * * @type {number} * @memberof ProfileCourseDto */ 'certificateThreshold': number; /** * * @type {string} * @memberof ProfileCourseDto */ 'wearecommunityUrl': string | null; /** * * @type {Array} * @memberof ProfileCourseDto */ 'certificateDisciplines': Array | null; } /** * * @export * @interface ProfileDto */ export interface ProfileDto { /** * * @type {string} * @memberof ProfileDto */ 'publicCvUrl': string | null; } /** * * @export * @interface PromptDto */ export interface PromptDto { /** * * @type {number} * @memberof PromptDto */ 'id': number; /** * * @type {string} * @memberof PromptDto */ 'type': string; /** * * @type {string} * @memberof PromptDto */ 'text': string; /** * * @type {number} * @memberof PromptDto */ 'temperature': number; } /** * * @export * @interface PublicAttributesDto */ export interface PublicAttributesDto { /** * * @type {number} * @memberof PublicAttributesDto */ 'maxAttemptsNumber': number; /** * * @type {number} * @memberof PublicAttributesDto */ 'numberOfQuestions': number; /** * * @type {boolean} * @memberof PublicAttributesDto */ 'strictAttemptsMode': boolean; /** * * @type {number} * @memberof PublicAttributesDto */ 'tresholdPercentage': number; /** * * @type {Array} * @memberof PublicAttributesDto */ 'questions': Array; } /** * * @export * @interface PutInterviewFeedbackDto */ export interface PutInterviewFeedbackDto { /** * * @type {number} * @memberof PutInterviewFeedbackDto */ 'version': number; /** * * @type {object} * @memberof PutInterviewFeedbackDto */ 'json': object; /** * * @type {string} * @memberof PutInterviewFeedbackDto */ 'decision'?: string; /** * * @type {boolean} * @memberof PutInterviewFeedbackDto */ 'isGoodCandidate'?: boolean; /** * * @type {boolean} * @memberof PutInterviewFeedbackDto */ 'isCompleted': boolean; /** * * @type {number} * @memberof PutInterviewFeedbackDto */ 'score'?: number; } /** * * @export * @interface QuestionDto */ export interface QuestionDto { /** * * @type {string} * @memberof QuestionDto */ 'question': string; /** * * @type {boolean} * @memberof QuestionDto */ 'multiple': boolean; /** * * @type {Array} * @memberof QuestionDto */ 'answers': Array; /** * * @type {string} * @memberof QuestionDto */ 'questionImage'?: string; /** * * @type {string} * @memberof QuestionDto */ 'answersType'?: string; } /** * * @export * @interface ResultDto */ export interface ResultDto { /** * * @type {number} * @memberof ResultDto */ 'score'?: number; /** * * @type {number} * @memberof ResultDto */ 'courseTaskId'?: number; } /** * * @export * @interface ResumeCourseDto */ export interface ResumeCourseDto { /** * * @type {number} * @memberof ResumeCourseDto */ 'id': number; /** * * @type {string} * @memberof ResumeCourseDto */ 'name': string; /** * * @type {string} * @memberof ResumeCourseDto */ 'fullName': string; /** * * @type {number} * @memberof ResumeCourseDto */ 'rank': number; /** * * @type {number} * @memberof ResumeCourseDto */ 'totalScore': number; /** * * @type {string} * @memberof ResumeCourseDto */ 'certificateId': string | null; /** * * @type {boolean} * @memberof ResumeCourseDto */ 'completed': boolean; /** * * @type {ResumeCourseMentor} * @memberof ResumeCourseDto */ 'mentor': ResumeCourseMentor | null; /** * * @type {string} * @memberof ResumeCourseDto */ 'locationName': string; } /** * * @export * @interface ResumeCourseMentor */ export interface ResumeCourseMentor { /** * * @type {string} * @memberof ResumeCourseMentor */ 'name': string; /** * * @type {string} * @memberof ResumeCourseMentor */ 'githubId': string; /** * * @type {number} * @memberof ResumeCourseMentor */ 'id': number; } /** * * @export * @interface ResumeDto */ export interface ResumeDto { /** * * @type {string} * @memberof ResumeDto */ 'uuid': string; /** * * @type {string} * @memberof ResumeDto */ 'avatarLink': string | null; /** * * @type {Array} * @memberof ResumeDto */ 'visibleCourses': Array; /** * * @type {Array} * @memberof ResumeDto */ 'courses': Array; /** * * @type {string} * @memberof ResumeDto */ 'desiredPosition': string | null; /** * * @type {string} * @memberof ResumeDto */ 'email': string | null; /** * * @type {string} * @memberof ResumeDto */ 'englishLevel': ResumeDtoEnglishLevelEnum; /** * * @type {number} * @memberof ResumeDto */ 'expires': number | null; /** * * @type {Array} * @memberof ResumeDto */ 'gratitudes': Array; /** * * @type {Array} * @memberof ResumeDto */ 'feedbacks': Array; /** * * @type {boolean} * @memberof ResumeDto */ 'fullTime': boolean; /** * * @type {string} * @memberof ResumeDto */ 'githubUsername': string | null; /** * * @type {number} * @memberof ResumeDto */ 'id': number; /** * * @type {string} * @memberof ResumeDto */ 'linkedin': string | null; /** * * @type {string} * @memberof ResumeDto */ 'locations': string | null; /** * * @type {string} * @memberof ResumeDto */ 'militaryService': ResumeDtoMilitaryServiceEnum; /** * * @type {string} * @memberof ResumeDto */ 'name': string | null; /** * * @type {string} * @memberof ResumeDto */ 'notes': string | null; /** * * @type {string} * @memberof ResumeDto */ 'phone': string | null; /** * * @type {string} * @memberof ResumeDto */ 'selfIntroLink': string | null; /** * * @type {string} * @memberof ResumeDto */ 'skype': string | null; /** * * @type {string} * @memberof ResumeDto */ 'startFrom': string | null; /** * * @type {string} * @memberof ResumeDto */ 'telegram': string | null; /** * * @type {string} * @memberof ResumeDto */ 'website': string | null; } export const ResumeDtoEnglishLevelEnum = { Unknown: 'unknown', A0: 'a0', A1: 'a1', A2: 'a2', B1: 'b1', B2: 'b2', C1: 'c1', C2: 'c2' } as const; export type ResumeDtoEnglishLevelEnum = typeof ResumeDtoEnglishLevelEnum[keyof typeof ResumeDtoEnglishLevelEnum]; export const ResumeDtoMilitaryServiceEnum = { Served: 'served', Liable: 'liable', NotLiable: 'notLiable' } as const; export type ResumeDtoMilitaryServiceEnum = typeof ResumeDtoMilitaryServiceEnum[keyof typeof ResumeDtoMilitaryServiceEnum]; /** * * @export * @interface SaveCertificateDto */ export interface SaveCertificateDto { /** * * @type {string} * @memberof SaveCertificateDto */ 'publicId': string; /** * * @type {number} * @memberof SaveCertificateDto */ 'studentId': number; /** * * @type {string} * @memberof SaveCertificateDto */ 's3Bucket': string; /** * * @type {string} * @memberof SaveCertificateDto */ 's3Key': string; /** * * @type {string} * @memberof SaveCertificateDto */ 'issueDate': string; } /** * * @export * @interface SaveTaskSolutionDto */ export interface SaveTaskSolutionDto { /** * * @type {string} * @memberof SaveTaskSolutionDto */ 'url': string; } /** * * @export * @interface ScoreDto */ export interface ScoreDto { /** * * @type {Array} * @memberof ScoreDto */ 'content': Array; /** * * @type {PaginationMetaDto} * @memberof ScoreDto */ 'pagination': PaginationMetaDto; } /** * * @export * @interface ScoreStudentDto */ export interface ScoreStudentDto { /** * * @type {string} * @memberof ScoreStudentDto */ 'name': string; /** * * @type {string} * @memberof ScoreStudentDto */ 'githubId': string; /** * * @type {number} * @memberof ScoreStudentDto */ 'id': number; /** * * @type {boolean} * @memberof ScoreStudentDto */ 'active': boolean; /** * * @type {string} * @memberof ScoreStudentDto */ 'cityName': string | null; /** * * @type {string} * @memberof ScoreStudentDto */ 'countryName': string | null; /** * * @type {number} * @memberof ScoreStudentDto */ 'totalScore': number; /** * * @type {number} * @memberof ScoreStudentDto */ 'rank': number; /** * * @type {MentorDto} * @memberof ScoreStudentDto */ 'mentor': MentorDto | null; /** * * @type {string} * @memberof ScoreStudentDto */ 'totalScoreChangeDate': string; /** * * @type {number} * @memberof ScoreStudentDto */ 'crossCheckScore': number; /** * * @type {string} * @memberof ScoreStudentDto */ 'repositoryLastActivityDate': string; /** * * @type {Array} * @memberof ScoreStudentDto */ 'taskResults': Array; /** * * @type {boolean} * @memberof ScoreStudentDto */ 'isActive': boolean; /** * * @type {ContactsDto} * @memberof ScoreStudentDto */ 'contacts': ContactsDto; } /** * * @export * @interface SearchMentorDto */ export interface SearchMentorDto { /** * * @type {number} * @memberof SearchMentorDto */ 'id': number; /** * * @type {string} * @memberof SearchMentorDto */ 'githubId': string; /** * * @type {string} * @memberof SearchMentorDto */ 'name': string; } /** * * @export * @interface SelfEducationQuestionSelectedAnswersDto */ export interface SelfEducationQuestionSelectedAnswersDto { /** * * @type {Array} * @memberof SelfEducationQuestionSelectedAnswersDto */ 'answers': Array; /** * * @type {string} * @memberof SelfEducationQuestionSelectedAnswersDto */ 'question': string; /** * * @type {boolean} * @memberof SelfEducationQuestionSelectedAnswersDto */ 'multiple': boolean; /** * * @type {string} * @memberof SelfEducationQuestionSelectedAnswersDto */ 'questionImage'?: string; /** * * @type {string} * @memberof SelfEducationQuestionSelectedAnswersDto */ 'answersType'?: string; /** * * @type {Array} * @memberof SelfEducationQuestionSelectedAnswersDto */ 'selectedAnswers': Array; } /** * * @export * @interface SendUserNotificationDto */ export interface SendUserNotificationDto { /** * * @type {string} * @memberof SendUserNotificationDto */ 'notificationId': string; /** * * @type {number} * @memberof SendUserNotificationDto */ 'userId': number; /** * * @type {object} * @memberof SendUserNotificationDto */ 'data': object; /** * * @type {number} * @memberof SendUserNotificationDto */ 'expireDate': number; } /** * * @export * @interface SenderDto */ export interface SenderDto { /** * * @type {SenderLoginDto} * @memberof SenderDto */ 'login': SenderLoginDto; } /** * * @export * @interface SenderLoginDto */ export interface SenderLoginDto { /** * * @type {string} * @memberof SenderLoginDto */ 'githubId': string; } /** * * @export * @interface SoftSkillEntry */ export interface SoftSkillEntry { /** * * @type {string} * @memberof SoftSkillEntry */ 'id': SoftSkillEntryIdEnum; /** * * @type {string} * @memberof SoftSkillEntry */ 'value': SoftSkillEntryValueEnum; } export const SoftSkillEntryIdEnum = { Responsible: 'skill.soft.responsible', TeamPlayer: 'skill.soft.team-player', Communicable: 'skill.soft.communicable' } as const; export type SoftSkillEntryIdEnum = typeof SoftSkillEntryIdEnum[keyof typeof SoftSkillEntryIdEnum]; export const SoftSkillEntryValueEnum = { None: 'None', Poor: 'Poor', Fair: 'Fair', Good: 'Good', Great: 'Great', Excellent: 'Excellent' } as const; export type SoftSkillEntryValueEnum = typeof SoftSkillEntryValueEnum[keyof typeof SoftSkillEntryValueEnum]; /** * * @export * @enum {string} */ export const SolutionItemStatusEnum = { InReview: 'in-review', Done: 'done', RandomTask: 'random-task' } as const; export type SolutionItemStatusEnum = typeof SolutionItemStatusEnum[keyof typeof SolutionItemStatusEnum]; /** * * @export * @interface StatusDto */ export interface StatusDto { /** * * @type {number} * @memberof StatusDto */ 'expires': number; } /** * * @export * @interface StudentDto */ export interface StudentDto { /** * * @type {string} * @memberof StudentDto */ 'name': string; /** * * @type {string} * @memberof StudentDto */ 'githubId': string; /** * * @type {number} * @memberof StudentDto */ 'id': number; /** * * @type {boolean} * @memberof StudentDto */ 'active': boolean; /** * * @type {string} * @memberof StudentDto */ 'cityName': string | null; /** * * @type {string} * @memberof StudentDto */ 'countryName': string | null; /** * * @type {number} * @memberof StudentDto */ 'totalScore': number; /** * * @type {number} * @memberof StudentDto */ 'rank': number; } /** * * @export * @interface StudentFeedbackContentDto */ export interface StudentFeedbackContentDto { /** * * @type {string} * @memberof StudentFeedbackContentDto */ 'suggestions': string; /** * * @type {string} * @memberof StudentFeedbackContentDto */ 'recommendationComment': string; /** * * @type {Array} * @memberof StudentFeedbackContentDto */ 'softSkills': Array; } /** * * @export * @interface StudentFeedbackDto */ export interface StudentFeedbackDto { /** * * @type {number} * @memberof StudentFeedbackDto */ 'id': number; /** * * @type {string} * @memberof StudentFeedbackDto */ 'createdDate': string; /** * * @type {string} * @memberof StudentFeedbackDto */ 'updatedDate': string; /** * * @type {StudentFeedbackContentDto} * @memberof StudentFeedbackDto */ 'content': StudentFeedbackContentDto; /** * * @type {string} * @memberof StudentFeedbackDto */ 'recommendation': StudentFeedbackDtoRecommendationEnum; /** * * @type {PersonDto} * @memberof StudentFeedbackDto */ 'author': PersonDto; /** * * @type {PersonDto} * @memberof StudentFeedbackDto */ 'mentor': PersonDto | null; /** * * @type {string} * @memberof StudentFeedbackDto */ 'englishLevel': StudentFeedbackDtoEnglishLevelEnum; } export const StudentFeedbackDtoRecommendationEnum = { Hire: 'hire', NotHire: 'not-hire' } as const; export type StudentFeedbackDtoRecommendationEnum = typeof StudentFeedbackDtoRecommendationEnum[keyof typeof StudentFeedbackDtoRecommendationEnum]; export const StudentFeedbackDtoEnglishLevelEnum = { Unknown: 'unknown', A0: 'a0', A1: 'a1', A2: 'a2', B1: 'b1', B2: 'b2', C1: 'c1', C2: 'c2' } as const; export type StudentFeedbackDtoEnglishLevelEnum = typeof StudentFeedbackDtoEnglishLevelEnum[keyof typeof StudentFeedbackDtoEnglishLevelEnum]; /** * * @export * @interface StudentId */ export interface StudentId { /** * * @type {number} * @memberof StudentId */ 'id': number; } /** * * @export * @interface StudentSummaryDto */ export interface StudentSummaryDto { /** * * @type {number} * @memberof StudentSummaryDto */ 'totalScore': number; /** * * @type {Array} * @memberof StudentSummaryDto */ 'results': Array; /** * * @type {boolean} * @memberof StudentSummaryDto */ 'isActive': boolean; /** * * @type {MentorStudentSummaryDto} * @memberof StudentSummaryDto */ 'mentor': MentorStudentSummaryDto | null; /** * * @type {number} * @memberof StudentSummaryDto */ 'rank': number; /** * * @type {string} * @memberof StudentSummaryDto */ 'repository': string | null; } /** * * @export * @interface StudentsDto */ export interface StudentsDto { /** * * @type {number} * @memberof StudentsDto */ 'id': number; /** * * @type {string} * @memberof StudentsDto */ 'githubId': string; /** * * @type {string} * @memberof StudentsDto */ 'name': string; } /** * * @export * @interface TaskCriteriaDto */ export interface TaskCriteriaDto { /** * * @type {Array} * @memberof TaskCriteriaDto */ 'criteria': Array; } /** * * @export * @interface TaskDto */ export interface TaskDto { /** * * @type {string} * @memberof TaskDto */ 'type': TaskDtoTypeEnum; /** * * @type {string} * @memberof TaskDto */ 'name': string; /** * * @type {number} * @memberof TaskDto */ 'id': number; /** * * @type {string} * @memberof TaskDto */ 'descriptionUrl': string; /** * * @type {string} * @memberof TaskDto */ 'description': string; /** * * @type {string} * @memberof TaskDto */ 'githubRepoName': string; /** * * @type {string} * @memberof TaskDto */ 'sourceGithubRepoUrl': string; /** * * @type {IdNameDto} * @memberof TaskDto */ 'discipline': IdNameDto; /** * * @type {boolean} * @memberof TaskDto */ 'githubPrRequired': boolean; /** * * @type {string} * @memberof TaskDto */ 'createdDate': string; /** * * @type {string} * @memberof TaskDto */ 'updatedDate': string; /** * * @type {Array} * @memberof TaskDto */ 'tags': Array; /** * * @type {Array} * @memberof TaskDto */ 'skills': Array; /** * * @type {object} * @memberof TaskDto */ 'attributes': object; /** * * @type {Array} * @memberof TaskDto */ 'courses': Array; } export const TaskDtoTypeEnum = { Jstask: 'jstask', Kotlintask: 'kotlintask', Objctask: 'objctask', Htmltask: 'htmltask', Ipynb: 'ipynb', Selfeducation: 'selfeducation', Codewars: 'codewars', Test: 'test', Codejam: 'codejam', Interview: 'interview', StageInterview: 'stage-interview', Cvhtml: 'cv:html', Cvmarkdown: 'cv:markdown' } as const; export type TaskDtoTypeEnum = typeof TaskDtoTypeEnum[keyof typeof TaskDtoTypeEnum]; /** * * @export * @interface TaskPerformanceStatsDto */ export interface TaskPerformanceStatsDto { /** * Total number of students who submitted the task * @type {number} * @memberof TaskPerformanceStatsDto */ 'totalAchievement': number; /** * Number of students scoring between 1% and 20% of the maximum points * @type {number} * @memberof TaskPerformanceStatsDto */ 'minimalAchievement': number; /** * Number of students scoring between 21% and 50% of the maximum points * @type {number} * @memberof TaskPerformanceStatsDto */ 'lowAchievement': number; /** * Number of students scoring between 51% and 70% of the maximum points * @type {number} * @memberof TaskPerformanceStatsDto */ 'moderateAchievement': number; /** * Number of students scoring between 71% and 90% of the maximum points * @type {number} * @memberof TaskPerformanceStatsDto */ 'highAchievement': number; /** * Number of students scoring between 91% and 99% of the maximum points * @type {number} * @memberof TaskPerformanceStatsDto */ 'exceptionalAchievement': number; /** * Number of students achieving a perfect score of 100% * @type {number} * @memberof TaskPerformanceStatsDto */ 'perfectScores': number; } /** * * @export * @interface TaskResultsDto */ export interface TaskResultsDto { /** * * @type {number} * @memberof TaskResultsDto */ 'courseTaskId': number; /** * * @type {number} * @memberof TaskResultsDto */ 'score': number; } /** * * @export * @interface TaskSolutionDto */ export interface TaskSolutionDto { /** * * @type {number} * @memberof TaskSolutionDto */ 'id': number; /** * * @type {number} * @memberof TaskSolutionDto */ 'courseTaskId': number; /** * * @type {string} * @memberof TaskSolutionDto */ 'url': string; } /** * * @export * @interface TaskVerificationAttemptDto */ export interface TaskVerificationAttemptDto { /** * * @type {number} * @memberof TaskVerificationAttemptDto */ 'createdDate': number; /** * * @type {number} * @memberof TaskVerificationAttemptDto */ 'courseTaskId': number; /** * * @type {number} * @memberof TaskVerificationAttemptDto */ 'score': number; /** * * @type {number} * @memberof TaskVerificationAttemptDto */ 'maxScore': number; /** * * @type {Array} * @memberof TaskVerificationAttemptDto */ 'questions': Array; } /** * * @export * @interface TeamDistributionDetailedDto */ export interface TeamDistributionDetailedDto { /** * * @type {number} * @memberof TeamDistributionDetailedDto */ 'id': number; /** * * @type {number} * @memberof TeamDistributionDetailedDto */ 'courseId': number; /** * * @type {string} * @memberof TeamDistributionDetailedDto */ 'name': string; /** * * @type {number} * @memberof TeamDistributionDetailedDto */ 'studentsWithoutTeamCount': number; /** * * @type {number} * @memberof TeamDistributionDetailedDto */ 'teamsCount': number; /** * * @type {TeamDto} * @memberof TeamDistributionDetailedDto */ 'myTeam': TeamDto; /** * * @type {number} * @memberof TeamDistributionDetailedDto */ 'minTeamSize': number; /** * * @type {number} * @memberof TeamDistributionDetailedDto */ 'maxTeamSize': number; /** * * @type {number} * @memberof TeamDistributionDetailedDto */ 'strictTeamSize': number; /** * * @type {boolean} * @memberof TeamDistributionDetailedDto */ 'strictTeamSizeMode': boolean; } /** * * @export * @interface TeamDistributionDto */ export interface TeamDistributionDto { /** * * @type {number} * @memberof TeamDistributionDto */ 'id': number; /** * * @type {string} * @memberof TeamDistributionDto */ 'name': string; /** * * @type {string} * @memberof TeamDistributionDto */ 'registrationStatus': TeamDistributionDtoRegistrationStatusEnum; /** * * @type {string} * @memberof TeamDistributionDto */ 'startDate': string; /** * * @type {string} * @memberof TeamDistributionDto */ 'endDate': string; /** * * @type {string} * @memberof TeamDistributionDto */ 'description': string; /** * * @type {string} * @memberof TeamDistributionDto */ 'descriptionUrl': string; /** * * @type {number} * @memberof TeamDistributionDto */ 'minTeamSize': number; /** * * @type {number} * @memberof TeamDistributionDto */ 'maxTeamSize': number; /** * * @type {number} * @memberof TeamDistributionDto */ 'strictTeamSize': number; /** * * @type {boolean} * @memberof TeamDistributionDto */ 'strictTeamSizeMode': boolean; /** * * @type {number} * @memberof TeamDistributionDto */ 'minTotalScore': number; } export const TeamDistributionDtoRegistrationStatusEnum = { Available: 'available', Unavailable: 'unavailable', Future: 'future', Completed: 'completed', Distributed: 'distributed', Closed: 'closed' } as const; export type TeamDistributionDtoRegistrationStatusEnum = typeof TeamDistributionDtoRegistrationStatusEnum[keyof typeof TeamDistributionDtoRegistrationStatusEnum]; /** * * @export * @interface TeamDistributionStudentDto */ export interface TeamDistributionStudentDto { /** * * @type {number} * @memberof TeamDistributionStudentDto */ 'id': number; /** * * @type {string} * @memberof TeamDistributionStudentDto */ 'fullName': string; /** * * @type {string} * @memberof TeamDistributionStudentDto */ 'cvLink': string; /** * * @type {Discord} * @memberof TeamDistributionStudentDto */ 'discord': Discord | null; /** * * @type {string} * @memberof TeamDistributionStudentDto */ 'telegram': string; /** * * @type {string} * @memberof TeamDistributionStudentDto */ 'email': string; /** * * @type {string} * @memberof TeamDistributionStudentDto */ 'githubId': string; /** * * @type {number} * @memberof TeamDistributionStudentDto */ 'rank': number; /** * * @type {number} * @memberof TeamDistributionStudentDto */ 'totalScore': number; /** * * @type {string} * @memberof TeamDistributionStudentDto */ 'location': string; /** * * @type {string} * @memberof TeamDistributionStudentDto */ 'cvUuid': string; } /** * * @export * @interface TeamDto */ export interface TeamDto { /** * * @type {number} * @memberof TeamDto */ 'id': number; /** * * @type {string} * @memberof TeamDto */ 'name': string; /** * * @type {string} * @memberof TeamDto */ 'chatLink': string; /** * * @type {string} * @memberof TeamDto */ 'description': string; /** * * @type {number} * @memberof TeamDto */ 'teamLeadId': number; /** * * @type {number} * @memberof TeamDto */ 'teamDistributionId': number; /** * * @type {Array} * @memberof TeamDto */ 'students': Array; } /** * * @export * @interface TeamInfoDto */ export interface TeamInfoDto { /** * * @type {number} * @memberof TeamInfoDto */ 'id': number; /** * * @type {string} * @memberof TeamInfoDto */ 'name': string; /** * * @type {string} * @memberof TeamInfoDto */ 'chatLink': string; /** * * @type {string} * @memberof TeamInfoDto */ 'description': string; /** * * @type {number} * @memberof TeamInfoDto */ 'teamLeadId': number; /** * * @type {number} * @memberof TeamInfoDto */ 'teamDistributionId': number; } /** * * @export * @interface TeamPasswordDto */ export interface TeamPasswordDto { /** * * @type {string} * @memberof TeamPasswordDto */ 'password': string; } /** * * @export * @interface TeamsDto */ export interface TeamsDto { /** * * @type {Array} * @memberof TeamsDto */ 'content': Array; /** * * @type {PaginationMetaDto} * @memberof TeamsDto */ 'pagination': PaginationMetaDto; } /** * * @export * @interface TopMentorDto */ export interface TopMentorDto { /** * Position in the mentors ranking * @type {number} * @memberof TopMentorDto */ 'rank': number; /** * GitHub username * @type {string} * @memberof TopMentorDto */ 'githubId': string; /** * Full name of the mentor * @type {string} * @memberof TopMentorDto */ 'name': string; /** * Total number of certified students mentored * @type {number} * @memberof TopMentorDto */ 'totalStudents': number; /** * Total number of gratitudes received * @type {number} * @memberof TopMentorDto */ 'totalGratitudes': number; /** * Student counts per course * @type {Array} * @memberof TopMentorDto */ 'courseStats': Array; } /** * * @export * @interface UpdateContributorDto */ export interface UpdateContributorDto { /** * * @type {string} * @memberof UpdateContributorDto */ 'description'?: string; /** * * @type {number} * @memberof UpdateContributorDto */ 'userId'?: number; } /** * * @export * @interface UpdateCourseDto */ export interface UpdateCourseDto { /** * * @type {string} * @memberof UpdateCourseDto */ 'name'?: string; /** * * @type {string} * @memberof UpdateCourseDto */ 'fullName'?: string; /** * * @type {string} * @memberof UpdateCourseDto */ 'alias'?: string; /** * * @type {string} * @memberof UpdateCourseDto */ 'description'?: string; /** * * @type {string} * @memberof UpdateCourseDto */ 'descriptionUrl'?: string; /** * * @type {number} * @memberof UpdateCourseDto */ 'year'?: number; /** * * @type {string} * @memberof UpdateCourseDto */ 'startDate'?: string; /** * * @type {string} * @memberof UpdateCourseDto */ 'endDate'?: string; /** * * @type {string} * @memberof UpdateCourseDto */ 'registrationEndDate'?: string | null; /** * * @type {string} * @memberof UpdateCourseDto */ 'locationName'?: string; /** * * @type {number} * @memberof UpdateCourseDto */ 'discordServerId'?: number; /** * * @type {boolean} * @memberof UpdateCourseDto */ 'completed'?: boolean; /** * * @type {boolean} * @memberof UpdateCourseDto */ 'planned'?: boolean; /** * * @type {boolean} * @memberof UpdateCourseDto */ 'inviteOnly'?: boolean; /** * * @type {string} * @memberof UpdateCourseDto */ 'certificateIssuer'?: string; /** * * @type {boolean} * @memberof UpdateCourseDto */ 'usePrivateRepositories'?: boolean; /** * * @type {boolean} * @memberof UpdateCourseDto */ 'personalMentoring'?: boolean; /** * * @type {string} * @memberof UpdateCourseDto */ 'personalMentoringStartDate'?: string | null; /** * * @type {string} * @memberof UpdateCourseDto */ 'personalMentoringEndDate'?: string | null; /** * * @type {string} * @memberof UpdateCourseDto */ 'logo'?: string; /** * * @type {number} * @memberof UpdateCourseDto */ 'disciplineId'?: number; /** * * @type {number} * @memberof UpdateCourseDto */ 'minStudentsPerMentor'?: number; /** * * @type {number} * @memberof UpdateCourseDto */ 'certificateThreshold': number; /** * * @type {string} * @memberof UpdateCourseDto */ 'wearecommunityUrl'?: string | null; /** * * @type {Array} * @memberof UpdateCourseDto */ 'certificateDisciplines'?: Array | null; } /** * * @export * @interface UpdateCourseEventDto */ export interface UpdateCourseEventDto { /** * * @type {string} * @memberof UpdateCourseEventDto */ 'special'?: string; /** * * @type {string} * @memberof UpdateCourseEventDto */ 'dateTime'?: string; /** * * @type {string} * @memberof UpdateCourseEventDto */ 'endTime'?: string; /** * * @type {number} * @memberof UpdateCourseEventDto */ 'duration'?: number; /** * * @type {string} * @memberof UpdateCourseEventDto */ 'place'?: string; /** * * @type {Organizer} * @memberof UpdateCourseEventDto */ 'organizer'?: Organizer; /** * * @type {number} * @memberof UpdateCourseEventDto */ 'organizerId'?: number; /** * * @type {string} * @memberof UpdateCourseEventDto */ 'broadcastUrl'?: string; /** * * @type {string} * @memberof UpdateCourseEventDto */ 'coordinator'?: string; /** * * @type {string} * @memberof UpdateCourseEventDto */ 'comment'?: string; } /** * * @export * @interface UpdateCourseTaskDto */ export interface UpdateCourseTaskDto { /** * * @type {string} * @memberof UpdateCourseTaskDto */ 'type'?: UpdateCourseTaskDtoTypeEnum; /** * * @type {string} * @memberof UpdateCourseTaskDto */ 'name'?: string; /** * * @type {string} * @memberof UpdateCourseTaskDto */ 'checker'?: string; /** * * @type {string} * @memberof UpdateCourseTaskDto */ 'studentStartDate': string; /** * * @type {string} * @memberof UpdateCourseTaskDto */ 'studentEndDate': string; /** * * @type {string} * @memberof UpdateCourseTaskDto */ 'descriptionUrl'?: string; /** * * @type {number} * @memberof UpdateCourseTaskDto */ 'taskOwnerId'?: number; /** * * @type {number} * @memberof UpdateCourseTaskDto */ 'maxScore'?: number; /** * * @type {number} * @memberof UpdateCourseTaskDto */ 'scoreWeight'?: number; /** * * @type {number} * @memberof UpdateCourseTaskDto */ 'pairsCount'?: number; /** * * @type {number} * @memberof UpdateCourseTaskDto */ 'taskId'?: number; /** * * @type {string} * @memberof UpdateCourseTaskDto */ 'crossCheckEndDate'?: string; /** * * @type {string} * @memberof UpdateCourseTaskDto */ 'submitText': string; /** * * @type {object} * @memberof UpdateCourseTaskDto */ 'validations': object; /** * * @type {string} * @memberof UpdateCourseTaskDto */ 'studentRegistrationStartDate'?: string; } export const UpdateCourseTaskDtoTypeEnum = { Jstask: 'jstask', Kotlintask: 'kotlintask', Objctask: 'objctask', Htmltask: 'htmltask', Ipynb: 'ipynb', Selfeducation: 'selfeducation', Codewars: 'codewars', Test: 'test', Codejam: 'codejam', Interview: 'interview', StageInterview: 'stage-interview', Cvhtml: 'cv:html', Cvmarkdown: 'cv:markdown' } as const; export type UpdateCourseTaskDtoTypeEnum = typeof UpdateCourseTaskDtoTypeEnum[keyof typeof UpdateCourseTaskDtoTypeEnum]; /** * * @export * @interface UpdateCourseUserDto */ export interface UpdateCourseUserDto { /** * * @type {boolean} * @memberof UpdateCourseUserDto */ 'isManager': boolean; /** * * @type {boolean} * @memberof UpdateCourseUserDto */ 'isSupervisor': boolean; /** * * @type {boolean} * @memberof UpdateCourseUserDto */ 'isDementor': boolean; /** * * @type {boolean} * @memberof UpdateCourseUserDto */ 'isActivist': boolean; /** * * @type {number} * @memberof UpdateCourseUserDto */ 'userId': number; } /** * * @export * @interface UpdateDisciplineDto */ export interface UpdateDisciplineDto { /** * * @type {string} * @memberof UpdateDisciplineDto */ 'name': string; } /** * * @export * @interface UpdateDiscordServerDto */ export interface UpdateDiscordServerDto { /** * * @type {string} * @memberof UpdateDiscordServerDto */ 'name': string; /** * * @type {string} * @memberof UpdateDiscordServerDto */ 'gratitudeUrl': string; /** * * @type {string} * @memberof UpdateDiscordServerDto */ 'mentorsChatUrl': string; } /** * * @export * @interface UpdateEventDto */ export interface UpdateEventDto { /** * * @type {string} * @memberof UpdateEventDto */ 'name': string; /** * * @type {string} * @memberof UpdateEventDto */ 'type': string; /** * * @type {number} * @memberof UpdateEventDto */ 'disciplineId': number; /** * * @type {string} * @memberof UpdateEventDto */ 'descriptionUrl': string; /** * * @type {string} * @memberof UpdateEventDto */ 'description': string; } /** * * @export * @interface UpdateNotificationDto */ export interface UpdateNotificationDto { /** * * @type {string} * @memberof UpdateNotificationDto */ 'id': string; /** * * @type {string} * @memberof UpdateNotificationDto */ 'name': string; /** * * @type {boolean} * @memberof UpdateNotificationDto */ 'enabled': boolean; /** * * @type {Array} * @memberof UpdateNotificationDto */ 'channels': Array; /** * * @type {NotificationType} * @memberof UpdateNotificationDto */ 'type': NotificationType; /** * * @type {string} * @memberof UpdateNotificationDto */ 'parentId': string; } /** * * @export * @interface UpdateNotificationUserSettingsDto */ export interface UpdateNotificationUserSettingsDto { /** * * @type {string} * @memberof UpdateNotificationUserSettingsDto */ 'notificationId': string; /** * * @type {boolean} * @memberof UpdateNotificationUserSettingsDto */ 'enabled': boolean; /** * * @type {string} * @memberof UpdateNotificationUserSettingsDto */ 'channelId': string; } /** * * @export * @interface UpdateProfileInfoDto */ export interface UpdateProfileInfoDto { /** * * @type {string} * @memberof UpdateProfileInfoDto */ 'name'?: string; /** * * @type {string} * @memberof UpdateProfileInfoDto */ 'githubId'?: string; /** * * @type {string} * @memberof UpdateProfileInfoDto */ 'aboutMyself'?: string | null; /** * * @type {string} * @memberof UpdateProfileInfoDto */ 'cityName'?: string | null; /** * * @type {string} * @memberof UpdateProfileInfoDto */ 'countryName'?: string | null; /** * * @type {Array} * @memberof UpdateProfileInfoDto */ 'educationHistory'?: Array | null; /** * * @type {string} * @memberof UpdateProfileInfoDto */ 'englishLevel'?: string | null; /** * * @type {Array} * @memberof UpdateProfileInfoDto */ 'languages'?: Array; /** * * @type {string} * @memberof UpdateProfileInfoDto */ 'contactsPhone'?: string | null; /** * * @type {string} * @memberof UpdateProfileInfoDto */ 'contactsEmail'?: string | null; /** * * @type {string} * @memberof UpdateProfileInfoDto */ 'contactsEpamEmail'?: string | null; /** * * @type {string} * @memberof UpdateProfileInfoDto */ 'contactsSkype'?: string | null; /** * * @type {string} * @memberof UpdateProfileInfoDto */ 'contactsWhatsApp'?: string | null; /** * * @type {string} * @memberof UpdateProfileInfoDto */ 'contactsTelegram'?: string | null; /** * * @type {string} * @memberof UpdateProfileInfoDto */ 'contactsNotes'?: string | null; /** * * @type {string} * @memberof UpdateProfileInfoDto */ 'contactsLinkedIn'?: string | null; /** * * @type {Discord} * @memberof UpdateProfileInfoDto */ 'discord'?: Discord | null; } /** * * @export * @interface UpdatePromptDto */ export interface UpdatePromptDto { /** * * @type {number} * @memberof UpdatePromptDto */ 'temperature': number; /** * * @type {string} * @memberof UpdatePromptDto */ 'type': string; /** * * @type {string} * @memberof UpdatePromptDto */ 'text': string; } /** * * @export * @interface UpdateStudentFeedbackDto */ export interface UpdateStudentFeedbackDto { /** * * @type {StudentFeedbackContentDto} * @memberof UpdateStudentFeedbackDto */ 'content': StudentFeedbackContentDto; /** * * @type {string} * @memberof UpdateStudentFeedbackDto */ 'recommendation': UpdateStudentFeedbackDtoRecommendationEnum; /** * * @type {string} * @memberof UpdateStudentFeedbackDto */ 'englishLevel': UpdateStudentFeedbackDtoEnglishLevelEnum; } export const UpdateStudentFeedbackDtoRecommendationEnum = { Hire: 'hire', NotHire: 'not-hire' } as const; export type UpdateStudentFeedbackDtoRecommendationEnum = typeof UpdateStudentFeedbackDtoRecommendationEnum[keyof typeof UpdateStudentFeedbackDtoRecommendationEnum]; export const UpdateStudentFeedbackDtoEnglishLevelEnum = { Unknown: 'unknown', A0: 'a0', A1: 'a1', A2: 'a2', B1: 'b1', B2: 'b2', C1: 'c1', C2: 'c2' } as const; export type UpdateStudentFeedbackDtoEnglishLevelEnum = typeof UpdateStudentFeedbackDtoEnglishLevelEnum[keyof typeof UpdateStudentFeedbackDtoEnglishLevelEnum]; /** * * @export * @interface UpdateTaskDto */ export interface UpdateTaskDto { /** * * @type {string} * @memberof UpdateTaskDto */ 'name': string; /** * * @type {object} * @memberof UpdateTaskDto */ 'attributes': object; /** * * @type {string} * @memberof UpdateTaskDto */ 'descriptionUrl': string; /** * * @type {string} * @memberof UpdateTaskDto */ 'description': string; /** * * @type {string} * @memberof UpdateTaskDto */ 'githubRepoName': string; /** * * @type {string} * @memberof UpdateTaskDto */ 'sourceGithubRepoUrl': string; /** * * @type {number} * @memberof UpdateTaskDto */ 'disciplineId': number; /** * * @type {boolean} * @memberof UpdateTaskDto */ 'githubPrRequired': boolean; /** * * @type {string} * @memberof UpdateTaskDto */ 'type': string; /** * * @type {Array} * @memberof UpdateTaskDto */ 'skills': Array; /** * * @type {Array} * @memberof UpdateTaskDto */ 'tags': Array; } /** * * @export * @interface UpdateTeamDistributionDto */ export interface UpdateTeamDistributionDto { /** * * @type {string} * @memberof UpdateTeamDistributionDto */ 'name': string; /** * * @type {string} * @memberof UpdateTeamDistributionDto */ 'startDate': string; /** * * @type {string} * @memberof UpdateTeamDistributionDto */ 'endDate': string; /** * * @type {string} * @memberof UpdateTeamDistributionDto */ 'description': string; /** * * @type {string} * @memberof UpdateTeamDistributionDto */ 'descriptionUrl': string; /** * * @type {number} * @memberof UpdateTeamDistributionDto */ 'minTeamSize': number; /** * * @type {number} * @memberof UpdateTeamDistributionDto */ 'maxTeamSize': number; /** * * @type {number} * @memberof UpdateTeamDistributionDto */ 'strictTeamSize': number; /** * * @type {boolean} * @memberof UpdateTeamDistributionDto */ 'strictTeamSizeMode': boolean; /** * * @type {number} * @memberof UpdateTeamDistributionDto */ 'minTotalScore': number; } /** * * @export * @interface UpdateTeamDto */ export interface UpdateTeamDto { /** * * @type {string} * @memberof UpdateTeamDto */ 'name': string; /** * * @type {string} * @memberof UpdateTeamDto */ 'description': string; /** * * @type {string} * @memberof UpdateTeamDto */ 'chatLink': string; /** * * @type {Array} * @memberof UpdateTeamDto */ 'studentIds'?: Array; } /** * * @export * @interface UpdateUserDto */ export interface UpdateUserDto { /** * * @type {string} * @memberof UpdateUserDto */ 'firstName'?: string | null; /** * * @type {string} * @memberof UpdateUserDto */ 'lastName'?: string | null; /** * * @type {string} * @memberof UpdateUserDto */ 'primaryEmail'?: string | null; /** * * @type {string} * @memberof UpdateUserDto */ 'cityName'?: string | null; /** * * @type {string} * @memberof UpdateUserDto */ 'countryName'?: string | null; /** * * @type {string} * @memberof UpdateUserDto */ 'contactsNotes'?: string | null; /** * * @type {string} * @memberof UpdateUserDto */ 'contactsPhone'?: string | null; /** * * @type {string} * @memberof UpdateUserDto */ 'contactsEmail'?: string | null; /** * * @type {string} * @memberof UpdateUserDto */ 'contactsEpamEmail'?: string | null; /** * * @type {string} * @memberof UpdateUserDto */ 'contactsSkype'?: string | null; /** * * @type {string} * @memberof UpdateUserDto */ 'contactsWhatsApp'?: string | null; /** * * @type {string} * @memberof UpdateUserDto */ 'contactsTelegram'?: string | null; /** * * @type {string} * @memberof UpdateUserDto */ 'contactsLinkedIn'?: string | null; /** * * @type {string} * @memberof UpdateUserDto */ 'notes'?: string | null; /** * * @type {string} * @memberof UpdateUserDto */ 'aboutMyself'?: string | null; /** * * @type {Array} * @memberof UpdateUserDto */ 'languages'?: Array; } export const UpdateUserDtoLanguagesEnum = { En: 'EN', Zh: 'ZH', Hi: 'HI', Es: 'ES', Fr: 'FR', Ar: 'AR', Bn: 'BN', Ru: 'RU', Pt: 'PT', Id: 'ID', Ur: 'UR', Ja: 'JA', De: 'DE', Pa: 'PA', Te: 'TE', Tr: 'TR', Ko: 'KO', Mr: 'MR', Ky: 'KY', Kk: 'KK', Uz: 'UZ', Ka: 'KA', Pl: 'PL', Lt: 'LT', Lv: 'LV', Be: 'BE', Uk: 'UK' } as const; export type UpdateUserDtoLanguagesEnum = typeof UpdateUserDtoLanguagesEnum[keyof typeof UpdateUserDtoLanguagesEnum]; /** * * @export * @interface UpdateUserGroupDto */ export interface UpdateUserGroupDto { /** * * @type {string} * @memberof UpdateUserGroupDto */ 'name': string; /** * * @type {Array} * @memberof UpdateUserGroupDto */ 'users': Array; /** * * @type {Array} * @memberof UpdateUserGroupDto */ 'roles': Array; } export const UpdateUserGroupDtoRolesEnum = { TaskOwner: 'taskOwner', Manager: 'manager', Supervisor: 'supervisor', Student: 'student', Mentor: 'mentor', Dementor: 'dementor', Activist: 'activist' } as const; export type UpdateUserGroupDtoRolesEnum = typeof UpdateUserGroupDtoRolesEnum[keyof typeof UpdateUserGroupDtoRolesEnum]; /** * * @export * @interface UpsertNotificationConnectionDto */ export interface UpsertNotificationConnectionDto { /** * * @type {string} * @memberof UpsertNotificationConnectionDto */ 'channelId': string; /** * * @type {string} * @memberof UpsertNotificationConnectionDto */ 'externalId': string; /** * * @type {number} * @memberof UpsertNotificationConnectionDto */ 'userId': number; /** * * @type {boolean} * @memberof UpsertNotificationConnectionDto */ 'enabled': boolean; } /** * * @export * @interface UsedCourseDto */ export interface UsedCourseDto { /** * * @type {string} * @memberof UsedCourseDto */ 'name': string; /** * * @type {boolean} * @memberof UsedCourseDto */ 'isActive': boolean; } /** * * @export * @interface UserDto */ export interface UserDto { /** * * @type {number} * @memberof UserDto */ 'id': number; /** * * @type {string} * @memberof UserDto */ 'name': string; /** * * @type {string} * @memberof UserDto */ 'githubId': string; } /** * * @export * @interface UserGroupDto */ export interface UserGroupDto { /** * * @type {number} * @memberof UserGroupDto */ 'id': number; /** * * @type {string} * @memberof UserGroupDto */ 'name': string; /** * * @type {Array} * @memberof UserGroupDto */ 'users': Array; /** * * @type {Array} * @memberof UserGroupDto */ 'roles': Array; } export const UserGroupDtoRolesEnum = { TaskOwner: 'taskOwner', Manager: 'manager', Supervisor: 'supervisor', Student: 'student', Mentor: 'mentor', Dementor: 'dementor', Activist: 'activist' } as const; export type UserGroupDtoRolesEnum = typeof UserGroupDtoRolesEnum[keyof typeof UserGroupDtoRolesEnum]; /** * * @export * @interface UserNotificationsDto */ export interface UserNotificationsDto { /** * * @type {Array} * @memberof UserNotificationsDto */ 'notifications': Array; /** * * @type {object} * @memberof UserNotificationsDto */ 'connections': object; } /** * * @export * @interface UserSearchDto */ export interface UserSearchDto { /** * * @type {number} * @memberof UserSearchDto */ 'id': number; /** * * @type {string} * @memberof UserSearchDto */ 'githubId': string; /** * * @type {string} * @memberof UserSearchDto */ 'name': string; /** * * @type {string} * @memberof UserSearchDto */ 'cityName': string | null; /** * * @type {string} * @memberof UserSearchDto */ 'countryName': string | null; /** * * @type {string} * @memberof UserSearchDto */ 'contactsEmail': string | null; /** * * @type {string} * @memberof UserSearchDto */ 'contactsEpamEmail': string | null; /** * * @type {string} * @memberof UserSearchDto */ 'primaryEmail': string | null; /** * * @type {string} * @memberof UserSearchDto */ 'contactsDiscord': string | null; /** * * @type {string} * @memberof UserSearchDto */ 'contactsTelegram': string | null; /** * * @type {Array} * @memberof UserSearchDto */ 'mentors': Array | null; /** * * @type {Array} * @memberof UserSearchDto */ 'students': Array | null; } /** * * @export * @interface UserStudentCourseDto */ export interface UserStudentCourseDto { /** * * @type {string} * @memberof UserStudentCourseDto */ 'alias': string; /** * * @type {string} * @memberof UserStudentCourseDto */ 'name': string; /** * * @type {boolean} * @memberof UserStudentCourseDto */ 'hasCertificate': boolean; /** * * @type {boolean} * @memberof UserStudentCourseDto */ 'completed': boolean; /** * * @type {boolean} * @memberof UserStudentCourseDto */ 'studentIsExpelled': boolean; /** * * @type {string} * @memberof UserStudentCourseDto */ 'certificateId': string; /** * * @type {string} * @memberof UserStudentCourseDto */ 'mentorGithubId': string; /** * * @type {string} * @memberof UserStudentCourseDto */ 'mentorFullName': string; /** * * @type {number} * @memberof UserStudentCourseDto */ 'totalScore': number; /** * * @type {number} * @memberof UserStudentCourseDto */ 'rank': number; } /** * * @export * @interface UserStudentDto */ export interface UserStudentDto { /** * User id * @type {number} * @memberof UserStudentDto */ 'id': number; /** * User github id * @type {string} * @memberof UserStudentDto */ 'githubId': string; /** * User full name * @type {string} * @memberof UserStudentDto */ 'fullName': string; /** * User country * @type {object} * @memberof UserStudentDto */ 'country': object; /** * User city * @type {object} * @memberof UserStudentDto */ 'city': object; /** * User email * @type {string} * @memberof UserStudentDto */ 'contactsEmail': string; /** * User telegram * @type {string} * @memberof UserStudentDto */ 'contactsTelegram': string; /** * User linkedIn * @type {string} * @memberof UserStudentDto */ 'contactsLinkedIn': string; /** * User skype * @type {string} * @memberof UserStudentDto */ 'contactsSkype': string; /** * User phone * @type {string} * @memberof UserStudentDto */ 'contactsPhone': string; /** * User discord * @type {Discord} * @memberof UserStudentDto */ 'discord': Discord; /** * User on going courses * @type {Array} * @memberof UserStudentDto */ 'onGoingCourses': Array; /** * User previous courses * @type {Array} * @memberof UserStudentDto */ 'previousCourses': Array; /** * User languages * @type {Array} * @memberof UserStudentDto */ 'languages': Array; } /** * * @export * @interface UserStudentsDto */ export interface UserStudentsDto { /** * * @type {Array} * @memberof UserStudentsDto */ 'content': Array; /** * * @type {PaginationMetaDto} * @memberof UserStudentsDto */ 'pagination': PaginationMetaDto; } /** * * @export * @interface Validations */ export interface Validations { /** * * @type {boolean} * @memberof Validations */ 'githubIdInUrl': boolean; /** * * @type {boolean} * @memberof Validations */ 'githubPrInUrl': boolean; } /** * * @export * @interface VisibilityDto */ export interface VisibilityDto { /** * * @type {boolean} * @memberof VisibilityDto */ 'isHidden': boolean; } /** * ActivityApi - axios parameter creator * @export */ export const ActivityApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {CreateActivityDto} createActivityDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createActivity: async (createActivityDto: CreateActivityDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'createActivityDto' is not null or undefined assertParamExists('createActivity', 'createActivityDto', createActivityDto) const localVarPath = `/activity`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(createActivityDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {CreateActivityWebhookDto} createActivityWebhookDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createActivityWebhook: async (createActivityWebhookDto: CreateActivityWebhookDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'createActivityWebhookDto' is not null or undefined assertParamExists('createActivityWebhook', 'createActivityWebhookDto', createActivityWebhookDto) const localVarPath = `/activity/webhook`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(createActivityWebhookDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getActivity: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/activity`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * ActivityApi - functional programming interface * @export */ export const ActivityApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = ActivityApiAxiosParamCreator(configuration) return { /** * * @param {CreateActivityDto} createActivityDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createActivity(createActivityDto: CreateActivityDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.createActivity(createActivityDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {CreateActivityWebhookDto} createActivityWebhookDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createActivityWebhook(createActivityWebhookDto: CreateActivityWebhookDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.createActivityWebhook(createActivityWebhookDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getActivity(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getActivity(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * ActivityApi - factory interface * @export */ export const ActivityApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = ActivityApiFp(configuration) return { /** * * @param {CreateActivityDto} createActivityDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createActivity(createActivityDto: CreateActivityDto, options?: any): AxiosPromise { return localVarFp.createActivity(createActivityDto, options).then((request) => request(axios, basePath)); }, /** * * @param {CreateActivityWebhookDto} createActivityWebhookDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createActivityWebhook(createActivityWebhookDto: CreateActivityWebhookDto, options?: any): AxiosPromise { return localVarFp.createActivityWebhook(createActivityWebhookDto, options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getActivity(options?: any): AxiosPromise { return localVarFp.getActivity(options).then((request) => request(axios, basePath)); }, }; }; /** * ActivityApi - object-oriented interface * @export * @class ActivityApi * @extends {BaseAPI} */ export class ActivityApi extends BaseAPI { /** * * @param {CreateActivityDto} createActivityDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof ActivityApi */ public createActivity(createActivityDto: CreateActivityDto, options?: AxiosRequestConfig) { return ActivityApiFp(this.configuration).createActivity(createActivityDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {CreateActivityWebhookDto} createActivityWebhookDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof ActivityApi */ public createActivityWebhook(createActivityWebhookDto: CreateActivityWebhookDto, options?: AxiosRequestConfig) { return ActivityApiFp(this.configuration).createActivityWebhook(createActivityWebhookDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof ActivityApi */ public getActivity(options?: AxiosRequestConfig) { return ActivityApiFp(this.configuration).getActivity(options).then((request) => request(this.axios, this.basePath)); } } /** * AlertsApi - axios parameter creator * @export */ export const AlertsApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {CreateAlertDto} createAlertDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createAlert: async (createAlertDto: CreateAlertDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'createAlertDto' is not null or undefined assertParamExists('createAlert', 'createAlertDto', createAlertDto) const localVarPath = `/alerts`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(createAlertDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteAlert: async (id: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined assertParamExists('deleteAlert', 'id', id) const localVarPath = `/alerts/{id}` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {boolean} enabled * @param {*} [options] Override http request option. * @throws {RequiredError} */ getAlerts: async (enabled: boolean, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'enabled' is not null or undefined assertParamExists('getAlerts', 'enabled', enabled) const localVarPath = `/alerts`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; if (enabled !== undefined) { localVarQueryParameter['enabled'] = enabled; } setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} id * @param {object} body * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateAlert: async (id: number, body: object, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined assertParamExists('updateAlert', 'id', id) // verify required parameter 'body' is not null or undefined assertParamExists('updateAlert', 'body', body) const localVarPath = `/alerts/{id}` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PATCH', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(body, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * AlertsApi - functional programming interface * @export */ export const AlertsApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = AlertsApiAxiosParamCreator(configuration) return { /** * * @param {CreateAlertDto} createAlertDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createAlert(createAlertDto: CreateAlertDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.createAlert(createAlertDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async deleteAlert(id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.deleteAlert(id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {boolean} enabled * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getAlerts(enabled: boolean, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getAlerts(enabled, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} id * @param {object} body * @param {*} [options] Override http request option. * @throws {RequiredError} */ async updateAlert(id: number, body: object, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.updateAlert(id, body, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * AlertsApi - factory interface * @export */ export const AlertsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = AlertsApiFp(configuration) return { /** * * @param {CreateAlertDto} createAlertDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createAlert(createAlertDto: CreateAlertDto, options?: any): AxiosPromise { return localVarFp.createAlert(createAlertDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteAlert(id: number, options?: any): AxiosPromise { return localVarFp.deleteAlert(id, options).then((request) => request(axios, basePath)); }, /** * * @param {boolean} enabled * @param {*} [options] Override http request option. * @throws {RequiredError} */ getAlerts(enabled: boolean, options?: any): AxiosPromise> { return localVarFp.getAlerts(enabled, options).then((request) => request(axios, basePath)); }, /** * * @param {number} id * @param {object} body * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateAlert(id: number, body: object, options?: any): AxiosPromise { return localVarFp.updateAlert(id, body, options).then((request) => request(axios, basePath)); }, }; }; /** * AlertsApi - object-oriented interface * @export * @class AlertsApi * @extends {BaseAPI} */ export class AlertsApi extends BaseAPI { /** * * @param {CreateAlertDto} createAlertDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof AlertsApi */ public createAlert(createAlertDto: CreateAlertDto, options?: AxiosRequestConfig) { return AlertsApiFp(this.configuration).createAlert(createAlertDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof AlertsApi */ public deleteAlert(id: number, options?: AxiosRequestConfig) { return AlertsApiFp(this.configuration).deleteAlert(id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {boolean} enabled * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof AlertsApi */ public getAlerts(enabled: boolean, options?: AxiosRequestConfig) { return AlertsApiFp(this.configuration).getAlerts(enabled, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} id * @param {object} body * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof AlertsApi */ public updateAlert(id: number, body: object, options?: AxiosRequestConfig) { return AlertsApiFp(this.configuration).updateAlert(id, body, options).then((request) => request(this.axios, this.basePath)); } } /** * AuthApi - axios parameter creator * @export */ export const AuthApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {AuthConnectionDto} authConnectionDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ authControllerCreateConnectLinkViaGithub: async (authConnectionDto: AuthConnectionDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'authConnectionDto' is not null or undefined assertParamExists('authControllerCreateConnectLinkViaGithub', 'authConnectionDto', authConnectionDto) const localVarPath = `/auth/github/connect`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(authConnectionDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} userId * @param {*} [options] Override http request option. * @throws {RequiredError} */ clearAuthUserSessionCache: async (userId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'userId' is not null or undefined assertParamExists('clearAuthUserSessionCache', 'userId', userId) const localVarPath = `/auth/cache/{userId}` .replace(`{${"userId"}}`, encodeURIComponent(String(userId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ githubCallback: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/auth/github/callback`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ githubLogin: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/auth/github/login`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ githubLogout: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/auth/github/logout`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * AuthApi - functional programming interface * @export */ export const AuthApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = AuthApiAxiosParamCreator(configuration) return { /** * * @param {AuthConnectionDto} authConnectionDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async authControllerCreateConnectLinkViaGithub(authConnectionDto: AuthConnectionDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.authControllerCreateConnectLinkViaGithub(authConnectionDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} userId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async clearAuthUserSessionCache(userId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.clearAuthUserSessionCache(userId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async githubCallback(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.githubCallback(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async githubLogin(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.githubLogin(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async githubLogout(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.githubLogout(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * AuthApi - factory interface * @export */ export const AuthApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = AuthApiFp(configuration) return { /** * * @param {AuthConnectionDto} authConnectionDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ authControllerCreateConnectLinkViaGithub(authConnectionDto: AuthConnectionDto, options?: any): AxiosPromise { return localVarFp.authControllerCreateConnectLinkViaGithub(authConnectionDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} userId * @param {*} [options] Override http request option. * @throws {RequiredError} */ clearAuthUserSessionCache(userId: number, options?: any): AxiosPromise { return localVarFp.clearAuthUserSessionCache(userId, options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ githubCallback(options?: any): AxiosPromise { return localVarFp.githubCallback(options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ githubLogin(options?: any): AxiosPromise { return localVarFp.githubLogin(options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ githubLogout(options?: any): AxiosPromise { return localVarFp.githubLogout(options).then((request) => request(axios, basePath)); }, }; }; /** * AuthApi - object-oriented interface * @export * @class AuthApi * @extends {BaseAPI} */ export class AuthApi extends BaseAPI { /** * * @param {AuthConnectionDto} authConnectionDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof AuthApi */ public authControllerCreateConnectLinkViaGithub(authConnectionDto: AuthConnectionDto, options?: AxiosRequestConfig) { return AuthApiFp(this.configuration).authControllerCreateConnectLinkViaGithub(authConnectionDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} userId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof AuthApi */ public clearAuthUserSessionCache(userId: number, options?: AxiosRequestConfig) { return AuthApiFp(this.configuration).clearAuthUserSessionCache(userId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof AuthApi */ public githubCallback(options?: AxiosRequestConfig) { return AuthApiFp(this.configuration).githubCallback(options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof AuthApi */ public githubLogin(options?: AxiosRequestConfig) { return AuthApiFp(this.configuration).githubLogin(options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof AuthApi */ public githubLogout(options?: AxiosRequestConfig) { return AuthApiFp(this.configuration).githubLogout(options).then((request) => request(this.axios, this.basePath)); } } /** * AutoTestsApi - axios parameter creator * @export */ export const AutoTestsApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ getAutoTest: async (id: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined assertParamExists('getAutoTest', 'id', id) const localVarPath = `/auto-test/{id}` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getBasicAutoTests: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/auto-test`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * AutoTestsApi - functional programming interface * @export */ export const AutoTestsApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = AutoTestsApiAxiosParamCreator(configuration) return { /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getAutoTest(id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getAutoTest(id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getBasicAutoTests(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getBasicAutoTests(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * AutoTestsApi - factory interface * @export */ export const AutoTestsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = AutoTestsApiFp(configuration) return { /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ getAutoTest(id: number, options?: any): AxiosPromise { return localVarFp.getAutoTest(id, options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getBasicAutoTests(options?: any): AxiosPromise> { return localVarFp.getBasicAutoTests(options).then((request) => request(axios, basePath)); }, }; }; /** * AutoTestsApi - object-oriented interface * @export * @class AutoTestsApi * @extends {BaseAPI} */ export class AutoTestsApi extends BaseAPI { /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof AutoTestsApi */ public getAutoTest(id: number, options?: AxiosRequestConfig) { return AutoTestsApiFp(this.configuration).getAutoTest(id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof AutoTestsApi */ public getBasicAutoTests(options?: AxiosRequestConfig) { return AutoTestsApiFp(this.configuration).getBasicAutoTests(options).then((request) => request(this.axios, this.basePath)); } } /** * CertificateApi - axios parameter creator * @export */ export const CertificateApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {string} publicId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCertificate: async (publicId: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'publicId' is not null or undefined assertParamExists('getCertificate', 'publicId', publicId) const localVarPath = `/certificate/{publicId}` .replace(`{${"publicId"}}`, encodeURIComponent(String(publicId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} studentId * @param {*} [options] Override http request option. * @throws {RequiredError} */ removeCertificate: async (studentId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'studentId' is not null or undefined assertParamExists('removeCertificate', 'studentId', studentId) const localVarPath = `/certificate/{studentId}` .replace(`{${"studentId"}}`, encodeURIComponent(String(studentId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {SaveCertificateDto} saveCertificateDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ saveCertificate: async (saveCertificateDto: SaveCertificateDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'saveCertificateDto' is not null or undefined assertParamExists('saveCertificate', 'saveCertificateDto', saveCertificateDto) const localVarPath = `/certificate`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(saveCertificateDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * CertificateApi - functional programming interface * @export */ export const CertificateApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = CertificateApiAxiosParamCreator(configuration) return { /** * * @param {string} publicId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getCertificate(publicId: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getCertificate(publicId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} studentId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async removeCertificate(studentId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.removeCertificate(studentId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {SaveCertificateDto} saveCertificateDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async saveCertificate(saveCertificateDto: SaveCertificateDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.saveCertificate(saveCertificateDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * CertificateApi - factory interface * @export */ export const CertificateApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = CertificateApiFp(configuration) return { /** * * @param {string} publicId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCertificate(publicId: string, options?: any): AxiosPromise { return localVarFp.getCertificate(publicId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} studentId * @param {*} [options] Override http request option. * @throws {RequiredError} */ removeCertificate(studentId: number, options?: any): AxiosPromise { return localVarFp.removeCertificate(studentId, options).then((request) => request(axios, basePath)); }, /** * * @param {SaveCertificateDto} saveCertificateDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ saveCertificate(saveCertificateDto: SaveCertificateDto, options?: any): AxiosPromise { return localVarFp.saveCertificate(saveCertificateDto, options).then((request) => request(axios, basePath)); }, }; }; /** * CertificateApi - object-oriented interface * @export * @class CertificateApi * @extends {BaseAPI} */ export class CertificateApi extends BaseAPI { /** * * @param {string} publicId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CertificateApi */ public getCertificate(publicId: string, options?: AxiosRequestConfig) { return CertificateApiFp(this.configuration).getCertificate(publicId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} studentId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CertificateApi */ public removeCertificate(studentId: number, options?: AxiosRequestConfig) { return CertificateApiFp(this.configuration).removeCertificate(studentId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {SaveCertificateDto} saveCertificateDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CertificateApi */ public saveCertificate(saveCertificateDto: SaveCertificateDto, options?: AxiosRequestConfig) { return CertificateApiFp(this.configuration).saveCertificate(saveCertificateDto, options).then((request) => request(this.axios, this.basePath)); } } /** * ContributorsApi - axios parameter creator * @export */ export const ContributorsApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {CreateContributorDto} createContributorDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createContributor: async (createContributorDto: CreateContributorDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'createContributorDto' is not null or undefined assertParamExists('createContributor', 'createContributorDto', createContributorDto) const localVarPath = `/contributors`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(createContributorDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteContributor: async (id: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined assertParamExists('deleteContributor', 'id', id) const localVarPath = `/contributors/{id}` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ getContributor: async (id: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined assertParamExists('getContributor', 'id', id) const localVarPath = `/contributors/{id}` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getContributors: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/contributors`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} id * @param {UpdateContributorDto} updateContributorDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateContributor: async (id: number, updateContributorDto: UpdateContributorDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined assertParamExists('updateContributor', 'id', id) // verify required parameter 'updateContributorDto' is not null or undefined assertParamExists('updateContributor', 'updateContributorDto', updateContributorDto) const localVarPath = `/contributors/{id}` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PATCH', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(updateContributorDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * ContributorsApi - functional programming interface * @export */ export const ContributorsApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = ContributorsApiAxiosParamCreator(configuration) return { /** * * @param {CreateContributorDto} createContributorDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createContributor(createContributorDto: CreateContributorDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.createContributor(createContributorDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async deleteContributor(id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.deleteContributor(id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getContributor(id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getContributor(id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getContributors(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getContributors(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} id * @param {UpdateContributorDto} updateContributorDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async updateContributor(id: number, updateContributorDto: UpdateContributorDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.updateContributor(id, updateContributorDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * ContributorsApi - factory interface * @export */ export const ContributorsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = ContributorsApiFp(configuration) return { /** * * @param {CreateContributorDto} createContributorDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createContributor(createContributorDto: CreateContributorDto, options?: any): AxiosPromise { return localVarFp.createContributor(createContributorDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteContributor(id: number, options?: any): AxiosPromise { return localVarFp.deleteContributor(id, options).then((request) => request(axios, basePath)); }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ getContributor(id: number, options?: any): AxiosPromise { return localVarFp.getContributor(id, options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getContributors(options?: any): AxiosPromise> { return localVarFp.getContributors(options).then((request) => request(axios, basePath)); }, /** * * @param {number} id * @param {UpdateContributorDto} updateContributorDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateContributor(id: number, updateContributorDto: UpdateContributorDto, options?: any): AxiosPromise { return localVarFp.updateContributor(id, updateContributorDto, options).then((request) => request(axios, basePath)); }, }; }; /** * ContributorsApi - object-oriented interface * @export * @class ContributorsApi * @extends {BaseAPI} */ export class ContributorsApi extends BaseAPI { /** * * @param {CreateContributorDto} createContributorDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof ContributorsApi */ public createContributor(createContributorDto: CreateContributorDto, options?: AxiosRequestConfig) { return ContributorsApiFp(this.configuration).createContributor(createContributorDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof ContributorsApi */ public deleteContributor(id: number, options?: AxiosRequestConfig) { return ContributorsApiFp(this.configuration).deleteContributor(id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof ContributorsApi */ public getContributor(id: number, options?: AxiosRequestConfig) { return ContributorsApiFp(this.configuration).getContributor(id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof ContributorsApi */ public getContributors(options?: AxiosRequestConfig) { return ContributorsApiFp(this.configuration).getContributors(options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} id * @param {UpdateContributorDto} updateContributorDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof ContributorsApi */ public updateContributor(id: number, updateContributorDto: UpdateContributorDto, options?: AxiosRequestConfig) { return ContributorsApiFp(this.configuration).updateContributor(id, updateContributorDto, options).then((request) => request(this.axios, this.basePath)); } } /** * CourseMentorsApi - axios parameter creator * @export */ export const CourseMentorsApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getMentorsDetails: async (courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getMentorsDetails', 'courseId', courseId) const localVarPath = `/course/{courseId}/mentors/details` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getMentorsDetailsCsv: async (courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getMentorsDetailsCsv', 'courseId', courseId) const localVarPath = `/course/{courseId}/mentors/details/csv` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {string} searchText * @param {*} [options] Override http request option. * @throws {RequiredError} */ searchMentors: async (courseId: number, searchText: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('searchMentors', 'courseId', courseId) // verify required parameter 'searchText' is not null or undefined assertParamExists('searchMentors', 'searchText', searchText) const localVarPath = `/course/{courseId}/mentors/search/{searchText}` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"searchText"}}`, encodeURIComponent(String(searchText))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * CourseMentorsApi - functional programming interface * @export */ export const CourseMentorsApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = CourseMentorsApiAxiosParamCreator(configuration) return { /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getMentorsDetails(courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getMentorsDetails(courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getMentorsDetailsCsv(courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getMentorsDetailsCsv(courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {string} searchText * @param {*} [options] Override http request option. * @throws {RequiredError} */ async searchMentors(courseId: number, searchText: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.searchMentors(courseId, searchText, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * CourseMentorsApi - factory interface * @export */ export const CourseMentorsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = CourseMentorsApiFp(configuration) return { /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getMentorsDetails(courseId: number, options?: any): AxiosPromise> { return localVarFp.getMentorsDetails(courseId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getMentorsDetailsCsv(courseId: number, options?: any): AxiosPromise { return localVarFp.getMentorsDetailsCsv(courseId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {string} searchText * @param {*} [options] Override http request option. * @throws {RequiredError} */ searchMentors(courseId: number, searchText: string, options?: any): AxiosPromise> { return localVarFp.searchMentors(courseId, searchText, options).then((request) => request(axios, basePath)); }, }; }; /** * CourseMentorsApi - object-oriented interface * @export * @class CourseMentorsApi * @extends {BaseAPI} */ export class CourseMentorsApi extends BaseAPI { /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CourseMentorsApi */ public getMentorsDetails(courseId: number, options?: AxiosRequestConfig) { return CourseMentorsApiFp(this.configuration).getMentorsDetails(courseId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CourseMentorsApi */ public getMentorsDetailsCsv(courseId: number, options?: AxiosRequestConfig) { return CourseMentorsApiFp(this.configuration).getMentorsDetailsCsv(courseId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {string} searchText * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CourseMentorsApi */ public searchMentors(courseId: number, searchText: string, options?: AxiosRequestConfig) { return CourseMentorsApiFp(this.configuration).searchMentors(courseId, searchText, options).then((request) => request(this.axios, this.basePath)); } } /** * CourseStatsApi - axios parameter creator * @export */ export const CourseStatsApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {string} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteExpelledStat: async (id: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined assertParamExists('deleteExpelledStat', 'id', id) const localVarPath = `/courses/stats/expelled/{id}` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseExpelledStats: async (courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getCourseExpelledStats', 'courseId', courseId) const localVarPath = `/courses/{courseId}/stats/expelled` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseMentorCountries: async (courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getCourseMentorCountries', 'courseId', courseId) const localVarPath = `/courses/{courseId}/stats/mentors/countries` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseMentors: async (courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getCourseMentors', 'courseId', courseId) const localVarPath = `/courses/{courseId}/stats/mentors` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseStats: async (courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getCourseStats', 'courseId', courseId) const localVarPath = `/courses/{courseId}/stats` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseStudentCertificatesCountries: async (courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getCourseStudentCertificatesCountries', 'courseId', courseId) const localVarPath = `/courses/{courseId}/stats/students/certificates/countries` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseStudentCountries: async (courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getCourseStudentCountries', 'courseId', courseId) const localVarPath = `/courses/{courseId}/stats/students/countries` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {Array} ids List of course IDs * @param {number} year Year for which stats are fetched * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCoursesStats: async (ids: Array, year: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'ids' is not null or undefined assertParamExists('getCoursesStats', 'ids', ids) // verify required parameter 'year' is not null or undefined assertParamExists('getCoursesStats', 'year', year) const localVarPath = `/courses/aggregate/stats`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; if (ids) { localVarQueryParameter['ids'] = ids; } if (year !== undefined) { localVarQueryParameter['year'] = year; } setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getExpelledStats: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/courses/stats/expelled`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} taskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getTaskPerformance: async (courseId: number, taskId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getTaskPerformance', 'courseId', courseId) // verify required parameter 'taskId' is not null or undefined assertParamExists('getTaskPerformance', 'taskId', taskId) const localVarPath = `/courses/{courseId}/stats/task/{taskId}/performance` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"taskId"}}`, encodeURIComponent(String(taskId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * CourseStatsApi - functional programming interface * @export */ export const CourseStatsApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = CourseStatsApiAxiosParamCreator(configuration) return { /** * * @param {string} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async deleteExpelledStat(id: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.deleteExpelledStat(id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getCourseExpelledStats(courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getCourseExpelledStats(courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getCourseMentorCountries(courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getCourseMentorCountries(courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getCourseMentors(courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getCourseMentors(courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getCourseStats(courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getCourseStats(courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getCourseStudentCertificatesCountries(courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getCourseStudentCertificatesCountries(courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getCourseStudentCountries(courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getCourseStudentCountries(courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {Array} ids List of course IDs * @param {number} year Year for which stats are fetched * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getCoursesStats(ids: Array, year: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getCoursesStats(ids, year, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getExpelledStats(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getExpelledStats(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} taskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getTaskPerformance(courseId: number, taskId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getTaskPerformance(courseId, taskId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * CourseStatsApi - factory interface * @export */ export const CourseStatsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = CourseStatsApiFp(configuration) return { /** * * @param {string} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteExpelledStat(id: string, options?: any): AxiosPromise { return localVarFp.deleteExpelledStat(id, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseExpelledStats(courseId: number, options?: any): AxiosPromise> { return localVarFp.getCourseExpelledStats(courseId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseMentorCountries(courseId: number, options?: any): AxiosPromise { return localVarFp.getCourseMentorCountries(courseId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseMentors(courseId: number, options?: any): AxiosPromise { return localVarFp.getCourseMentors(courseId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseStats(courseId: number, options?: any): AxiosPromise { return localVarFp.getCourseStats(courseId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseStudentCertificatesCountries(courseId: number, options?: any): AxiosPromise { return localVarFp.getCourseStudentCertificatesCountries(courseId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseStudentCountries(courseId: number, options?: any): AxiosPromise { return localVarFp.getCourseStudentCountries(courseId, options).then((request) => request(axios, basePath)); }, /** * * @param {Array} ids List of course IDs * @param {number} year Year for which stats are fetched * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCoursesStats(ids: Array, year: number, options?: any): AxiosPromise { return localVarFp.getCoursesStats(ids, year, options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getExpelledStats(options?: any): AxiosPromise> { return localVarFp.getExpelledStats(options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} taskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getTaskPerformance(courseId: number, taskId: number, options?: any): AxiosPromise { return localVarFp.getTaskPerformance(courseId, taskId, options).then((request) => request(axios, basePath)); }, }; }; /** * CourseStatsApi - object-oriented interface * @export * @class CourseStatsApi * @extends {BaseAPI} */ export class CourseStatsApi extends BaseAPI { /** * * @param {string} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CourseStatsApi */ public deleteExpelledStat(id: string, options?: AxiosRequestConfig) { return CourseStatsApiFp(this.configuration).deleteExpelledStat(id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CourseStatsApi */ public getCourseExpelledStats(courseId: number, options?: AxiosRequestConfig) { return CourseStatsApiFp(this.configuration).getCourseExpelledStats(courseId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CourseStatsApi */ public getCourseMentorCountries(courseId: number, options?: AxiosRequestConfig) { return CourseStatsApiFp(this.configuration).getCourseMentorCountries(courseId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CourseStatsApi */ public getCourseMentors(courseId: number, options?: AxiosRequestConfig) { return CourseStatsApiFp(this.configuration).getCourseMentors(courseId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CourseStatsApi */ public getCourseStats(courseId: number, options?: AxiosRequestConfig) { return CourseStatsApiFp(this.configuration).getCourseStats(courseId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CourseStatsApi */ public getCourseStudentCertificatesCountries(courseId: number, options?: AxiosRequestConfig) { return CourseStatsApiFp(this.configuration).getCourseStudentCertificatesCountries(courseId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CourseStatsApi */ public getCourseStudentCountries(courseId: number, options?: AxiosRequestConfig) { return CourseStatsApiFp(this.configuration).getCourseStudentCountries(courseId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {Array} ids List of course IDs * @param {number} year Year for which stats are fetched * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CourseStatsApi */ public getCoursesStats(ids: Array, year: number, options?: AxiosRequestConfig) { return CourseStatsApiFp(this.configuration).getCoursesStats(ids, year, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CourseStatsApi */ public getExpelledStats(options?: AxiosRequestConfig) { return CourseStatsApiFp(this.configuration).getExpelledStats(options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} taskId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CourseStatsApi */ public getTaskPerformance(courseId: number, taskId: number, options?: AxiosRequestConfig) { return CourseStatsApiFp(this.configuration).getTaskPerformance(courseId, taskId, options).then((request) => request(this.axios, this.basePath)); } } /** * CourseTaskVerificationsApi - axios parameter creator * @export */ export const CourseTaskVerificationsApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {number} courseId * @param {number} courseTaskId * @param {object} body * @param {*} [options] Override http request option. * @throws {RequiredError} */ createTaskVerification: async (courseId: number, courseTaskId: number, body: object, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('createTaskVerification', 'courseId', courseId) // verify required parameter 'courseTaskId' is not null or undefined assertParamExists('createTaskVerification', 'courseTaskId', courseTaskId) // verify required parameter 'body' is not null or undefined assertParamExists('createTaskVerification', 'body', body) const localVarPath = `/courses/{courseId}/tasks/{courseTaskId}/verifications` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"courseTaskId"}}`, encodeURIComponent(String(courseTaskId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(body, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} courseTaskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getAnswers: async (courseId: number, courseTaskId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getAnswers', 'courseId', courseId) // verify required parameter 'courseTaskId' is not null or undefined assertParamExists('getAnswers', 'courseTaskId', courseTaskId) const localVarPath = `/courses/{courseId}/tasks/{courseTaskId}/verifications/answers` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"courseTaskId"}}`, encodeURIComponent(String(courseTaskId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * CourseTaskVerificationsApi - functional programming interface * @export */ export const CourseTaskVerificationsApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = CourseTaskVerificationsApiAxiosParamCreator(configuration) return { /** * * @param {number} courseId * @param {number} courseTaskId * @param {object} body * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createTaskVerification(courseId: number, courseTaskId: number, body: object, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.createTaskVerification(courseId, courseTaskId, body, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} courseTaskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getAnswers(courseId: number, courseTaskId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getAnswers(courseId, courseTaskId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * CourseTaskVerificationsApi - factory interface * @export */ export const CourseTaskVerificationsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = CourseTaskVerificationsApiFp(configuration) return { /** * * @param {number} courseId * @param {number} courseTaskId * @param {object} body * @param {*} [options] Override http request option. * @throws {RequiredError} */ createTaskVerification(courseId: number, courseTaskId: number, body: object, options?: any): AxiosPromise { return localVarFp.createTaskVerification(courseId, courseTaskId, body, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} courseTaskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getAnswers(courseId: number, courseTaskId: number, options?: any): AxiosPromise> { return localVarFp.getAnswers(courseId, courseTaskId, options).then((request) => request(axios, basePath)); }, }; }; /** * CourseTaskVerificationsApi - object-oriented interface * @export * @class CourseTaskVerificationsApi * @extends {BaseAPI} */ export class CourseTaskVerificationsApi extends BaseAPI { /** * * @param {number} courseId * @param {number} courseTaskId * @param {object} body * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CourseTaskVerificationsApi */ public createTaskVerification(courseId: number, courseTaskId: number, body: object, options?: AxiosRequestConfig) { return CourseTaskVerificationsApiFp(this.configuration).createTaskVerification(courseId, courseTaskId, body, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} courseTaskId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CourseTaskVerificationsApi */ public getAnswers(courseId: number, courseTaskId: number, options?: AxiosRequestConfig) { return CourseTaskVerificationsApiFp(this.configuration).getAnswers(courseId, courseTaskId, options).then((request) => request(this.axios, this.basePath)); } } /** * CourseUsersApi - axios parameter creator * @export */ export const CourseUsersApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseUsers: async (courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getCourseUsers', 'courseId', courseId) const localVarPath = `/courses/{courseId}/users` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {string} githubId * @param {CourseRolesDto} courseRolesDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ putCourseUser: async (courseId: number, githubId: string, courseRolesDto: CourseRolesDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('putCourseUser', 'courseId', courseId) // verify required parameter 'githubId' is not null or undefined assertParamExists('putCourseUser', 'githubId', githubId) // verify required parameter 'courseRolesDto' is not null or undefined assertParamExists('putCourseUser', 'courseRolesDto', courseRolesDto) const localVarPath = `/courses/{courseId}/users/{githubId}` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"githubId"}}`, encodeURIComponent(String(githubId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(courseRolesDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {Array} updateCourseUserDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ putCourseUsers: async (courseId: number, updateCourseUserDto: Array, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('putCourseUsers', 'courseId', courseId) // verify required parameter 'updateCourseUserDto' is not null or undefined assertParamExists('putCourseUsers', 'updateCourseUserDto', updateCourseUserDto) const localVarPath = `/courses/{courseId}/users` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(updateCourseUserDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * CourseUsersApi - functional programming interface * @export */ export const CourseUsersApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = CourseUsersApiAxiosParamCreator(configuration) return { /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getCourseUsers(courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getCourseUsers(courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {string} githubId * @param {CourseRolesDto} courseRolesDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async putCourseUser(courseId: number, githubId: string, courseRolesDto: CourseRolesDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.putCourseUser(courseId, githubId, courseRolesDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {Array} updateCourseUserDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async putCourseUsers(courseId: number, updateCourseUserDto: Array, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.putCourseUsers(courseId, updateCourseUserDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * CourseUsersApi - factory interface * @export */ export const CourseUsersApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = CourseUsersApiFp(configuration) return { /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseUsers(courseId: number, options?: any): AxiosPromise> { return localVarFp.getCourseUsers(courseId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {string} githubId * @param {CourseRolesDto} courseRolesDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ putCourseUser(courseId: number, githubId: string, courseRolesDto: CourseRolesDto, options?: any): AxiosPromise { return localVarFp.putCourseUser(courseId, githubId, courseRolesDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {Array} updateCourseUserDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ putCourseUsers(courseId: number, updateCourseUserDto: Array, options?: any): AxiosPromise { return localVarFp.putCourseUsers(courseId, updateCourseUserDto, options).then((request) => request(axios, basePath)); }, }; }; /** * CourseUsersApi - object-oriented interface * @export * @class CourseUsersApi * @extends {BaseAPI} */ export class CourseUsersApi extends BaseAPI { /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CourseUsersApi */ public getCourseUsers(courseId: number, options?: AxiosRequestConfig) { return CourseUsersApiFp(this.configuration).getCourseUsers(courseId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {string} githubId * @param {CourseRolesDto} courseRolesDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CourseUsersApi */ public putCourseUser(courseId: number, githubId: string, courseRolesDto: CourseRolesDto, options?: AxiosRequestConfig) { return CourseUsersApiFp(this.configuration).putCourseUser(courseId, githubId, courseRolesDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {Array} updateCourseUserDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CourseUsersApi */ public putCourseUsers(courseId: number, updateCourseUserDto: Array, options?: AxiosRequestConfig) { return CourseUsersApiFp(this.configuration).putCourseUsers(courseId, updateCourseUserDto, options).then((request) => request(this.axios, this.basePath)); } } /** * CoursesApi - axios parameter creator * @export */ export const CoursesApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {number} courseId * @param {CreateCourseDto} createCourseDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ copyCourse: async (courseId: number, createCourseDto: CreateCourseDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('copyCourse', 'courseId', courseId) // verify required parameter 'createCourseDto' is not null or undefined assertParamExists('copyCourse', 'createCourseDto', createCourseDto) const localVarPath = `/courses/{courseId}/copy` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(createCourseDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {CreateCourseDto} createCourseDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createCourse: async (createCourseDto: CreateCourseDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'createCourseDto' is not null or undefined assertParamExists('createCourse', 'createCourseDto', createCourseDto) const localVarPath = `/courses`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(createCourseDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourse: async (courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getCourse', 'courseId', courseId) const localVarPath = `/courses/{courseId}` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourses: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/courses`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {LeaveCourseRequestDto} [leaveCourseRequestDto] * @param {*} [options] Override http request option. * @throws {RequiredError} */ leaveCourse: async (courseId: number, leaveCourseRequestDto?: LeaveCourseRequestDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('leaveCourse', 'courseId', courseId) const localVarPath = `/courses/{courseId}/leave` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(leaveCourseRequestDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ rejoinCourse: async (courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('rejoinCourse', 'courseId', courseId) const localVarPath = `/courses/{courseId}/rejoin` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {UpdateCourseDto} updateCourseDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateCourse: async (courseId: number, updateCourseDto: UpdateCourseDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('updateCourse', 'courseId', courseId) // verify required parameter 'updateCourseDto' is not null or undefined assertParamExists('updateCourse', 'updateCourseDto', updateCourseDto) const localVarPath = `/courses/{courseId}` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(updateCourseDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * CoursesApi - functional programming interface * @export */ export const CoursesApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = CoursesApiAxiosParamCreator(configuration) return { /** * * @param {number} courseId * @param {CreateCourseDto} createCourseDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async copyCourse(courseId: number, createCourseDto: CreateCourseDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.copyCourse(courseId, createCourseDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {CreateCourseDto} createCourseDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createCourse(createCourseDto: CreateCourseDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.createCourse(createCourseDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getCourse(courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getCourse(courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getCourses(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getCourses(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {LeaveCourseRequestDto} [leaveCourseRequestDto] * @param {*} [options] Override http request option. * @throws {RequiredError} */ async leaveCourse(courseId: number, leaveCourseRequestDto?: LeaveCourseRequestDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.leaveCourse(courseId, leaveCourseRequestDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async rejoinCourse(courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.rejoinCourse(courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {UpdateCourseDto} updateCourseDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async updateCourse(courseId: number, updateCourseDto: UpdateCourseDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.updateCourse(courseId, updateCourseDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * CoursesApi - factory interface * @export */ export const CoursesApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = CoursesApiFp(configuration) return { /** * * @param {number} courseId * @param {CreateCourseDto} createCourseDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ copyCourse(courseId: number, createCourseDto: CreateCourseDto, options?: any): AxiosPromise { return localVarFp.copyCourse(courseId, createCourseDto, options).then((request) => request(axios, basePath)); }, /** * * @param {CreateCourseDto} createCourseDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createCourse(createCourseDto: CreateCourseDto, options?: any): AxiosPromise { return localVarFp.createCourse(createCourseDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourse(courseId: number, options?: any): AxiosPromise { return localVarFp.getCourse(courseId, options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourses(options?: any): AxiosPromise> { return localVarFp.getCourses(options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {LeaveCourseRequestDto} [leaveCourseRequestDto] * @param {*} [options] Override http request option. * @throws {RequiredError} */ leaveCourse(courseId: number, leaveCourseRequestDto?: LeaveCourseRequestDto, options?: any): AxiosPromise { return localVarFp.leaveCourse(courseId, leaveCourseRequestDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ rejoinCourse(courseId: number, options?: any): AxiosPromise { return localVarFp.rejoinCourse(courseId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {UpdateCourseDto} updateCourseDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateCourse(courseId: number, updateCourseDto: UpdateCourseDto, options?: any): AxiosPromise { return localVarFp.updateCourse(courseId, updateCourseDto, options).then((request) => request(axios, basePath)); }, }; }; /** * CoursesApi - object-oriented interface * @export * @class CoursesApi * @extends {BaseAPI} */ export class CoursesApi extends BaseAPI { /** * * @param {number} courseId * @param {CreateCourseDto} createCourseDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesApi */ public copyCourse(courseId: number, createCourseDto: CreateCourseDto, options?: AxiosRequestConfig) { return CoursesApiFp(this.configuration).copyCourse(courseId, createCourseDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {CreateCourseDto} createCourseDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesApi */ public createCourse(createCourseDto: CreateCourseDto, options?: AxiosRequestConfig) { return CoursesApiFp(this.configuration).createCourse(createCourseDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesApi */ public getCourse(courseId: number, options?: AxiosRequestConfig) { return CoursesApiFp(this.configuration).getCourse(courseId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesApi */ public getCourses(options?: AxiosRequestConfig) { return CoursesApiFp(this.configuration).getCourses(options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {LeaveCourseRequestDto} [leaveCourseRequestDto] * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesApi */ public leaveCourse(courseId: number, leaveCourseRequestDto?: LeaveCourseRequestDto, options?: AxiosRequestConfig) { return CoursesApiFp(this.configuration).leaveCourse(courseId, leaveCourseRequestDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesApi */ public rejoinCourse(courseId: number, options?: AxiosRequestConfig) { return CoursesApiFp(this.configuration).rejoinCourse(courseId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {UpdateCourseDto} updateCourseDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesApi */ public updateCourse(courseId: number, updateCourseDto: UpdateCourseDto, options?: AxiosRequestConfig) { return CoursesApiFp(this.configuration).updateCourse(courseId, updateCourseDto, options).then((request) => request(this.axios, this.basePath)); } } /** * CoursesEventsApi - axios parameter creator * @export */ export const CoursesEventsApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {number} courseId * @param {CreateCourseEventDto} createCourseEventDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createCourseEvent: async (courseId: number, createCourseEventDto: CreateCourseEventDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('createCourseEvent', 'courseId', courseId) // verify required parameter 'createCourseEventDto' is not null or undefined assertParamExists('createCourseEvent', 'createCourseEventDto', createCourseEventDto) const localVarPath = `/courses/{courseId}/events` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(createCourseEventDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseEventId * @param {any} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteCourseEvent: async (courseEventId: number, courseId: any, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseEventId' is not null or undefined assertParamExists('deleteCourseEvent', 'courseEventId', courseEventId) // verify required parameter 'courseId' is not null or undefined assertParamExists('deleteCourseEvent', 'courseId', courseId) const localVarPath = `/courses/{courseId}/events/{courseEventId}` .replace(`{${"courseEventId"}}`, encodeURIComponent(String(courseEventId))) .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} courseEventId * @param {UpdateCourseEventDto} updateCourseEventDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateCourseEvent: async (courseId: number, courseEventId: number, updateCourseEventDto: UpdateCourseEventDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('updateCourseEvent', 'courseId', courseId) // verify required parameter 'courseEventId' is not null or undefined assertParamExists('updateCourseEvent', 'courseEventId', courseEventId) // verify required parameter 'updateCourseEventDto' is not null or undefined assertParamExists('updateCourseEvent', 'updateCourseEventDto', updateCourseEventDto) const localVarPath = `/courses/{courseId}/events/{courseEventId}` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"courseEventId"}}`, encodeURIComponent(String(courseEventId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(updateCourseEventDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * CoursesEventsApi - functional programming interface * @export */ export const CoursesEventsApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = CoursesEventsApiAxiosParamCreator(configuration) return { /** * * @param {number} courseId * @param {CreateCourseEventDto} createCourseEventDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createCourseEvent(courseId: number, createCourseEventDto: CreateCourseEventDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.createCourseEvent(courseId, createCourseEventDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseEventId * @param {any} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async deleteCourseEvent(courseEventId: number, courseId: any, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.deleteCourseEvent(courseEventId, courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} courseEventId * @param {UpdateCourseEventDto} updateCourseEventDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async updateCourseEvent(courseId: number, courseEventId: number, updateCourseEventDto: UpdateCourseEventDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.updateCourseEvent(courseId, courseEventId, updateCourseEventDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * CoursesEventsApi - factory interface * @export */ export const CoursesEventsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = CoursesEventsApiFp(configuration) return { /** * * @param {number} courseId * @param {CreateCourseEventDto} createCourseEventDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createCourseEvent(courseId: number, createCourseEventDto: CreateCourseEventDto, options?: any): AxiosPromise> { return localVarFp.createCourseEvent(courseId, createCourseEventDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseEventId * @param {any} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteCourseEvent(courseEventId: number, courseId: any, options?: any): AxiosPromise { return localVarFp.deleteCourseEvent(courseEventId, courseId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} courseEventId * @param {UpdateCourseEventDto} updateCourseEventDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateCourseEvent(courseId: number, courseEventId: number, updateCourseEventDto: UpdateCourseEventDto, options?: any): AxiosPromise { return localVarFp.updateCourseEvent(courseId, courseEventId, updateCourseEventDto, options).then((request) => request(axios, basePath)); }, }; }; /** * CoursesEventsApi - object-oriented interface * @export * @class CoursesEventsApi * @extends {BaseAPI} */ export class CoursesEventsApi extends BaseAPI { /** * * @param {number} courseId * @param {CreateCourseEventDto} createCourseEventDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesEventsApi */ public createCourseEvent(courseId: number, createCourseEventDto: CreateCourseEventDto, options?: AxiosRequestConfig) { return CoursesEventsApiFp(this.configuration).createCourseEvent(courseId, createCourseEventDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseEventId * @param {any} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesEventsApi */ public deleteCourseEvent(courseEventId: number, courseId: any, options?: AxiosRequestConfig) { return CoursesEventsApiFp(this.configuration).deleteCourseEvent(courseEventId, courseId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} courseEventId * @param {UpdateCourseEventDto} updateCourseEventDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesEventsApi */ public updateCourseEvent(courseId: number, courseEventId: number, updateCourseEventDto: UpdateCourseEventDto, options?: AxiosRequestConfig) { return CoursesEventsApiFp(this.configuration).updateCourseEvent(courseId, courseEventId, updateCourseEventDto, options).then((request) => request(this.axios, this.basePath)); } } /** * CoursesInterviewsApi - axios parameter creator * @export */ export const CoursesInterviewsApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {number} courseId * @param {number} interviewId * @param {string} type * @param {PutInterviewFeedbackDto} putInterviewFeedbackDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createInterviewFeedback: async (courseId: number, interviewId: number, type: string, putInterviewFeedbackDto: PutInterviewFeedbackDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('createInterviewFeedback', 'courseId', courseId) // verify required parameter 'interviewId' is not null or undefined assertParamExists('createInterviewFeedback', 'interviewId', interviewId) // verify required parameter 'type' is not null or undefined assertParamExists('createInterviewFeedback', 'type', type) // verify required parameter 'putInterviewFeedbackDto' is not null or undefined assertParamExists('createInterviewFeedback', 'putInterviewFeedbackDto', putInterviewFeedbackDto) const localVarPath = `/courses/{courseId}/interviews/{interviewId}/{type}/feedback` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"interviewId"}}`, encodeURIComponent(String(interviewId))) .replace(`{${"type"}}`, encodeURIComponent(String(type))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(putInterviewFeedbackDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} courseTaskId * @param {InterviewDistributeDto} interviewDistributeDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ distributeInterviewPairs: async (courseId: number, courseTaskId: number, interviewDistributeDto: InterviewDistributeDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('distributeInterviewPairs', 'courseId', courseId) // verify required parameter 'courseTaskId' is not null or undefined assertParamExists('distributeInterviewPairs', 'courseTaskId', courseTaskId) // verify required parameter 'interviewDistributeDto' is not null or undefined assertParamExists('distributeInterviewPairs', 'interviewDistributeDto', interviewDistributeDto) const localVarPath = `/courses/{courseId}/interviews/{courseTaskId}/auto-distribute` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"courseTaskId"}}`, encodeURIComponent(String(courseTaskId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(interviewDistributeDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} interviewId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getAvailableStudents: async (courseId: number, interviewId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getAvailableStudents', 'courseId', courseId) // verify required parameter 'interviewId' is not null or undefined assertParamExists('getAvailableStudents', 'interviewId', interviewId) const localVarPath = `/courses/{courseId}/interviews/{interviewId}/students/available` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"interviewId"}}`, encodeURIComponent(String(interviewId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} interviewId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getInterview: async (interviewId: number, courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'interviewId' is not null or undefined assertParamExists('getInterview', 'interviewId', interviewId) // verify required parameter 'courseId' is not null or undefined assertParamExists('getInterview', 'courseId', courseId) const localVarPath = `/courses/{courseId}/interviews/{interviewId}` .replace(`{${"interviewId"}}`, encodeURIComponent(String(interviewId))) .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} interviewId * @param {string} type * @param {*} [options] Override http request option. * @throws {RequiredError} */ getInterviewFeedback: async (courseId: number, interviewId: number, type: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getInterviewFeedback', 'courseId', courseId) // verify required parameter 'interviewId' is not null or undefined assertParamExists('getInterviewFeedback', 'interviewId', interviewId) // verify required parameter 'type' is not null or undefined assertParamExists('getInterviewFeedback', 'type', type) const localVarPath = `/courses/{courseId}/interviews/{interviewId}/{type}/feedback` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"interviewId"}}`, encodeURIComponent(String(interviewId))) .replace(`{${"type"}}`, encodeURIComponent(String(type))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} interviewId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getInterviewPairs: async (interviewId: number, courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'interviewId' is not null or undefined assertParamExists('getInterviewPairs', 'interviewId', interviewId) // verify required parameter 'courseId' is not null or undefined assertParamExists('getInterviewPairs', 'courseId', courseId) const localVarPath = `/courses/{courseId}/interviews/{interviewId}/pairs` .replace(`{${"interviewId"}}`, encodeURIComponent(String(interviewId))) .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {boolean} [disabled] * @param {Array} [types] * @param {*} [options] Override http request option. * @throws {RequiredError} */ getInterviews: async (courseId: number, disabled?: boolean, types?: Array, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getInterviews', 'courseId', courseId) const localVarPath = `/courses/{courseId}/interviews` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; if (disabled !== undefined) { localVarQueryParameter['disabled'] = disabled; } if (types) { localVarQueryParameter['types'] = types; } setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getStageInterviewsCommentToStudent: async (courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getStageInterviewsCommentToStudent', 'courseId', courseId) const localVarPath = `/courses/{courseId}/interviews/comments` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} interviewId * @param {*} [options] Override http request option. * @throws {RequiredError} */ registerToInterview: async (courseId: number, interviewId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('registerToInterview', 'courseId', courseId) // verify required parameter 'interviewId' is not null or undefined assertParamExists('registerToInterview', 'interviewId', interviewId) const localVarPath = `/courses/{courseId}/interviews/{interviewId}/register` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"interviewId"}}`, encodeURIComponent(String(interviewId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * CoursesInterviewsApi - functional programming interface * @export */ export const CoursesInterviewsApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = CoursesInterviewsApiAxiosParamCreator(configuration) return { /** * * @param {number} courseId * @param {number} interviewId * @param {string} type * @param {PutInterviewFeedbackDto} putInterviewFeedbackDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createInterviewFeedback(courseId: number, interviewId: number, type: string, putInterviewFeedbackDto: PutInterviewFeedbackDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.createInterviewFeedback(courseId, interviewId, type, putInterviewFeedbackDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} courseTaskId * @param {InterviewDistributeDto} interviewDistributeDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async distributeInterviewPairs(courseId: number, courseTaskId: number, interviewDistributeDto: InterviewDistributeDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.distributeInterviewPairs(courseId, courseTaskId, interviewDistributeDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} interviewId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getAvailableStudents(courseId: number, interviewId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getAvailableStudents(courseId, interviewId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} interviewId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getInterview(interviewId: number, courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getInterview(interviewId, courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} interviewId * @param {string} type * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getInterviewFeedback(courseId: number, interviewId: number, type: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getInterviewFeedback(courseId, interviewId, type, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} interviewId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getInterviewPairs(interviewId: number, courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getInterviewPairs(interviewId, courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {boolean} [disabled] * @param {Array} [types] * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getInterviews(courseId: number, disabled?: boolean, types?: Array, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getInterviews(courseId, disabled, types, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getStageInterviewsCommentToStudent(courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getStageInterviewsCommentToStudent(courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} interviewId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async registerToInterview(courseId: number, interviewId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.registerToInterview(courseId, interviewId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * CoursesInterviewsApi - factory interface * @export */ export const CoursesInterviewsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = CoursesInterviewsApiFp(configuration) return { /** * * @param {number} courseId * @param {number} interviewId * @param {string} type * @param {PutInterviewFeedbackDto} putInterviewFeedbackDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createInterviewFeedback(courseId: number, interviewId: number, type: string, putInterviewFeedbackDto: PutInterviewFeedbackDto, options?: any): AxiosPromise { return localVarFp.createInterviewFeedback(courseId, interviewId, type, putInterviewFeedbackDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} courseTaskId * @param {InterviewDistributeDto} interviewDistributeDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ distributeInterviewPairs(courseId: number, courseTaskId: number, interviewDistributeDto: InterviewDistributeDto, options?: any): AxiosPromise> { return localVarFp.distributeInterviewPairs(courseId, courseTaskId, interviewDistributeDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} interviewId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getAvailableStudents(courseId: number, interviewId: number, options?: any): AxiosPromise> { return localVarFp.getAvailableStudents(courseId, interviewId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} interviewId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getInterview(interviewId: number, courseId: number, options?: any): AxiosPromise { return localVarFp.getInterview(interviewId, courseId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} interviewId * @param {string} type * @param {*} [options] Override http request option. * @throws {RequiredError} */ getInterviewFeedback(courseId: number, interviewId: number, type: string, options?: any): AxiosPromise { return localVarFp.getInterviewFeedback(courseId, interviewId, type, options).then((request) => request(axios, basePath)); }, /** * * @param {number} interviewId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getInterviewPairs(interviewId: number, courseId: number, options?: any): AxiosPromise> { return localVarFp.getInterviewPairs(interviewId, courseId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {boolean} [disabled] * @param {Array} [types] * @param {*} [options] Override http request option. * @throws {RequiredError} */ getInterviews(courseId: number, disabled?: boolean, types?: Array, options?: any): AxiosPromise> { return localVarFp.getInterviews(courseId, disabled, types, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getStageInterviewsCommentToStudent(courseId: number, options?: any): AxiosPromise> { return localVarFp.getStageInterviewsCommentToStudent(courseId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} interviewId * @param {*} [options] Override http request option. * @throws {RequiredError} */ registerToInterview(courseId: number, interviewId: number, options?: any): AxiosPromise { return localVarFp.registerToInterview(courseId, interviewId, options).then((request) => request(axios, basePath)); }, }; }; /** * CoursesInterviewsApi - object-oriented interface * @export * @class CoursesInterviewsApi * @extends {BaseAPI} */ export class CoursesInterviewsApi extends BaseAPI { /** * * @param {number} courseId * @param {number} interviewId * @param {string} type * @param {PutInterviewFeedbackDto} putInterviewFeedbackDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesInterviewsApi */ public createInterviewFeedback(courseId: number, interviewId: number, type: string, putInterviewFeedbackDto: PutInterviewFeedbackDto, options?: AxiosRequestConfig) { return CoursesInterviewsApiFp(this.configuration).createInterviewFeedback(courseId, interviewId, type, putInterviewFeedbackDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} courseTaskId * @param {InterviewDistributeDto} interviewDistributeDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesInterviewsApi */ public distributeInterviewPairs(courseId: number, courseTaskId: number, interviewDistributeDto: InterviewDistributeDto, options?: AxiosRequestConfig) { return CoursesInterviewsApiFp(this.configuration).distributeInterviewPairs(courseId, courseTaskId, interviewDistributeDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} interviewId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesInterviewsApi */ public getAvailableStudents(courseId: number, interviewId: number, options?: AxiosRequestConfig) { return CoursesInterviewsApiFp(this.configuration).getAvailableStudents(courseId, interviewId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} interviewId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesInterviewsApi */ public getInterview(interviewId: number, courseId: number, options?: AxiosRequestConfig) { return CoursesInterviewsApiFp(this.configuration).getInterview(interviewId, courseId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} interviewId * @param {string} type * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesInterviewsApi */ public getInterviewFeedback(courseId: number, interviewId: number, type: string, options?: AxiosRequestConfig) { return CoursesInterviewsApiFp(this.configuration).getInterviewFeedback(courseId, interviewId, type, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} interviewId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesInterviewsApi */ public getInterviewPairs(interviewId: number, courseId: number, options?: AxiosRequestConfig) { return CoursesInterviewsApiFp(this.configuration).getInterviewPairs(interviewId, courseId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {boolean} [disabled] * @param {Array} [types] * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesInterviewsApi */ public getInterviews(courseId: number, disabled?: boolean, types?: Array, options?: AxiosRequestConfig) { return CoursesInterviewsApiFp(this.configuration).getInterviews(courseId, disabled, types, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesInterviewsApi */ public getStageInterviewsCommentToStudent(courseId: number, options?: AxiosRequestConfig) { return CoursesInterviewsApiFp(this.configuration).getStageInterviewsCommentToStudent(courseId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} interviewId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesInterviewsApi */ public registerToInterview(courseId: number, interviewId: number, options?: AxiosRequestConfig) { return CoursesInterviewsApiFp(this.configuration).registerToInterview(courseId, interviewId, options).then((request) => request(this.axios, this.basePath)); } } /** * CoursesScheduleApi - axios parameter creator * @export */ export const CoursesScheduleApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {number} courseId * @param {CourseCopyFromDto} courseCopyFromDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ copySchedule: async (courseId: number, courseCopyFromDto: CourseCopyFromDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('copySchedule', 'courseId', courseId) // verify required parameter 'courseCopyFromDto' is not null or undefined assertParamExists('copySchedule', 'courseCopyFromDto', courseCopyFromDto) const localVarPath = `/courses/{courseId}/schedule/copy` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(courseCopyFromDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getSchedule: async (courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getSchedule', 'courseId', courseId) const localVarPath = `/courses/{courseId}/schedule` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * CoursesScheduleApi - functional programming interface * @export */ export const CoursesScheduleApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = CoursesScheduleApiAxiosParamCreator(configuration) return { /** * * @param {number} courseId * @param {CourseCopyFromDto} courseCopyFromDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async copySchedule(courseId: number, courseCopyFromDto: CourseCopyFromDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.copySchedule(courseId, courseCopyFromDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getSchedule(courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getSchedule(courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * CoursesScheduleApi - factory interface * @export */ export const CoursesScheduleApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = CoursesScheduleApiFp(configuration) return { /** * * @param {number} courseId * @param {CourseCopyFromDto} courseCopyFromDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ copySchedule(courseId: number, courseCopyFromDto: CourseCopyFromDto, options?: any): AxiosPromise { return localVarFp.copySchedule(courseId, courseCopyFromDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getSchedule(courseId: number, options?: any): AxiosPromise> { return localVarFp.getSchedule(courseId, options).then((request) => request(axios, basePath)); }, }; }; /** * CoursesScheduleApi - object-oriented interface * @export * @class CoursesScheduleApi * @extends {BaseAPI} */ export class CoursesScheduleApi extends BaseAPI { /** * * @param {number} courseId * @param {CourseCopyFromDto} courseCopyFromDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesScheduleApi */ public copySchedule(courseId: number, courseCopyFromDto: CourseCopyFromDto, options?: AxiosRequestConfig) { return CoursesScheduleApiFp(this.configuration).copySchedule(courseId, courseCopyFromDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesScheduleApi */ public getSchedule(courseId: number, options?: AxiosRequestConfig) { return CoursesScheduleApiFp(this.configuration).getSchedule(courseId, options).then((request) => request(this.axios, this.basePath)); } } /** * CoursesScheduleIcalApi - axios parameter creator * @export */ export const CoursesScheduleIcalApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {number} courseId * @param {string} token * @param {string} timezone * @param {*} [options] Override http request option. * @throws {RequiredError} */ getScheduleICalendar: async (courseId: number, token: string, timezone: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getScheduleICalendar', 'courseId', courseId) // verify required parameter 'token' is not null or undefined assertParamExists('getScheduleICalendar', 'token', token) // verify required parameter 'timezone' is not null or undefined assertParamExists('getScheduleICalendar', 'timezone', timezone) const localVarPath = `/courses/{courseId}/icalendar/{token}` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"token"}}`, encodeURIComponent(String(token))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; if (timezone !== undefined) { localVarQueryParameter['timezone'] = timezone; } setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getScheduleICalendarToken: async (courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getScheduleICalendarToken', 'courseId', courseId) const localVarPath = `/courses/{courseId}/icalendar/token` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * CoursesScheduleIcalApi - functional programming interface * @export */ export const CoursesScheduleIcalApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = CoursesScheduleIcalApiAxiosParamCreator(configuration) return { /** * * @param {number} courseId * @param {string} token * @param {string} timezone * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getScheduleICalendar(courseId: number, token: string, timezone: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getScheduleICalendar(courseId, token, timezone, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getScheduleICalendarToken(courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getScheduleICalendarToken(courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * CoursesScheduleIcalApi - factory interface * @export */ export const CoursesScheduleIcalApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = CoursesScheduleIcalApiFp(configuration) return { /** * * @param {number} courseId * @param {string} token * @param {string} timezone * @param {*} [options] Override http request option. * @throws {RequiredError} */ getScheduleICalendar(courseId: number, token: string, timezone: string, options?: any): AxiosPromise { return localVarFp.getScheduleICalendar(courseId, token, timezone, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getScheduleICalendarToken(courseId: number, options?: any): AxiosPromise { return localVarFp.getScheduleICalendarToken(courseId, options).then((request) => request(axios, basePath)); }, }; }; /** * CoursesScheduleIcalApi - object-oriented interface * @export * @class CoursesScheduleIcalApi * @extends {BaseAPI} */ export class CoursesScheduleIcalApi extends BaseAPI { /** * * @param {number} courseId * @param {string} token * @param {string} timezone * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesScheduleIcalApi */ public getScheduleICalendar(courseId: number, token: string, timezone: string, options?: AxiosRequestConfig) { return CoursesScheduleIcalApiFp(this.configuration).getScheduleICalendar(courseId, token, timezone, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesScheduleIcalApi */ public getScheduleICalendarToken(courseId: number, options?: AxiosRequestConfig) { return CoursesScheduleIcalApiFp(this.configuration).getScheduleICalendarToken(courseId, options).then((request) => request(this.axios, this.basePath)); } } /** * CoursesTaskSolutionsApi - axios parameter creator * @export */ export const CoursesTaskSolutionsApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {number} courseId * @param {number} courseTaskId * @param {SaveTaskSolutionDto} saveTaskSolutionDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createTaskSolution: async (courseId: number, courseTaskId: number, saveTaskSolutionDto: SaveTaskSolutionDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('createTaskSolution', 'courseId', courseId) // verify required parameter 'courseTaskId' is not null or undefined assertParamExists('createTaskSolution', 'courseTaskId', courseTaskId) // verify required parameter 'saveTaskSolutionDto' is not null or undefined assertParamExists('createTaskSolution', 'saveTaskSolutionDto', saveTaskSolutionDto) const localVarPath = `/courses/{courseId}/tasks/{courseTaskId}/solutions` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"courseTaskId"}}`, encodeURIComponent(String(courseTaskId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(saveTaskSolutionDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * CoursesTaskSolutionsApi - functional programming interface * @export */ export const CoursesTaskSolutionsApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = CoursesTaskSolutionsApiAxiosParamCreator(configuration) return { /** * * @param {number} courseId * @param {number} courseTaskId * @param {SaveTaskSolutionDto} saveTaskSolutionDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createTaskSolution(courseId: number, courseTaskId: number, saveTaskSolutionDto: SaveTaskSolutionDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.createTaskSolution(courseId, courseTaskId, saveTaskSolutionDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * CoursesTaskSolutionsApi - factory interface * @export */ export const CoursesTaskSolutionsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = CoursesTaskSolutionsApiFp(configuration) return { /** * * @param {number} courseId * @param {number} courseTaskId * @param {SaveTaskSolutionDto} saveTaskSolutionDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createTaskSolution(courseId: number, courseTaskId: number, saveTaskSolutionDto: SaveTaskSolutionDto, options?: any): AxiosPromise { return localVarFp.createTaskSolution(courseId, courseTaskId, saveTaskSolutionDto, options).then((request) => request(axios, basePath)); }, }; }; /** * CoursesTaskSolutionsApi - object-oriented interface * @export * @class CoursesTaskSolutionsApi * @extends {BaseAPI} */ export class CoursesTaskSolutionsApi extends BaseAPI { /** * * @param {number} courseId * @param {number} courseTaskId * @param {SaveTaskSolutionDto} saveTaskSolutionDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesTaskSolutionsApi */ public createTaskSolution(courseId: number, courseTaskId: number, saveTaskSolutionDto: SaveTaskSolutionDto, options?: AxiosRequestConfig) { return CoursesTaskSolutionsApiFp(this.configuration).createTaskSolution(courseId, courseTaskId, saveTaskSolutionDto, options).then((request) => request(this.axios, this.basePath)); } } /** * CoursesTasksApi - axios parameter creator * @export */ export const CoursesTasksApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {number} courseId * @param {CreateCourseTaskDto} createCourseTaskDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createCourseTask: async (courseId: number, createCourseTaskDto: CreateCourseTaskDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('createCourseTask', 'courseId', courseId) // verify required parameter 'createCourseTaskDto' is not null or undefined assertParamExists('createCourseTask', 'createCourseTaskDto', createCourseTaskDto) const localVarPath = `/courses/{courseId}/tasks` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(createCourseTaskDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} courseTaskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteCourseTask: async (courseId: number, courseTaskId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('deleteCourseTask', 'courseId', courseId) // verify required parameter 'courseTaskId' is not null or undefined assertParamExists('deleteCourseTask', 'courseTaskId', courseTaskId) const localVarPath = `/courses/{courseId}/tasks/{courseTaskId}` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"courseTaskId"}}`, encodeURIComponent(String(courseTaskId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getAvailableCrossCheckReviewStats: async (courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getAvailableCrossCheckReviewStats', 'courseId', courseId) const localVarPath = `/courses/{courseId}/cross-checks/available-review-stats` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} courseTaskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseTask: async (courseId: number, courseTaskId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getCourseTask', 'courseId', courseId) // verify required parameter 'courseTaskId' is not null or undefined assertParamExists('getCourseTask', 'courseTaskId', courseTaskId) const localVarPath = `/courses/{courseId}/tasks/{courseTaskId}` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"courseTaskId"}}`, encodeURIComponent(String(courseTaskId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {'started' | 'inprogress' | 'finished'} [status] * @param {'auto-test' | 'assigned' | 'mentor' | 'taskOwner' | 'crossCheck'} [checker] * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseTasks: async (courseId: number, status?: 'started' | 'inprogress' | 'finished', checker?: 'auto-test' | 'assigned' | 'mentor' | 'taskOwner' | 'crossCheck', options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getCourseTasks', 'courseId', courseId) const localVarPath = `/courses/{courseId}/tasks` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; if (status !== undefined) { localVarQueryParameter['status'] = status; } if (checker !== undefined) { localVarQueryParameter['checker'] = checker; } setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseTasksDetailed: async (courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getCourseTasksDetailed', 'courseId', courseId) const localVarPath = `/courses/{courseId}/tasks/detailed` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {'started' | 'inprogress' | 'finished'} [status] * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseTasksWithStudentSolution: async (courseId: number, status?: 'started' | 'inprogress' | 'finished', options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getCourseTasksWithStudentSolution', 'courseId', courseId) const localVarPath = `/courses/{courseId}/tasks/solutions` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; if (status !== undefined) { localVarQueryParameter['status'] = status; } setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} courseTaskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCrossCheckCsv: async (courseId: number, courseTaskId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getCrossCheckCsv', 'courseId', courseId) // verify required parameter 'courseTaskId' is not null or undefined assertParamExists('getCrossCheckCsv', 'courseTaskId', courseTaskId) const localVarPath = `/courses/{courseId}/cross-checks/{courseTaskId}/csv` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"courseTaskId"}}`, encodeURIComponent(String(courseTaskId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} pageSize * @param {number} current * @param {string} [orderBy] * @param {string} [orderDirection] * @param {string} [checker] * @param {string} [student] * @param {string} [url] * @param {string} [task] * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCrossCheckPairs: async (courseId: number, pageSize: number, current: number, orderBy?: string, orderDirection?: string, checker?: string, student?: string, url?: string, task?: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getCrossCheckPairs', 'courseId', courseId) // verify required parameter 'pageSize' is not null or undefined assertParamExists('getCrossCheckPairs', 'pageSize', pageSize) // verify required parameter 'current' is not null or undefined assertParamExists('getCrossCheckPairs', 'current', current) const localVarPath = `/courses/{courseId}/cross-checks/pairs` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; if (pageSize !== undefined) { localVarQueryParameter['pageSize'] = pageSize; } if (current !== undefined) { localVarQueryParameter['current'] = current; } if (orderBy !== undefined) { localVarQueryParameter['orderBy'] = orderBy; } if (orderDirection !== undefined) { localVarQueryParameter['orderDirection'] = orderDirection; } if (checker !== undefined) { localVarQueryParameter['checker'] = checker; } if (student !== undefined) { localVarQueryParameter['student'] = student; } if (url !== undefined) { localVarQueryParameter['url'] = url; } if (task !== undefined) { localVarQueryParameter['task'] = task; } setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} courseTaskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getMyCrossCheckFeedbacks: async (courseId: number, courseTaskId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getMyCrossCheckFeedbacks', 'courseId', courseId) // verify required parameter 'courseTaskId' is not null or undefined assertParamExists('getMyCrossCheckFeedbacks', 'courseTaskId', courseTaskId) const localVarPath = `/courses/{courseId}/cross-checks/{courseTaskId}/feedbacks/my` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"courseTaskId"}}`, encodeURIComponent(String(courseTaskId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {CheckTasksDeadlineDto} checkTasksDeadlineDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ notifyTasksDeadlines: async (checkTasksDeadlineDto: CheckTasksDeadlineDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'checkTasksDeadlineDto' is not null or undefined assertParamExists('notifyTasksDeadlines', 'checkTasksDeadlineDto', checkTasksDeadlineDto) const localVarPath = `/tasks/notify/changes`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(checkTasksDeadlineDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} courseTaskId * @param {UpdateCourseTaskDto} updateCourseTaskDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateCourseTask: async (courseId: number, courseTaskId: number, updateCourseTaskDto: UpdateCourseTaskDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('updateCourseTask', 'courseId', courseId) // verify required parameter 'courseTaskId' is not null or undefined assertParamExists('updateCourseTask', 'courseTaskId', courseTaskId) // verify required parameter 'updateCourseTaskDto' is not null or undefined assertParamExists('updateCourseTask', 'updateCourseTaskDto', updateCourseTaskDto) const localVarPath = `/courses/{courseId}/tasks/{courseTaskId}` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"courseTaskId"}}`, encodeURIComponent(String(courseTaskId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(updateCourseTaskDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * CoursesTasksApi - functional programming interface * @export */ export const CoursesTasksApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = CoursesTasksApiAxiosParamCreator(configuration) return { /** * * @param {number} courseId * @param {CreateCourseTaskDto} createCourseTaskDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createCourseTask(courseId: number, createCourseTaskDto: CreateCourseTaskDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.createCourseTask(courseId, createCourseTaskDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} courseTaskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async deleteCourseTask(courseId: number, courseTaskId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.deleteCourseTask(courseId, courseTaskId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getAvailableCrossCheckReviewStats(courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getAvailableCrossCheckReviewStats(courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} courseTaskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getCourseTask(courseId: number, courseTaskId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getCourseTask(courseId, courseTaskId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {'started' | 'inprogress' | 'finished'} [status] * @param {'auto-test' | 'assigned' | 'mentor' | 'taskOwner' | 'crossCheck'} [checker] * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getCourseTasks(courseId: number, status?: 'started' | 'inprogress' | 'finished', checker?: 'auto-test' | 'assigned' | 'mentor' | 'taskOwner' | 'crossCheck', options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getCourseTasks(courseId, status, checker, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getCourseTasksDetailed(courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getCourseTasksDetailed(courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {'started' | 'inprogress' | 'finished'} [status] * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getCourseTasksWithStudentSolution(courseId: number, status?: 'started' | 'inprogress' | 'finished', options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getCourseTasksWithStudentSolution(courseId, status, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} courseTaskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getCrossCheckCsv(courseId: number, courseTaskId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getCrossCheckCsv(courseId, courseTaskId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} pageSize * @param {number} current * @param {string} [orderBy] * @param {string} [orderDirection] * @param {string} [checker] * @param {string} [student] * @param {string} [url] * @param {string} [task] * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getCrossCheckPairs(courseId: number, pageSize: number, current: number, orderBy?: string, orderDirection?: string, checker?: string, student?: string, url?: string, task?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getCrossCheckPairs(courseId, pageSize, current, orderBy, orderDirection, checker, student, url, task, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} courseTaskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getMyCrossCheckFeedbacks(courseId: number, courseTaskId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getMyCrossCheckFeedbacks(courseId, courseTaskId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {CheckTasksDeadlineDto} checkTasksDeadlineDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async notifyTasksDeadlines(checkTasksDeadlineDto: CheckTasksDeadlineDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.notifyTasksDeadlines(checkTasksDeadlineDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} courseTaskId * @param {UpdateCourseTaskDto} updateCourseTaskDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async updateCourseTask(courseId: number, courseTaskId: number, updateCourseTaskDto: UpdateCourseTaskDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.updateCourseTask(courseId, courseTaskId, updateCourseTaskDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * CoursesTasksApi - factory interface * @export */ export const CoursesTasksApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = CoursesTasksApiFp(configuration) return { /** * * @param {number} courseId * @param {CreateCourseTaskDto} createCourseTaskDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createCourseTask(courseId: number, createCourseTaskDto: CreateCourseTaskDto, options?: any): AxiosPromise { return localVarFp.createCourseTask(courseId, createCourseTaskDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} courseTaskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteCourseTask(courseId: number, courseTaskId: number, options?: any): AxiosPromise { return localVarFp.deleteCourseTask(courseId, courseTaskId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getAvailableCrossCheckReviewStats(courseId: number, options?: any): AxiosPromise> { return localVarFp.getAvailableCrossCheckReviewStats(courseId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} courseTaskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseTask(courseId: number, courseTaskId: number, options?: any): AxiosPromise { return localVarFp.getCourseTask(courseId, courseTaskId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {'started' | 'inprogress' | 'finished'} [status] * @param {'auto-test' | 'assigned' | 'mentor' | 'taskOwner' | 'crossCheck'} [checker] * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseTasks(courseId: number, status?: 'started' | 'inprogress' | 'finished', checker?: 'auto-test' | 'assigned' | 'mentor' | 'taskOwner' | 'crossCheck', options?: any): AxiosPromise> { return localVarFp.getCourseTasks(courseId, status, checker, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseTasksDetailed(courseId: number, options?: any): AxiosPromise> { return localVarFp.getCourseTasksDetailed(courseId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {'started' | 'inprogress' | 'finished'} [status] * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseTasksWithStudentSolution(courseId: number, status?: 'started' | 'inprogress' | 'finished', options?: any): AxiosPromise> { return localVarFp.getCourseTasksWithStudentSolution(courseId, status, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} courseTaskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCrossCheckCsv(courseId: number, courseTaskId: number, options?: any): AxiosPromise { return localVarFp.getCrossCheckCsv(courseId, courseTaskId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} pageSize * @param {number} current * @param {string} [orderBy] * @param {string} [orderDirection] * @param {string} [checker] * @param {string} [student] * @param {string} [url] * @param {string} [task] * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCrossCheckPairs(courseId: number, pageSize: number, current: number, orderBy?: string, orderDirection?: string, checker?: string, student?: string, url?: string, task?: string, options?: any): AxiosPromise { return localVarFp.getCrossCheckPairs(courseId, pageSize, current, orderBy, orderDirection, checker, student, url, task, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} courseTaskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getMyCrossCheckFeedbacks(courseId: number, courseTaskId: number, options?: any): AxiosPromise { return localVarFp.getMyCrossCheckFeedbacks(courseId, courseTaskId, options).then((request) => request(axios, basePath)); }, /** * * @param {CheckTasksDeadlineDto} checkTasksDeadlineDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ notifyTasksDeadlines(checkTasksDeadlineDto: CheckTasksDeadlineDto, options?: any): AxiosPromise { return localVarFp.notifyTasksDeadlines(checkTasksDeadlineDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} courseTaskId * @param {UpdateCourseTaskDto} updateCourseTaskDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateCourseTask(courseId: number, courseTaskId: number, updateCourseTaskDto: UpdateCourseTaskDto, options?: any): AxiosPromise { return localVarFp.updateCourseTask(courseId, courseTaskId, updateCourseTaskDto, options).then((request) => request(axios, basePath)); }, }; }; /** * CoursesTasksApi - object-oriented interface * @export * @class CoursesTasksApi * @extends {BaseAPI} */ export class CoursesTasksApi extends BaseAPI { /** * * @param {number} courseId * @param {CreateCourseTaskDto} createCourseTaskDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesTasksApi */ public createCourseTask(courseId: number, createCourseTaskDto: CreateCourseTaskDto, options?: AxiosRequestConfig) { return CoursesTasksApiFp(this.configuration).createCourseTask(courseId, createCourseTaskDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} courseTaskId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesTasksApi */ public deleteCourseTask(courseId: number, courseTaskId: number, options?: AxiosRequestConfig) { return CoursesTasksApiFp(this.configuration).deleteCourseTask(courseId, courseTaskId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesTasksApi */ public getAvailableCrossCheckReviewStats(courseId: number, options?: AxiosRequestConfig) { return CoursesTasksApiFp(this.configuration).getAvailableCrossCheckReviewStats(courseId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} courseTaskId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesTasksApi */ public getCourseTask(courseId: number, courseTaskId: number, options?: AxiosRequestConfig) { return CoursesTasksApiFp(this.configuration).getCourseTask(courseId, courseTaskId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {'started' | 'inprogress' | 'finished'} [status] * @param {'auto-test' | 'assigned' | 'mentor' | 'taskOwner' | 'crossCheck'} [checker] * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesTasksApi */ public getCourseTasks(courseId: number, status?: 'started' | 'inprogress' | 'finished', checker?: 'auto-test' | 'assigned' | 'mentor' | 'taskOwner' | 'crossCheck', options?: AxiosRequestConfig) { return CoursesTasksApiFp(this.configuration).getCourseTasks(courseId, status, checker, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesTasksApi */ public getCourseTasksDetailed(courseId: number, options?: AxiosRequestConfig) { return CoursesTasksApiFp(this.configuration).getCourseTasksDetailed(courseId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {'started' | 'inprogress' | 'finished'} [status] * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesTasksApi */ public getCourseTasksWithStudentSolution(courseId: number, status?: 'started' | 'inprogress' | 'finished', options?: AxiosRequestConfig) { return CoursesTasksApiFp(this.configuration).getCourseTasksWithStudentSolution(courseId, status, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} courseTaskId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesTasksApi */ public getCrossCheckCsv(courseId: number, courseTaskId: number, options?: AxiosRequestConfig) { return CoursesTasksApiFp(this.configuration).getCrossCheckCsv(courseId, courseTaskId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} pageSize * @param {number} current * @param {string} [orderBy] * @param {string} [orderDirection] * @param {string} [checker] * @param {string} [student] * @param {string} [url] * @param {string} [task] * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesTasksApi */ public getCrossCheckPairs(courseId: number, pageSize: number, current: number, orderBy?: string, orderDirection?: string, checker?: string, student?: string, url?: string, task?: string, options?: AxiosRequestConfig) { return CoursesTasksApiFp(this.configuration).getCrossCheckPairs(courseId, pageSize, current, orderBy, orderDirection, checker, student, url, task, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} courseTaskId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesTasksApi */ public getMyCrossCheckFeedbacks(courseId: number, courseTaskId: number, options?: AxiosRequestConfig) { return CoursesTasksApiFp(this.configuration).getMyCrossCheckFeedbacks(courseId, courseTaskId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {CheckTasksDeadlineDto} checkTasksDeadlineDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesTasksApi */ public notifyTasksDeadlines(checkTasksDeadlineDto: CheckTasksDeadlineDto, options?: AxiosRequestConfig) { return CoursesTasksApiFp(this.configuration).notifyTasksDeadlines(checkTasksDeadlineDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} courseTaskId * @param {UpdateCourseTaskDto} updateCourseTaskDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof CoursesTasksApi */ public updateCourseTask(courseId: number, courseTaskId: number, updateCourseTaskDto: UpdateCourseTaskDto, options?: AxiosRequestConfig) { return CoursesTasksApiFp(this.configuration).updateCourseTask(courseId, courseTaskId, updateCourseTaskDto, options).then((request) => request(this.axios, this.basePath)); } } /** * DevtoolsApi - axios parameter creator * @export */ export const DevtoolsApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {string} githubId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getDevUserLogin: async (githubId: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'githubId' is not null or undefined assertParamExists('getDevUserLogin', 'githubId', githubId) const localVarPath = `/devtools/user/{githubId}/login` .replace(`{${"githubId"}}`, encodeURIComponent(String(githubId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getDevUsers: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/devtools/users`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * DevtoolsApi - functional programming interface * @export */ export const DevtoolsApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = DevtoolsApiAxiosParamCreator(configuration) return { /** * * @param {string} githubId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getDevUserLogin(githubId: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getDevUserLogin(githubId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getDevUsers(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getDevUsers(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * DevtoolsApi - factory interface * @export */ export const DevtoolsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = DevtoolsApiFp(configuration) return { /** * * @param {string} githubId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getDevUserLogin(githubId: string, options?: any): AxiosPromise { return localVarFp.getDevUserLogin(githubId, options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getDevUsers(options?: any): AxiosPromise> { return localVarFp.getDevUsers(options).then((request) => request(axios, basePath)); }, }; }; /** * DevtoolsApi - object-oriented interface * @export * @class DevtoolsApi * @extends {BaseAPI} */ export class DevtoolsApi extends BaseAPI { /** * * @param {string} githubId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DevtoolsApi */ public getDevUserLogin(githubId: string, options?: AxiosRequestConfig) { return DevtoolsApiFp(this.configuration).getDevUserLogin(githubId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DevtoolsApi */ public getDevUsers(options?: AxiosRequestConfig) { return DevtoolsApiFp(this.configuration).getDevUsers(options).then((request) => request(this.axios, this.basePath)); } } /** * DisciplinesApi - axios parameter creator * @export */ export const DisciplinesApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {CreateDisciplineDto} createDisciplineDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createDiscipline: async (createDisciplineDto: CreateDisciplineDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'createDisciplineDto' is not null or undefined assertParamExists('createDiscipline', 'createDisciplineDto', createDisciplineDto) const localVarPath = `/disciplines`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(createDisciplineDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteDiscipline: async (id: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined assertParamExists('deleteDiscipline', 'id', id) const localVarPath = `/disciplines/{id}` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getDisciplines: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/disciplines`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {DisciplineIdsDto} disciplineIdsDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ getDisciplinesByIds: async (disciplineIdsDto: DisciplineIdsDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'disciplineIdsDto' is not null or undefined assertParamExists('getDisciplinesByIds', 'disciplineIdsDto', disciplineIdsDto) const localVarPath = `/disciplines/ids`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(disciplineIdsDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} id * @param {UpdateDisciplineDto} updateDisciplineDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateDiscipline: async (id: number, updateDisciplineDto: UpdateDisciplineDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined assertParamExists('updateDiscipline', 'id', id) // verify required parameter 'updateDisciplineDto' is not null or undefined assertParamExists('updateDiscipline', 'updateDisciplineDto', updateDisciplineDto) const localVarPath = `/disciplines/{id}` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PATCH', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(updateDisciplineDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * DisciplinesApi - functional programming interface * @export */ export const DisciplinesApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = DisciplinesApiAxiosParamCreator(configuration) return { /** * * @param {CreateDisciplineDto} createDisciplineDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createDiscipline(createDisciplineDto: CreateDisciplineDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.createDiscipline(createDisciplineDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async deleteDiscipline(id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.deleteDiscipline(id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getDisciplines(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getDisciplines(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {DisciplineIdsDto} disciplineIdsDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getDisciplinesByIds(disciplineIdsDto: DisciplineIdsDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getDisciplinesByIds(disciplineIdsDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} id * @param {UpdateDisciplineDto} updateDisciplineDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async updateDiscipline(id: number, updateDisciplineDto: UpdateDisciplineDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.updateDiscipline(id, updateDisciplineDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * DisciplinesApi - factory interface * @export */ export const DisciplinesApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = DisciplinesApiFp(configuration) return { /** * * @param {CreateDisciplineDto} createDisciplineDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createDiscipline(createDisciplineDto: CreateDisciplineDto, options?: any): AxiosPromise { return localVarFp.createDiscipline(createDisciplineDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteDiscipline(id: number, options?: any): AxiosPromise { return localVarFp.deleteDiscipline(id, options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getDisciplines(options?: any): AxiosPromise> { return localVarFp.getDisciplines(options).then((request) => request(axios, basePath)); }, /** * * @param {DisciplineIdsDto} disciplineIdsDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ getDisciplinesByIds(disciplineIdsDto: DisciplineIdsDto, options?: any): AxiosPromise> { return localVarFp.getDisciplinesByIds(disciplineIdsDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} id * @param {UpdateDisciplineDto} updateDisciplineDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateDiscipline(id: number, updateDisciplineDto: UpdateDisciplineDto, options?: any): AxiosPromise { return localVarFp.updateDiscipline(id, updateDisciplineDto, options).then((request) => request(axios, basePath)); }, }; }; /** * DisciplinesApi - object-oriented interface * @export * @class DisciplinesApi * @extends {BaseAPI} */ export class DisciplinesApi extends BaseAPI { /** * * @param {CreateDisciplineDto} createDisciplineDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DisciplinesApi */ public createDiscipline(createDisciplineDto: CreateDisciplineDto, options?: AxiosRequestConfig) { return DisciplinesApiFp(this.configuration).createDiscipline(createDisciplineDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DisciplinesApi */ public deleteDiscipline(id: number, options?: AxiosRequestConfig) { return DisciplinesApiFp(this.configuration).deleteDiscipline(id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DisciplinesApi */ public getDisciplines(options?: AxiosRequestConfig) { return DisciplinesApiFp(this.configuration).getDisciplines(options).then((request) => request(this.axios, this.basePath)); } /** * * @param {DisciplineIdsDto} disciplineIdsDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DisciplinesApi */ public getDisciplinesByIds(disciplineIdsDto: DisciplineIdsDto, options?: AxiosRequestConfig) { return DisciplinesApiFp(this.configuration).getDisciplinesByIds(disciplineIdsDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} id * @param {UpdateDisciplineDto} updateDisciplineDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DisciplinesApi */ public updateDiscipline(id: number, updateDisciplineDto: UpdateDisciplineDto, options?: AxiosRequestConfig) { return DisciplinesApiFp(this.configuration).updateDiscipline(id, updateDisciplineDto, options).then((request) => request(this.axios, this.basePath)); } } /** * DiscordServersApi - axios parameter creator * @export */ export const DiscordServersApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {CreateDiscordServerDto} createDiscordServerDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createDiscordServer: async (createDiscordServerDto: CreateDiscordServerDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'createDiscordServerDto' is not null or undefined assertParamExists('createDiscordServer', 'createDiscordServerDto', createDiscordServerDto) const localVarPath = `/discord-servers`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(createDiscordServerDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteDiscordServer: async (id: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined assertParamExists('deleteDiscordServer', 'id', id) const localVarPath = `/discord-servers/{id}` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getDiscordServers: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/discord-servers`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ getInviteLinkByDiscordServerId: async (courseId: number, id: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getInviteLinkByDiscordServerId', 'courseId', courseId) // verify required parameter 'id' is not null or undefined assertParamExists('getInviteLinkByDiscordServerId', 'id', id) const localVarPath = `/discord-servers/{courseId}/invite/{id}` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getReducedDiscordServers: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/discord-servers/reduced`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} id * @param {UpdateDiscordServerDto} updateDiscordServerDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateDiscordServer: async (id: number, updateDiscordServerDto: UpdateDiscordServerDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined assertParamExists('updateDiscordServer', 'id', id) // verify required parameter 'updateDiscordServerDto' is not null or undefined assertParamExists('updateDiscordServer', 'updateDiscordServerDto', updateDiscordServerDto) const localVarPath = `/discord-servers/{id}` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(updateDiscordServerDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * DiscordServersApi - functional programming interface * @export */ export const DiscordServersApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = DiscordServersApiAxiosParamCreator(configuration) return { /** * * @param {CreateDiscordServerDto} createDiscordServerDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createDiscordServer(createDiscordServerDto: CreateDiscordServerDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.createDiscordServer(createDiscordServerDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async deleteDiscordServer(id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.deleteDiscordServer(id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getDiscordServers(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getDiscordServers(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getInviteLinkByDiscordServerId(courseId: number, id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getInviteLinkByDiscordServerId(courseId, id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getReducedDiscordServers(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getReducedDiscordServers(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} id * @param {UpdateDiscordServerDto} updateDiscordServerDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async updateDiscordServer(id: number, updateDiscordServerDto: UpdateDiscordServerDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.updateDiscordServer(id, updateDiscordServerDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * DiscordServersApi - factory interface * @export */ export const DiscordServersApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = DiscordServersApiFp(configuration) return { /** * * @param {CreateDiscordServerDto} createDiscordServerDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createDiscordServer(createDiscordServerDto: CreateDiscordServerDto, options?: any): AxiosPromise { return localVarFp.createDiscordServer(createDiscordServerDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteDiscordServer(id: number, options?: any): AxiosPromise { return localVarFp.deleteDiscordServer(id, options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getDiscordServers(options?: any): AxiosPromise> { return localVarFp.getDiscordServers(options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ getInviteLinkByDiscordServerId(courseId: number, id: number, options?: any): AxiosPromise { return localVarFp.getInviteLinkByDiscordServerId(courseId, id, options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getReducedDiscordServers(options?: any): AxiosPromise> { return localVarFp.getReducedDiscordServers(options).then((request) => request(axios, basePath)); }, /** * * @param {number} id * @param {UpdateDiscordServerDto} updateDiscordServerDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateDiscordServer(id: number, updateDiscordServerDto: UpdateDiscordServerDto, options?: any): AxiosPromise { return localVarFp.updateDiscordServer(id, updateDiscordServerDto, options).then((request) => request(axios, basePath)); }, }; }; /** * DiscordServersApi - object-oriented interface * @export * @class DiscordServersApi * @extends {BaseAPI} */ export class DiscordServersApi extends BaseAPI { /** * * @param {CreateDiscordServerDto} createDiscordServerDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DiscordServersApi */ public createDiscordServer(createDiscordServerDto: CreateDiscordServerDto, options?: AxiosRequestConfig) { return DiscordServersApiFp(this.configuration).createDiscordServer(createDiscordServerDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DiscordServersApi */ public deleteDiscordServer(id: number, options?: AxiosRequestConfig) { return DiscordServersApiFp(this.configuration).deleteDiscordServer(id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DiscordServersApi */ public getDiscordServers(options?: AxiosRequestConfig) { return DiscordServersApiFp(this.configuration).getDiscordServers(options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DiscordServersApi */ public getInviteLinkByDiscordServerId(courseId: number, id: number, options?: AxiosRequestConfig) { return DiscordServersApiFp(this.configuration).getInviteLinkByDiscordServerId(courseId, id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DiscordServersApi */ public getReducedDiscordServers(options?: AxiosRequestConfig) { return DiscordServersApiFp(this.configuration).getReducedDiscordServers(options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} id * @param {UpdateDiscordServerDto} updateDiscordServerDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DiscordServersApi */ public updateDiscordServer(id: number, updateDiscordServerDto: UpdateDiscordServerDto, options?: AxiosRequestConfig) { return DiscordServersApiFp(this.configuration).updateDiscordServer(id, updateDiscordServerDto, options).then((request) => request(this.axios, this.basePath)); } } /** * EventsApi - axios parameter creator * @export */ export const EventsApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {CreateEventDto} createEventDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createEvent: async (createEventDto: CreateEventDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'createEventDto' is not null or undefined assertParamExists('createEvent', 'createEventDto', createEventDto) const localVarPath = `/events`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(createEventDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteEvent: async (id: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined assertParamExists('deleteEvent', 'id', id) const localVarPath = `/events/{id}` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getEvents: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/events`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} id * @param {UpdateEventDto} updateEventDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateEvent: async (id: number, updateEventDto: UpdateEventDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined assertParamExists('updateEvent', 'id', id) // verify required parameter 'updateEventDto' is not null or undefined assertParamExists('updateEvent', 'updateEventDto', updateEventDto) const localVarPath = `/events/{id}` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PATCH', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(updateEventDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * EventsApi - functional programming interface * @export */ export const EventsApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = EventsApiAxiosParamCreator(configuration) return { /** * * @param {CreateEventDto} createEventDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createEvent(createEventDto: CreateEventDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.createEvent(createEventDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async deleteEvent(id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.deleteEvent(id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getEvents(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getEvents(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} id * @param {UpdateEventDto} updateEventDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async updateEvent(id: number, updateEventDto: UpdateEventDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.updateEvent(id, updateEventDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * EventsApi - factory interface * @export */ export const EventsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = EventsApiFp(configuration) return { /** * * @param {CreateEventDto} createEventDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createEvent(createEventDto: CreateEventDto, options?: any): AxiosPromise { return localVarFp.createEvent(createEventDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteEvent(id: number, options?: any): AxiosPromise { return localVarFp.deleteEvent(id, options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getEvents(options?: any): AxiosPromise> { return localVarFp.getEvents(options).then((request) => request(axios, basePath)); }, /** * * @param {number} id * @param {UpdateEventDto} updateEventDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateEvent(id: number, updateEventDto: UpdateEventDto, options?: any): AxiosPromise { return localVarFp.updateEvent(id, updateEventDto, options).then((request) => request(axios, basePath)); }, }; }; /** * EventsApi - object-oriented interface * @export * @class EventsApi * @extends {BaseAPI} */ export class EventsApi extends BaseAPI { /** * * @param {CreateEventDto} createEventDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof EventsApi */ public createEvent(createEventDto: CreateEventDto, options?: AxiosRequestConfig) { return EventsApiFp(this.configuration).createEvent(createEventDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof EventsApi */ public deleteEvent(id: number, options?: AxiosRequestConfig) { return EventsApiFp(this.configuration).deleteEvent(id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof EventsApi */ public getEvents(options?: AxiosRequestConfig) { return EventsApiFp(this.configuration).getEvents(options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} id * @param {UpdateEventDto} updateEventDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof EventsApi */ public updateEvent(id: number, updateEventDto: UpdateEventDto, options?: AxiosRequestConfig) { return EventsApiFp(this.configuration).updateEvent(id, updateEventDto, options).then((request) => request(this.axios, this.basePath)); } } /** * GratitudesApi - axios parameter creator * @export */ export const GratitudesApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {CreateGratitudeDto} createGratitudeDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createGratitude: async (createGratitudeDto: CreateGratitudeDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'createGratitudeDto' is not null or undefined assertParamExists('createGratitude', 'createGratitudeDto', createGratitudeDto) const localVarPath = `/gratitudes`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(createGratitudeDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getBadges: async (courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getBadges', 'courseId', courseId) const localVarPath = `/gratitudes/badges/{courseId}` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getHeroesCountries: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/gratitudes/heroes/countries`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} current * @param {number} pageSize * @param {number} [courseId] * @param {boolean} [notActivist] * @param {string} [countryName] * @param {string} [startDate] * @param {string} [endDate] * @param {*} [options] Override http request option. * @throws {RequiredError} */ getHeroesRadar: async (current: number, pageSize: number, courseId?: number, notActivist?: boolean, countryName?: string, startDate?: string, endDate?: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'current' is not null or undefined assertParamExists('getHeroesRadar', 'current', current) // verify required parameter 'pageSize' is not null or undefined assertParamExists('getHeroesRadar', 'pageSize', pageSize) const localVarPath = `/gratitudes/heroes/radar`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; if (current !== undefined) { localVarQueryParameter['current'] = current; } if (pageSize !== undefined) { localVarQueryParameter['pageSize'] = pageSize; } if (courseId !== undefined) { localVarQueryParameter['courseId'] = courseId; } if (notActivist !== undefined) { localVarQueryParameter['notActivist'] = notActivist; } if (countryName !== undefined) { localVarQueryParameter['countryName'] = countryName; } if (startDate !== undefined) { localVarQueryParameter['startDate'] = (startDate as any instanceof Date) ? (startDate as any).toISOString() : startDate; } if (endDate !== undefined) { localVarQueryParameter['endDate'] = (endDate as any instanceof Date) ? (endDate as any).toISOString() : endDate; } setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} current * @param {number} pageSize * @param {number} [courseId] * @param {boolean} [notActivist] * @param {string} [countryName] * @param {string} [startDate] * @param {string} [endDate] * @param {*} [options] Override http request option. * @throws {RequiredError} */ getHeroesRadarCsv: async (current: number, pageSize: number, courseId?: number, notActivist?: boolean, countryName?: string, startDate?: string, endDate?: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'current' is not null or undefined assertParamExists('getHeroesRadarCsv', 'current', current) // verify required parameter 'pageSize' is not null or undefined assertParamExists('getHeroesRadarCsv', 'pageSize', pageSize) const localVarPath = `/gratitudes/heroes/radar/csv`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; if (current !== undefined) { localVarQueryParameter['current'] = current; } if (pageSize !== undefined) { localVarQueryParameter['pageSize'] = pageSize; } if (courseId !== undefined) { localVarQueryParameter['courseId'] = courseId; } if (notActivist !== undefined) { localVarQueryParameter['notActivist'] = notActivist; } if (countryName !== undefined) { localVarQueryParameter['countryName'] = countryName; } if (startDate !== undefined) { localVarQueryParameter['startDate'] = (startDate as any instanceof Date) ? (startDate as any).toISOString() : startDate; } if (endDate !== undefined) { localVarQueryParameter['endDate'] = (endDate as any instanceof Date) ? (endDate as any).toISOString() : endDate; } setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * GratitudesApi - functional programming interface * @export */ export const GratitudesApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = GratitudesApiAxiosParamCreator(configuration) return { /** * * @param {CreateGratitudeDto} createGratitudeDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createGratitude(createGratitudeDto: CreateGratitudeDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.createGratitude(createGratitudeDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getBadges(courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getBadges(courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getHeroesCountries(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getHeroesCountries(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} current * @param {number} pageSize * @param {number} [courseId] * @param {boolean} [notActivist] * @param {string} [countryName] * @param {string} [startDate] * @param {string} [endDate] * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getHeroesRadar(current: number, pageSize: number, courseId?: number, notActivist?: boolean, countryName?: string, startDate?: string, endDate?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getHeroesRadar(current, pageSize, courseId, notActivist, countryName, startDate, endDate, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} current * @param {number} pageSize * @param {number} [courseId] * @param {boolean} [notActivist] * @param {string} [countryName] * @param {string} [startDate] * @param {string} [endDate] * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getHeroesRadarCsv(current: number, pageSize: number, courseId?: number, notActivist?: boolean, countryName?: string, startDate?: string, endDate?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getHeroesRadarCsv(current, pageSize, courseId, notActivist, countryName, startDate, endDate, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * GratitudesApi - factory interface * @export */ export const GratitudesApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = GratitudesApiFp(configuration) return { /** * * @param {CreateGratitudeDto} createGratitudeDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createGratitude(createGratitudeDto: CreateGratitudeDto, options?: any): AxiosPromise { return localVarFp.createGratitude(createGratitudeDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getBadges(courseId: number, options?: any): AxiosPromise> { return localVarFp.getBadges(courseId, options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getHeroesCountries(options?: any): AxiosPromise> { return localVarFp.getHeroesCountries(options).then((request) => request(axios, basePath)); }, /** * * @param {number} current * @param {number} pageSize * @param {number} [courseId] * @param {boolean} [notActivist] * @param {string} [countryName] * @param {string} [startDate] * @param {string} [endDate] * @param {*} [options] Override http request option. * @throws {RequiredError} */ getHeroesRadar(current: number, pageSize: number, courseId?: number, notActivist?: boolean, countryName?: string, startDate?: string, endDate?: string, options?: any): AxiosPromise { return localVarFp.getHeroesRadar(current, pageSize, courseId, notActivist, countryName, startDate, endDate, options).then((request) => request(axios, basePath)); }, /** * * @param {number} current * @param {number} pageSize * @param {number} [courseId] * @param {boolean} [notActivist] * @param {string} [countryName] * @param {string} [startDate] * @param {string} [endDate] * @param {*} [options] Override http request option. * @throws {RequiredError} */ getHeroesRadarCsv(current: number, pageSize: number, courseId?: number, notActivist?: boolean, countryName?: string, startDate?: string, endDate?: string, options?: any): AxiosPromise { return localVarFp.getHeroesRadarCsv(current, pageSize, courseId, notActivist, countryName, startDate, endDate, options).then((request) => request(axios, basePath)); }, }; }; /** * GratitudesApi - object-oriented interface * @export * @class GratitudesApi * @extends {BaseAPI} */ export class GratitudesApi extends BaseAPI { /** * * @param {CreateGratitudeDto} createGratitudeDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof GratitudesApi */ public createGratitude(createGratitudeDto: CreateGratitudeDto, options?: AxiosRequestConfig) { return GratitudesApiFp(this.configuration).createGratitude(createGratitudeDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof GratitudesApi */ public getBadges(courseId: number, options?: AxiosRequestConfig) { return GratitudesApiFp(this.configuration).getBadges(courseId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof GratitudesApi */ public getHeroesCountries(options?: AxiosRequestConfig) { return GratitudesApiFp(this.configuration).getHeroesCountries(options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} current * @param {number} pageSize * @param {number} [courseId] * @param {boolean} [notActivist] * @param {string} [countryName] * @param {string} [startDate] * @param {string} [endDate] * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof GratitudesApi */ public getHeroesRadar(current: number, pageSize: number, courseId?: number, notActivist?: boolean, countryName?: string, startDate?: string, endDate?: string, options?: AxiosRequestConfig) { return GratitudesApiFp(this.configuration).getHeroesRadar(current, pageSize, courseId, notActivist, countryName, startDate, endDate, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} current * @param {number} pageSize * @param {number} [courseId] * @param {boolean} [notActivist] * @param {string} [countryName] * @param {string} [startDate] * @param {string} [endDate] * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof GratitudesApi */ public getHeroesRadarCsv(current: number, pageSize: number, courseId?: number, notActivist?: boolean, countryName?: string, startDate?: string, endDate?: string, options?: AxiosRequestConfig) { return GratitudesApiFp(this.configuration).getHeroesRadarCsv(current, pageSize, courseId, notActivist, countryName, startDate, endDate, options).then((request) => request(this.axios, this.basePath)); } } /** * MentorReviewsApi - axios parameter creator * @export */ export const MentorReviewsApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {number} courseId * @param {MentorReviewAssignDto} mentorReviewAssignDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ assignReviewer: async (courseId: number, mentorReviewAssignDto: MentorReviewAssignDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('assignReviewer', 'courseId', courseId) // verify required parameter 'mentorReviewAssignDto' is not null or undefined assertParamExists('assignReviewer', 'mentorReviewAssignDto', mentorReviewAssignDto) const localVarPath = `/course/{courseId}/mentor-reviews` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(mentorReviewAssignDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {string} current * @param {string} pageSize * @param {number} courseId * @param {string} [tasks] * @param {string} [student] * @param {string} [checker] * @param {string} [sortField] * @param {string} [sortOrder] * @param {*} [options] Override http request option. * @throws {RequiredError} */ getMentorReviews: async (current: string, pageSize: string, courseId: number, tasks?: string, student?: string, checker?: string, sortField?: string, sortOrder?: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'current' is not null or undefined assertParamExists('getMentorReviews', 'current', current) // verify required parameter 'pageSize' is not null or undefined assertParamExists('getMentorReviews', 'pageSize', pageSize) // verify required parameter 'courseId' is not null or undefined assertParamExists('getMentorReviews', 'courseId', courseId) const localVarPath = `/course/{courseId}/mentor-reviews` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; if (current !== undefined) { localVarQueryParameter['current'] = current; } if (pageSize !== undefined) { localVarQueryParameter['pageSize'] = pageSize; } if (tasks !== undefined) { localVarQueryParameter['tasks'] = tasks; } if (student !== undefined) { localVarQueryParameter['student'] = student; } if (checker !== undefined) { localVarQueryParameter['checker'] = checker; } if (sortField !== undefined) { localVarQueryParameter['sortField'] = sortField; } if (sortOrder !== undefined) { localVarQueryParameter['sortOrder'] = sortOrder; } setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * MentorReviewsApi - functional programming interface * @export */ export const MentorReviewsApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = MentorReviewsApiAxiosParamCreator(configuration) return { /** * * @param {number} courseId * @param {MentorReviewAssignDto} mentorReviewAssignDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async assignReviewer(courseId: number, mentorReviewAssignDto: MentorReviewAssignDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.assignReviewer(courseId, mentorReviewAssignDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {string} current * @param {string} pageSize * @param {number} courseId * @param {string} [tasks] * @param {string} [student] * @param {string} [checker] * @param {string} [sortField] * @param {string} [sortOrder] * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getMentorReviews(current: string, pageSize: string, courseId: number, tasks?: string, student?: string, checker?: string, sortField?: string, sortOrder?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getMentorReviews(current, pageSize, courseId, tasks, student, checker, sortField, sortOrder, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * MentorReviewsApi - factory interface * @export */ export const MentorReviewsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = MentorReviewsApiFp(configuration) return { /** * * @param {number} courseId * @param {MentorReviewAssignDto} mentorReviewAssignDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ assignReviewer(courseId: number, mentorReviewAssignDto: MentorReviewAssignDto, options?: any): AxiosPromise { return localVarFp.assignReviewer(courseId, mentorReviewAssignDto, options).then((request) => request(axios, basePath)); }, /** * * @param {string} current * @param {string} pageSize * @param {number} courseId * @param {string} [tasks] * @param {string} [student] * @param {string} [checker] * @param {string} [sortField] * @param {string} [sortOrder] * @param {*} [options] Override http request option. * @throws {RequiredError} */ getMentorReviews(current: string, pageSize: string, courseId: number, tasks?: string, student?: string, checker?: string, sortField?: string, sortOrder?: string, options?: any): AxiosPromise { return localVarFp.getMentorReviews(current, pageSize, courseId, tasks, student, checker, sortField, sortOrder, options).then((request) => request(axios, basePath)); }, }; }; /** * MentorReviewsApi - object-oriented interface * @export * @class MentorReviewsApi * @extends {BaseAPI} */ export class MentorReviewsApi extends BaseAPI { /** * * @param {number} courseId * @param {MentorReviewAssignDto} mentorReviewAssignDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof MentorReviewsApi */ public assignReviewer(courseId: number, mentorReviewAssignDto: MentorReviewAssignDto, options?: AxiosRequestConfig) { return MentorReviewsApiFp(this.configuration).assignReviewer(courseId, mentorReviewAssignDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {string} current * @param {string} pageSize * @param {number} courseId * @param {string} [tasks] * @param {string} [student] * @param {string} [checker] * @param {string} [sortField] * @param {string} [sortOrder] * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof MentorReviewsApi */ public getMentorReviews(current: string, pageSize: string, courseId: number, tasks?: string, student?: string, checker?: string, sortField?: string, sortOrder?: string, options?: AxiosRequestConfig) { return MentorReviewsApiFp(this.configuration).getMentorReviews(current, pageSize, courseId, tasks, student, checker, sortField, sortOrder, options).then((request) => request(this.axios, this.basePath)); } } /** * MentorsApi - axios parameter creator * @export */ export const MentorsApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {number} mentorId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseStudentsCount: async (mentorId: number, courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'mentorId' is not null or undefined assertParamExists('getCourseStudentsCount', 'mentorId', mentorId) // verify required parameter 'courseId' is not null or undefined assertParamExists('getCourseStudentsCount', 'courseId', courseId) const localVarPath = `/mentors/{mentorId}/course/{courseId}/students` .replace(`{${"mentorId"}}`, encodeURIComponent(String(mentorId))) .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} mentorId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getMentorDashboardData: async (mentorId: number, courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'mentorId' is not null or undefined assertParamExists('getMentorDashboardData', 'mentorId', mentorId) // verify required parameter 'courseId' is not null or undefined assertParamExists('getMentorDashboardData', 'courseId', courseId) const localVarPath = `/mentors/{mentorId}/course/{courseId}/dashboard` .replace(`{${"mentorId"}}`, encodeURIComponent(String(mentorId))) .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} mentorId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getMentorOptions: async (mentorId: number, courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'mentorId' is not null or undefined assertParamExists('getMentorOptions', 'mentorId', mentorId) // verify required parameter 'courseId' is not null or undefined assertParamExists('getMentorOptions', 'courseId', courseId) const localVarPath = `/mentors/{mentorId}/course/{courseId}/options` .replace(`{${"mentorId"}}`, encodeURIComponent(String(mentorId))) .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} mentorId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getMentorStudents: async (mentorId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'mentorId' is not null or undefined assertParamExists('getMentorStudents', 'mentorId', mentorId) const localVarPath = `/mentors/{mentorId}/students` .replace(`{${"mentorId"}}`, encodeURIComponent(String(mentorId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} mentorId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getRandomTask: async (mentorId: number, courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'mentorId' is not null or undefined assertParamExists('getRandomTask', 'mentorId', mentorId) // verify required parameter 'courseId' is not null or undefined assertParamExists('getRandomTask', 'courseId', courseId) const localVarPath = `/mentors/{mentorId}/course/{courseId}/random-task` .replace(`{${"mentorId"}}`, encodeURIComponent(String(mentorId))) .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * MentorsApi - functional programming interface * @export */ export const MentorsApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = MentorsApiAxiosParamCreator(configuration) return { /** * * @param {number} mentorId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getCourseStudentsCount(mentorId: number, courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getCourseStudentsCount(mentorId, courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} mentorId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getMentorDashboardData(mentorId: number, courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getMentorDashboardData(mentorId, courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} mentorId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getMentorOptions(mentorId: number, courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getMentorOptions(mentorId, courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} mentorId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getMentorStudents(mentorId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getMentorStudents(mentorId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} mentorId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getRandomTask(mentorId: number, courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getRandomTask(mentorId, courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * MentorsApi - factory interface * @export */ export const MentorsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = MentorsApiFp(configuration) return { /** * * @param {number} mentorId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseStudentsCount(mentorId: number, courseId: number, options?: any): AxiosPromise { return localVarFp.getCourseStudentsCount(mentorId, courseId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} mentorId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getMentorDashboardData(mentorId: number, courseId: number, options?: any): AxiosPromise> { return localVarFp.getMentorDashboardData(mentorId, courseId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} mentorId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getMentorOptions(mentorId: number, courseId: number, options?: any): AxiosPromise { return localVarFp.getMentorOptions(mentorId, courseId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} mentorId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getMentorStudents(mentorId: number, options?: any): AxiosPromise> { return localVarFp.getMentorStudents(mentorId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} mentorId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getRandomTask(mentorId: number, courseId: number, options?: any): AxiosPromise { return localVarFp.getRandomTask(mentorId, courseId, options).then((request) => request(axios, basePath)); }, }; }; /** * MentorsApi - object-oriented interface * @export * @class MentorsApi * @extends {BaseAPI} */ export class MentorsApi extends BaseAPI { /** * * @param {number} mentorId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof MentorsApi */ public getCourseStudentsCount(mentorId: number, courseId: number, options?: AxiosRequestConfig) { return MentorsApiFp(this.configuration).getCourseStudentsCount(mentorId, courseId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} mentorId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof MentorsApi */ public getMentorDashboardData(mentorId: number, courseId: number, options?: AxiosRequestConfig) { return MentorsApiFp(this.configuration).getMentorDashboardData(mentorId, courseId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} mentorId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof MentorsApi */ public getMentorOptions(mentorId: number, courseId: number, options?: AxiosRequestConfig) { return MentorsApiFp(this.configuration).getMentorOptions(mentorId, courseId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} mentorId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof MentorsApi */ public getMentorStudents(mentorId: number, options?: AxiosRequestConfig) { return MentorsApiFp(this.configuration).getMentorStudents(mentorId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} mentorId * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof MentorsApi */ public getRandomTask(mentorId: number, courseId: number, options?: AxiosRequestConfig) { return MentorsApiFp(this.configuration).getRandomTask(mentorId, courseId, options).then((request) => request(this.axios, this.basePath)); } } /** * MentorsHallOfFameApi - axios parameter creator * @export */ export const MentorsHallOfFameApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {boolean} [allTime] * @param {*} [options] Override http request option. * @throws {RequiredError} */ getTopMentors: async (allTime?: boolean, options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/mentors-hall-of-fame`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; if (allTime !== undefined) { localVarQueryParameter['allTime'] = allTime; } setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * MentorsHallOfFameApi - functional programming interface * @export */ export const MentorsHallOfFameApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = MentorsHallOfFameApiAxiosParamCreator(configuration) return { /** * * @param {boolean} [allTime] * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getTopMentors(allTime?: boolean, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getTopMentors(allTime, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * MentorsHallOfFameApi - factory interface * @export */ export const MentorsHallOfFameApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = MentorsHallOfFameApiFp(configuration) return { /** * * @param {boolean} [allTime] * @param {*} [options] Override http request option. * @throws {RequiredError} */ getTopMentors(allTime?: boolean, options?: any): AxiosPromise> { return localVarFp.getTopMentors(allTime, options).then((request) => request(axios, basePath)); }, }; }; /** * MentorsHallOfFameApi - object-oriented interface * @export * @class MentorsHallOfFameApi * @extends {BaseAPI} */ export class MentorsHallOfFameApi extends BaseAPI { /** * * @param {boolean} [allTime] * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof MentorsHallOfFameApi */ public getTopMentors(allTime?: boolean, options?: AxiosRequestConfig) { return MentorsHallOfFameApiFp(this.configuration).getTopMentors(allTime, options).then((request) => request(this.axios, this.basePath)); } } /** * NotificationsApi - axios parameter creator * @export */ export const NotificationsApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {UpdateNotificationDto} updateNotificationDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createNotification: async (updateNotificationDto: UpdateNotificationDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'updateNotificationDto' is not null or undefined assertParamExists('createNotification', 'updateNotificationDto', updateNotificationDto) const localVarPath = `/notifications`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(updateNotificationDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {string} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteNotification: async (id: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined assertParamExists('deleteNotification', 'id', id) const localVarPath = `/notifications/{id}` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getNotifications: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/notifications`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {UpdateNotificationDto} updateNotificationDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateNotification: async (updateNotificationDto: UpdateNotificationDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'updateNotificationDto' is not null or undefined assertParamExists('updateNotification', 'updateNotificationDto', updateNotificationDto) const localVarPath = `/notifications`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(updateNotificationDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * NotificationsApi - functional programming interface * @export */ export const NotificationsApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = NotificationsApiAxiosParamCreator(configuration) return { /** * * @param {UpdateNotificationDto} updateNotificationDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createNotification(updateNotificationDto: UpdateNotificationDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.createNotification(updateNotificationDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {string} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async deleteNotification(id: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.deleteNotification(id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getNotifications(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getNotifications(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {UpdateNotificationDto} updateNotificationDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async updateNotification(updateNotificationDto: UpdateNotificationDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.updateNotification(updateNotificationDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * NotificationsApi - factory interface * @export */ export const NotificationsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = NotificationsApiFp(configuration) return { /** * * @param {UpdateNotificationDto} updateNotificationDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createNotification(updateNotificationDto: UpdateNotificationDto, options?: any): AxiosPromise { return localVarFp.createNotification(updateNotificationDto, options).then((request) => request(axios, basePath)); }, /** * * @param {string} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteNotification(id: string, options?: any): AxiosPromise { return localVarFp.deleteNotification(id, options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getNotifications(options?: any): AxiosPromise> { return localVarFp.getNotifications(options).then((request) => request(axios, basePath)); }, /** * * @param {UpdateNotificationDto} updateNotificationDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateNotification(updateNotificationDto: UpdateNotificationDto, options?: any): AxiosPromise { return localVarFp.updateNotification(updateNotificationDto, options).then((request) => request(axios, basePath)); }, }; }; /** * NotificationsApi - object-oriented interface * @export * @class NotificationsApi * @extends {BaseAPI} */ export class NotificationsApi extends BaseAPI { /** * * @param {UpdateNotificationDto} updateNotificationDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof NotificationsApi */ public createNotification(updateNotificationDto: UpdateNotificationDto, options?: AxiosRequestConfig) { return NotificationsApiFp(this.configuration).createNotification(updateNotificationDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {string} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof NotificationsApi */ public deleteNotification(id: string, options?: AxiosRequestConfig) { return NotificationsApiFp(this.configuration).deleteNotification(id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof NotificationsApi */ public getNotifications(options?: AxiosRequestConfig) { return NotificationsApiFp(this.configuration).getNotifications(options).then((request) => request(this.axios, this.basePath)); } /** * * @param {UpdateNotificationDto} updateNotificationDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof NotificationsApi */ public updateNotification(updateNotificationDto: UpdateNotificationDto, options?: AxiosRequestConfig) { return NotificationsApiFp(this.configuration).updateNotification(updateNotificationDto, options).then((request) => request(this.axios, this.basePath)); } } /** * OpportunitiesApi - axios parameter creator * @export */ export const OpportunitiesApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ createConsent: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/opportunities/consent`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteConsent: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/opportunities/consent`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getApplicants: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/opportunities/applicants`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getConsent: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/opportunities/consent`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {string} uuid * @param {*} [options] Override http request option. * @throws {RequiredError} */ getPublicResume: async (uuid: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'uuid' is not null or undefined assertParamExists('getPublicResume', 'uuid', uuid) const localVarPath = `/opportunities/public/{uuid}` .replace(`{${"uuid"}}`, encodeURIComponent(String(uuid))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {string} githubId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getResume: async (githubId: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'githubId' is not null or undefined assertParamExists('getResume', 'githubId', githubId) const localVarPath = `/opportunities/{githubId}/resume` .replace(`{${"githubId"}}`, encodeURIComponent(String(githubId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ prolong: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/opportunities/prolong`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {string} githubId * @param {FormDataDto} formDataDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ saveResume: async (githubId: string, formDataDto: FormDataDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'githubId' is not null or undefined assertParamExists('saveResume', 'githubId', githubId) // verify required parameter 'formDataDto' is not null or undefined assertParamExists('saveResume', 'formDataDto', formDataDto) const localVarPath = `/opportunities/{githubId}/resume` .replace(`{${"githubId"}}`, encodeURIComponent(String(githubId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PATCH', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(formDataDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ setVisibility: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/opportunities/visibility`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * OpportunitiesApi - functional programming interface * @export */ export const OpportunitiesApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = OpportunitiesApiAxiosParamCreator(configuration) return { /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createConsent(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.createConsent(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async deleteConsent(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.deleteConsent(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getApplicants(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getApplicants(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getConsent(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getConsent(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {string} uuid * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getPublicResume(uuid: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getPublicResume(uuid, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {string} githubId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getResume(githubId: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getResume(githubId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async prolong(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.prolong(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {string} githubId * @param {FormDataDto} formDataDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async saveResume(githubId: string, formDataDto: FormDataDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.saveResume(githubId, formDataDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async setVisibility(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.setVisibility(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * OpportunitiesApi - factory interface * @export */ export const OpportunitiesApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = OpportunitiesApiFp(configuration) return { /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ createConsent(options?: any): AxiosPromise { return localVarFp.createConsent(options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteConsent(options?: any): AxiosPromise { return localVarFp.deleteConsent(options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getApplicants(options?: any): AxiosPromise> { return localVarFp.getApplicants(options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getConsent(options?: any): AxiosPromise { return localVarFp.getConsent(options).then((request) => request(axios, basePath)); }, /** * * @param {string} uuid * @param {*} [options] Override http request option. * @throws {RequiredError} */ getPublicResume(uuid: string, options?: any): AxiosPromise { return localVarFp.getPublicResume(uuid, options).then((request) => request(axios, basePath)); }, /** * * @param {string} githubId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getResume(githubId: string, options?: any): AxiosPromise { return localVarFp.getResume(githubId, options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ prolong(options?: any): AxiosPromise { return localVarFp.prolong(options).then((request) => request(axios, basePath)); }, /** * * @param {string} githubId * @param {FormDataDto} formDataDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ saveResume(githubId: string, formDataDto: FormDataDto, options?: any): AxiosPromise { return localVarFp.saveResume(githubId, formDataDto, options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ setVisibility(options?: any): AxiosPromise { return localVarFp.setVisibility(options).then((request) => request(axios, basePath)); }, }; }; /** * OpportunitiesApi - object-oriented interface * @export * @class OpportunitiesApi * @extends {BaseAPI} */ export class OpportunitiesApi extends BaseAPI { /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof OpportunitiesApi */ public createConsent(options?: AxiosRequestConfig) { return OpportunitiesApiFp(this.configuration).createConsent(options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof OpportunitiesApi */ public deleteConsent(options?: AxiosRequestConfig) { return OpportunitiesApiFp(this.configuration).deleteConsent(options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof OpportunitiesApi */ public getApplicants(options?: AxiosRequestConfig) { return OpportunitiesApiFp(this.configuration).getApplicants(options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof OpportunitiesApi */ public getConsent(options?: AxiosRequestConfig) { return OpportunitiesApiFp(this.configuration).getConsent(options).then((request) => request(this.axios, this.basePath)); } /** * * @param {string} uuid * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof OpportunitiesApi */ public getPublicResume(uuid: string, options?: AxiosRequestConfig) { return OpportunitiesApiFp(this.configuration).getPublicResume(uuid, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {string} githubId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof OpportunitiesApi */ public getResume(githubId: string, options?: AxiosRequestConfig) { return OpportunitiesApiFp(this.configuration).getResume(githubId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof OpportunitiesApi */ public prolong(options?: AxiosRequestConfig) { return OpportunitiesApiFp(this.configuration).prolong(options).then((request) => request(this.axios, this.basePath)); } /** * * @param {string} githubId * @param {FormDataDto} formDataDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof OpportunitiesApi */ public saveResume(githubId: string, formDataDto: FormDataDto, options?: AxiosRequestConfig) { return OpportunitiesApiFp(this.configuration).saveResume(githubId, formDataDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof OpportunitiesApi */ public setVisibility(options?: AxiosRequestConfig) { return OpportunitiesApiFp(this.configuration).setVisibility(options).then((request) => request(this.axios, this.basePath)); } } /** * ProfileApi - axios parameter creator * @export */ export const ProfileApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} */ getEndorsement: async (username: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'username' is not null or undefined assertParamExists('getEndorsement', 'username', username) const localVarPath = `/profile/{username}/endorsement` .replace(`{${"username"}}`, encodeURIComponent(String(username))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} */ getEndorsementData: async (username: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'username' is not null or undefined assertParamExists('getEndorsementData', 'username', username) const localVarPath = `/profile/{username}/endorsement-data` .replace(`{${"username"}}`, encodeURIComponent(String(username))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} */ getPersonalProfile: async (username: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'username' is not null or undefined assertParamExists('getPersonalProfile', 'username', username) const localVarPath = `/profile/{username}/personal` .replace(`{${"username"}}`, encodeURIComponent(String(username))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} */ getProfile: async (username: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'username' is not null or undefined assertParamExists('getProfile', 'username', username) const localVarPath = `/profile/{username}` .replace(`{${"username"}}`, encodeURIComponent(String(username))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} */ getUserCourses: async (username: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'username' is not null or undefined assertParamExists('getUserCourses', 'username', username) const localVarPath = `/profile/{username}/courses` .replace(`{${"username"}}`, encodeURIComponent(String(username))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} */ obfuscateProfile: async (username: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'username' is not null or undefined assertParamExists('obfuscateProfile', 'username', username) const localVarPath = `/profile/{username}` .replace(`{${"username"}}`, encodeURIComponent(String(username))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {UpdateProfileInfoDto} updateProfileInfoDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateProfileInfoFlat: async (updateProfileInfoDto: UpdateProfileInfoDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'updateProfileInfoDto' is not null or undefined assertParamExists('updateProfileInfoFlat', 'updateProfileInfoDto', updateProfileInfoDto) const localVarPath = `/profile/info`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PATCH', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(updateProfileInfoDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {UpdateUserDto} updateUserDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateUser: async (updateUserDto: UpdateUserDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'updateUserDto' is not null or undefined assertParamExists('updateUser', 'updateUserDto', updateUserDto) const localVarPath = `/profile/user`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(updateUserDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * ProfileApi - functional programming interface * @export */ export const ProfileApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = ProfileApiAxiosParamCreator(configuration) return { /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getEndorsement(username: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getEndorsement(username, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getEndorsementData(username: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getEndorsementData(username, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getPersonalProfile(username: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getPersonalProfile(username, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getProfile(username: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getProfile(username, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getUserCourses(username: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getUserCourses(username, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} */ async obfuscateProfile(username: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.obfuscateProfile(username, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {UpdateProfileInfoDto} updateProfileInfoDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async updateProfileInfoFlat(updateProfileInfoDto: UpdateProfileInfoDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.updateProfileInfoFlat(updateProfileInfoDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {UpdateUserDto} updateUserDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async updateUser(updateUserDto: UpdateUserDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.updateUser(updateUserDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * ProfileApi - factory interface * @export */ export const ProfileApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = ProfileApiFp(configuration) return { /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} */ getEndorsement(username: string, options?: any): AxiosPromise { return localVarFp.getEndorsement(username, options).then((request) => request(axios, basePath)); }, /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} */ getEndorsementData(username: string, options?: any): AxiosPromise { return localVarFp.getEndorsementData(username, options).then((request) => request(axios, basePath)); }, /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} */ getPersonalProfile(username: string, options?: any): AxiosPromise { return localVarFp.getPersonalProfile(username, options).then((request) => request(axios, basePath)); }, /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} */ getProfile(username: string, options?: any): AxiosPromise { return localVarFp.getProfile(username, options).then((request) => request(axios, basePath)); }, /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} */ getUserCourses(username: string, options?: any): AxiosPromise> { return localVarFp.getUserCourses(username, options).then((request) => request(axios, basePath)); }, /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} */ obfuscateProfile(username: string, options?: any): AxiosPromise { return localVarFp.obfuscateProfile(username, options).then((request) => request(axios, basePath)); }, /** * * @param {UpdateProfileInfoDto} updateProfileInfoDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateProfileInfoFlat(updateProfileInfoDto: UpdateProfileInfoDto, options?: any): AxiosPromise { return localVarFp.updateProfileInfoFlat(updateProfileInfoDto, options).then((request) => request(axios, basePath)); }, /** * * @param {UpdateUserDto} updateUserDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateUser(updateUserDto: UpdateUserDto, options?: any): AxiosPromise { return localVarFp.updateUser(updateUserDto, options).then((request) => request(axios, basePath)); }, }; }; /** * ProfileApi - object-oriented interface * @export * @class ProfileApi * @extends {BaseAPI} */ export class ProfileApi extends BaseAPI { /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof ProfileApi */ public getEndorsement(username: string, options?: AxiosRequestConfig) { return ProfileApiFp(this.configuration).getEndorsement(username, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof ProfileApi */ public getEndorsementData(username: string, options?: AxiosRequestConfig) { return ProfileApiFp(this.configuration).getEndorsementData(username, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof ProfileApi */ public getPersonalProfile(username: string, options?: AxiosRequestConfig) { return ProfileApiFp(this.configuration).getPersonalProfile(username, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof ProfileApi */ public getProfile(username: string, options?: AxiosRequestConfig) { return ProfileApiFp(this.configuration).getProfile(username, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof ProfileApi */ public getUserCourses(username: string, options?: AxiosRequestConfig) { return ProfileApiFp(this.configuration).getUserCourses(username, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {string} username * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof ProfileApi */ public obfuscateProfile(username: string, options?: AxiosRequestConfig) { return ProfileApiFp(this.configuration).obfuscateProfile(username, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {UpdateProfileInfoDto} updateProfileInfoDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof ProfileApi */ public updateProfileInfoFlat(updateProfileInfoDto: UpdateProfileInfoDto, options?: AxiosRequestConfig) { return ProfileApiFp(this.configuration).updateProfileInfoFlat(updateProfileInfoDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {UpdateUserDto} updateUserDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof ProfileApi */ public updateUser(updateUserDto: UpdateUserDto, options?: AxiosRequestConfig) { return ProfileApiFp(this.configuration).updateUser(updateUserDto, options).then((request) => request(this.axios, this.basePath)); } } /** * PromptsApi - axios parameter creator * @export */ export const PromptsApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {CreatePromptDto} createPromptDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createPrompt: async (createPromptDto: CreatePromptDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'createPromptDto' is not null or undefined assertParamExists('createPrompt', 'createPromptDto', createPromptDto) const localVarPath = `/prompts`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(createPromptDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ deletePrompt: async (id: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined assertParamExists('deletePrompt', 'id', id) const localVarPath = `/prompts/{id}` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getPrompts: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/prompts`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} id * @param {UpdatePromptDto} updatePromptDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updatePrompt: async (id: number, updatePromptDto: UpdatePromptDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined assertParamExists('updatePrompt', 'id', id) // verify required parameter 'updatePromptDto' is not null or undefined assertParamExists('updatePrompt', 'updatePromptDto', updatePromptDto) const localVarPath = `/prompts/{id}` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PATCH', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(updatePromptDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * PromptsApi - functional programming interface * @export */ export const PromptsApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = PromptsApiAxiosParamCreator(configuration) return { /** * * @param {CreatePromptDto} createPromptDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createPrompt(createPromptDto: CreatePromptDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.createPrompt(createPromptDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async deletePrompt(id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.deletePrompt(id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getPrompts(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getPrompts(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} id * @param {UpdatePromptDto} updatePromptDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async updatePrompt(id: number, updatePromptDto: UpdatePromptDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.updatePrompt(id, updatePromptDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * PromptsApi - factory interface * @export */ export const PromptsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = PromptsApiFp(configuration) return { /** * * @param {CreatePromptDto} createPromptDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createPrompt(createPromptDto: CreatePromptDto, options?: any): AxiosPromise { return localVarFp.createPrompt(createPromptDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ deletePrompt(id: number, options?: any): AxiosPromise { return localVarFp.deletePrompt(id, options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getPrompts(options?: any): AxiosPromise> { return localVarFp.getPrompts(options).then((request) => request(axios, basePath)); }, /** * * @param {number} id * @param {UpdatePromptDto} updatePromptDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updatePrompt(id: number, updatePromptDto: UpdatePromptDto, options?: any): AxiosPromise { return localVarFp.updatePrompt(id, updatePromptDto, options).then((request) => request(axios, basePath)); }, }; }; /** * PromptsApi - object-oriented interface * @export * @class PromptsApi * @extends {BaseAPI} */ export class PromptsApi extends BaseAPI { /** * * @param {CreatePromptDto} createPromptDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof PromptsApi */ public createPrompt(createPromptDto: CreatePromptDto, options?: AxiosRequestConfig) { return PromptsApiFp(this.configuration).createPrompt(createPromptDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof PromptsApi */ public deletePrompt(id: number, options?: AxiosRequestConfig) { return PromptsApiFp(this.configuration).deletePrompt(id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof PromptsApi */ public getPrompts(options?: AxiosRequestConfig) { return PromptsApiFp(this.configuration).getPrompts(options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} id * @param {UpdatePromptDto} updatePromptDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof PromptsApi */ public updatePrompt(id: number, updatePromptDto: UpdatePromptDto, options?: AxiosRequestConfig) { return PromptsApiFp(this.configuration).updatePrompt(id, updatePromptDto, options).then((request) => request(this.axios, this.basePath)); } } /** * RegistryApi - axios parameter creator * @export */ export const RegistryApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {string} githubId * @param {ApproveMentorDto} approveMentorDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ approveMentor: async (githubId: string, approveMentorDto: ApproveMentorDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'githubId' is not null or undefined assertParamExists('approveMentor', 'githubId', githubId) // verify required parameter 'approveMentorDto' is not null or undefined assertParamExists('approveMentor', 'approveMentorDto', approveMentorDto) const localVarPath = `/registry/mentor/{githubId}` .replace(`{${"githubId"}}`, encodeURIComponent(String(githubId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(approveMentorDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {string} githubId * @param {*} [options] Override http request option. * @throws {RequiredError} */ cancelMentorRegistry: async (githubId: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'githubId' is not null or undefined assertParamExists('cancelMentorRegistry', 'githubId', githubId) const localVarPath = `/registry/mentor/{githubId}` .replace(`{${"githubId"}}`, encodeURIComponent(String(githubId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {string} githubId * @param {CommentMentorRegistryDto} commentMentorRegistryDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ commentMentorRegistry: async (githubId: string, commentMentorRegistryDto: CommentMentorRegistryDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'githubId' is not null or undefined assertParamExists('commentMentorRegistry', 'githubId', githubId) // verify required parameter 'commentMentorRegistryDto' is not null or undefined assertParamExists('commentMentorRegistry', 'commentMentorRegistryDto', commentMentorRegistryDto) const localVarPath = `/registry/mentor/{githubId}/comment` .replace(`{${"githubId"}}`, encodeURIComponent(String(githubId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(commentMentorRegistryDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {'new' | 'all'} [status] * @param {number} [pageSize] * @param {number} [currentPage] * @param {string} [githubId] * @param {string} [cityName] * @param {Array} [preferedCourses] * @param {Array} [preselectedCourses] * @param {Array} [technicalMentoring] * @param {*} [options] Override http request option. * @throws {RequiredError} */ getMentorRegistries: async (status?: 'new' | 'all', pageSize?: number, currentPage?: number, githubId?: string, cityName?: string, preferedCourses?: Array, preselectedCourses?: Array, technicalMentoring?: Array, options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/registry/mentors`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; if (status !== undefined) { localVarQueryParameter['status'] = status; } if (pageSize !== undefined) { localVarQueryParameter['pageSize'] = pageSize; } if (currentPage !== undefined) { localVarQueryParameter['currentPage'] = currentPage; } if (githubId !== undefined) { localVarQueryParameter['githubId'] = githubId; } if (cityName !== undefined) { localVarQueryParameter['cityName'] = cityName; } if (preferedCourses) { localVarQueryParameter['preferedCourses'] = preferedCourses; } if (preselectedCourses) { localVarQueryParameter['preselectedCourses'] = preselectedCourses; } if (technicalMentoring) { localVarQueryParameter['technicalMentoring'] = technicalMentoring; } setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {InviteMentorsDto} inviteMentorsDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ inviteMentors: async (inviteMentorsDto: InviteMentorsDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'inviteMentorsDto' is not null or undefined assertParamExists('inviteMentors', 'inviteMentorsDto', inviteMentorsDto) const localVarPath = `/registry/mentors/invite`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(inviteMentorsDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * RegistryApi - functional programming interface * @export */ export const RegistryApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = RegistryApiAxiosParamCreator(configuration) return { /** * * @param {string} githubId * @param {ApproveMentorDto} approveMentorDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async approveMentor(githubId: string, approveMentorDto: ApproveMentorDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.approveMentor(githubId, approveMentorDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {string} githubId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async cancelMentorRegistry(githubId: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.cancelMentorRegistry(githubId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {string} githubId * @param {CommentMentorRegistryDto} commentMentorRegistryDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async commentMentorRegistry(githubId: string, commentMentorRegistryDto: CommentMentorRegistryDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.commentMentorRegistry(githubId, commentMentorRegistryDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {'new' | 'all'} [status] * @param {number} [pageSize] * @param {number} [currentPage] * @param {string} [githubId] * @param {string} [cityName] * @param {Array} [preferedCourses] * @param {Array} [preselectedCourses] * @param {Array} [technicalMentoring] * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getMentorRegistries(status?: 'new' | 'all', pageSize?: number, currentPage?: number, githubId?: string, cityName?: string, preferedCourses?: Array, preselectedCourses?: Array, technicalMentoring?: Array, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getMentorRegistries(status, pageSize, currentPage, githubId, cityName, preferedCourses, preselectedCourses, technicalMentoring, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {InviteMentorsDto} inviteMentorsDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async inviteMentors(inviteMentorsDto: InviteMentorsDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.inviteMentors(inviteMentorsDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * RegistryApi - factory interface * @export */ export const RegistryApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = RegistryApiFp(configuration) return { /** * * @param {string} githubId * @param {ApproveMentorDto} approveMentorDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ approveMentor(githubId: string, approveMentorDto: ApproveMentorDto, options?: any): AxiosPromise { return localVarFp.approveMentor(githubId, approveMentorDto, options).then((request) => request(axios, basePath)); }, /** * * @param {string} githubId * @param {*} [options] Override http request option. * @throws {RequiredError} */ cancelMentorRegistry(githubId: string, options?: any): AxiosPromise { return localVarFp.cancelMentorRegistry(githubId, options).then((request) => request(axios, basePath)); }, /** * * @param {string} githubId * @param {CommentMentorRegistryDto} commentMentorRegistryDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ commentMentorRegistry(githubId: string, commentMentorRegistryDto: CommentMentorRegistryDto, options?: any): AxiosPromise { return localVarFp.commentMentorRegistry(githubId, commentMentorRegistryDto, options).then((request) => request(axios, basePath)); }, /** * * @param {'new' | 'all'} [status] * @param {number} [pageSize] * @param {number} [currentPage] * @param {string} [githubId] * @param {string} [cityName] * @param {Array} [preferedCourses] * @param {Array} [preselectedCourses] * @param {Array} [technicalMentoring] * @param {*} [options] Override http request option. * @throws {RequiredError} */ getMentorRegistries(status?: 'new' | 'all', pageSize?: number, currentPage?: number, githubId?: string, cityName?: string, preferedCourses?: Array, preselectedCourses?: Array, technicalMentoring?: Array, options?: any): AxiosPromise { return localVarFp.getMentorRegistries(status, pageSize, currentPage, githubId, cityName, preferedCourses, preselectedCourses, technicalMentoring, options).then((request) => request(axios, basePath)); }, /** * * @param {InviteMentorsDto} inviteMentorsDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ inviteMentors(inviteMentorsDto: InviteMentorsDto, options?: any): AxiosPromise { return localVarFp.inviteMentors(inviteMentorsDto, options).then((request) => request(axios, basePath)); }, }; }; /** * RegistryApi - object-oriented interface * @export * @class RegistryApi * @extends {BaseAPI} */ export class RegistryApi extends BaseAPI { /** * * @param {string} githubId * @param {ApproveMentorDto} approveMentorDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof RegistryApi */ public approveMentor(githubId: string, approveMentorDto: ApproveMentorDto, options?: AxiosRequestConfig) { return RegistryApiFp(this.configuration).approveMentor(githubId, approveMentorDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {string} githubId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof RegistryApi */ public cancelMentorRegistry(githubId: string, options?: AxiosRequestConfig) { return RegistryApiFp(this.configuration).cancelMentorRegistry(githubId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {string} githubId * @param {CommentMentorRegistryDto} commentMentorRegistryDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof RegistryApi */ public commentMentorRegistry(githubId: string, commentMentorRegistryDto: CommentMentorRegistryDto, options?: AxiosRequestConfig) { return RegistryApiFp(this.configuration).commentMentorRegistry(githubId, commentMentorRegistryDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {'new' | 'all'} [status] * @param {number} [pageSize] * @param {number} [currentPage] * @param {string} [githubId] * @param {string} [cityName] * @param {Array} [preferedCourses] * @param {Array} [preselectedCourses] * @param {Array} [technicalMentoring] * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof RegistryApi */ public getMentorRegistries(status?: 'new' | 'all', pageSize?: number, currentPage?: number, githubId?: string, cityName?: string, preferedCourses?: Array, preselectedCourses?: Array, technicalMentoring?: Array, options?: AxiosRequestConfig) { return RegistryApiFp(this.configuration).getMentorRegistries(status, pageSize, currentPage, githubId, cityName, preferedCourses, preselectedCourses, technicalMentoring, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {InviteMentorsDto} inviteMentorsDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof RegistryApi */ public inviteMentors(inviteMentorsDto: InviteMentorsDto, options?: AxiosRequestConfig) { return RegistryApiFp(this.configuration).inviteMentors(inviteMentorsDto, options).then((request) => request(this.axios, this.basePath)); } } /** * ScheduleApi - axios parameter creator * @export */ export const ScheduleApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {CheckScheduleChangesDto} checkScheduleChangesDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ notifyScheduleChanges: async (checkScheduleChangesDto: CheckScheduleChangesDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'checkScheduleChangesDto' is not null or undefined assertParamExists('notifyScheduleChanges', 'checkScheduleChangesDto', checkScheduleChangesDto) const localVarPath = `/schedule/notify/changes`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(checkScheduleChangesDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * ScheduleApi - functional programming interface * @export */ export const ScheduleApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = ScheduleApiAxiosParamCreator(configuration) return { /** * * @param {CheckScheduleChangesDto} checkScheduleChangesDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async notifyScheduleChanges(checkScheduleChangesDto: CheckScheduleChangesDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.notifyScheduleChanges(checkScheduleChangesDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * ScheduleApi - factory interface * @export */ export const ScheduleApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = ScheduleApiFp(configuration) return { /** * * @param {CheckScheduleChangesDto} checkScheduleChangesDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ notifyScheduleChanges(checkScheduleChangesDto: CheckScheduleChangesDto, options?: any): AxiosPromise { return localVarFp.notifyScheduleChanges(checkScheduleChangesDto, options).then((request) => request(axios, basePath)); }, }; }; /** * ScheduleApi - object-oriented interface * @export * @class ScheduleApi * @extends {BaseAPI} */ export class ScheduleApi extends BaseAPI { /** * * @param {CheckScheduleChangesDto} checkScheduleChangesDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof ScheduleApi */ public notifyScheduleChanges(checkScheduleChangesDto: CheckScheduleChangesDto, options?: AxiosRequestConfig) { return ScheduleApiFp(this.configuration).notifyScheduleChanges(checkScheduleChangesDto, options).then((request) => request(this.axios, this.basePath)); } } /** * SessionApi - axios parameter creator * @export */ export const SessionApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getSession: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/session`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * SessionApi - functional programming interface * @export */ export const SessionApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = SessionApiAxiosParamCreator(configuration) return { /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getSession(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getSession(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * SessionApi - factory interface * @export */ export const SessionApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = SessionApiFp(configuration) return { /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getSession(options?: any): AxiosPromise { return localVarFp.getSession(options).then((request) => request(axios, basePath)); }, }; }; /** * SessionApi - object-oriented interface * @export * @class SessionApi * @extends {BaseAPI} */ export class SessionApi extends BaseAPI { /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof SessionApi */ public getSession(options?: AxiosRequestConfig) { return SessionApiFp(this.configuration).getSession(options).then((request) => request(this.axios, this.basePath)); } } /** * StudentsApi - axios parameter creator * @export */ export const StudentsApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {number} courseId * @param {ExpelStatusDto} expelStatusDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ expelStudents: async (courseId: number, expelStatusDto: ExpelStatusDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('expelStudents', 'courseId', courseId) // verify required parameter 'expelStatusDto' is not null or undefined assertParamExists('expelStudents', 'expelStatusDto', expelStatusDto) const localVarPath = `/courses/{courseId}/students/expel` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(expelStatusDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} studentId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getStudent: async (studentId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'studentId' is not null or undefined assertParamExists('getStudent', 'studentId', studentId) const localVarPath = `/students/{studentId}` .replace(`{${"studentId"}}`, encodeURIComponent(String(studentId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {string} githubId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getStudentSummary: async (courseId: number, githubId: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getStudentSummary', 'courseId', courseId) // verify required parameter 'githubId' is not null or undefined assertParamExists('getStudentSummary', 'githubId', githubId) const localVarPath = `/courses/{courseId}/students/{githubId}/summary` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"githubId"}}`, encodeURIComponent(String(githubId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {string} current * @param {string} pageSize * @param {string} [student] * @param {string} [country] * @param {string} [city] * @param {string} [ongoingCourses] * @param {string} [previousCourses] * @param {*} [options] Override http request option. * @throws {RequiredError} */ getUserStudents: async (current: string, pageSize: string, student?: string, country?: string, city?: string, ongoingCourses?: string, previousCourses?: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'current' is not null or undefined assertParamExists('getUserStudents', 'current', current) // verify required parameter 'pageSize' is not null or undefined assertParamExists('getUserStudents', 'pageSize', pageSize) const localVarPath = `/students`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; if (current !== undefined) { localVarQueryParameter['current'] = current; } if (pageSize !== undefined) { localVarQueryParameter['pageSize'] = pageSize; } if (student !== undefined) { localVarQueryParameter['student'] = student; } if (country !== undefined) { localVarQueryParameter['country'] = country; } if (city !== undefined) { localVarQueryParameter['city'] = city; } if (ongoingCourses !== undefined) { localVarQueryParameter['ongoingCourses'] = ongoingCourses; } if (previousCourses !== undefined) { localVarQueryParameter['previousCourses'] = previousCourses; } setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * StudentsApi - functional programming interface * @export */ export const StudentsApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = StudentsApiAxiosParamCreator(configuration) return { /** * * @param {number} courseId * @param {ExpelStatusDto} expelStatusDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async expelStudents(courseId: number, expelStatusDto: ExpelStatusDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.expelStudents(courseId, expelStatusDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} studentId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getStudent(studentId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getStudent(studentId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {string} githubId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getStudentSummary(courseId: number, githubId: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getStudentSummary(courseId, githubId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {string} current * @param {string} pageSize * @param {string} [student] * @param {string} [country] * @param {string} [city] * @param {string} [ongoingCourses] * @param {string} [previousCourses] * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getUserStudents(current: string, pageSize: string, student?: string, country?: string, city?: string, ongoingCourses?: string, previousCourses?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getUserStudents(current, pageSize, student, country, city, ongoingCourses, previousCourses, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * StudentsApi - factory interface * @export */ export const StudentsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = StudentsApiFp(configuration) return { /** * * @param {number} courseId * @param {ExpelStatusDto} expelStatusDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ expelStudents(courseId: number, expelStatusDto: ExpelStatusDto, options?: any): AxiosPromise { return localVarFp.expelStudents(courseId, expelStatusDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} studentId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getStudent(studentId: number, options?: any): AxiosPromise { return localVarFp.getStudent(studentId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {string} githubId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getStudentSummary(courseId: number, githubId: string, options?: any): AxiosPromise { return localVarFp.getStudentSummary(courseId, githubId, options).then((request) => request(axios, basePath)); }, /** * * @param {string} current * @param {string} pageSize * @param {string} [student] * @param {string} [country] * @param {string} [city] * @param {string} [ongoingCourses] * @param {string} [previousCourses] * @param {*} [options] Override http request option. * @throws {RequiredError} */ getUserStudents(current: string, pageSize: string, student?: string, country?: string, city?: string, ongoingCourses?: string, previousCourses?: string, options?: any): AxiosPromise { return localVarFp.getUserStudents(current, pageSize, student, country, city, ongoingCourses, previousCourses, options).then((request) => request(axios, basePath)); }, }; }; /** * StudentsApi - object-oriented interface * @export * @class StudentsApi * @extends {BaseAPI} */ export class StudentsApi extends BaseAPI { /** * * @param {number} courseId * @param {ExpelStatusDto} expelStatusDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof StudentsApi */ public expelStudents(courseId: number, expelStatusDto: ExpelStatusDto, options?: AxiosRequestConfig) { return StudentsApiFp(this.configuration).expelStudents(courseId, expelStatusDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} studentId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof StudentsApi */ public getStudent(studentId: number, options?: AxiosRequestConfig) { return StudentsApiFp(this.configuration).getStudent(studentId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {string} githubId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof StudentsApi */ public getStudentSummary(courseId: number, githubId: string, options?: AxiosRequestConfig) { return StudentsApiFp(this.configuration).getStudentSummary(courseId, githubId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {string} current * @param {string} pageSize * @param {string} [student] * @param {string} [country] * @param {string} [city] * @param {string} [ongoingCourses] * @param {string} [previousCourses] * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof StudentsApi */ public getUserStudents(current: string, pageSize: string, student?: string, country?: string, city?: string, ongoingCourses?: string, previousCourses?: string, options?: AxiosRequestConfig) { return StudentsApiFp(this.configuration).getUserStudents(current, pageSize, student, country, city, ongoingCourses, previousCourses, options).then((request) => request(this.axios, this.basePath)); } } /** * StudentsFeedbacksApi - axios parameter creator * @export */ export const StudentsFeedbacksApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {number} studentId * @param {CreateStudentFeedbackDto} createStudentFeedbackDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createStudentFeedback: async (studentId: number, createStudentFeedbackDto: CreateStudentFeedbackDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'studentId' is not null or undefined assertParamExists('createStudentFeedback', 'studentId', studentId) // verify required parameter 'createStudentFeedbackDto' is not null or undefined assertParamExists('createStudentFeedback', 'createStudentFeedbackDto', createStudentFeedbackDto) const localVarPath = `/students/{studentId}/feedbacks` .replace(`{${"studentId"}}`, encodeURIComponent(String(studentId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(createStudentFeedbackDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} studentId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ getStudentFeedback: async (studentId: number, id: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'studentId' is not null or undefined assertParamExists('getStudentFeedback', 'studentId', studentId) // verify required parameter 'id' is not null or undefined assertParamExists('getStudentFeedback', 'id', id) const localVarPath = `/students/{studentId}/feedbacks/{id}` .replace(`{${"studentId"}}`, encodeURIComponent(String(studentId))) .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} studentId * @param {number} id * @param {UpdateStudentFeedbackDto} updateStudentFeedbackDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateStudentFeedback: async (studentId: number, id: number, updateStudentFeedbackDto: UpdateStudentFeedbackDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'studentId' is not null or undefined assertParamExists('updateStudentFeedback', 'studentId', studentId) // verify required parameter 'id' is not null or undefined assertParamExists('updateStudentFeedback', 'id', id) // verify required parameter 'updateStudentFeedbackDto' is not null or undefined assertParamExists('updateStudentFeedback', 'updateStudentFeedbackDto', updateStudentFeedbackDto) const localVarPath = `/students/{studentId}/feedbacks/{id}` .replace(`{${"studentId"}}`, encodeURIComponent(String(studentId))) .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PATCH', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(updateStudentFeedbackDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * StudentsFeedbacksApi - functional programming interface * @export */ export const StudentsFeedbacksApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = StudentsFeedbacksApiAxiosParamCreator(configuration) return { /** * * @param {number} studentId * @param {CreateStudentFeedbackDto} createStudentFeedbackDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createStudentFeedback(studentId: number, createStudentFeedbackDto: CreateStudentFeedbackDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.createStudentFeedback(studentId, createStudentFeedbackDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} studentId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getStudentFeedback(studentId: number, id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getStudentFeedback(studentId, id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} studentId * @param {number} id * @param {UpdateStudentFeedbackDto} updateStudentFeedbackDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async updateStudentFeedback(studentId: number, id: number, updateStudentFeedbackDto: UpdateStudentFeedbackDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.updateStudentFeedback(studentId, id, updateStudentFeedbackDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * StudentsFeedbacksApi - factory interface * @export */ export const StudentsFeedbacksApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = StudentsFeedbacksApiFp(configuration) return { /** * * @param {number} studentId * @param {CreateStudentFeedbackDto} createStudentFeedbackDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createStudentFeedback(studentId: number, createStudentFeedbackDto: CreateStudentFeedbackDto, options?: any): AxiosPromise { return localVarFp.createStudentFeedback(studentId, createStudentFeedbackDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} studentId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ getStudentFeedback(studentId: number, id: number, options?: any): AxiosPromise { return localVarFp.getStudentFeedback(studentId, id, options).then((request) => request(axios, basePath)); }, /** * * @param {number} studentId * @param {number} id * @param {UpdateStudentFeedbackDto} updateStudentFeedbackDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateStudentFeedback(studentId: number, id: number, updateStudentFeedbackDto: UpdateStudentFeedbackDto, options?: any): AxiosPromise { return localVarFp.updateStudentFeedback(studentId, id, updateStudentFeedbackDto, options).then((request) => request(axios, basePath)); }, }; }; /** * StudentsFeedbacksApi - object-oriented interface * @export * @class StudentsFeedbacksApi * @extends {BaseAPI} */ export class StudentsFeedbacksApi extends BaseAPI { /** * * @param {number} studentId * @param {CreateStudentFeedbackDto} createStudentFeedbackDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof StudentsFeedbacksApi */ public createStudentFeedback(studentId: number, createStudentFeedbackDto: CreateStudentFeedbackDto, options?: AxiosRequestConfig) { return StudentsFeedbacksApiFp(this.configuration).createStudentFeedback(studentId, createStudentFeedbackDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} studentId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof StudentsFeedbacksApi */ public getStudentFeedback(studentId: number, id: number, options?: AxiosRequestConfig) { return StudentsFeedbacksApiFp(this.configuration).getStudentFeedback(studentId, id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} studentId * @param {number} id * @param {UpdateStudentFeedbackDto} updateStudentFeedbackDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof StudentsFeedbacksApi */ public updateStudentFeedback(studentId: number, id: number, updateStudentFeedbackDto: UpdateStudentFeedbackDto, options?: AxiosRequestConfig) { return StudentsFeedbacksApiFp(this.configuration).updateStudentFeedback(studentId, id, updateStudentFeedbackDto, options).then((request) => request(this.axios, this.basePath)); } } /** * StudentsScoreApi - axios parameter creator * @export */ export const StudentsScoreApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {string} activeOnly * @param {'rank' | 'totalScore' | 'crossCheckScore' | 'githubId' | 'name' | 'cityName' | 'mentor' | 'totalScoreChangeDate' | 'repositoryLastActivityDate'} orderBy * @param {'asc' | 'desc'} orderDirection * @param {string} current * @param {string} pageSize * @param {number} courseId * @param {string} [githubId] * @param {string} [name] * @param {string} [mentorGithubId] * @param {string} [cityName] * @param {*} [options] Override http request option. * @throws {RequiredError} */ getScore: async (activeOnly: string, orderBy: 'rank' | 'totalScore' | 'crossCheckScore' | 'githubId' | 'name' | 'cityName' | 'mentor' | 'totalScoreChangeDate' | 'repositoryLastActivityDate', orderDirection: 'asc' | 'desc', current: string, pageSize: string, courseId: number, githubId?: string, name?: string, mentorGithubId?: string, cityName?: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'activeOnly' is not null or undefined assertParamExists('getScore', 'activeOnly', activeOnly) // verify required parameter 'orderBy' is not null or undefined assertParamExists('getScore', 'orderBy', orderBy) // verify required parameter 'orderDirection' is not null or undefined assertParamExists('getScore', 'orderDirection', orderDirection) // verify required parameter 'current' is not null or undefined assertParamExists('getScore', 'current', current) // verify required parameter 'pageSize' is not null or undefined assertParamExists('getScore', 'pageSize', pageSize) // verify required parameter 'courseId' is not null or undefined assertParamExists('getScore', 'courseId', courseId) const localVarPath = `/course/{courseId}/students/score` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; if (activeOnly !== undefined) { localVarQueryParameter['activeOnly'] = activeOnly; } if (orderBy !== undefined) { localVarQueryParameter['orderBy'] = orderBy; } if (orderDirection !== undefined) { localVarQueryParameter['orderDirection'] = orderDirection; } if (current !== undefined) { localVarQueryParameter['current'] = current; } if (pageSize !== undefined) { localVarQueryParameter['pageSize'] = pageSize; } if (githubId !== undefined) { localVarQueryParameter['githubId'] = githubId; } if (name !== undefined) { localVarQueryParameter['name'] = name; } if (mentorGithubId !== undefined) { localVarQueryParameter['mentor.githubId'] = mentorGithubId; } if (cityName !== undefined) { localVarQueryParameter['cityName'] = cityName; } setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {string} githubId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getStudentScore: async (courseId: number, githubId: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getStudentScore', 'courseId', courseId) // verify required parameter 'githubId' is not null or undefined assertParamExists('getStudentScore', 'githubId', githubId) const localVarPath = `/course/{courseId}/students/score/{githubId}` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"githubId"}}`, encodeURIComponent(String(githubId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * StudentsScoreApi - functional programming interface * @export */ export const StudentsScoreApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = StudentsScoreApiAxiosParamCreator(configuration) return { /** * * @param {string} activeOnly * @param {'rank' | 'totalScore' | 'crossCheckScore' | 'githubId' | 'name' | 'cityName' | 'mentor' | 'totalScoreChangeDate' | 'repositoryLastActivityDate'} orderBy * @param {'asc' | 'desc'} orderDirection * @param {string} current * @param {string} pageSize * @param {number} courseId * @param {string} [githubId] * @param {string} [name] * @param {string} [mentorGithubId] * @param {string} [cityName] * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getScore(activeOnly: string, orderBy: 'rank' | 'totalScore' | 'crossCheckScore' | 'githubId' | 'name' | 'cityName' | 'mentor' | 'totalScoreChangeDate' | 'repositoryLastActivityDate', orderDirection: 'asc' | 'desc', current: string, pageSize: string, courseId: number, githubId?: string, name?: string, mentorGithubId?: string, cityName?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getScore(activeOnly, orderBy, orderDirection, current, pageSize, courseId, githubId, name, mentorGithubId, cityName, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {string} githubId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getStudentScore(courseId: number, githubId: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getStudentScore(courseId, githubId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * StudentsScoreApi - factory interface * @export */ export const StudentsScoreApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = StudentsScoreApiFp(configuration) return { /** * * @param {string} activeOnly * @param {'rank' | 'totalScore' | 'crossCheckScore' | 'githubId' | 'name' | 'cityName' | 'mentor' | 'totalScoreChangeDate' | 'repositoryLastActivityDate'} orderBy * @param {'asc' | 'desc'} orderDirection * @param {string} current * @param {string} pageSize * @param {number} courseId * @param {string} [githubId] * @param {string} [name] * @param {string} [mentorGithubId] * @param {string} [cityName] * @param {*} [options] Override http request option. * @throws {RequiredError} */ getScore(activeOnly: string, orderBy: 'rank' | 'totalScore' | 'crossCheckScore' | 'githubId' | 'name' | 'cityName' | 'mentor' | 'totalScoreChangeDate' | 'repositoryLastActivityDate', orderDirection: 'asc' | 'desc', current: string, pageSize: string, courseId: number, githubId?: string, name?: string, mentorGithubId?: string, cityName?: string, options?: any): AxiosPromise { return localVarFp.getScore(activeOnly, orderBy, orderDirection, current, pageSize, courseId, githubId, name, mentorGithubId, cityName, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {string} githubId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getStudentScore(courseId: number, githubId: string, options?: any): AxiosPromise { return localVarFp.getStudentScore(courseId, githubId, options).then((request) => request(axios, basePath)); }, }; }; /** * StudentsScoreApi - object-oriented interface * @export * @class StudentsScoreApi * @extends {BaseAPI} */ export class StudentsScoreApi extends BaseAPI { /** * * @param {string} activeOnly * @param {'rank' | 'totalScore' | 'crossCheckScore' | 'githubId' | 'name' | 'cityName' | 'mentor' | 'totalScoreChangeDate' | 'repositoryLastActivityDate'} orderBy * @param {'asc' | 'desc'} orderDirection * @param {string} current * @param {string} pageSize * @param {number} courseId * @param {string} [githubId] * @param {string} [name] * @param {string} [mentorGithubId] * @param {string} [cityName] * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof StudentsScoreApi */ public getScore(activeOnly: string, orderBy: 'rank' | 'totalScore' | 'crossCheckScore' | 'githubId' | 'name' | 'cityName' | 'mentor' | 'totalScoreChangeDate' | 'repositoryLastActivityDate', orderDirection: 'asc' | 'desc', current: string, pageSize: string, courseId: number, githubId?: string, name?: string, mentorGithubId?: string, cityName?: string, options?: AxiosRequestConfig) { return StudentsScoreApiFp(this.configuration).getScore(activeOnly, orderBy, orderDirection, current, pageSize, courseId, githubId, name, mentorGithubId, cityName, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {string} githubId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof StudentsScoreApi */ public getStudentScore(courseId: number, githubId: string, options?: AxiosRequestConfig) { return StudentsScoreApiFp(this.configuration).getStudentScore(courseId, githubId, options).then((request) => request(this.axios, this.basePath)); } } /** * TasksApi - axios parameter creator * @export */ export const TasksApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {CreateTaskDto} createTaskDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createTask: async (createTaskDto: CreateTaskDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'createTaskDto' is not null or undefined assertParamExists('createTask', 'createTaskDto', createTaskDto) const localVarPath = `/tasks`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(createTaskDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteTask: async (id: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined assertParamExists('deleteTask', 'id', id) const localVarPath = `/tasks/{id}` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getTasks: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/tasks`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} id * @param {UpdateTaskDto} updateTaskDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateTask: async (id: number, updateTaskDto: UpdateTaskDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined assertParamExists('updateTask', 'id', id) // verify required parameter 'updateTaskDto' is not null or undefined assertParamExists('updateTask', 'updateTaskDto', updateTaskDto) const localVarPath = `/tasks/{id}` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PATCH', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(updateTaskDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * TasksApi - functional programming interface * @export */ export const TasksApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = TasksApiAxiosParamCreator(configuration) return { /** * * @param {CreateTaskDto} createTaskDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createTask(createTaskDto: CreateTaskDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.createTask(createTaskDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async deleteTask(id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.deleteTask(id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getTasks(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getTasks(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} id * @param {UpdateTaskDto} updateTaskDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async updateTask(id: number, updateTaskDto: UpdateTaskDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.updateTask(id, updateTaskDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * TasksApi - factory interface * @export */ export const TasksApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = TasksApiFp(configuration) return { /** * * @param {CreateTaskDto} createTaskDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createTask(createTaskDto: CreateTaskDto, options?: any): AxiosPromise { return localVarFp.createTask(createTaskDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteTask(id: number, options?: any): AxiosPromise { return localVarFp.deleteTask(id, options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getTasks(options?: any): AxiosPromise> { return localVarFp.getTasks(options).then((request) => request(axios, basePath)); }, /** * * @param {number} id * @param {UpdateTaskDto} updateTaskDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateTask(id: number, updateTaskDto: UpdateTaskDto, options?: any): AxiosPromise { return localVarFp.updateTask(id, updateTaskDto, options).then((request) => request(axios, basePath)); }, }; }; /** * TasksApi - object-oriented interface * @export * @class TasksApi * @extends {BaseAPI} */ export class TasksApi extends BaseAPI { /** * * @param {CreateTaskDto} createTaskDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TasksApi */ public createTask(createTaskDto: CreateTaskDto, options?: AxiosRequestConfig) { return TasksApiFp(this.configuration).createTask(createTaskDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TasksApi */ public deleteTask(id: number, options?: AxiosRequestConfig) { return TasksApiFp(this.configuration).deleteTask(id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TasksApi */ public getTasks(options?: AxiosRequestConfig) { return TasksApiFp(this.configuration).getTasks(options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} id * @param {UpdateTaskDto} updateTaskDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TasksApi */ public updateTask(id: number, updateTaskDto: UpdateTaskDto, options?: AxiosRequestConfig) { return TasksApiFp(this.configuration).updateTask(id, updateTaskDto, options).then((request) => request(this.axios, this.basePath)); } } /** * TasksCriteriaApi - axios parameter creator * @export */ export const TasksCriteriaApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {number} taskId * @param {TaskCriteriaDto} taskCriteriaDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createTaskCriteria: async (taskId: number, taskCriteriaDto: TaskCriteriaDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'taskId' is not null or undefined assertParamExists('createTaskCriteria', 'taskId', taskId) // verify required parameter 'taskCriteriaDto' is not null or undefined assertParamExists('createTaskCriteria', 'taskCriteriaDto', taskCriteriaDto) const localVarPath = `/tasks/{taskId}/criteria` .replace(`{${"taskId"}}`, encodeURIComponent(String(taskId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(taskCriteriaDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} taskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getTaskCriteria: async (taskId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'taskId' is not null or undefined assertParamExists('getTaskCriteria', 'taskId', taskId) const localVarPath = `/tasks/{taskId}/criteria` .replace(`{${"taskId"}}`, encodeURIComponent(String(taskId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} taskId * @param {TaskCriteriaDto} taskCriteriaDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateTaskCriteria: async (taskId: number, taskCriteriaDto: TaskCriteriaDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'taskId' is not null or undefined assertParamExists('updateTaskCriteria', 'taskId', taskId) // verify required parameter 'taskCriteriaDto' is not null or undefined assertParamExists('updateTaskCriteria', 'taskCriteriaDto', taskCriteriaDto) const localVarPath = `/tasks/{taskId}/criteria` .replace(`{${"taskId"}}`, encodeURIComponent(String(taskId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PATCH', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(taskCriteriaDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * TasksCriteriaApi - functional programming interface * @export */ export const TasksCriteriaApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = TasksCriteriaApiAxiosParamCreator(configuration) return { /** * * @param {number} taskId * @param {TaskCriteriaDto} taskCriteriaDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createTaskCriteria(taskId: number, taskCriteriaDto: TaskCriteriaDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.createTaskCriteria(taskId, taskCriteriaDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} taskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getTaskCriteria(taskId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getTaskCriteria(taskId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} taskId * @param {TaskCriteriaDto} taskCriteriaDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async updateTaskCriteria(taskId: number, taskCriteriaDto: TaskCriteriaDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.updateTaskCriteria(taskId, taskCriteriaDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * TasksCriteriaApi - factory interface * @export */ export const TasksCriteriaApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = TasksCriteriaApiFp(configuration) return { /** * * @param {number} taskId * @param {TaskCriteriaDto} taskCriteriaDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createTaskCriteria(taskId: number, taskCriteriaDto: TaskCriteriaDto, options?: any): AxiosPromise { return localVarFp.createTaskCriteria(taskId, taskCriteriaDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} taskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getTaskCriteria(taskId: number, options?: any): AxiosPromise { return localVarFp.getTaskCriteria(taskId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} taskId * @param {TaskCriteriaDto} taskCriteriaDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateTaskCriteria(taskId: number, taskCriteriaDto: TaskCriteriaDto, options?: any): AxiosPromise { return localVarFp.updateTaskCriteria(taskId, taskCriteriaDto, options).then((request) => request(axios, basePath)); }, }; }; /** * TasksCriteriaApi - object-oriented interface * @export * @class TasksCriteriaApi * @extends {BaseAPI} */ export class TasksCriteriaApi extends BaseAPI { /** * * @param {number} taskId * @param {TaskCriteriaDto} taskCriteriaDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TasksCriteriaApi */ public createTaskCriteria(taskId: number, taskCriteriaDto: TaskCriteriaDto, options?: AxiosRequestConfig) { return TasksCriteriaApiFp(this.configuration).createTaskCriteria(taskId, taskCriteriaDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} taskId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TasksCriteriaApi */ public getTaskCriteria(taskId: number, options?: AxiosRequestConfig) { return TasksCriteriaApiFp(this.configuration).getTaskCriteria(taskId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} taskId * @param {TaskCriteriaDto} taskCriteriaDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TasksCriteriaApi */ public updateTaskCriteria(taskId: number, taskCriteriaDto: TaskCriteriaDto, options?: AxiosRequestConfig) { return TasksCriteriaApiFp(this.configuration).updateTaskCriteria(taskId, taskCriteriaDto, options).then((request) => request(this.axios, this.basePath)); } } /** * TeamApi - axios parameter creator * @export */ export const TeamApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {number} courseId * @param {number} distributionId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ changeTeamPassword: async (courseId: number, distributionId: number, id: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('changeTeamPassword', 'courseId', courseId) // verify required parameter 'distributionId' is not null or undefined assertParamExists('changeTeamPassword', 'distributionId', distributionId) // verify required parameter 'id' is not null or undefined assertParamExists('changeTeamPassword', 'id', id) const localVarPath = `/courses/{courseId}/team-distribution/{distributionId}/team/{id}/password` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"distributionId"}}`, encodeURIComponent(String(distributionId))) .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} distributionId * @param {CreateTeamDto} createTeamDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createTeam: async (courseId: number, distributionId: number, createTeamDto: CreateTeamDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('createTeam', 'courseId', courseId) // verify required parameter 'distributionId' is not null or undefined assertParamExists('createTeam', 'distributionId', distributionId) // verify required parameter 'createTeamDto' is not null or undefined assertParamExists('createTeam', 'createTeamDto', createTeamDto) const localVarPath = `/courses/{courseId}/team-distribution/{distributionId}/team` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"distributionId"}}`, encodeURIComponent(String(distributionId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(createTeamDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} distributionId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ getTeamPassword: async (courseId: number, distributionId: number, id: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getTeamPassword', 'courseId', courseId) // verify required parameter 'distributionId' is not null or undefined assertParamExists('getTeamPassword', 'distributionId', distributionId) // verify required parameter 'id' is not null or undefined assertParamExists('getTeamPassword', 'id', id) const localVarPath = `/courses/{courseId}/team-distribution/{distributionId}/team/{id}/password` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"distributionId"}}`, encodeURIComponent(String(distributionId))) .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} distributionId * @param {number} pageSize * @param {number} current * @param {string} search * @param {*} [options] Override http request option. * @throws {RequiredError} */ getTeams: async (courseId: number, distributionId: number, pageSize: number, current: number, search: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getTeams', 'courseId', courseId) // verify required parameter 'distributionId' is not null or undefined assertParamExists('getTeams', 'distributionId', distributionId) // verify required parameter 'pageSize' is not null or undefined assertParamExists('getTeams', 'pageSize', pageSize) // verify required parameter 'current' is not null or undefined assertParamExists('getTeams', 'current', current) // verify required parameter 'search' is not null or undefined assertParamExists('getTeams', 'search', search) const localVarPath = `/courses/{courseId}/team-distribution/{distributionId}/team` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"distributionId"}}`, encodeURIComponent(String(distributionId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; if (pageSize !== undefined) { localVarQueryParameter['pageSize'] = pageSize; } if (current !== undefined) { localVarQueryParameter['current'] = current; } if (search !== undefined) { localVarQueryParameter['search'] = search; } setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} distributionId * @param {number} id * @param {JoinTeamDto} joinTeamDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ joinTeam: async (courseId: number, distributionId: number, id: number, joinTeamDto: JoinTeamDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('joinTeam', 'courseId', courseId) // verify required parameter 'distributionId' is not null or undefined assertParamExists('joinTeam', 'distributionId', distributionId) // verify required parameter 'id' is not null or undefined assertParamExists('joinTeam', 'id', id) // verify required parameter 'joinTeamDto' is not null or undefined assertParamExists('joinTeam', 'joinTeamDto', joinTeamDto) const localVarPath = `/courses/{courseId}/team-distribution/{distributionId}/team/{id}/join` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"distributionId"}}`, encodeURIComponent(String(distributionId))) .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(joinTeamDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} distributionId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ leaveTeam: async (courseId: number, distributionId: number, id: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('leaveTeam', 'courseId', courseId) // verify required parameter 'distributionId' is not null or undefined assertParamExists('leaveTeam', 'distributionId', distributionId) // verify required parameter 'id' is not null or undefined assertParamExists('leaveTeam', 'id', id) const localVarPath = `/courses/{courseId}/team-distribution/{distributionId}/team/{id}/leave` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"distributionId"}}`, encodeURIComponent(String(distributionId))) .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} distributionId * @param {number} id * @param {UpdateTeamDto} updateTeamDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateTeam: async (courseId: number, distributionId: number, id: number, updateTeamDto: UpdateTeamDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('updateTeam', 'courseId', courseId) // verify required parameter 'distributionId' is not null or undefined assertParamExists('updateTeam', 'distributionId', distributionId) // verify required parameter 'id' is not null or undefined assertParamExists('updateTeam', 'id', id) // verify required parameter 'updateTeamDto' is not null or undefined assertParamExists('updateTeam', 'updateTeamDto', updateTeamDto) const localVarPath = `/courses/{courseId}/team-distribution/{distributionId}/team/{id}` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"distributionId"}}`, encodeURIComponent(String(distributionId))) .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PATCH', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(updateTeamDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * TeamApi - functional programming interface * @export */ export const TeamApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = TeamApiAxiosParamCreator(configuration) return { /** * * @param {number} courseId * @param {number} distributionId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async changeTeamPassword(courseId: number, distributionId: number, id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.changeTeamPassword(courseId, distributionId, id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} distributionId * @param {CreateTeamDto} createTeamDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createTeam(courseId: number, distributionId: number, createTeamDto: CreateTeamDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.createTeam(courseId, distributionId, createTeamDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} distributionId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getTeamPassword(courseId: number, distributionId: number, id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getTeamPassword(courseId, distributionId, id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} distributionId * @param {number} pageSize * @param {number} current * @param {string} search * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getTeams(courseId: number, distributionId: number, pageSize: number, current: number, search: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getTeams(courseId, distributionId, pageSize, current, search, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} distributionId * @param {number} id * @param {JoinTeamDto} joinTeamDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async joinTeam(courseId: number, distributionId: number, id: number, joinTeamDto: JoinTeamDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.joinTeam(courseId, distributionId, id, joinTeamDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} distributionId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async leaveTeam(courseId: number, distributionId: number, id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.leaveTeam(courseId, distributionId, id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} distributionId * @param {number} id * @param {UpdateTeamDto} updateTeamDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async updateTeam(courseId: number, distributionId: number, id: number, updateTeamDto: UpdateTeamDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.updateTeam(courseId, distributionId, id, updateTeamDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * TeamApi - factory interface * @export */ export const TeamApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = TeamApiFp(configuration) return { /** * * @param {number} courseId * @param {number} distributionId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ changeTeamPassword(courseId: number, distributionId: number, id: number, options?: any): AxiosPromise { return localVarFp.changeTeamPassword(courseId, distributionId, id, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} distributionId * @param {CreateTeamDto} createTeamDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createTeam(courseId: number, distributionId: number, createTeamDto: CreateTeamDto, options?: any): AxiosPromise { return localVarFp.createTeam(courseId, distributionId, createTeamDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} distributionId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ getTeamPassword(courseId: number, distributionId: number, id: number, options?: any): AxiosPromise { return localVarFp.getTeamPassword(courseId, distributionId, id, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} distributionId * @param {number} pageSize * @param {number} current * @param {string} search * @param {*} [options] Override http request option. * @throws {RequiredError} */ getTeams(courseId: number, distributionId: number, pageSize: number, current: number, search: string, options?: any): AxiosPromise { return localVarFp.getTeams(courseId, distributionId, pageSize, current, search, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} distributionId * @param {number} id * @param {JoinTeamDto} joinTeamDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ joinTeam(courseId: number, distributionId: number, id: number, joinTeamDto: JoinTeamDto, options?: any): AxiosPromise { return localVarFp.joinTeam(courseId, distributionId, id, joinTeamDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} distributionId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ leaveTeam(courseId: number, distributionId: number, id: number, options?: any): AxiosPromise { return localVarFp.leaveTeam(courseId, distributionId, id, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} distributionId * @param {number} id * @param {UpdateTeamDto} updateTeamDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateTeam(courseId: number, distributionId: number, id: number, updateTeamDto: UpdateTeamDto, options?: any): AxiosPromise { return localVarFp.updateTeam(courseId, distributionId, id, updateTeamDto, options).then((request) => request(axios, basePath)); }, }; }; /** * TeamApi - object-oriented interface * @export * @class TeamApi * @extends {BaseAPI} */ export class TeamApi extends BaseAPI { /** * * @param {number} courseId * @param {number} distributionId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TeamApi */ public changeTeamPassword(courseId: number, distributionId: number, id: number, options?: AxiosRequestConfig) { return TeamApiFp(this.configuration).changeTeamPassword(courseId, distributionId, id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} distributionId * @param {CreateTeamDto} createTeamDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TeamApi */ public createTeam(courseId: number, distributionId: number, createTeamDto: CreateTeamDto, options?: AxiosRequestConfig) { return TeamApiFp(this.configuration).createTeam(courseId, distributionId, createTeamDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} distributionId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TeamApi */ public getTeamPassword(courseId: number, distributionId: number, id: number, options?: AxiosRequestConfig) { return TeamApiFp(this.configuration).getTeamPassword(courseId, distributionId, id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} distributionId * @param {number} pageSize * @param {number} current * @param {string} search * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TeamApi */ public getTeams(courseId: number, distributionId: number, pageSize: number, current: number, search: string, options?: AxiosRequestConfig) { return TeamApiFp(this.configuration).getTeams(courseId, distributionId, pageSize, current, search, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} distributionId * @param {number} id * @param {JoinTeamDto} joinTeamDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TeamApi */ public joinTeam(courseId: number, distributionId: number, id: number, joinTeamDto: JoinTeamDto, options?: AxiosRequestConfig) { return TeamApiFp(this.configuration).joinTeam(courseId, distributionId, id, joinTeamDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} distributionId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TeamApi */ public leaveTeam(courseId: number, distributionId: number, id: number, options?: AxiosRequestConfig) { return TeamApiFp(this.configuration).leaveTeam(courseId, distributionId, id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} distributionId * @param {number} id * @param {UpdateTeamDto} updateTeamDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TeamApi */ public updateTeam(courseId: number, distributionId: number, id: number, updateTeamDto: UpdateTeamDto, options?: AxiosRequestConfig) { return TeamApiFp(this.configuration).updateTeam(courseId, distributionId, id, updateTeamDto, options).then((request) => request(this.axios, this.basePath)); } } /** * TeamDistributionApi - axios parameter creator * @export */ export const TeamDistributionApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {number} courseId * @param {CreateTeamDistributionDto} createTeamDistributionDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createTeamDistribution: async (courseId: number, createTeamDistributionDto: CreateTeamDistributionDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('createTeamDistribution', 'courseId', courseId) // verify required parameter 'createTeamDistributionDto' is not null or undefined assertParamExists('createTeamDistribution', 'createTeamDistributionDto', createTeamDistributionDto) const localVarPath = `/courses/{courseId}/team-distribution` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(createTeamDistributionDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteTeamDistribution: async (courseId: number, id: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('deleteTeamDistribution', 'courseId', courseId) // verify required parameter 'id' is not null or undefined assertParamExists('deleteTeamDistribution', 'id', id) const localVarPath = `/courses/{courseId}/team-distribution/{id}` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ distributeStudentsToTeam: async (courseId: number, id: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('distributeStudentsToTeam', 'courseId', courseId) // verify required parameter 'id' is not null or undefined assertParamExists('distributeStudentsToTeam', 'id', id) const localVarPath = `/courses/{courseId}/team-distribution/{id}/distribution` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseTeamDistributionDetailed: async (courseId: number, id: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getCourseTeamDistributionDetailed', 'courseId', courseId) // verify required parameter 'id' is not null or undefined assertParamExists('getCourseTeamDistributionDetailed', 'id', id) const localVarPath = `/courses/{courseId}/team-distribution/{id}/detailed` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseTeamDistributions: async (courseId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getCourseTeamDistributions', 'courseId', courseId) const localVarPath = `/courses/{courseId}/team-distribution` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} id * @param {number} pageSize * @param {number} current * @param {string} search * @param {*} [options] Override http request option. * @throws {RequiredError} */ getStudentsWithoutTeam: async (courseId: number, id: number, pageSize: number, current: number, search: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('getStudentsWithoutTeam', 'courseId', courseId) // verify required parameter 'id' is not null or undefined assertParamExists('getStudentsWithoutTeam', 'id', id) // verify required parameter 'pageSize' is not null or undefined assertParamExists('getStudentsWithoutTeam', 'pageSize', pageSize) // verify required parameter 'current' is not null or undefined assertParamExists('getStudentsWithoutTeam', 'current', current) // verify required parameter 'search' is not null or undefined assertParamExists('getStudentsWithoutTeam', 'search', search) const localVarPath = `/courses/{courseId}/team-distribution/{id}/students` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; if (pageSize !== undefined) { localVarQueryParameter['pageSize'] = pageSize; } if (current !== undefined) { localVarQueryParameter['current'] = current; } if (search !== undefined) { localVarQueryParameter['search'] = search; } setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} id * @param {number} taskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ submitScore: async (courseId: number, id: number, taskId: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('submitScore', 'courseId', courseId) // verify required parameter 'id' is not null or undefined assertParamExists('submitScore', 'id', id) // verify required parameter 'taskId' is not null or undefined assertParamExists('submitScore', 'taskId', taskId) const localVarPath = `/courses/{courseId}/team-distribution/{id}/submit-score/{taskId}` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"id"}}`, encodeURIComponent(String(id))) .replace(`{${"taskId"}}`, encodeURIComponent(String(taskId))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} studentId * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ teamDistributionControllerDeleteStudentFromDistribution: async (studentId: number, courseId: number, id: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'studentId' is not null or undefined assertParamExists('teamDistributionControllerDeleteStudentFromDistribution', 'studentId', studentId) // verify required parameter 'courseId' is not null or undefined assertParamExists('teamDistributionControllerDeleteStudentFromDistribution', 'courseId', courseId) // verify required parameter 'id' is not null or undefined assertParamExists('teamDistributionControllerDeleteStudentFromDistribution', 'id', id) const localVarPath = `/courses/{courseId}/team-distribution/{id}/students/{studentId}` .replace(`{${"studentId"}}`, encodeURIComponent(String(studentId))) .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ teamDistributionDeleteRegistry: async (courseId: number, id: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('teamDistributionDeleteRegistry', 'courseId', courseId) // verify required parameter 'id' is not null or undefined assertParamExists('teamDistributionDeleteRegistry', 'id', id) const localVarPath = `/courses/{courseId}/team-distribution/{id}/registry` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ teamDistributionRegistry: async (courseId: number, id: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('teamDistributionRegistry', 'courseId', courseId) // verify required parameter 'id' is not null or undefined assertParamExists('teamDistributionRegistry', 'id', id) const localVarPath = `/courses/{courseId}/team-distribution/{id}/registry` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} courseId * @param {number} id * @param {UpdateTeamDistributionDto} updateTeamDistributionDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateTeamDistribution: async (courseId: number, id: number, updateTeamDistributionDto: UpdateTeamDistributionDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'courseId' is not null or undefined assertParamExists('updateTeamDistribution', 'courseId', courseId) // verify required parameter 'id' is not null or undefined assertParamExists('updateTeamDistribution', 'id', id) // verify required parameter 'updateTeamDistributionDto' is not null or undefined assertParamExists('updateTeamDistribution', 'updateTeamDistributionDto', updateTeamDistributionDto) const localVarPath = `/courses/{courseId}/team-distribution/{id}` .replace(`{${"courseId"}}`, encodeURIComponent(String(courseId))) .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(updateTeamDistributionDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * TeamDistributionApi - functional programming interface * @export */ export const TeamDistributionApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = TeamDistributionApiAxiosParamCreator(configuration) return { /** * * @param {number} courseId * @param {CreateTeamDistributionDto} createTeamDistributionDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createTeamDistribution(courseId: number, createTeamDistributionDto: CreateTeamDistributionDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.createTeamDistribution(courseId, createTeamDistributionDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async deleteTeamDistribution(courseId: number, id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.deleteTeamDistribution(courseId, id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async distributeStudentsToTeam(courseId: number, id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.distributeStudentsToTeam(courseId, id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getCourseTeamDistributionDetailed(courseId: number, id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getCourseTeamDistributionDetailed(courseId, id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getCourseTeamDistributions(courseId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getCourseTeamDistributions(courseId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} id * @param {number} pageSize * @param {number} current * @param {string} search * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getStudentsWithoutTeam(courseId: number, id: number, pageSize: number, current: number, search: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getStudentsWithoutTeam(courseId, id, pageSize, current, search, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} id * @param {number} taskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ async submitScore(courseId: number, id: number, taskId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.submitScore(courseId, id, taskId, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} studentId * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async teamDistributionControllerDeleteStudentFromDistribution(studentId: number, courseId: number, id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.teamDistributionControllerDeleteStudentFromDistribution(studentId, courseId, id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async teamDistributionDeleteRegistry(courseId: number, id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.teamDistributionDeleteRegistry(courseId, id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async teamDistributionRegistry(courseId: number, id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.teamDistributionRegistry(courseId, id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} courseId * @param {number} id * @param {UpdateTeamDistributionDto} updateTeamDistributionDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async updateTeamDistribution(courseId: number, id: number, updateTeamDistributionDto: UpdateTeamDistributionDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.updateTeamDistribution(courseId, id, updateTeamDistributionDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * TeamDistributionApi - factory interface * @export */ export const TeamDistributionApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = TeamDistributionApiFp(configuration) return { /** * * @param {number} courseId * @param {CreateTeamDistributionDto} createTeamDistributionDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createTeamDistribution(courseId: number, createTeamDistributionDto: CreateTeamDistributionDto, options?: any): AxiosPromise { return localVarFp.createTeamDistribution(courseId, createTeamDistributionDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteTeamDistribution(courseId: number, id: number, options?: any): AxiosPromise { return localVarFp.deleteTeamDistribution(courseId, id, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ distributeStudentsToTeam(courseId: number, id: number, options?: any): AxiosPromise { return localVarFp.distributeStudentsToTeam(courseId, id, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseTeamDistributionDetailed(courseId: number, id: number, options?: any): AxiosPromise { return localVarFp.getCourseTeamDistributionDetailed(courseId, id, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} */ getCourseTeamDistributions(courseId: number, options?: any): AxiosPromise> { return localVarFp.getCourseTeamDistributions(courseId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} id * @param {number} pageSize * @param {number} current * @param {string} search * @param {*} [options] Override http request option. * @throws {RequiredError} */ getStudentsWithoutTeam(courseId: number, id: number, pageSize: number, current: number, search: string, options?: any): AxiosPromise> { return localVarFp.getStudentsWithoutTeam(courseId, id, pageSize, current, search, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} id * @param {number} taskId * @param {*} [options] Override http request option. * @throws {RequiredError} */ submitScore(courseId: number, id: number, taskId: number, options?: any): AxiosPromise { return localVarFp.submitScore(courseId, id, taskId, options).then((request) => request(axios, basePath)); }, /** * * @param {number} studentId * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ teamDistributionControllerDeleteStudentFromDistribution(studentId: number, courseId: number, id: number, options?: any): AxiosPromise { return localVarFp.teamDistributionControllerDeleteStudentFromDistribution(studentId, courseId, id, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ teamDistributionDeleteRegistry(courseId: number, id: number, options?: any): AxiosPromise { return localVarFp.teamDistributionDeleteRegistry(courseId, id, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ teamDistributionRegistry(courseId: number, id: number, options?: any): AxiosPromise { return localVarFp.teamDistributionRegistry(courseId, id, options).then((request) => request(axios, basePath)); }, /** * * @param {number} courseId * @param {number} id * @param {UpdateTeamDistributionDto} updateTeamDistributionDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateTeamDistribution(courseId: number, id: number, updateTeamDistributionDto: UpdateTeamDistributionDto, options?: any): AxiosPromise { return localVarFp.updateTeamDistribution(courseId, id, updateTeamDistributionDto, options).then((request) => request(axios, basePath)); }, }; }; /** * TeamDistributionApi - object-oriented interface * @export * @class TeamDistributionApi * @extends {BaseAPI} */ export class TeamDistributionApi extends BaseAPI { /** * * @param {number} courseId * @param {CreateTeamDistributionDto} createTeamDistributionDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TeamDistributionApi */ public createTeamDistribution(courseId: number, createTeamDistributionDto: CreateTeamDistributionDto, options?: AxiosRequestConfig) { return TeamDistributionApiFp(this.configuration).createTeamDistribution(courseId, createTeamDistributionDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TeamDistributionApi */ public deleteTeamDistribution(courseId: number, id: number, options?: AxiosRequestConfig) { return TeamDistributionApiFp(this.configuration).deleteTeamDistribution(courseId, id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TeamDistributionApi */ public distributeStudentsToTeam(courseId: number, id: number, options?: AxiosRequestConfig) { return TeamDistributionApiFp(this.configuration).distributeStudentsToTeam(courseId, id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TeamDistributionApi */ public getCourseTeamDistributionDetailed(courseId: number, id: number, options?: AxiosRequestConfig) { return TeamDistributionApiFp(this.configuration).getCourseTeamDistributionDetailed(courseId, id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TeamDistributionApi */ public getCourseTeamDistributions(courseId: number, options?: AxiosRequestConfig) { return TeamDistributionApiFp(this.configuration).getCourseTeamDistributions(courseId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} id * @param {number} pageSize * @param {number} current * @param {string} search * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TeamDistributionApi */ public getStudentsWithoutTeam(courseId: number, id: number, pageSize: number, current: number, search: string, options?: AxiosRequestConfig) { return TeamDistributionApiFp(this.configuration).getStudentsWithoutTeam(courseId, id, pageSize, current, search, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} id * @param {number} taskId * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TeamDistributionApi */ public submitScore(courseId: number, id: number, taskId: number, options?: AxiosRequestConfig) { return TeamDistributionApiFp(this.configuration).submitScore(courseId, id, taskId, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} studentId * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TeamDistributionApi */ public teamDistributionControllerDeleteStudentFromDistribution(studentId: number, courseId: number, id: number, options?: AxiosRequestConfig) { return TeamDistributionApiFp(this.configuration).teamDistributionControllerDeleteStudentFromDistribution(studentId, courseId, id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TeamDistributionApi */ public teamDistributionDeleteRegistry(courseId: number, id: number, options?: AxiosRequestConfig) { return TeamDistributionApiFp(this.configuration).teamDistributionDeleteRegistry(courseId, id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TeamDistributionApi */ public teamDistributionRegistry(courseId: number, id: number, options?: AxiosRequestConfig) { return TeamDistributionApiFp(this.configuration).teamDistributionRegistry(courseId, id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} courseId * @param {number} id * @param {UpdateTeamDistributionDto} updateTeamDistributionDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof TeamDistributionApi */ public updateTeamDistribution(courseId: number, id: number, updateTeamDistributionDto: UpdateTeamDistributionDto, options?: AxiosRequestConfig) { return TeamDistributionApiFp(this.configuration).updateTeamDistribution(courseId, id, updateTeamDistributionDto, options).then((request) => request(this.axios, this.basePath)); } } /** * UserGroupApi - axios parameter creator * @export */ export const UserGroupApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {CreateUserGroupDto} createUserGroupDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createUserGroup: async (createUserGroupDto: CreateUserGroupDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'createUserGroupDto' is not null or undefined assertParamExists('createUserGroup', 'createUserGroupDto', createUserGroupDto) const localVarPath = `/user-group`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(createUserGroupDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteUserGroup: async (id: number, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined assertParamExists('deleteUserGroup', 'id', id) const localVarPath = `/user-group/{id}` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getUserGroups: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/user-group`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {number} id * @param {UpdateUserGroupDto} updateUserGroupDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateUserGroup: async (id: number, updateUserGroupDto: UpdateUserGroupDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined assertParamExists('updateUserGroup', 'id', id) // verify required parameter 'updateUserGroupDto' is not null or undefined assertParamExists('updateUserGroup', 'updateUserGroupDto', updateUserGroupDto) const localVarPath = `/user-group/{id}` .replace(`{${"id"}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(updateUserGroupDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * UserGroupApi - functional programming interface * @export */ export const UserGroupApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = UserGroupApiAxiosParamCreator(configuration) return { /** * * @param {CreateUserGroupDto} createUserGroupDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async createUserGroup(createUserGroupDto: CreateUserGroupDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.createUserGroup(createUserGroupDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ async deleteUserGroup(id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.deleteUserGroup(id, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getUserGroups(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.getUserGroups(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {number} id * @param {UpdateUserGroupDto} updateUserGroupDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async updateUserGroup(id: number, updateUserGroupDto: UpdateUserGroupDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.updateUserGroup(id, updateUserGroupDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * UserGroupApi - factory interface * @export */ export const UserGroupApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = UserGroupApiFp(configuration) return { /** * * @param {CreateUserGroupDto} createUserGroupDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ createUserGroup(createUserGroupDto: CreateUserGroupDto, options?: any): AxiosPromise { return localVarFp.createUserGroup(createUserGroupDto, options).then((request) => request(axios, basePath)); }, /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ deleteUserGroup(id: number, options?: any): AxiosPromise { return localVarFp.deleteUserGroup(id, options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getUserGroups(options?: any): AxiosPromise> { return localVarFp.getUserGroups(options).then((request) => request(axios, basePath)); }, /** * * @param {number} id * @param {UpdateUserGroupDto} updateUserGroupDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateUserGroup(id: number, updateUserGroupDto: UpdateUserGroupDto, options?: any): AxiosPromise { return localVarFp.updateUserGroup(id, updateUserGroupDto, options).then((request) => request(axios, basePath)); }, }; }; /** * UserGroupApi - object-oriented interface * @export * @class UserGroupApi * @extends {BaseAPI} */ export class UserGroupApi extends BaseAPI { /** * * @param {CreateUserGroupDto} createUserGroupDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof UserGroupApi */ public createUserGroup(createUserGroupDto: CreateUserGroupDto, options?: AxiosRequestConfig) { return UserGroupApiFp(this.configuration).createUserGroup(createUserGroupDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} id * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof UserGroupApi */ public deleteUserGroup(id: number, options?: AxiosRequestConfig) { return UserGroupApiFp(this.configuration).deleteUserGroup(id, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof UserGroupApi */ public getUserGroups(options?: AxiosRequestConfig) { return UserGroupApiFp(this.configuration).getUserGroups(options).then((request) => request(this.axios, this.basePath)); } /** * * @param {number} id * @param {UpdateUserGroupDto} updateUserGroupDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof UserGroupApi */ public updateUserGroup(id: number, updateUserGroupDto: UpdateUserGroupDto, options?: AxiosRequestConfig) { return UserGroupApiFp(this.configuration).updateUserGroup(id, updateUserGroupDto, options).then((request) => request(this.axios, this.basePath)); } } /** * UsersApi - axios parameter creator * @export */ export const UsersApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {string} query * @param {*} [options] Override http request option. * @throws {RequiredError} */ searchUsers: async (query: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'query' is not null or undefined assertParamExists('searchUsers', 'query', query) const localVarPath = `/users/search`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; if (query !== undefined) { localVarQueryParameter['query'] = query; } setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * UsersApi - functional programming interface * @export */ export const UsersApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = UsersApiAxiosParamCreator(configuration) return { /** * * @param {string} query * @param {*} [options] Override http request option. * @throws {RequiredError} */ async searchUsers(query: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.searchUsers(query, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * UsersApi - factory interface * @export */ export const UsersApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = UsersApiFp(configuration) return { /** * * @param {string} query * @param {*} [options] Override http request option. * @throws {RequiredError} */ searchUsers(query: string, options?: any): AxiosPromise> { return localVarFp.searchUsers(query, options).then((request) => request(axios, basePath)); }, }; }; /** * UsersApi - object-oriented interface * @export * @class UsersApi * @extends {BaseAPI} */ export class UsersApi extends BaseAPI { /** * * @param {string} query * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof UsersApi */ public searchUsers(query: string, options?: AxiosRequestConfig) { return UsersApiFp(this.configuration).searchUsers(query, options).then((request) => request(this.axios, this.basePath)); } } /** * UsersNotificationsApi - axios parameter creator * @export */ export const UsersNotificationsApiAxiosParamCreator = function (configuration?: Configuration) { return { /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getUserNotificationConnections: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/users/notifications/connections`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getUserNotifications: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/users/notifications`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ sendEmailConfirmationLink: async (options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/users/notifications/confirmation/email`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {SendUserNotificationDto} sendUserNotificationDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ sendNotification: async (sendUserNotificationDto: SendUserNotificationDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'sendUserNotificationDto' is not null or undefined assertParamExists('sendNotification', 'sendUserNotificationDto', sendUserNotificationDto) const localVarPath = `/users/notifications/send`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(sendUserNotificationDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {Array} updateNotificationUserSettingsDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateUserNotifications: async (updateNotificationUserSettingsDto: Array, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'updateNotificationUserSettingsDto' is not null or undefined assertParamExists('updateUserNotifications', 'updateNotificationUserSettingsDto', updateNotificationUserSettingsDto) const localVarPath = `/users/notifications`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(updateNotificationUserSettingsDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {UpsertNotificationConnectionDto} upsertNotificationConnectionDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ usersNotificationsControllerCreateUserConnection: async (upsertNotificationConnectionDto: UpsertNotificationConnectionDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'upsertNotificationConnectionDto' is not null or undefined assertParamExists('usersNotificationsControllerCreateUserConnection', 'upsertNotificationConnectionDto', upsertNotificationConnectionDto) const localVarPath = `/users/notifications/connection`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(upsertNotificationConnectionDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, /** * * @param {NotificationConnectionExistsDto} notificationConnectionExistsDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ usersNotificationsControllerFindConnection: async (notificationConnectionExistsDto: NotificationConnectionExistsDto, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'notificationConnectionExistsDto' is not null or undefined assertParamExists('usersNotificationsControllerFindConnection', 'notificationConnectionExistsDto', notificationConnectionExistsDto) const localVarPath = `/users/notifications/connection/find`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; if (configuration) { baseOptions = configuration.baseOptions; } const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; localVarRequestOptions.data = serializeDataIfNeeded(notificationConnectionExistsDto, localVarRequestOptions, configuration) return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, } }; /** * UsersNotificationsApi - functional programming interface * @export */ export const UsersNotificationsApiFp = function(configuration?: Configuration) { const localVarAxiosParamCreator = UsersNotificationsApiAxiosParamCreator(configuration) return { /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getUserNotificationConnections(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getUserNotificationConnections(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async getUserNotifications(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getUserNotifications(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ async sendEmailConfirmationLink(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.sendEmailConfirmationLink(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {SendUserNotificationDto} sendUserNotificationDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async sendNotification(sendUserNotificationDto: SendUserNotificationDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.sendNotification(sendUserNotificationDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {Array} updateNotificationUserSettingsDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async updateUserNotifications(updateNotificationUserSettingsDto: Array, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { const localVarAxiosArgs = await localVarAxiosParamCreator.updateUserNotifications(updateNotificationUserSettingsDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {UpsertNotificationConnectionDto} upsertNotificationConnectionDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async usersNotificationsControllerCreateUserConnection(upsertNotificationConnectionDto: UpsertNotificationConnectionDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.usersNotificationsControllerCreateUserConnection(upsertNotificationConnectionDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** * * @param {NotificationConnectionExistsDto} notificationConnectionExistsDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ async usersNotificationsControllerFindConnection(notificationConnectionExistsDto: NotificationConnectionExistsDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.usersNotificationsControllerFindConnection(notificationConnectionExistsDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, } }; /** * UsersNotificationsApi - factory interface * @export */ export const UsersNotificationsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = UsersNotificationsApiFp(configuration) return { /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getUserNotificationConnections(options?: any): AxiosPromise { return localVarFp.getUserNotificationConnections(options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ getUserNotifications(options?: any): AxiosPromise { return localVarFp.getUserNotifications(options).then((request) => request(axios, basePath)); }, /** * * @param {*} [options] Override http request option. * @throws {RequiredError} */ sendEmailConfirmationLink(options?: any): AxiosPromise { return localVarFp.sendEmailConfirmationLink(options).then((request) => request(axios, basePath)); }, /** * * @param {SendUserNotificationDto} sendUserNotificationDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ sendNotification(sendUserNotificationDto: SendUserNotificationDto, options?: any): AxiosPromise { return localVarFp.sendNotification(sendUserNotificationDto, options).then((request) => request(axios, basePath)); }, /** * * @param {Array} updateNotificationUserSettingsDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ updateUserNotifications(updateNotificationUserSettingsDto: Array, options?: any): AxiosPromise> { return localVarFp.updateUserNotifications(updateNotificationUserSettingsDto, options).then((request) => request(axios, basePath)); }, /** * * @param {UpsertNotificationConnectionDto} upsertNotificationConnectionDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ usersNotificationsControllerCreateUserConnection(upsertNotificationConnectionDto: UpsertNotificationConnectionDto, options?: any): AxiosPromise { return localVarFp.usersNotificationsControllerCreateUserConnection(upsertNotificationConnectionDto, options).then((request) => request(axios, basePath)); }, /** * * @param {NotificationConnectionExistsDto} notificationConnectionExistsDto * @param {*} [options] Override http request option. * @throws {RequiredError} */ usersNotificationsControllerFindConnection(notificationConnectionExistsDto: NotificationConnectionExistsDto, options?: any): AxiosPromise { return localVarFp.usersNotificationsControllerFindConnection(notificationConnectionExistsDto, options).then((request) => request(axios, basePath)); }, }; }; /** * UsersNotificationsApi - object-oriented interface * @export * @class UsersNotificationsApi * @extends {BaseAPI} */ export class UsersNotificationsApi extends BaseAPI { /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof UsersNotificationsApi */ public getUserNotificationConnections(options?: AxiosRequestConfig) { return UsersNotificationsApiFp(this.configuration).getUserNotificationConnections(options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof UsersNotificationsApi */ public getUserNotifications(options?: AxiosRequestConfig) { return UsersNotificationsApiFp(this.configuration).getUserNotifications(options).then((request) => request(this.axios, this.basePath)); } /** * * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof UsersNotificationsApi */ public sendEmailConfirmationLink(options?: AxiosRequestConfig) { return UsersNotificationsApiFp(this.configuration).sendEmailConfirmationLink(options).then((request) => request(this.axios, this.basePath)); } /** * * @param {SendUserNotificationDto} sendUserNotificationDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof UsersNotificationsApi */ public sendNotification(sendUserNotificationDto: SendUserNotificationDto, options?: AxiosRequestConfig) { return UsersNotificationsApiFp(this.configuration).sendNotification(sendUserNotificationDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {Array} updateNotificationUserSettingsDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof UsersNotificationsApi */ public updateUserNotifications(updateNotificationUserSettingsDto: Array, options?: AxiosRequestConfig) { return UsersNotificationsApiFp(this.configuration).updateUserNotifications(updateNotificationUserSettingsDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {UpsertNotificationConnectionDto} upsertNotificationConnectionDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof UsersNotificationsApi */ public usersNotificationsControllerCreateUserConnection(upsertNotificationConnectionDto: UpsertNotificationConnectionDto, options?: AxiosRequestConfig) { return UsersNotificationsApiFp(this.configuration).usersNotificationsControllerCreateUserConnection(upsertNotificationConnectionDto, options).then((request) => request(this.axios, this.basePath)); } /** * * @param {NotificationConnectionExistsDto} notificationConnectionExistsDto * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof UsersNotificationsApi */ public usersNotificationsControllerFindConnection(notificationConnectionExistsDto: NotificationConnectionExistsDto, options?: AxiosRequestConfig) { return UsersNotificationsApiFp(this.configuration).usersNotificationsControllerFindConnection(notificationConnectionExistsDto, options).then((request) => request(this.axios, this.basePath)); } } ================================================ FILE: client/src/api/base.ts ================================================ /* tslint:disable */ /* eslint-disable */ /** * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * * The version of the OpenAPI document: 1.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech * Do not edit the class manually. */ import { Configuration } from "./configuration"; // Some imports not used depending on template conditions // @ts-ignore import globalAxios, { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios'; export const BASE_PATH = "/api/v2".replace(/\/+$/, ""); /** * * @export */ export const COLLECTION_FORMATS = { csv: ",", ssv: " ", tsv: "\t", pipes: "|", }; /** * * @export * @interface RequestArgs */ export interface RequestArgs { url: string; options: AxiosRequestConfig; } /** * * @export * @class BaseAPI */ export class BaseAPI { protected configuration: Configuration | undefined; constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected axios: AxiosInstance = globalAxios) { if (configuration) { this.configuration = configuration; this.basePath = configuration.basePath || this.basePath; } } }; /** * * @export * @class RequiredError * @extends {Error} */ export class RequiredError extends Error { name: "RequiredError" = "RequiredError"; constructor(public field: string, msg?: string) { super(msg); } } ================================================ FILE: client/src/api/common.ts ================================================ /* tslint:disable */ /* eslint-disable */ /** * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * * The version of the OpenAPI document: 1.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech * Do not edit the class manually. */ import { Configuration } from "./configuration"; import { RequiredError, RequestArgs } from "./base"; import { AxiosInstance, AxiosResponse } from 'axios'; /** * * @export */ export const DUMMY_BASE_URL = 'https://example.com' /** * * @throws {RequiredError} * @export */ export const assertParamExists = function (functionName: string, paramName: string, paramValue: unknown) { if (paramValue === null || paramValue === undefined) { throw new RequiredError(paramName, `Required parameter ${paramName} was null or undefined when calling ${functionName}.`); } } /** * * @export */ export const setApiKeyToObject = async function (object: any, keyParamName: string, configuration?: Configuration) { if (configuration && configuration.apiKey) { const localVarApiKeyValue = typeof configuration.apiKey === 'function' ? await configuration.apiKey(keyParamName) : await configuration.apiKey; object[keyParamName] = localVarApiKeyValue; } } /** * * @export */ export const setBasicAuthToObject = function (object: any, configuration?: Configuration) { if (configuration && (configuration.username || configuration.password)) { object["auth"] = { username: configuration.username, password: configuration.password }; } } /** * * @export */ export const setBearerAuthToObject = async function (object: any, configuration?: Configuration) { if (configuration && configuration.accessToken) { const accessToken = typeof configuration.accessToken === 'function' ? await configuration.accessToken() : await configuration.accessToken; object["Authorization"] = "Bearer " + accessToken; } } /** * * @export */ export const setOAuthToObject = async function (object: any, name: string, scopes: string[], configuration?: Configuration) { if (configuration && configuration.accessToken) { const localVarAccessTokenValue = typeof configuration.accessToken === 'function' ? await configuration.accessToken(name, scopes) : await configuration.accessToken; object["Authorization"] = "Bearer " + localVarAccessTokenValue; } } /** * * @export */ export const setSearchParams = function (url: URL, ...objects: any[]) { const searchParams = new URLSearchParams(url.search); for (const object of objects) { for (const key in object) { if (Array.isArray(object[key])) { searchParams.delete(key); for (const item of object[key]) { searchParams.append(key, item); } } else { searchParams.set(key, object[key]); } } } url.search = searchParams.toString(); } /** * * @export */ export const serializeDataIfNeeded = function (value: any, requestOptions: any, configuration?: Configuration) { const nonString = typeof value !== 'string'; const needsSerialization = nonString && configuration && configuration.isJsonMime ? configuration.isJsonMime(requestOptions.headers['Content-Type']) : nonString; return needsSerialization ? JSON.stringify(value !== undefined ? value : {}) : (value || ""); } /** * * @export */ export const toPathString = function (url: URL) { return url.pathname + url.search + url.hash } /** * * @export */ export const createRequestFunction = function (axiosArgs: RequestArgs, globalAxios: AxiosInstance, BASE_PATH: string, configuration?: Configuration) { return >(axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => { const axiosRequestArgs = {...axiosArgs.options, url: (configuration?.basePath || basePath) + axiosArgs.url}; return axios.request(axiosRequestArgs); }; } ================================================ FILE: client/src/api/configuration.ts ================================================ /* tslint:disable */ /* eslint-disable */ /** * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * * The version of the OpenAPI document: 1.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech * Do not edit the class manually. */ export interface ConfigurationParameters { apiKey?: string | Promise | ((name: string) => string) | ((name: string) => Promise); username?: string; password?: string; accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise); basePath?: string; baseOptions?: any; formDataCtor?: new () => any; } export class Configuration { /** * parameter for apiKey security * @param name security name * @memberof Configuration */ apiKey?: string | Promise | ((name: string) => string) | ((name: string) => Promise); /** * parameter for basic security * * @type {string} * @memberof Configuration */ username?: string; /** * parameter for basic security * * @type {string} * @memberof Configuration */ password?: string; /** * parameter for oauth2 security * @param name security name * @param scopes oauth2 scope * @memberof Configuration */ accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise); /** * override base path * * @type {string} * @memberof Configuration */ basePath?: string; /** * base options for axios calls * * @type {any} * @memberof Configuration */ baseOptions?: any; /** * The FormData constructor that will be used to create multipart form data * requests. You can inject this here so that execution environments that * do not support the FormData class can still run the generated client. * * @type {new () => FormData} */ formDataCtor?: new () => any; constructor(param: ConfigurationParameters = {}) { this.apiKey = param.apiKey; this.username = param.username; this.password = param.password; this.accessToken = param.accessToken; this.basePath = param.basePath; this.baseOptions = param.baseOptions; this.formDataCtor = param.formDataCtor; } /** * Check if the given MIME is a JSON MIME. * JSON MIME examples: * application/json * application/json; charset=UTF8 * APPLICATION/JSON * application/vnd.company+json * @param mime - MIME (Multipurpose Internet Mail Extensions) * @return True if the given MIME is JSON, false otherwise. */ public isJsonMime(mime: string): boolean { const jsonMime: RegExp = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i'); return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json'); } } ================================================ FILE: client/src/api/git_push.sh ================================================ #!/bin/sh # ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ # # Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com" git_user_id=$1 git_repo_id=$2 release_note=$3 git_host=$4 if [ "$git_host" = "" ]; then git_host="github.com" echo "[INFO] No command line input provided. Set \$git_host to $git_host" fi if [ "$git_user_id" = "" ]; then git_user_id="GIT_USER_ID" echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" fi if [ "$git_repo_id" = "" ]; then git_repo_id="GIT_REPO_ID" echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" fi if [ "$release_note" = "" ]; then release_note="Minor update" echo "[INFO] No command line input provided. Set \$release_note to $release_note" fi # Initialize the local directory as a Git repository git init # Adds the files in the local repository and stages them for commit. git add . # Commits the tracked changes and prepares them to be pushed to a remote repository. git commit -m "$release_note" # Sets the new remote git_remote=$(git remote) if [ "$git_remote" = "" ]; then # git remote not defined if [ "$GIT_TOKEN" = "" ]; then echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git else git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git fi fi git pull origin master # Pushes (Forces) the changes in the local repository up to the remote repository echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" git push origin master 2>&1 | grep -v 'To https' ================================================ FILE: client/src/api/index.ts ================================================ /* tslint:disable */ /* eslint-disable */ /** * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * * The version of the OpenAPI document: 1.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech * Do not edit the class manually. */ export * from "./api"; export * from "./configuration"; ================================================ FILE: client/src/components/Analytics.tsx ================================================ import Script from 'next/script'; const enableAnalytics = process.env.NODE_ENV === 'production'; export function Analytics() { if (!enableAnalytics) { return null; } return ( <> )} ); } }; } export { withGoogleMaps }; ================================================ FILE: client/src/components/withSession.tsx ================================================ import { CourseRole } from '@client/services/models'; export interface CourseInfo { mentorId?: number; studentId?: number; roles: CourseRole[]; isExpelled?: boolean; } export interface Session { id: number; githubId: string; isAdmin: boolean; isHirer: boolean; courses: { [courseId: string]: CourseInfo | undefined }; } ================================================ FILE: client/src/configs/cdn.ts ================================================ export const CDN_AVATARS_URL = 'https://cdn.rs.school/avatars'; ================================================ FILE: client/src/configs/course-icons.ts ================================================ export const DEFAULT_COURSE_ICONS: Record = { javascript: { active: '/static/svg/disciplines/javascript.svg', archived: '/static/svg/disciplines/javascript-archived.svg', label: 'Javascript', }, angular: { active: '/static/svg/disciplines/angular.svg', archived: '/static/svg/disciplines/angular-archived.svg', label: 'Angular', }, reactjs: { active: '/static/svg/disciplines/reactjs.svg', archived: '/static/svg/disciplines/reactjs-archived.svg', label: 'Reactjs', }, android: { active: '/static/svg/disciplines/android.svg', archived: '/static/svg/disciplines/android-archived.svg', label: 'Android', }, ios: { active: '/static/svg/disciplines/ios.svg', archived: '/static/svg/disciplines/ios-archived.svg', label: 'iOS', }, nodejs: { active: '/static/svg/disciplines/nodejs.svg', archived: '/static/svg/disciplines/nodejs-archived.svg', label: 'Nodejs', }, ml: { active: '/static/svg/disciplines/machine-learning.svg', archived: '/static/svg/disciplines/machine-learning-archived.svg', label: 'Machine learning', }, nodejsAws: { active: '/static/svg/disciplines/nodejs-aws.svg', archived: '/static/svg/disciplines/nodejs-aws-archived.svg', label: 'Nodejs In AWS', }, }; ================================================ FILE: client/src/configs/discord-integration.ts ================================================ const isDevMode = process.env.NODE_ENV !== 'production'; const clientId = isDevMode ? '625945676009963521' : '978920245743976448'; const redirectUrl = isDevMode ? 'http://localhost:3000/profile' : 'https://app.rs.school/profile'; export default { api: { auth: `https://discord.com/api/oauth2/authorize?client_id=${clientId}&redirect_uri=${encodeURIComponent( redirectUrl, )}&response_type=token&scope=identify`, me: 'https://discordapp.com/api/users/@me', }, }; ================================================ FILE: client/src/configs/gcp.ts ================================================ export const mapsApiKey = process.env.RSSHCOOL_UI_GCP_MAPS_API_KEY; ================================================ FILE: client/src/configs/heroes-badges.ts ================================================ const heroesBadges: Record = { Congratulations: { name: 'Congratulations', pictureId: 23, url: 'Congratulations.svg', }, Expert_help: { name: 'Expert help', pictureId: 39, url: 'ExpertHelp.svg', }, Great_speaker: { name: 'Great speaker', pictureId: 29, url: 'GreatSpeaker.svg', }, Good_job: { name: 'Good job', pictureId: 17, url: 'GoodJob.svg', }, Helping_hand: { name: 'Helping hand', pictureId: 14, url: 'HelpingHandSloth.svg', }, Hero: { name: 'Hero', pictureId: 25, url: 'Hero.svg', }, Thank_you: { name: 'Thank you', pictureId: 12, url: 'ThankYou.svg', }, Outstanding_work: { name: 'Outstanding work', pictureId: 33, url: 'OutstandingWork.svg', }, Top_performer: { name: 'Top performer', pictureId: 34, url: 'TopPerformer.svg', }, Job_Offer: { name: 'Job Offer', pictureId: 35, url: 'JobOffer.svg', }, RS_activist: { name: 'RS activist', pictureId: 36, url: 'RSActivist.svg', }, Jury_Team: { name: 'Jury team', pictureId: 36, url: 'JuryTeam.svg', }, Mentor: { name: 'Mentor', pictureId: 37, url: 'Mentor.svg', }, Coordinator: { name: 'Coordinator', pictureId: 38, url: 'Coordinator.svg', }, Contributor: { name: 'Contributor', pictureId: 40, url: 'Contributor.svg', }, Thanks: { name: 'Thanks', pictureId: 41, url: 'Thanks.svg', }, }; export default heroesBadges; ================================================ FILE: client/src/configs/registry.ts ================================================ import { Session } from '@client/components/withSession'; import { Course } from '@client/services/models'; export const TYPES = { MENTOR: 'mentor', STUDENT: 'student', }; export type Props = { courses?: Course[]; session: Session; }; ================================================ FILE: client/src/configs/timezones.ts ================================================ export const TIMEZONES = [ 'UTC', 'Europe/London', 'Europe/Warsaw', 'Europe/Kiev', 'Europe/Minsk', 'Europe/Moscow', 'Europe/Volgograd', 'Asia/Yekaterinburg', 'Asia/Tashkent', 'Asia/Tbilisi', ]; const europeTimezones = [ 'Europe/Amsterdam', 'Europe/Andorra', 'Europe/Athens', 'Europe/Belgrade', 'Europe/Berlin', 'Europe/Bratislava', 'Europe/Brussels', 'Europe/Bucharest', 'Europe/Budapest', 'Europe/Chisinau', 'Europe/Copenhagen', 'Europe/Helsinki', 'Europe/Istanbul', 'Europe/Kiev', 'Europe/Lisbon', 'Europe/Ljubljana', 'Europe/Luxembourg', 'Europe/Madrid', 'Europe/Malta', 'Europe/Minsk', 'Europe/Monaco', 'Europe/Moscow', 'Europe/Oslo', 'Europe/Paris', 'Europe/Prague', 'Europe/Riga', 'Europe/Rome', 'Europe/San_Marino', 'Europe/Sofia', 'Europe/Stockholm', 'Europe/Tallinn', 'Europe/Vienna', 'Europe/Vilnius', 'Europe/Warsaw', 'Europe/Zaporozhye', 'Europe/Zurich', ]; const asiaTimezones = [ 'Asia/Almaty', 'Asia/Amman', 'Asia/Anadyr', 'Asia/Aqtau', 'Asia/Aqtobe', 'Asia/Ashgabat', 'Asia/Baghdad', 'Asia/Bahrain', 'Asia/Baku', 'Asia/Bangkok', 'Asia/Beirut', 'Asia/Bishkek', 'Asia/Brunei', 'Asia/Choibalsan', 'Asia/Chongqing', 'Asia/Colombo', 'Asia/Damascus', 'Asia/Dhaka', 'Asia/Dili', 'Asia/Dubai', 'Asia/Dushanbe', 'Asia/Tashkent', 'Asia/Tbilisi', 'Asia/Tehran', 'Asia/Thimphu', 'Asia/Tokyo', 'Asia/Ulaanbaatar', 'Asia/Urumqi', 'Asia/Ust-Nera', 'Asia/Vientiane', 'Asia/Vladivostok', 'Asia/Yakutsk', 'Asia/Yangon', 'Asia/Yekaterinburg', ]; const americaTimezones = [ 'America/Araguaina', 'America/Argentina/Buenos_Aires', 'America/Barbados', 'America/Belem', 'America/Belize', 'America/Bogota', 'America/Cancun', 'America/Caracas', 'America/Cayenne', 'America/Chicago', 'America/Chihuahua', 'America/Costa_Rica', 'America/Denver', 'America/Detroit', 'America/Edmonton', 'America/Guatemala', 'America/Jamaica', 'America/Los_Angeles', 'America/Mexico_City', 'America/Monterrey', 'America/Montevideo', 'America/Montreal', 'America/New_York', 'America/Santiago', 'America/Sao_Paulo', ]; export const ALL_TIMEZONES = ['UTC', ...europeTimezones, ...asiaTimezones, ...americaTimezones]; export const DEFAULT_TIMEZONE = 'Europe/Minsk'; ================================================ FILE: client/src/data/course-leave-reasons.ts ================================================ export const CourseLeaveReason = { TooDifficult: 'too_difficult', NotUseful: 'not_useful', LackOfTime: 'lack_of_time', Other: 'other', NoInterest: 'no_interest', PoorQuality: 'poor_quality', FoundAlternative: 'found_alternative', PersonalReasons: 'personal_reasons', GotJob: 'got_job', GotInternship: 'got_internship', } as const; export type CourseLeaveReason = (typeof CourseLeaveReason)[keyof typeof CourseLeaveReason]; ================================================ FILE: client/src/data/english.ts ================================================ export const ENGLISH_LEVELS = ['A0', 'A1', 'A1+', 'A2', 'A2+', 'B1', 'B1+', 'B2', 'B2+', 'C1', 'C1+', 'C2'] as const; ================================================ FILE: client/src/data/eventTypes.ts ================================================ import { CourseEventDtoTypeEnum } from '@client/api'; export const EVENT_TYPES: { id: CourseEventDtoTypeEnum; name: string }[] = [ { name: 'Online Lecture', id: CourseEventDtoTypeEnum.LectureOnline }, { name: 'Offline Lecture', id: CourseEventDtoTypeEnum.LectureOffline }, { name: 'Online/Offline Lecture', id: CourseEventDtoTypeEnum.LectureMixed }, { name: 'Self-studying', id: CourseEventDtoTypeEnum.LectureSelfStudy }, { name: 'Warm-up', id: CourseEventDtoTypeEnum.Warmup }, { name: 'Info', id: CourseEventDtoTypeEnum.Info }, { name: 'Webinar', id: CourseEventDtoTypeEnum.Webinar }, { name: 'Workshop', id: CourseEventDtoTypeEnum.Workshop }, { name: 'Meetup', id: CourseEventDtoTypeEnum.Meetup }, { name: 'Special', id: CourseEventDtoTypeEnum.Special }, { name: 'Cross-Check deadline', id: CourseEventDtoTypeEnum.CrossCheckDeadline }, ]; export const EVENT_TYPES_MAP = EVENT_TYPES.reduce( (acc, { id, name }) => ({ ...acc, [id]: name }), {} as Record, ); ================================================ FILE: client/src/data/index.ts ================================================ import { EVENT_TYPES_MAP } from './eventTypes'; import { TASK_TYPES_MAP } from './taskTypes'; export const TASK_EVENT_TYPES_MAP = { ...TASK_TYPES_MAP, ...EVENT_TYPES_MAP, }; ================================================ FILE: client/src/data/interviews/__tests__/templateValidator.test.ts ================================================ import { InterviewTemplate, Question, QuestionCategory } from '@client/data/interviews'; import { validateInterviewTemplate, validateQuestionCategory, TemplateValidationError } from '../templateValidator'; const mockQuestion1: Question = { id: 101, name: 'Q1' }; const mockQuestion2: Question = { id: 102, name: 'Q2' }; const mockQuestionDuplicateId: Question = { id: 101, name: 'Q_DUP' }; const mockCategory1: QuestionCategory = { id: 1, name: 'Behavioral', questions: [mockQuestion1, mockQuestion2], }; const mockCategory2: QuestionCategory = { id: 2, name: 'Technical', description: 'Deep dive questions', questions: [], }; const mockCategoryDuplicateId: QuestionCategory = { id: 1, name: 'Duplicate Category', questions: [], }; const mockValidTemplate: InterviewTemplate = { name: 'Valid Tech Interview', categories: [mockCategory1, mockCategory2], examplesUrl: 'http://example.com/examples', descriptionHtml: '

A description

', }; const mockTemplateWithDuplicateCategory: InterviewTemplate = { name: 'Invalid Template - Dup Cat ID', categories: [mockCategory1, mockCategoryDuplicateId], examplesUrl: 'http://example.com/examples', }; const mockCategoryWithDuplicateQuestions: QuestionCategory = { id: 3, name: 'Invalid Category - Dup Q ID', questions: [mockQuestion1, mockQuestionDuplicateId], // IDs are 101, 101 }; describe('TemplateValidationError', () => { it('should be an instance of Error', () => { const error = new TemplateValidationError('Test'); expect(error).toBeInstanceOf(Error); }); it('should have the correct name property', () => { const error = new TemplateValidationError('Test'); expect(error.name).toBe('TemplateValidationError'); expect(error.message).toBe('Test'); }); it('should have the correct message property', () => { const error = new TemplateValidationError('Test'); expect(error.message).toBe('Test'); }); }); describe('validateQuestionCategory', () => { it('should successfully validate a category with unique question IDs', () => { expect(() => validateQuestionCategory(mockCategory1)).not.toThrow(); }); it('should successfully validate a category with no questions', () => { expect(() => validateQuestionCategory(mockCategory2)).not.toThrow(); }); it('should throw TemplateValidationError for duplicate question IDs', () => { expect(() => validateQuestionCategory(mockCategoryWithDuplicateQuestions)).toThrow(TemplateValidationError); expect(() => validateQuestionCategory(mockCategoryWithDuplicateQuestions)).toThrow( 'Questions must have unique ids. Category ID: 3', ); }); }); describe('validateInterviewTemplate', () => { it('should successfully validate a valid template with unique category and question IDs', () => { expect(() => validateInterviewTemplate(mockValidTemplate)).not.toThrow(); }); it('should throw TemplateValidationError for duplicate category IDs', () => { expect(() => validateInterviewTemplate(mockTemplateWithDuplicateCategory)).toThrow(TemplateValidationError); expect(() => validateInterviewTemplate(mockTemplateWithDuplicateCategory)).toThrow( 'Categories must have unique ids. Template name: Invalid Template - Dup Cat ID', ); }); it('should throw TemplateValidationError when a nested category has duplicate question IDs, and prepend template name', () => { const templateWithNestedError: InterviewTemplate = { name: 'Template-WithError', categories: [mockCategory1, mockCategoryWithDuplicateQuestions], examplesUrl: 'url', }; expect(() => validateInterviewTemplate(templateWithNestedError)).toThrow(TemplateValidationError); expect(() => validateInterviewTemplate(templateWithNestedError)).toThrow( 'Template name Template-WithError. Questions must have unique ids. Category ID: 3', ); }); }); ================================================ FILE: client/src/data/interviews/angular.ts ================================================ import { InterviewTemplate } from './types'; export const angularTemplate: InterviewTemplate = { name: 'Angular interview ', examplesUrl: 'https://github.com/rolling-scopes-school/tasks/blob/master/angular/modules/interview/questions.md', categories: [ { id: 3000, name: 'General', questions: [ { id: 3001, name: 'What is Angular and what is it used for?' }, { id: 3002, name: 'What is Angular CLI and what are its main features?' }, { id: 3003, name: 'What is a module in Angular, and what is its role in an application?' }, { id: 3004, name: 'What is inter-component communication in Angular?(e.g., @Input/@Output, services with Observables, etc.).', }, { id: 3005, name: 'Shadow DOM in Angular', }, ], }, { id: 3010, name: 'Components', questions: [ { id: 3011, name: 'What are Components in Angular, and how are they the foundation of an application structure?', }, { id: 3012, name: "How do you configure a component's selector, template, and style using the @Component decorator?", }, { id: 3013, name: 'How would you explain the component lifecycle and its main methods (e.g., ngOnInit, ngOnChanges, ngOnDestroy)?', }, { id: 3014, name: 'How does two-way data binding work in Angular, and how does it differ from one-way data binding?', }, { id: 3015, name: 'Standalone components.' }, { id: 3016, name: 'What are ViewChild and ViewChildren?' }, { id: 3017, name: 'What is the difference between ElementRef and Renderer2?' }, { id: 3018, name: 'How do HostBinding and HostListener decorators work?' }, ], }, { id: 3020, name: 'Directives', questions: [ { id: 3021, name: 'What are "Directives" in Angular, and what are they used for?' }, { id: 3022, name: 'How do you create and use a custom directive? Explain the use of the "@Directive" decorator', }, { id: 3023, name: 'What is the difference between structural and attribute directives? Please provide examples.', }, { id: 3024, name: 'Explain *ngIf and *ngFor and their usage.' }, { id: 3025, name: 'What is the difference between *ngIf and [hidden]?' }, { id: 3026, name: 'What is the purpose of *ngSwitch, *ngSwitchCase, and *ngSwitchDefault, and how do you use them?', }, { id: 3027, name: 'What is the difference between *ngStyle and *ngClass?' }, { id: 3028, name: 'What is *ngContainer and what is it used for? Provide an example.' }, { id: 3029, name: 'How do you create custom structural directives using ?' }, ], }, { id: 3030, name: 'Pipe', questions: [ { id: 3031, name: 'What is a Pipe, and what is its purpose in Angular?' }, { id: 3032, name: 'Can you provide examples of some built-in pipes (e.g., date, uppercase, lowercase)?' }, { id: 3033, name: 'What is the difference between Pure and Impure pipes. How do they affect performance?' }, { id: 3034, name: 'How do you use multiple pipes simultaneously?' }, { id: 3035, name: 'How do you pass parameters to a Pipe to change behavior or format data?' }, { id: 3036, name: 'What are the advantages of using Async pipes. How do you apply them with Observable or Promise?', }, { id: 3037, name: 'How does the process of registering a custom pipe in a module occur?' }, ], }, { id: 3040, name: 'Routing', questions: [ { id: 3041, name: 'What is Routing in Angular, and what is it used for?' }, { id: 3042, name: 'How do you configure a basic routing system using RouterModule and ?' }, { id: 3043, name: 'How do you use route parameters and queryParams to pass and retrieve data in routes?' }, { id: 3044, name: 'Can you provide an example of using child routes?' }, { id: 3045, name: 'What are the preloading strategies, and how do you use them?' }, { id: 3046, name: 'How do you use Route Guards (e.g., CanActivate and CanDeactivate) to protect routes?' }, { id: 3047, name: 'What is ActivatedRoute, and how do you apply it to get information about the current route?', }, ], }, { id: 3050, name: 'RxJS', questions: [ { id: 3051, name: 'Define the concept of RxJS and its usage in Angular' }, { id: 3052, name: 'What are Observable, Observer, and Subscriptions?' }, { id: 3053, name: 'What is the difference between Observable and Promise?' }, { id: 3054, name: 'Can you provide examples of basic RxJS operators in Angular (e.g., map, filter, catchError, switchMap)?', }, { id: 3055, name: 'What are Subject and BehaviorSubject, and how are they used in Angular?' }, { id: 3056, name: 'How would you explain the concepts of Hot and Cold Observables?' }, { id: 3057, name: 'How do you properly unsubscribe from an Observable?' }, ], }, { id: 3060, name: 'Dependency Injection', questions: [ { id: 3061, name: 'What is Dependency Injection, and what are its objectives in Angular?' }, { id: 3062, name: 'How do you create a service and use it in components for dependency injection?' }, { id: 3063, name: 'What is the difference between "providedIn: root", "providedIn: any", and registering a provider in the "providers" section of NgModule?', }, { id: 3064, name: 'What are useClass, useValue, and useFactory? How are they used when creating providers?' }, { id: 3065, name: 'Explain the concept of Injector and provider hierarchy.' }, { id: 3066, name: 'What is a DI token, and how do you use it for dependency injection?' }, { id: 3067, name: 'How do you use @Optional, @Self, and @SkipSelf decorators to control dependency injection and their handling?', }, { id: 3068, name: 'How do you inject dependencies based on conditions or by different provided implementations?', }, ], }, { id: 3070, name: 'Forms', questions: [ { id: 3071, name: 'What is the difference between Template-driven Forms and Reactive Forms?' }, { id: 3072, name: 'What are FormControl, FormGroup, and FormArray in the context of Reactive Forms?' }, { id: 3073, name: 'What are the differences in working with validation for Template-driven Forms and Reactive Forms?', }, { id: 3074, name: 'How do you implement custom validators for forms?' }, { id: 3075, name: 'How can you retrieve and process data from forms after submission?' }, { id: 3076, name: 'What is two-way data binding in the context of Template-driven Forms?' }, { id: 3077, name: 'How do you track the change state of forms or form controls (e.g., touched, dirty)?' }, ], }, { id: 3080, name: 'Lazy Loading', questions: [ { id: 3081, name: 'What is Lazy loading, and what is its purpose in Angular applications?' }, { id: 3082, name: 'How do you configure lazy loading for a specific module?' }, { id: 3083, name: 'What changes to the routing system are necessary to support lazy loading?' }, { id: 3084, name: 'What are the advantages of using lazy loading in your application?' }, ], }, { id: 3090, name: 'Modules', questions: [ { id: 3091, name: 'What is a Module in Angular, and what role does it play in an application?' }, { id: 3092, name: 'Can you explain the structure of a module and its metadata?' }, { id: 3093, name: 'How can you separate functionality into different modules and connect them to the main application module?', }, ], }, { id: 3100, name: 'HTTP', questions: [ { id: 3101, name: 'What is HttpClientModule, and why is it important in Angular applications?' }, { id: 3102, name: "How can you make HTTP requests using Angular's HttpClient?" }, { id: 3103, name: 'Can you explain the difference between Observables and Promises in handling HTTP responses?', }, { id: 3104, name: 'How can you handle errors during HTTP requests in Angular?' }, { id: 3105, name: 'What are some techniques to optimize HTTP requests and handle caching considerations for Angular applications?', }, { id: 3106, name: 'What is the purpose of HttpInterceptor in Angular, and how does it work?' }, ], }, { id: 3110, name: 'Tests (Testing)', questions: [ { id: 3111, name: 'What types of Testing does Angular support (e.g., unit tests, integration tests, e2e tests)?', }, { id: 3112, name: 'What are the main tools and libraries used by Angular for testing (Jasmine, Karma, and Protractor)?', }, { id: 3113, name: 'What is TestBed, and how is it used to set up a testing environment?' }, { id: 3114, name: 'How do you test Angular components using ComponentFixture and DebugElement?' }, { id: 3115, name: 'How do you test directives and pipes in Angular?' }, { id: 3116, name: 'How do you mock and stub dependencies in tests for services?' }, { id: 3117, name: 'What are async, fakeAsync, and tick, and how are they used when testing asynchronous code?', }, ], }, { id: 3120, name: 'Coding task', questions: [{ id: 3121, name: 'Small Angular app: component, service, pipe, directives...' }], }, ], }; ================================================ FILE: client/src/data/interviews/corejs1.ts ================================================ import { InputType, InterviewTemplate } from './types'; export const corejs1Template: InterviewTemplate = { name: 'CoreJS', examplesUrl: 'https://github.com/rolling-scopes-school/tasks/blob/master/tasks/interview-basic-coreJS.md', descriptionHtml: ` During the interview, students can score up to 100 points. Each topic has its value in points.
Please see the details below.
  • Advanced Expressions - up to 10 points
  • Function - up to 5 points
  • Functional Scope - up to 10 points
  • Functions Parameters / Arguments - up to 5 points
  • Closures Advanced - up to 15 points
  • Advanced Functions - up to 15 points
  • ECMAScript Intermediate - up to 10 points
  • Objects Built-in Methods - up to 10 points
  • Arrays Built-in Methods - up to 5 points
  • Arrays Iterating, Sorting, Filtering - up to 5 points
  • Events Basics - up to 5 points
  • Events Propagation / Preventing - up to 5 points
  • Timers - up to 5 points
  • Web Storage API & Cookies - up to 5 points
  • Date & Time - up to 5 points
  • Software Development Best Practices - up to 5 points

`, categories: [ { id: 1010, name: 'Advanced Expressions', description: `10 points`, questions: [ { id: 1011, name: 'Object.is (optional)' }, { id: 1012, name: 'Differences between let, var, and const' }, { id: 1013, name: 'Exploring the Temporal Dead Zone', type: InputType.Input }, { id: 1015, name: 'Hoisting', type: InputType.Input }, ], }, { id: 1020, name: 'Function', description: `5 points`, questions: [ { id: 1021, name: 'Differences and uses of arrow functions, function expressions, and function declarations', type: InputType.Input, }, ], }, { id: 1030, name: 'Functional Scope', description: '10 points', questions: [ { id: 1031, name: 'Global scope vs. Functional scope' }, { id: 1032, name: 'Variable visibility areas' }, { id: 1033, name: 'Working with nested scopes' }, ], }, { id: 1040, name: 'Functions Parameters / Arguments', description: '5 points', questions: [ { id: 1041, name: 'Defining function parameters', }, { id: 1042, name: 'Differences in parameters passing by value and by reference', }, { id: 1043, name: 'Handling a dynamic amount of function parameters', }, ], }, { id: 1050, name: 'Closures Advanced', description: '15 points', questions: [ { id: 1051, name: 'Understanding context and lexical environments', type: InputType.Input, }, { id: 1052, name: 'Differences between scope and context', type: InputType.Input, }, { id: 1053, name: 'The mechanism of lexical environment traversal', }, { id: 1054, name: 'Connection between function and its lexical environment', type: InputType.Input, }, ], }, { id: 1060, name: 'Advanced Functions', description: '15 points', questions: [ { id: 1061, name: '`this` in functions', type: InputType.Input, }, { id: 1062, name: 'Reference Type & losing `this`', }, { id: 1063, name: 'Understand difference between function and method', }, { id: 1064, name: 'Understand how `this` works, realize `this` possible issues', type: InputType.Input, }, { id: 1065, name: 'Manage `this`', type: InputType.Input, }, { id: 1067, name: 'Be able to use `call` and `apply` Function built-in methods', }, { id: 1068, name: 'Know how to bind `this` scope to function', }, { id: 1069, name: 'Binding, binding one function twice', }, ], }, { id: 1070, name: 'ECMAScript Intermediate', description: '10 points', questions: [ { id: 1071, name: 'Function default parameters', }, { id: 1072, name: 'Using spread operator for function arguments', }, { id: 1073, name: 'Comparing `arguments` and `rest parameters`', }, { id: 1074, name: 'Array concatenation with spread operator', }, { id: 1075, name: 'Destructuring assignments for variables and function arguments', }, ], }, { id: 1080, name: 'Objects Built-in Methods', description: '10 points', questions: [ { id: 1081, name: 'Utilizing `Object.keys` and `Object.values`', }, { id: 1082, name: 'Working with static Object methods', }, { id: 1083, name: 'Property flags and descriptors', }, { id: 1084, name: 'Creating iterable objects and using `Symbol.iterator` (optional)', }, ], }, { id: 1100, name: 'Arrays Built-in Methods', description: '5 points', questions: [ { id: 1101, name: 'Copying and modifying arrays', }, { id: 1102, name: 'Flattening nested arrays', }, ], }, { id: 1110, name: 'Arrays Iterating, Sorting, Filtering', description: '5 points', questions: [ { id: 1111, name: 'Sorting and custom sorting arrays', }, { id: 1112, name: 'Filtering array elements', }, ], }, { id: 1130, name: 'Events Basics', description: '5 points', questions: [ { id: 1131, name: 'Types of DOM Events', }, { id: 1132, name: 'Working with Mouse and Keyboard Events', }, { id: 1133, name: 'Handling Form and Input Events', }, { id: 1134, name: 'Event Listeners', }, { id: 1135, name: 'Event Phases and their differences', }, { id: 1136, name: 'Custom events (optional)', }, ], }, { id: 1140, name: 'Events Propagation / Preventing', description: '5 points', questions: [ { id: 1141, name: 'Event propagation cycle', }, { id: 1142, name: 'Stopping event propagation', }, { id: 1143, name: 'Preventing default browser behavior', }, { id: 1144, name: 'Event delegation and its pros/cons', }, ], }, { id: 1150, name: 'Timers', description: '5 points', questions: [ { id: 1151, name: 'Usage of `setTimeout` / `setInterval`', }, { id: 1152, name: 'Clearing timers with `clearTimeout` / `clearInterval`', }, ], }, { id: 1160, name: 'Web Storage API & Cookies', description: '5 points', questions: [ { id: 1161, name: 'Differences between LocalStorage, SessionStorage, and Cookies', }, ], }, { id: 1170, name: 'Date & Time', description: '5 points', questions: [ { id: 1171, name: 'Working with the Date object', }, { id: 1172, name: 'Timezones and Internationalization in JavaScript (Intl)', }, ], }, { id: 1180, name: 'Software Development Best Practices', description: '5 points', questions: [ { id: 1181, name: 'Understanding and applying KISS, DRY, and YAGNI principles', }, ], }, ], }; ================================================ FILE: client/src/data/interviews/corejs2.ts ================================================ import { InputType, InterviewTemplate } from './types'; export const corejs2Template: InterviewTemplate = { name: 'CoreJS2', examplesUrl: 'https://github.com/rolling-scopes-school/tasks/blob/master/tasks/interview-corejs.md', descriptionHtml: ` During the interview, students can score up to 100 points. Each topic has its value in points.
Please see the details below.
  • Functional Patterns - up to 10 points
  • Prototypal Inheritance Basics - up to 15 points
  • ECMAScript Classes - up to 10 points
  • ECMAScript Data Types & Expressions - up to 10 points
  • ECMAScript Advanced - up to 10 points
  • Network requests - up to 10 points
  • Page Lifecycle - up to 10 points
  • Typescript - up to 10 points
  • Testing (optional) - up to 10 points

`, categories: [ { id: 2060, name: 'Functional Patterns', description: '10 points', questions: [ { id: 2061, name: 'Immediately invoked functional expression (IIFE)' }, { id: 2062, name: 'Callback (Function as argument)' }, { id: 2063, name: 'Binding, binding one function twice' }, { id: 2064, name: 'Know how to bind this scope to function' }, ], }, { id: 2080, name: 'Prototypal Inheritance Basics', description: '15 points', questions: [ { id: 2081, name: '__proto__ property' }, { id: 2082, name: 'Able to use [Object.create] and define __proto__ explicitly' }, { id: 2083, name: 'prototype property' }, { id: 2084, name: 'Understand dependency between function constructor prototype and instance __proto__' }, ], }, { id: 2090, name: 'ECMAScript Classes', description: '10 points', questions: [ { id: 2091, name: 'Class declaration' }, { id: 2092, name: 'What does super() do and where we have to use it?' }, ], }, { id: 2100, name: 'ECMAScript Data Types & Expressions', description: '10 points', questions: [ { id: 2101, name: 'Set/Map data types' }, { id: 2102, name: 'WeakSet/WeakMap data types' }, ], }, { id: 2110, name: 'ECMAScript Advanced', description: '10 points', questions: [ { id: 2115, name: 'event loop' }, { id: 2111, name: 'Promises' }, { id: 2112, name: 'Promise Chaining' }, { id: 2113, name: 'Promise static methods' }, { id: 2114, name: 'Be able to handle errors in promises' }, { id: 2116, name: 'async/await' }, ], }, { id: 2120, name: 'Network requests', description: '5 points', questions: [ { id: 2121, name: 'Fetch' }, { id: 2122, name: 'XMLHTTPRequest (concept) (optional)' }, ], }, { id: 2130, name: 'Page Lifecycle', description: '10 points', questions: [ { id: 2131, name: 'Parsing' }, { id: 2132, name: 'Reflow' }, { id: 2133, name: 'Repaint' }, ], }, { id: 2150, name: 'Typescript', description: '10 points', questions: [ { id: 2151, name: 'basic types' }, { id: 2152, name: 'enums' }, { id: 2153, name: 'type / interface, differences between them' }, { id: 2154, name: 'function types' }, { id: 2155, name: 'generic types (concept)' }, { id: 2156, name: 'utitily types (optional)' }, { id: 2157, name: 'typeguards (optional)' }, ], }, { id: 2160, name: 'Testing (optional)', description: '10 points', questions: [ { id: 2161, name: 'Testing Types' }, { id: 2162, name: 'Test Pyramid' }, ], }, { id: 2170, name: 'Software Development Methodologies', description: '10 points', questions: [ { id: 2171, name: 'Agile' }, { id: 2172, name: 'Scrum / Kanban / Waterfall' }, ], }, { id: 2180, name: 'Coding Task', description: '20 points', questions: [{ id: 2181, name: 'Coding Task', type: InputType.Input }], }, ], }; ================================================ FILE: client/src/data/interviews/index.ts ================================================ import { angularTemplate } from './angular'; import { corejs1Template } from './corejs1'; import { corejs2Template } from './corejs2'; import { reactTemplate } from './react'; import { shortTrackScreeningTemplate } from './shortTrackScreening'; import { shortTrackJavaScriptTemplate } from './shortTrackJavaScript'; import { shortTrackTypeScriptTemplate } from './shortTrackTypeScript'; import { shortTrackPerformanceTemplate } from '@client/data/interviews/shortTrackPerformance'; import { validateInterviewTemplate } from './templateValidator'; export * from './types'; export const templates = { corejs1: corejs1Template, corejs2: corejs2Template, react: reactTemplate, angular: angularTemplate, shortTrackScreening: shortTrackScreeningTemplate, shortTrackJavaScript: shortTrackJavaScriptTemplate, shortTrackTypeScript: shortTrackTypeScriptTemplate, shortTrackPerformance: shortTrackPerformanceTemplate, }; Object.values(templates).forEach(template => { validateInterviewTemplate(template); }); ================================================ FILE: client/src/data/interviews/react.ts ================================================ import { InterviewTemplate } from './types'; export const reactTemplate: InterviewTemplate = { name: 'React interview', examplesUrl: 'https://github.com/rolling-scopes-school/tasks/blob/master/react/questions.md', descriptionHtml: ` During the interview, students can score up to 10 points. Each block of questions is rated 0.5 points.
`, categories: [ { id: 1000, name: 'Core Concepts & JSX', questions: [ { id: 1001, name: 'JSX syntax and how it compiles' }, { id: 1002, name: 'React.createElement and virtual DOM basics' }, { id: 1003, name: 'Differences between className and class' }, { id: 1004, name: 'Embedding expressions' }, { id: 1005, name: 'Conditional rendering (&&, ternary)' }, ], }, { id: 1010, name: 'Components', questions: [ { id: 1011, name: 'Functional vs class components' }, { id: 1012, name: 'Props and defaultProps' }, { id: 1013, name: "Prop 'children'" }, { id: 1014, name: 'Component composition' }, { id: 1015, name: 'Component state' }, { id: 1016, name: 'Manipulating DOM directly using Refs' }, ], }, { id: 1020, name: 'Hooks', questions: [ { id: 1021, name: 'useState' }, { id: 1022, name: 'useRef' }, { id: 1023, name: 'useMemo' }, { id: 1024, name: 'useCallback' }, { id: 1025, name: 'useEffect basics' }, { id: 1026, name: 'Rule of hooks' }, ], }, { id: 1030, name: 'Lifecycle Methods & Effects', questions: [ { id: 1031, name: 'Lifecycle in class vs functional components' }, { id: 1032, name: 'Cleanup functions in useEffect' }, { id: 1033, name: 'Dependency array pitfalls' }, ], }, { id: 1040, name: 'Event Handling and Forms', questions: [ { id: 1041, name: 'Event binding in class components [legacy]' }, { id: 1042, name: 'Controlled vs uncontrolled components' }, { id: 1043, name: 'Preventing default and handling submission' }, { id: 1044, name: 'useActionState' }, ], }, { id: 1050, name: 'Lifting State, Data Flow', questions: [ { id: 1051, name: 'Lifting state up, Props drilling' }, { id: 1052, name: 'Passing values' }, ], }, { id: 1060, name: 'Lists, Keys, and Reconciliation', questions: [ { id: 1061, name: 'Rendering lists with .map, the role and rules of key, avoiding unstable keys' }, { id: 1062, name: 'Reconciliation algorithm (VDOM diffing)' }, ], }, { id: 1070, name: 'Styling in React', questions: [ { id: 1071, name: 'Inline styles, CSS modules' }, { id: 1072, name: 'Styled Components' }, ], }, { id: 1080, name: 'Testing in React', questions: [ { id: 1081, name: 'Testing component logic with RTL' }, { id: 1082, name: 'Queries in RTL' }, { id: 1083, name: 'Firing events' }, { id: 1084, name: 'Testing Frameworks' }, ], }, { id: 1090, name: 'Advanced Hooks & Patterns', questions: [ { id: 1091, name: 'useReducer for complex state' }, { id: 1092, name: 'Lazy initialization in hooks' }, { id: 1093, name: 'Writing custom hooks' }, { id: 1094, name: 'Dependency memoization (useCallback, useMemo)' }, { id: 1095, name: 'useLayoutEffect' }, { id: 1096, name: 'use' }, ], }, { id: 1100, name: 'Advanced Rendering Patterns', questions: [{ id: 1101, name: 'React Portal' }], }, { id: 1110, name: 'Context API', questions: [ { id: 1111, name: 'Creating and consuming contexts' }, { id: 1112, name: 'Providing default values' }, { id: 1113, name: 'Avoiding unnecessary re-renders' }, { id: 1114, name: 'Usecases' }, ], }, { id: 1120, name: 'Performance Optimization', questions: [ { id: 1121, name: 'State collocation' }, { id: 1122, name: 'Memoization (React.memo, useMemo, useCallback)' }, { id: 1123, name: 'React.lazy' }, { id: 1124, name: 'Avoiding prop drilling' }, { id: 1125, name: 'Profiling with React DevTools' }, { id: 1126, name: 'Avoiding unnecessary re-renders in large trees' }, { id: 1127, name: 'Deoptimization using flushSync' }, ], }, { id: 1130, name: 'React Router (v7+)', questions: [ { id: 1131, name: 'Declarative mode. Routing' }, { id: 1132, name: 'Data mode. Routing using createBrowserRouter' }, { id: 1133, name: 'Nested routes' }, { id: 1134, name: 'Dynamic params' }, { id: 1135, name: 'Redirects and navigation' }, { id: 1136, name: 'Programmatic navigation (useNavigate)' }, { id: 1137, name: 'Outlet' }, ], }, { id: 1140, name: 'Error Handling', questions: [ { id: 1141, name: 'Error boundaries (class component pattern)' }, { id: 1142, name: 'Async errors in useEffect' }, { id: 1143, name: 'Fallback UI handling with Suspense and boundaries' }, ], }, { id: 1150, name: 'State Management Libraries', questions: [ { id: 1151, name: 'Redux (store, reducers, actions)' }, { id: 1152, name: 'Redux Middleware' }, { id: 1153, name: 'Redux Toolkit' }, { id: 1154, name: 'Zustand / Jotai / MobX' }, { id: 1155, name: 'React Query (server-state)' }, { id: 1156, name: 'Redux Toolkt Query' }, ], }, { id: 1160, name: 'SSR & Meta Frameworks', questions: [ { id: 1161, name: 'Server-side rendering and Server-side generation' }, { id: 1162, name: 'Next.JS. Pages router, getStaticProps, getStaticPaths, getServerSideProps' }, { id: 1163, name: 'Next.JS. App router. Route Handlers and Middleware in App router' }, { id: 1164, name: 'Next.JS. Fetching data' }, { id: 1165, name: 'Next.JS. Client/server separation in React Server Components' }, { id: 1166, name: 'React Router Framework. Routing' }, { id: 1167, name: 'React Router Framework. Client, Server and Static Data Loading' }, { id: 1168, name: 'TanStack Start' }, { id: 1169, name: 'Waku' }, ], }, { id: 1170, name: 'Concurrent Features & Suspense', questions: [ { id: 1171, name: 'Suspense for lazy-loading' }, { id: 1172, name: 'Concurrent rendering in React 18' }, { id: 1173, name: 'useTransition, startTransition' }, { id: 1174, name: 'useDeferredValue' }, ], }, { id: 1180, name: 'Build Process / CI/CD / Tooling', questions: [ { id: 1181, name: 'CRA vs Vite vs custom Webpack' }, { id: 1182, name: 'Linting, formatting, pre-commit hooks' }, { id: 1183, name: 'Dockerizing React apps, CI/CD pipelines' }, ], }, { id: 1190, name: 'Coding task', questions: [{ id: 1191, name: 'Small react app: form, button, results list' }], }, ], }; ================================================ FILE: client/src/data/interviews/shortTrackJavaScript.ts ================================================ import { InputType, InterviewTemplate } from './types'; export const shortTrackJavaScriptTemplate: InterviewTemplate = { name: 'EPAM ShortTrack Core JS mentors interview #1', examplesUrl: 'https://rolling-scopes-school.github.io/epam-short-track/core-js-ts/interviews/mentors-checkpoint-2', descriptionHtml: ` During the interview, students can score up to 10 points. Each question is rated 0.5 points.
`, categories: [ { id: 1100, name: 'Core JS mentors interview #1', description: `10 points`, questions: [ { id: 1101, name: 'What is the difference between Git and GitHub?' }, { id: 1102, name: 'Describe the Git flow branching model.' }, { id: 1103, name: 'What is the difference between git pull and git fetch, git reset and git revert, git merge and git rebase?', }, { id: 1104, name: 'What are the key differences between primitive data types and Object data types in JavaScript?', }, { id: 1105, name: "How does the 'typeof' operator work in JavaScript, and when would you use it?", type: InputType.Input, }, { id: 1106, name: 'What are the differences between var, let, and const in JavaScript? Can you explain what hoisting and TDZ are?', }, { id: 1107, name: 'How do you use conditional statements in JavaScript to make decisions in your code?' }, { id: 1108, name: 'How does destructuring work in JavaScript, and give examples of its use with arrays and objects?', type: InputType.Input, }, { id: 1109, name: 'What are some ways to create arrays in JavaScript? What array methods that modify the original array in JavaScript?', }, { id: 1110, name: 'Explain how you would use array operations such as sort, filter, find, map, and reduce in JavaScript.', }, { id: 1111, name: 'What are static methods in JavaScript? Provide an example.' }, { id: 1112, name: 'Can you explain automatic data type conversion in JavaScript?', type: InputType.Input, }, { id: 1113, name: "What is the difference between 'Object.create()' and new keyword for creating objects?" }, { id: 1114, name: 'What are property descriptors in JavaScript objects and how can you manipulate them? How do getter and setter methods work in JavaScript objects?', }, { id: 1115, name: 'How can you clone an object in JavaScript? How can you prevent modifications to an object in JavaScript?', }, { id: 1116, name: 'Explain the difference between function declarations, function expressions, and arrow functions.', }, { id: 1117, name: "How do default parameters work in JavaScript functions and why are they useful? What is a rest operator and how can it be used in functions? Rest operator vs 'arguments'.", }, { id: 1118, name: "How does the 'this' keyword work in different types of functions? What are the 'call', 'apply', and 'bind' methods? Provide examples of how and when you would use each.", }, { id: 1119, name: "How does 'use strict' mode affect 'this' behavior in functions?" }, { id: 1120, name: 'Explain the concept of closure in JavaScript. Explain functional patterns in JavaScript: IIFE (Immediately Invoked Function Expressions), callback, memoization, currying, chaining, higher-order function, recursion.', }, ], }, ], }; ================================================ FILE: client/src/data/interviews/shortTrackPerformance.ts ================================================ import { InterviewTemplate } from './types'; export const shortTrackPerformanceTemplate: InterviewTemplate = { name: 'EPAM ShortTrack TS mentors interview #2', examplesUrl: 'https://rolling-scopes-school.github.io/epam-short-track/core-js-ts/interviews/mentors-checkpoint-2', descriptionHtml: ` During the interview, students can score up to 10 points. Each question is rated 0.5 points.
`, categories: [ { id: 1100, name: 'Node.js Basics', description: `2.5 points`, questions: [ { id: 1101, name: 'What is Node.js, and how does it differ from browser-based JavaScript?', }, { id: 1102, name: 'What is the purpose of the package.json file, and how does it differ from package-lock.json?', }, { id: 1103, name: 'How do CommonJS (require) and ES Modules (import) differ in Node.js?', }, { id: 1104, name: 'Briefly explain what streams are in Node.js and describe their types.', }, { id: 1105, name: 'How do environment variables work in Node.js, and how are they used in applications?', }, ], }, { id: 1200, name: 'Networking', description: '2 points', questions: [ { id: 1201, name: 'What is HTTP, and how does the HTTP request-response cycle work?', }, { id: 1202, name: 'Compare and contrast HTTP/1, HTTP/2, and HTTP/3.', }, { id: 1203, name: 'What is REST, and what are its key principles?', }, { id: 1204, name: 'What are HTTP status codes? Provide examples of success, client error, and server error status codes.', }, ], }, { id: 1300, name: 'Security', description: '2 points', questions: [ { id: 1301, name: 'What is XSS (Cross-Site Scripting), and how can it be prevented?', }, { id: 1302, name: 'What is CSRF (Cross-Site Request Forgery), and how does it differ from XSS?', }, { id: 1303, name: 'Explain how you would secure sensitive environment variables in a Node.js application.', }, { id: 1304, name: 'What is CORS, and how does it relate to the Same-Origin Policy?', }, ], }, { id: 1400, name: 'Testing', description: '2 points', questions: [ { id: 1401, name: 'What is the Arrange-Act-Assert (AAA) pattern, and why is it important in writing unit tests?', }, { id: 1402, name: 'What is mocking, and how does it help isolate dependencies in tests?', }, { id: 1403, name: 'Explain the principles of the FIRST (Fast, Independent, Repeatable, Self-validating, Timely) acronym in testing.', }, { id: 1404, name: 'What are flaky tests, and how can they be addressed to improve CI/CD pipelines?', }, ], }, { id: 1500, name: 'Critical Rendering Path (CRP)', description: '1 point', questions: [ { id: 1501, name: 'What are the main stages of the Critical Rendering Path (CRP)?', }, { id: 1502, name: 'For moving an element during animation, is it better to use transition: translate or modify properties like left, top, etc.? Why?', }, ], }, { id: 1600, name: 'Debugging Tools', description: '0.5 point', questions: [ { id: 1601, name: 'What debugging tools do you use for Node.js or web development, such as Chrome DevTools? Share an example of how you’ve identified or resolved an issue using these tools.', }, ], }, ], }; ================================================ FILE: client/src/data/interviews/shortTrackScreening.ts ================================================ import { InputType, InterviewTemplate } from './types'; export const shortTrackScreeningTemplate: InterviewTemplate = { name: 'EPAM ShortTrack Technical Screening', examplesUrl: 'https://github.com/rolling-scopes-school/epam-short-track/tree/main/screening', descriptionHtml: ` During the interview, students can score up to 100 points. Each topic has its value in points.
Please see the details below.
  • Basics of JS - up to 10 points
  • Arrays - up to 10 points
  • Objects - up to 10 points
  • Functions - up to 10 points
  • Classes - up to 10 points
  • Async - up to 10 points
  • Basics of HTML/CSS - up to 10 points
  • Client Side - up to 10 points
  • DOM manipulation - up to 10 points
  • Event Handling - up to 10 points

`, categories: [ { id: 1010, name: 'Basics of JS', description: `10 points`, questions: [ { id: 1011, name: 'Data types: Primitives vs Objects' }, { id: 1012, name: 'Variables: var vs let vs const (Hoisting, Temporal Dead Zone)' }, { id: 1013, name: 'Ternary, Nullish Coalescing, Optional Chaining, and Logical Operators (few practical tasks)', type: InputType.Input, }, { id: 1014, name: 'Loops - for, while, do while, for of, for in' }, { id: 1015, name: 'Type conversions, == / === (few practical tasks)', type: InputType.Input }, ], }, { id: 1020, name: 'Arrays', description: `10 points`, questions: [ { id: 1021, name: 'Most popular methods: map vs forEach, filter vs find, sort, reduce, pop/push, shift/unshift, toSorted (node 20)', type: InputType.Input, }, { id: 1022, name: 'Modification: mutating vs non mutating methods' }, { id: 1023, name: 'Array vs Set' }, ], }, { id: 1030, name: 'Objects', description: '10 points', questions: [ { id: 1031, name: 'How to get keys/values (Object.keys, values, entries)' }, { id: 1032, name: 'How to copy object ({...obj}, Object.assign, JSON.parse/stringify, using loop, Object.create): shallow copy vs deep copy', type: InputType.Input, }, { id: 1033, name: 'Destructuring (few practical tasks)', type: InputType.Input, }, { id: 1034, name: 'Getter/setter (optional)' }, { id: 1035, name: 'Object vs Map' }, ], }, { id: 1040, name: 'Functions', description: '10 points', questions: [ { id: 1041, name: 'Declaration vs expression vs arrow functions' }, { id: 1042, name: 'Default params' }, { id: 1043, name: 'Rest operator' }, { id: 1044, name: 'this' }, { id: 1045, name: 'Call vs apply vs bind' }, ], }, { id: 1050, name: 'Classes', description: '10 points', questions: [ { id: 1051, name: 'Constructor' }, { id: 1052, name: 'Public vs Private methods' }, { id: 1053, name: 'Static methods' }, { id: 1054, name: 'Inheritance' }, ], }, { id: 1060, name: 'Async', description: '10 points', questions: [ { id: 1061, name: 'Promise and its methods' }, { id: 1062, name: 'Promises vs async/await' }, { id: 1063, name: 'Error handling (try / catch / finally)' }, { id: 1064, name: 'EventLoop (high lvl!)' }, ], }, { id: 1070, name: 'Basics of HTML/CSS', description: '10 points', questions: [ { id: 1071, name: 'Selector weights' }, { id: 1072, name: 'Pseudo-classes and pseudo-elements (optional)' }, { id: 1073, name: 'em vs rem, relative and absolute values (optional)' }, { id: 1074, name: 'FlexBox vs Grid' }, ], }, { id: 1080, name: 'Client Side', description: '10 points', questions: [ { id: 1081, name: 'Global object window (document, location, history, cookies)' }, { id: 1082, name: 'Web Storage (sessionStorage vs localStorage)' }, ], }, { id: 1090, name: 'DOM manipulation', description: '10 points', questions: [ { id: 1091, name: 'Selection (getElementBy vs querySelector)' }, { id: 1092, name: 'HTML attributes' }, { id: 1093, name: 'Traversing (...child, ...sibling, element vs node) (optional)' }, ], }, { id: 1100, name: 'Event Handling', description: '10 points', questions: [ { id: 1101, name: 'AddEventListener vs on[Event]' }, { id: 1102, name: 'PreventDefault vs stopPropagation vs stopImmediatePropagation' }, { id: 1103, name: 'Event delegation (optional)' }, { id: 1104, name: 'target vs currentTarget' }, ], }, { id: 1110, name: 'English Level', description: 'Checking by Mentor', questions: [ { id: 1111, name: 'less then B1' }, { id: 1112, name: 'B1' }, { id: 1113, name: 'B1+' }, { id: 1114, name: 'B2' }, { id: 1115, name: 'B2+' }, { id: 1116, name: 'C1' }, ], }, { id: 1120, name: 'Practical task', description: 'additional 20 points', questions: [ { id: 1121, name: 'Task solved', type: InputType.Input, }, ], }, ], }; ================================================ FILE: client/src/data/interviews/shortTrackTypeScript.ts ================================================ import { InputType, InterviewTemplate } from './types'; export const shortTrackTypeScriptTemplate: InterviewTemplate = { name: 'EPAM ShortTrack TS mentors interview #2', examplesUrl: 'https://rolling-scopes-school.github.io/epam-short-track/core-js-ts/interviews/mentors-checkpoint-2', descriptionHtml: ` During the interview, students can score up to 10 points. Each question is rated 0.5 points.
`, categories: [ { id: 1100, name: 'TypeScript', description: `5 points`, questions: [ { id: 1101, name: 'What is the difference between TypeScript and JavaScript, and why would you choose TypeScript for a project?', }, { id: 1102, name: 'Explain what structural typing is in TypeScript and how it differs from nominal typing.' }, { id: 1103, name: 'What are the primitive types in TypeScript, and how are they different from JavaScript’s types?', }, { id: 1104, name: 'How do Partial<T> and Required<T> utility types differ, and why would you use them in your code?', }, { id: 1105, name: 'What is the difference between never and void in TypeScript, and when would you use each?', }, { id: 1106, name: 'What are generics in TypeScript, and how can they make functions or classes more reusable? Provide examples.', }, { id: 1107, name: 'How does the Pick<T, K> utility type differ from Omit<T, K>, and when would you use each of them?', }, { id: 1108, name: 'Describe the difference between any and unknown in TypeScript and discuss scenarios where one should be preferred over the other.', }, { id: 1109, name: 'How does the use of interface differ from type in TypeScript, and when would you choose one over the other?', }, { id: 1110, name: 'What is the purpose of the readonly modifier in TypeScript, and how does it differ from a const variable?', }, ], }, { id: 1200, name: 'Object-Oriented Programming (OOP)', description: '2.5 points', questions: [ { id: 1201, name: 'What is the difference between public, private, and static members in a TypeScript/JavaScript class, and what are their use cases?', }, { id: 1202, name: 'Discuss SOLID principles and how they apply to Object-Oriented Programming in JavaScript or TypeScript.', }, { id: 1203, name: 'What is the instanceof operator in JavaScript, and how does it work when checking if an object belongs to a specific class?', }, { id: 1204, name: 'How do ES2015 classes simplify creating objects compared to traditional functions and prototypal inheritance in JavaScript?', }, { id: 1205, name: 'Explain how you can achieve multiple inheritance in TypeScript and how it differs from extending classes in traditional OOP.', }, ], }, { id: 1300, name: 'Async JavaScript', description: '1.5 points', questions: [ { id: 1301, name: 'What is the difference between setTimeout and setInterval in JavaScript, and how can you stop them from running?', }, { id: 1302, name: 'What are Promise.all() and Promise.race(), and how do they differ in their behavior when resolving multiple promises?', }, { id: 1303, name: 'How does async/await syntax enhances the readability and maintainability of asynchronous JavaScript code compared to traditional promise chains?', type: InputType.Input, }, ], }, { id: 1400, name: 'Errors and Debugging', description: '1 point', questions: [ { id: 1401, name: 'What is the role of the finally block in error handling, and how does it interact with try and catch blocks in JavaScript?', }, { id: 1402, name: 'How can you create and throw a custom error in JavaScript, and why would you do so? Provide a practical example.', type: InputType.Input, }, ], }, ], }; ================================================ FILE: client/src/data/interviews/technical-screening.tsx ================================================ import { ReactNode } from 'react'; import ClockCircleOutlined from '@ant-design/icons/ClockCircleOutlined'; import { InputType } from './types'; import { InterviewFeedbackStepData, InterviewQuestion } from '@common/models'; function TimeForStep({ minutes }: { minutes: string }) { return (
  {minutes} min
); } /** * define steps for interview feedback, required to be filled */ export enum FeedbackStepId { Introduction = 'intro', Theory = 'theory', Practice = 'practice', English = 'english', Decision = 'decision', } export type Step = { id: FeedbackStepId; title: string; /** description of the step shown as a content */ description: ReactNode; /** description of the step shown on the stepper description section */ stepperDescription: string; items: StepFormItem[]; }; export type FeedbackStep = Step & InterviewFeedbackStepData; export type Feedback = { /** * version of the feedback template. Currently supported only 1. */ version: number; /** the steps of the feedback template */ steps: FeedbackStep[]; /** * defines whether interview is completed */ isCompleted: boolean; }; /** * The mentor decision about the student */ export enum Decision { Yes = 'yes', No = 'no', Draft = 'draft', SeparateStudy = 'separateStudy', MissedWithReason = 'missedWithReason', MissedIgnoresMentor = 'missedIgnoresMentor', } export type StepFormItem = RadioItem | TextItem | InputItem | CheckboxItem | RadioButtonItem | QuestionItem; interface Field { id: string; title: string; required?: boolean; } export interface RadioItem extends Field { type: InputType.Radio; options: RadioOption[]; } export interface RadioButtonItem extends Field { type: InputType.RadioButton; options: RadioOption[]; description?: string; } interface InputItem extends Field { type: InputType.Input; placeholder?: string; description?: string; inputType: 'number' | 'text'; defaultValue?: string | number; min?: number; max?: number; } interface TextItem extends Field { type: InputType.TextArea; description?: string; placeholder: string; } interface CheckboxItem extends Field { type: InputType.Checkbox; options: CheckboxOption[]; } export interface QuestionItem { id: string; title: string; required?: boolean; type: InputType.Rating; /** * The default list of questions for the interview */ questions: InterviewQuestion[]; /** * The pool of the questions available for consideration. Once the some of the `questions` are removed they are automatically added to the pool on the client */ examples?: InterviewQuestion[]; /** * The explanation of the rating values */ tooltips?: string[]; } export const SKILLS_LEVELS = [ `Doesn't know`, `Poor knowledge (almost doesn't know)`, 'Knows something (with tips)', 'Good knowledge (makes not critical mistakes)', 'Great knowledge', ]; export const CODING_LEVELS = [ `Isn't able to code`, `Poor coding ability (almost isn't able to)`, 'Can code with tips', 'Good coding ability (makes not critical mistakes)', 'Great coding ability', ]; type CheckboxOption = { id: string; title: string; }; export type RadioOption = { id: string; title: string; options?: RadioOption[]; }; //#region Steps definition export const introduction: Step = { id: FeedbackStepId.Introduction, title: 'Introduction', stepperDescription: 'Interview confirmation', description: ( <>
The interviewer checks a student's camera, sound and video.
Then the mentor tells about himself in some words and becomes ready to listen to student's brief intro. Face to face interviewing helps both parties to interact and form a connection.
Make a mark, if the interview can't be managed.
), items: [ { id: 'interviewResult', type: InputType.Radio, title: 'Did the student show up for the interview?', required: true, options: [ { id: 'completed', title: "Yes, it's ok." }, { id: 'missed', title: 'No, interview is failed.', options: [ { id: Decision.MissedWithReason, title: 'Student has a significant reason.' }, { id: Decision.MissedIgnoresMentor, title: 'Student ignores mentor.' }, { id: Decision.SeparateStudy, title: 'Student continues separate studying.' }, ], }, ], }, { id: 'comment', type: InputType.TextArea, title: 'Your comment', placeholder: "Comment about student's skills", }, ], }; const theoryQuestions = [ { id: 'html', title: 'Position and display attribute values, tags, weight of selectors, pseudo-classes and elements, box model, relative and absolute values, em vs rem, semantic, semantic tags, etc.', topic: 'HTML/CSS', }, { id: 'oop', title: 'OOP (Encapsulation, Polymorphism, and Inheritance)', topic: 'Computer Science', }, { id: 'algorithms', title: 'Sorting and search algorithms (Binary search, Bubble sort, Quick sort, etc.)', topic: 'Computer Science', }, { id: 'bigO', title: 'Big O notation', topic: 'Computer Science', }, { id: 'binaryNumbers', title: 'Binary number', topic: 'Computer Science', }, { id: 'array', title: 'Array. Operations complexity.', topic: 'Data structures', }, { id: 'list', title: 'List. Operations complexity.', topic: 'Data structures', }, { id: 'stack', title: 'Stack. Operations complexity.', topic: 'Data structures', }, { id: 'queue', title: 'Queue. Operations complexity.', topic: 'Data structures', }, { id: 'tree', title: 'Tree. Operations complexity.', topic: 'Data structures', }, { id: 'hashTable', title: 'Hash table. Operations complexity.', topic: 'Data structures', }, { id: 'heap', title: 'Heap. Operations complexity.', topic: 'Data structures', }, { id: 'structuresOverview', title: 'Difference between list and array, or between stack and queue.', topic: 'Data structures', }, { id: 'dataTypes', title: 'Understanding data types - from primitives to objects', topic: 'JavaScript Fundamentals', }, { id: 'variables', title: 'Variables', topic: 'JavaScript Fundamentals', }, { id: 'numbers', title: 'Number & Math methods', topic: 'JavaScript Fundamentals', }, { id: 'strings', title: 'String methods & String templates', topic: 'JavaScript Fundamentals', }, { id: 'operators', title: 'Ternary, Nullish Coalescing, Optional Chaining, and Logical Operators – Syntax and Use Cases', topic: 'JavaScript Fundamentals', }, { id: 'switchCase', title: 'Switch case - examples where it can be useful', topic: 'JavaScript Fundamentals', }, { id: 'loops', title: 'Loops - for, while, do while', topic: 'JavaScript Fundamentals', }, { id: 'typesConversion', title: 'Be able to discover cases of implicit data types conversion into boolean, string, number', topic: 'JavaScript Fundamentals', }, { id: 'strictComparison', title: 'Strict comparison', topic: 'JavaScript Fundamentals', }, ]; const theory: Step = { id: FeedbackStepId.Theory, title: 'Theory', stepperDescription: 'Talk about theory, how things work', description: ( <>
Ask student some questions from the self-study course. You can use the list of recommended questions or add your own.
), items: [ { type: InputType.Rating, id: 'questions', title: 'What questions did the student have to answer?', required: true, tooltips: SKILLS_LEVELS, questions: theoryQuestions, examples: theoryQuestions, }, { id: 'comment', type: InputType.TextArea, title: 'Your comment', placeholder: "Comment about student's skills", }, ], }; const practiceQuestions = [ { id: '1', title: `Given an integer array arr and a filtering function fn, return a new array with a fewer or equal number of elements. The returned array should only contain elements where fn(arr[i], i) evaluated to a truthy value.`, }, { id: '2', title: `Implement a function that takes two arrays of numbers and returns an array of numbers that are common between the two input arrays.`, }, ]; const practice: Step = { id: FeedbackStepId.Practice, title: 'Practice', stepperDescription: 'Propose technical tasks to solve', description: ( <> Ask the student to solve the coding problem. See the list of examples of coding tasks or suggest another problem of the same level. ), items: [ { id: 'questions', type: InputType.Rating, title: 'What task did the student have to solve?', required: true, tooltips: CODING_LEVELS, questions: practiceQuestions, examples: practiceQuestions, }, { id: 'comment', type: InputType.TextArea, title: 'Your comment', required: true, placeholder: "Comment about student's skills", }, ], }; const english: Step = { id: FeedbackStepId.English, title: 'English-language proficiency', stepperDescription: 'Check English level', description: ( <> Ask the student to tell about themselves (2—3 min), hobby, favorite book, film etc. ), items: [ { id: 'englishCertificate', type: InputType.RadioButton, required: true, title: 'Certified level of English', description: 'Make a mark, if the student has a certificate, proving his English level', options: [ { id: 'none', title: 'No certificate' }, { id: 'A1', title: 'A1' }, { id: 'A2', title: 'A2' }, { id: 'B1', title: 'B1' }, { id: 'B2', title: 'B2' }, { id: 'C1', title: 'C1' }, { id: 'C2', title: 'C2' }, ], }, { id: 'selfAssessment', type: InputType.RadioButton, title: 'English level by interviewers opinion', required: true, description: "Make a mark showing your own opinion of student's English level. It doesn't influence on the final score of the interview.", options: [ { id: 'A1', title: 'A1' }, { id: 'A2', title: 'A2' }, { id: 'B1', title: 'B1' }, { id: 'B2', title: 'B2' }, { id: 'C1', title: 'C1' }, { id: 'C2', title: 'C2' }, ], }, { id: 'comment', type: InputType.TextArea, title: 'Where did the student learn English? Your comment ', placeholder: "Comment about student's skills", }, ], }; const mentorDecision: Step = { id: FeedbackStepId.Decision, title: 'Mentor decision', stepperDescription: 'Student admission to the mentoring program', description: ( <> Make a decision to accept a student into a mentoring program. ), items: [ { id: 'finalScore', type: InputType.Input, title: 'Final Score', description: 'We calculated average score based on your marks, but you can adjust the final score', inputType: 'number', required: true, min: 0, }, { id: 'isGoodCandidate', type: InputType.Checkbox, title: 'In your opinion, is this student a good candidate for mentoring with active interest and motivation? Make a mark', options: [{ id: 'true', title: 'The student is a good candidate for mentoring.' }], }, { id: 'decision', type: InputType.Radio, title: 'Do you want to mentor this student and take them in your group?', required: true, options: [ { id: Decision.Yes, title: 'Yes, I will mentor this student.' }, { id: Decision.No, title: "No, I won't." }, { id: Decision.Draft, title: "I haven't decided yet. I'll submit the feedback later." }, { id: Decision.SeparateStudy, title: "No, I won't. Student continues separate studying." }, ], }, { id: 'redFlags', type: InputType.TextArea, title: 'Red flags', placeholder: "Specify any red flags you've noticed during the interview", }, { id: 'comment', type: InputType.TextArea, title: 'You can say something to the student (optional)', description: 'The student will see this comment in interview results', placeholder: "Comment about student's skills", }, ], }; //#endregion /** * define the order of the steps in the interview feedback template */ const steps: Step[] = [introduction, theory, practice, english, mentorDecision]; export const feedbackTemplate = { version: 1, steps, }; ================================================ FILE: client/src/data/interviews/templateValidator.ts ================================================ import { InterviewTemplate, QuestionCategory } from '@client/data/interviews/types'; export class TemplateValidationError extends Error { constructor(message: string) { super(message); this.name = 'TemplateValidationError'; } } export function validateInterviewTemplate(template: InterviewTemplate) { const { name, categories } = template; const uniqueCatIds = new Set(categories.map(c => c.id)); if (uniqueCatIds.size !== categories.length) { throw new TemplateValidationError(`Categories must have unique ids. Template name: ${name}`); } try { categories.forEach(category => { validateQuestionCategory(category); }); } catch (error: unknown) { if (error instanceof TemplateValidationError) { throw new TemplateValidationError(`Template name ${name}. ${error.message}`); } throw error; } } export function validateQuestionCategory(category: QuestionCategory) { const { id, questions } = category; const uniqueQuestionIds = new Set(questions.map(q => q.id) ?? []); if (uniqueQuestionIds.size !== questions.length) { throw new TemplateValidationError(`Questions must have unique ids. Category ID: ${id}`); } } ================================================ FILE: client/src/data/interviews/types.ts ================================================ export enum InputType { Input = 'input', TextArea = 'textarea', Checkbox = 'checkbox', Radio = 'radio', RadioButton = 'radioButton', Rating = 'rating', } export type Question = { id: number; name: string; type?: InputType; }; export type QuestionCategory = { id: number; name: string; description?: string; questions: Question[]; }; export type InterviewTemplate = { name: string; categories: QuestionCategory[]; examplesUrl: string; descriptionHtml?: string; }; ================================================ FILE: client/src/data/skills.ts ================================================ export const SKILLS = [ 'react', 'angular', 'unit-tests', 'jest', 'javascript-core', 'redux', 'html', 'css', 'scss', 'less', 'nestjs', ]; ================================================ FILE: client/src/data/taskTypes.ts ================================================ import { TaskDtoTypeEnum } from '../api'; export const TASK_TYPES: { id: TaskDtoTypeEnum; name: string }[] = [ { id: TaskDtoTypeEnum.Jstask, name: 'JS task' }, { id: TaskDtoTypeEnum.Kotlintask, name: 'Kotlin task' }, { id: TaskDtoTypeEnum.Objctask, name: 'Objective-C task' }, { id: TaskDtoTypeEnum.Htmltask, name: 'HTML task' }, { id: TaskDtoTypeEnum.Ipynb, name: 'Jupyter Notebook' }, { id: TaskDtoTypeEnum.Cvmarkdown, name: 'CV (Markdown)' }, { id: TaskDtoTypeEnum.Cvhtml, name: 'CV (HTML)' }, { id: TaskDtoTypeEnum.Selfeducation, name: 'RS School App Test' }, { id: TaskDtoTypeEnum.Codewars, name: 'Codewars' }, { id: TaskDtoTypeEnum.Test, name: 'Google Form Test' }, { id: TaskDtoTypeEnum.Codejam, name: 'Codejam' }, { id: TaskDtoTypeEnum.Interview, name: 'Interview' }, { id: TaskDtoTypeEnum.StageInterview, name: 'Technical Screening' }, ]; export const TASK_TYPES_MAP = TASK_TYPES.reduce( (acc, { id, name }) => ({ ...acc, [id]: name }), {} as Record, ); ================================================ FILE: client/src/data/tshirts.ts ================================================ export const TSHIRT_SIZES = [ { id: 'xs', name: 'XS', }, { id: 's', name: 'S', }, { id: 'm', name: 'M', }, { id: 'l', name: 'L', }, { id: 'xl', name: 'XL', }, { id: 'xxl', name: 'XXL', }, { id: 'xxxl', name: 'XXXL', }, ]; ================================================ FILE: client/src/domain/course.test.ts ================================================ import { CourseTaskDto } from '@client/api'; import { getTasksTotalScore } from './course'; describe('getTasksTotalScore', () => { test('should calculate total score', () => { expect( getTasksTotalScore([ { maxScore: 100, scoreWeight: 1, }, { maxScore: 100, scoreWeight: 0.5, }, ] as CourseTaskDto[]), ).toBe(100 + 50); }); }); ================================================ FILE: client/src/domain/course.ts ================================================ import { CourseTaskDto } from '@client/api'; export function getTasksTotalScore(courseTasks: CourseTaskDto[]) { return courseTasks.reduce((score, task) => score + (task.maxScore ?? 0) * task.scoreWeight, 0); } ================================================ FILE: client/src/domain/interview.test.ts ================================================ import { getRating, isInterviewRegistrationInProgress, isInterviewStarted } from './interview'; describe('interview', () => { beforeAll(() => vi.useFakeTimers().setSystemTime(new Date('2023-01-01'))); afterAll(() => vi.useRealTimers()); describe('isInterviewRegistrationInProgess', () => { test('should return false if interview period starts more than in 2 weeks from now', () => { const isInProgress = isInterviewRegistrationInProgress('2023-02-01'); expect(isInProgress).toBe(false); }); test('should return false if interview period already started', () => { const isInProgress = isInterviewRegistrationInProgress('2022-11-25'); expect(isInProgress).toBe(false); }); test('should return true if interview period starts less than in 2 weeks', () => { const isInProgress = isInterviewRegistrationInProgress('2023-01-10'); expect(isInProgress).toBe(true); }); }); describe('isInterviewStarted', () => { test('should return false if interview starts in future', () => { const isStarted = isInterviewStarted('2023-02-01'); expect(isStarted).toBe(false); }); test('should return true if current date is after interview start date', () => { const isStarted = isInterviewStarted('2022-12-01'); expect(isStarted).toBe(true); }); }); describe('getRating', () => { test.each([ [5, 0.5], [30, 3], [50, 5], [100, 5], ])(`should calculate %s rating based on score %s for legacy feedback`, (score, expected) => { const rating = getRating(score, 100, 0); expect(rating).toBe(expected); }); test.each([ [5, 0.25], [30, 1.5], [50, 2.5], [90, 4.5], [100, 5], ])(`should calculate %s rating based on score %s`, (score, expected) => { const rating = getRating(score, 100, 1); expect(rating).toBe(expected); }); }); }); ================================================ FILE: client/src/domain/interview.tsx ================================================ import { Tag, Typography } from 'antd'; import { TaskDtoTypeEnum } from '@client/api'; import { StageInterviewFeedbackVerdict, InterviewDetails as CommonInterviewDetails } from '@common/models'; import { Decision } from '@client/data/interviews/technical-screening'; import dayjs from 'dayjs'; import between from 'dayjs/plugin/isBetween'; import { featureToggles } from '@client/services/features'; import CalendarOutlined from '@ant-design/icons/CalendarOutlined'; import { formatDate, formatShortDate } from '@client/services/formatter'; dayjs.extend(between); export function friendlyStageInterviewVerdict(value: StageInterviewFeedbackVerdict) { switch (value) { case 'didNotDecideYet': return 'No decision yet'; case 'no': return 'No'; case 'yes': return 'Yes'; case 'noButGoodCandidate': return 'No, but good student'; default: return value; } } export function getInterviewResult(decision: Decision) { switch (decision) { case Decision.Yes: return 'Mentor accepted'; case Decision.No: return 'Mentor declined'; case Decision.Draft: return 'No decision yet'; case Decision.SeparateStudy: return 'Separate study'; case Decision.MissedIgnoresMentor: return 'Ignored mentor'; case Decision.MissedWithReason: return 'Missed with a reason'; default: return friendlyStageInterviewVerdict(decision as StageInterviewFeedbackVerdict); } } export type InterviewDetails = CommonInterviewDetails; export enum InterviewStatus { NotCompleted, Completed, Canceled, } export enum InterviewResult { Yes = 'yes', No = 'no', Draft = 'draft', } export function getInterviewCardResult(decision: Decision | StageInterviewFeedbackVerdict) { switch (decision) { case Decision.Yes: return InterviewResult.Yes; case Decision.Draft: return InterviewResult.Draft; case 'didNotDecideYet': return InterviewResult.Draft; case Decision.No: return InterviewResult.No; case Decision.SeparateStudy: return InterviewResult.No; case Decision.MissedIgnoresMentor: return InterviewResult.No; case Decision.MissedWithReason: return InterviewResult.No; case 'noButGoodCandidate': return InterviewResult.No; default: return InterviewResult.Yes; } } export function isInterviewRegistrationInProgress(interviewStartDate: string) { const startDate = dayjs(interviewStartDate).subtract(2, 'weeks'); return dayjs().isBetween(startDate, interviewStartDate); } export function isInterviewStarted(interviewStartDate: string) { return dayjs().isAfter(dayjs(interviewStartDate)); } export function getInterviewFeedbackUrl({ courseAlias, interviewName, interviewId, studentGithubId, template, studentId, }: { courseAlias: string; studentGithubId: string; studentId: number; template?: string | null; interviewName: string; interviewId: number; }) { const isScreening = isTechnicalScreening(interviewName); if (!featureToggles.feedback && isScreening) { return `/course/mentor/interview-technical-screening?course=${courseAlias}&githubId=${studentGithubId}`; } const type = isScreening ? TaskDtoTypeEnum.StageInterview : template; return `/course/interview/${type}/feedback?course=${courseAlias}&githubId=${studentGithubId}&studentId=${studentId}&interviewId=${interviewId}`; } export function isTechnicalScreening(name: string) { return name.includes('Technical Screening'); } export const getInterviewWaitList = (courseAlias: string, interviewId: number) => `/course/mentor/interview-wait-list?course=${courseAlias}&interviewId=${interviewId}`; /** calculates the rating based on the interview score. rating scales from [0,5] */ export function getRating(score: number, maxScore: number, feedbackVersion: number) { if (!feedbackVersion) { // In the legacy feedback, the score is a number with limit 50 const maxScore = 50; return (score > maxScore ? maxScore : score) / 10; } // calculate rating on the scale from 0 to 5 const rating = (score / maxScore) * 5; return rating; } export function DecisionTag({ decision, status }: { decision?: Decision; status?: InterviewStatus }) { if (!decision) { return ( {status === InterviewStatus.Completed ? 'Completed' : 'Uncompleted'} ); } switch (decision) { case Decision.Yes: case Decision.No: return Completed; case Decision.Draft: return Unfilled form; case Decision.SeparateStudy: return Separate study; case Decision.MissedIgnoresMentor: return Ignored mentor; case Decision.MissedWithReason: return Missed with a reason; default: { // fallback to the old feedback values if (decision === 'noButGoodCandidate') { return Completed; } if (decision === 'didNotDecideYet') { return Unfilled form; } return Uncompleted; } } } export function InterviewPeriod({ startDate, endDate, shortDate, }: { startDate: string; endDate: string; shortDate?: boolean; }) { const format = shortDate ? formatShortDate : formatDate; return (
{`${format(startDate)} - ${format(endDate)}`}
); } export const isRegistrationNotStarted = (studentRegistrationStartDate: string): boolean => { return !!studentRegistrationStartDate && new Date() < new Date(studentRegistrationStartDate); }; ================================================ FILE: client/src/domain/user.test.tsx ================================================ import { Session } from '@client/components/withSession'; import { CourseRole } from '@client/services/models'; import * as user from './user'; const mockSession: Session = { courses: {}, githubId: 'test', id: 1, isAdmin: false, isHirer: false, }; describe('User', () => { it('isAdmin', () => { expect(user.isAdmin({} as Session)).toBe(false); expect(user.isAdmin({ isAdmin: true } as Session)).toBe(true); }); it('isAnyCourseManager', () => { expect(user.isAnyCourseManager({ ...mockSession, courses: { 1: { roles: [CourseRole.Manager] } } })).toBe(true); expect(user.isAnyCourseManager({ ...mockSession, courses: { 1: { roles: [CourseRole.Mentor] } } })).toBe(false); }); it('isCourseManager', () => { expect(user.isCourseManager({ ...mockSession, courses: { 1: { roles: [CourseRole.Manager] } } }, 1)).toBe(true); expect(user.isCourseManager({ ...mockSession, courses: { 2: { roles: [CourseRole.Manager] } } }, 1)).toBe(false); }); it('isPowerUser', () => { expect(user.isPowerUser({ ...mockSession, courses: { 1: { roles: [CourseRole.Manager] } } }, 1)).toBe(true); expect(user.isPowerUser({ ...mockSession, courses: { 1: { roles: [CourseRole.Supervisor] } } }, 1)).toBe(true); expect(user.isPowerUser({ ...mockSession, isAdmin: true } as Session, 0)).toBe(true); expect(user.isPowerUser({ ...mockSession, courses: { 1: { roles: [CourseRole.Supervisor] } } }, 2)).toBe(false); }); it('isAnyMentor', () => { expect(user.isAnyMentor({ ...mockSession, courses: { 1: { roles: [CourseRole.Mentor] } } })).toBe(true); expect(user.isAnyMentor({ ...mockSession, courses: { 1: { roles: [] } } })).toBe(false); }); }); ================================================ FILE: client/src/domain/user.ts ================================================ import { Session } from '@client/components/withSession'; import keys from 'lodash/keys'; import { CourseRole } from '@client/services/models'; function hasRole(session: Session, courseId: number, role: CourseRole) { return session.courses[courseId]?.roles.includes(role) ?? false; } export function isExpelledStudent(session: Session, courseId: number) { return session.courses[courseId]?.isExpelled === true; } export function hasRoleInAnyCourse(session: Session, role: CourseRole) { return keys(session.courses).some(courseId => hasRole(session, Number(courseId), role)); } export function isAdmin(session: Session) { return Boolean(session.isAdmin); } export function isMentor(session: Session, courseId: number) { return !!courseId && hasRole(session, courseId, CourseRole.Mentor); } export function getMentorId(session: Session, courseId: number) { return session.courses[courseId]?.mentorId ?? null; } export function isAnyMentor(session: Session) { return hasRoleInAnyCourse(session, CourseRole.Mentor); } export function isStudent(session: Session, courseId: number) { return !!courseId && hasRole(session, courseId, CourseRole.Student); } export function isActiveStudent(session: Session, courseId: number) { return isStudent(session, courseId) && !isExpelledStudent(session, courseId); } export function isCourseManager(session: Session, courseId: number) { return isAdmin(session) || hasRole(session, courseId, CourseRole.Manager); } export function isDementor(session: Session, courseId: number) { return isAdmin(session) || hasRole(session, courseId, CourseRole.Dementor); } export function isPowerUser(session: Session, courseId: number) { return isAdmin(session) || isCourseManager(session, courseId) || isCourseSupervisor(session, courseId); } export function isAnyCourseManager(session: Session) { return hasRoleInAnyCourse(session, CourseRole.Manager); } export function isCourseSupervisor(session: Session, courseId: number) { return isAdmin(session) || hasRole(session, courseId, CourseRole.Supervisor); } export function isAnyCourseSupervisor(session: Session) { return isAdmin(session) || hasRoleInAnyCourse(session, CourseRole.Supervisor); } export function isAnyCoursePowerUser(session: Session) { return ( isAdmin(session) || hasRoleInAnyCourse(session, CourseRole.Manager) || hasRoleInAnyCourse(session, CourseRole.Supervisor) ); } export function isAnyCourseDementor(session: Session) { return isAdmin(session) || hasRoleInAnyCourse(session, CourseRole.Dementor); } export function isTaskOwner(session: Session, courseId: number) { return hasRole(session, courseId, CourseRole.TaskOwner); } export function isHirer(session: Session) { return Boolean(session.isHirer); } export function getFullName(user: { firstName: string | null; lastName: string | null; githubId: string }) { return user.firstName && user.lastName ? `${user.firstName} ${user.lastName}` : `${user.githubId}`; } ================================================ FILE: client/src/hooks/index.ts ================================================ export { useModalForm } from '@client/shared/hooks/useModal/useModalForm'; export type { ModalFormMode } from '@client/shared/hooks/useModal/useModalForm'; export { useMessage } from '@client/shared/hooks/useMessage'; export { useTheme } from '@client/shared/hooks/useTheme'; ================================================ FILE: client/src/index.d.ts ================================================ declare module 'mq-polyfill'; ================================================ FILE: client/src/modules/AutoTest/components/AttemptsAnswers/AttemptsAnswers.tsx ================================================ import { Button, Col, Form, Row, Space, theme, Typography } from 'antd'; import { TaskVerificationAttemptDto } from '@client/api'; import { Question } from '@client/modules/AutoTest/components'; import dayjs from 'dayjs'; import { Fragment } from 'react'; import CalendarOutlined from '@ant-design/icons/CalendarOutlined'; type Props = { attempts: TaskVerificationAttemptDto[]; hideAnswers: () => void; }; const { Title, Text } = Typography; function AttemptsAnswers({ attempts, hideAnswers }: Props) { const { token } = theme.useToken(); return ( Check your incorrect answers per attempt.
{attempts.map((attempt, idx) => ( Attempt #{attempts.length - idx} {dayjs(attempt.createdDate).format('YYYY-MM-DD HH:mm')} Score: {attempt.score} / {attempt.maxScore} {attempt.questions?.map((question, questionIdx) => ( ))} ))}
); } export default AttemptsAnswers; ================================================ FILE: client/src/modules/AutoTest/components/AutoTestTaskCard/AutoTestTaskCard.tsx ================================================ import { Button, Card, Col, Divider, Row, Typography, Switch } from 'antd'; import { TaskCardColumn } from '..'; import { BasicAutoTestTaskDto } from '@client/api'; import Link from 'next/link'; const { Paragraph } = Typography; export interface AutoTestTaskCardProps { courseTask: BasicAutoTestTaskDto; } function AutoTestTaskCard({ courseTask }: AutoTestTaskCardProps) { const columns = [ { label: 'Max attempts number', value: courseTask.maxAttemptsNumber ? <>{courseTask.maxAttemptsNumber} : <>–, }, { label: 'Number of Questions', value: courseTask.numberOfQuestions ? <>{courseTask.numberOfQuestions} : <>–, }, { label: 'Strict attempts mode', value: , }, { label: 'Threshold percentage', value: courseTask.thresholdPercentage ? <>{courseTask.thresholdPercentage} : <>–, }, ]; return ( {' ' + courseTask.name} {columns.map(item => ( ))} ); } export default AutoTestTaskCard; ================================================ FILE: client/src/modules/AutoTest/components/Coding/Coding.test.tsx ================================================ import { render, screen } from '@testing-library/react'; import { CourseTaskDetailedDtoTypeEnum, CheckerEnum } from '@client/api'; import { CourseTaskVerifications } from '@client/modules/AutoTest/types'; import Coding, { CodingProps } from './Coding'; function renderCoding(type: CourseTaskDetailedDtoTypeEnum) { const courseTask = { name: 'Course Task', studentStartDate: '2022-09-10 12:00', studentEndDate: '2022-10-10 12:00', checker: CheckerEnum.AutoTest, id: 10, descriptionUrl: 'description-url', githubRepoName: 'github-repo-name', type, publicAttributes: { maxAttemptsNumber: 2, }, } as CourseTaskVerifications; const props: CodingProps = { courseTask, githubId: 'github-id', }; return render(); } describe('Coding', () => { it.each` type | text ${CourseTaskDetailedDtoTypeEnum.Codewars} | ${/Please use the next username in your/i} ${CourseTaskDetailedDtoTypeEnum.Codewars} | ${/codewars profile/i} ${CourseTaskDetailedDtoTypeEnum.Jstask} | ${/Tests run on Node.js version 22. Please make sure your solution works on Node.js version 22./i} ${CourseTaskDetailedDtoTypeEnum.Jstask} | ${/The system will run tests in the following repository and will update the score based on the result:/i} ${CourseTaskDetailedDtoTypeEnum.Jstask} | ${/https:\/\/github.com\/github-id\/github-repo-name/i} ${CourseTaskDetailedDtoTypeEnum.Kotlintask} | ${/The system will run tests in the following repository and will update the score based on the result:/i} ${CourseTaskDetailedDtoTypeEnum.Kotlintask} | ${/https:\/\/github.com\/github-id\/github-repo-name/i} `( 'should render $type task with $text', async ({ type, text }: { type: CourseTaskDetailedDtoTypeEnum; text: RegExp | string }) => { renderCoding(type); expect(await screen.findByText(text)).toBeInTheDocument(); }, ); }); ================================================ FILE: client/src/modules/AutoTest/components/Coding/Coding.tsx ================================================ import { Space, Typography } from 'antd'; import { CourseTaskDetailedDtoTypeEnum } from '@client/api'; import CopyToClipboardButton from '@client/shared/components/CopyToClipboardButton'; import { CourseTaskVerifications } from '@client/modules/AutoTest/types'; import { useAsync } from 'react-use'; import { useState } from 'react'; export type CodingProps = { courseTask: CourseTaskVerifications; githubId: string; }; const { Paragraph, Text, Link } = Typography; async function getCodewarsUsername(githubId: string) { const digest = await window.crypto.subtle.digest('sha-1', new TextEncoder().encode(githubId)); const bytes = [...new Uint8Array(digest)]; const hash = bytes.map(x => x.toString(16).padStart(2, '0')).join(''); return `rsschool_${hash.slice(0, 16)}`; } function Coding({ courseTask, githubId }: CodingProps) { const repoUrl = `https://github.com/${githubId}/${courseTask?.githubRepoName}`; const codewarsLink = 'https://www.codewars.com/users/edit'; const [codewarsUsername, setCodewarsUsername] = useState(null); useAsync(async () => { setCodewarsUsername(await getCodewarsUsername(githubId)); }, []); if (courseTask.type === CourseTaskDetailedDtoTypeEnum.Codewars) { return ( <> Please use the next username in your{' '} codewars profile : {codewarsUsername ? ( {codewarsUsername} ) : null} ); } return ( <> {courseTask.type === CourseTaskDetailedDtoTypeEnum.Jstask && ( Tests run on Node.js version 22. Please make sure your solution works on Node.js version 22. )} The system will run tests in the following repository and will update the score based on the result: {repoUrl} ); } export default Coding; ================================================ FILE: client/src/modules/AutoTest/components/Exercise/Exercise.tsx ================================================ import { Coding, JupyterNotebook, SelfEducation } from '@client/modules/AutoTest/components'; import { CourseTaskDetailedDtoTypeEnum } from '@client/api'; import { Col, ColProps, Form, Row, theme } from 'antd'; import { useCourseTaskSubmit } from '@client/modules/AutoTest/hooks'; import { CourseTaskVerifications } from '@client/modules/AutoTest/types'; import { useEffect, useState } from 'react'; import { TooltipedButton } from '@client/shared/components/TooltipedButton'; type ExerciseProps = { githubId: string; courseId: number; courseTask: CourseTaskVerifications; finishTask: () => void; }; function responsiveColumns(type: CourseTaskDetailedDtoTypeEnum): ColProps | undefined { if (type !== CourseTaskDetailedDtoTypeEnum.Selfeducation) { return; } return { xs: 24, lg: 18, xl: 12, }; } function Exercise({ githubId, courseId, courseTask, finishTask }: ExerciseProps) { const { form, loading, submit, change } = useCourseTaskSubmit(courseId, courseTask, finishTask); const [validationError, setValidationError] = useState(false); const values = Form.useWatch([], form); useEffect(() => { if (!values || !Object.values(values).every(Boolean)) { return; } form.validateFields({ validateOnly: true }).then( () => { setValidationError(false); }, () => { setValidationError(true); }, ); }, [values]); const getExercise = () => { switch (courseTask?.type) { case CourseTaskDetailedDtoTypeEnum.Jstask: case CourseTaskDetailedDtoTypeEnum.Codewars: case CourseTaskDetailedDtoTypeEnum.Kotlintask: case CourseTaskDetailedDtoTypeEnum.Objctask: return ; case CourseTaskDetailedDtoTypeEnum.Ipynb: return ; case CourseTaskDetailedDtoTypeEnum.Selfeducation: return ; default: return null; } }; const { token } = theme.useToken(); return (
setValidationError(true)} onChange={change} > {getExercise()}
); } export default Exercise; ================================================ FILE: client/src/modules/AutoTest/components/JupyterNotebook/JupyterNotebook.tsx ================================================ import { UploadFile, Form, Upload, Button, Row } from 'antd'; import { useState } from 'react'; import { UploadOutlined } from '@ant-design/icons'; function JupyterNotebook() { const [uploadFile, setUploadFile] = useState(null); const handleFileChose = async (info: any) => setUploadFile(info.file); return ( ); } export default JupyterNotebook; ================================================ FILE: client/src/modules/AutoTest/components/Question/Question.module.css ================================================ .question :global(.ant-radio) { align-self: flex-start !important; margin-top: 3px !important; } ================================================ FILE: client/src/modules/AutoTest/components/Question/Question.tsx ================================================ import { Typography, Form, Row, Checkbox, Radio, Col, Space } from 'antd'; import { SelfEducationQuestionSelectedAnswersDto } from '@client/api'; import styles from './Question.module.css'; const { Title } = Typography; type QuestionProps = { question: SelfEducationQuestionSelectedAnswersDto; }; function Question({ question: selfEducationQuestion }: QuestionProps): JSX.Element { const { question, questionImage, answers, answersType, multiple, selectedAnswers } = selfEducationQuestion; const Element = multiple ? Checkbox : Radio; return (
{question} {questionImage && ( )} } > {answers?.map((answer, answerIndex) => { const checked = selectedAnswers?.includes(answerIndex); return ( {answersType === 'image' ? ( <> ({answerIndex + 1}){' '} ) : ( answer )} ); })}
); } export default Question; ================================================ FILE: client/src/modules/AutoTest/components/SelfEducation/SelfEducation.module.css ================================================ .selfEducation :global(.ant-radio) { align-self: flex-start !important; margin-top: 3px !important; } ================================================ FILE: client/src/modules/AutoTest/components/SelfEducation/SelfEducation.test.tsx ================================================ import { render, screen } from '@testing-library/react'; import { Form } from 'antd'; import { SelfEducationPublicAttributes, SelfEducationQuestion } from '@client/services/course'; import { CourseTaskVerifications } from '../../types'; import SelfEducation from './SelfEducation'; describe('SelfEducation', () => { it('should show 10 questions when total question count is 15', () => { const NUMBER_OF_QUESTIONS = 10; renderSelfEducation({ numberOfQuestions: NUMBER_OF_QUESTIONS }); const questions = screen.getAllByRole('heading', { name: /question-/i }); expect(questions).toHaveLength(NUMBER_OF_QUESTIONS); }); it.each` type | multiple ${'radio'} | ${false} ${'checkbox'} | ${true} `( "should render $type when question's multiple option is $multiple", ({ type, multiple }: { type: string; multiple: boolean }) => { renderSelfEducation({ questions: [generateQuestion({ multiple })] }); const question = screen.getByLabelText(/answer-0/i); expect(question).toHaveClass(`ant-${type}-input`); }, ); it.each` condition | question ${'image in question'} | ${{ questionImage: 'some-image-url' }} ${'image in radio answer'} | ${{ answersType: 'image', answers: ['some-image-url'] }} ${'image in checkbox answer'} | ${{ answersType: 'image', answers: ['some-image-url'], multiple: true }} `('should render image when question has $condition', ({ question }: { question: SelfEducationQuestion }) => { renderSelfEducation({ questions: [generateQuestion(question)] }); const image = screen.getByRole('img'); expect(image).toBeInTheDocument(); }); }); function renderSelfEducation({ numberOfQuestions = 5, questions = generateQuestions() }) { const courseTask = { publicAttributes: { questions, numberOfQuestions, maxAttemptsNumber: 2, tresholdPercentage: 90, } as SelfEducationPublicAttributes, } as CourseTaskVerifications; return render(
, ); } function generateQuestion({ question = 'question-0', answers = ['answer-0', 'answer-1'], multiple = false, answersType, questionImage, }: Partial): SelfEducationQuestion { return { question, answers, multiple, answersType, questionImage, }; } function generateQuestions(count = 15): SelfEducationQuestion[] { return new Array(count).fill({}).map((_, idx) => { const question = `Question-${idx}`; const answers = new Array(2).fill('').map((_, index) => `${question} Answer-${index}`); return generateQuestion({ question, answers, multiple: false, }); }); } ================================================ FILE: client/src/modules/AutoTest/components/SelfEducation/SelfEducation.tsx ================================================ import { Typography, Form, Row, Checkbox, Radio, Col, Space } from 'antd'; import { useMemo } from 'react'; import { SelfEducationQuestionWithIndex, SelfEducationQuestion } from '@client/services/course'; import shuffle from 'lodash/shuffle'; import { CourseTaskVerifications } from '@client/modules/AutoTest/types'; import styles from './SelfEducation.module.css'; type SelfEducationProps = { courseTask: CourseTaskVerifications; }; const { Paragraph, Title } = Typography; function getRandomQuestions(questions: SelfEducationQuestion[]): SelfEducationQuestionWithIndex[] { const questionsWithIndex = questions?.map((question, index) => ({ ...question, index })); return shuffle(questionsWithIndex); } function SelfEducation({ courseTask }: SelfEducationProps) { const { questions, numberOfQuestions } = courseTask.publicAttributes || {}; const randomQuestions = useMemo( () => (getRandomQuestions(questions) || []).slice(0, numberOfQuestions), [questions?.length, numberOfQuestions], ); return (
To submit the task answer the questions. {randomQuestions?.map( ({ question, answers, multiple, questionImage, answersType, index: questionIndex }, idx) => { const questionNumber = idx + 1; return ( {questionNumber}. {question} {questionImage && ( )} } name={`answer-${questionIndex}`} rules={[{ required: true, message: 'Please answer the question' }]} > {multiple ? ( {answers?.map((answer, answerIndex) => ( {answersType === 'image' ? ( <> ({answerIndex + 1}){' '} ) : ( answer )} ))} ) : ( {answers?.map((answer, index) => ( {answersType === 'image' ? ( <> ({index + 1}){' '} ) : ( answer )} ))} )} ); }, )}
); } export default SelfEducation; ================================================ FILE: client/src/modules/AutoTest/components/StatusTabs/StatusTabs.test.tsx ================================================ import { fireEvent, render, screen } from '@testing-library/react'; import StatusTabs from './StatusTabs'; import { CourseTaskStatus } from '@client/modules/AutoTest/types'; describe('StatusTabs', () => { const onTabChangeMock = vi.fn(); it('should render status tabs', () => { const statuses = generateStatuses(); render(); expect(screen.getAllByRole('tab')).toHaveLength(3); }); it('should render status tabs when statuses were not provided', () => { render(); expect(screen.getAllByRole('tab')).toHaveLength(3); }); it.each` status | count ${CourseTaskStatus.Available} | ${2} ${CourseTaskStatus.Missed} | ${3} ${CourseTaskStatus.Done} | ${4} `( 'should render badge with count of $count for "$status" tab', ({ status, count }: { status: string; count: number }) => { const statuses = generateStatuses(undefined, { [status]: count }); render(); expect(screen.getByText(count)).toBeInTheDocument(); }, ); describe('when active tab was changed', () => { it.each` tabName ${CourseTaskStatus.Missed} ${CourseTaskStatus.Done} `('should call onTabChange with tab name "$tabName"', ({ tabName }: { tabName: string }) => { const statuses = generateStatuses(undefined, { [tabName]: 2 }); render(); const selectedTab = screen.getByText(new RegExp(tabName, 'i')); fireEvent.click(selectedTab); expect(onTabChangeMock).toHaveBeenCalledWith(tabName); }); }); }); function generateStatuses(count = 3, statusTypeAndCount: Record | null = null): CourseTaskStatus[] { if (statusTypeAndCount) { const statuses: CourseTaskStatus[] = []; for (const statusType in statusTypeAndCount) { if (Object.prototype.hasOwnProperty.call(statusTypeAndCount, statusType)) { const statusCount = statusTypeAndCount[statusType]; statuses.push(...new Array(statusCount).fill(statusType)); } } return statuses; } return new Array(count).fill('').map(() => CourseTaskStatus.Missed); } ================================================ FILE: client/src/modules/AutoTest/components/StatusTabs/StatusTabs.tsx ================================================ import { Tabs } from 'antd'; import { useMemo } from 'react'; import { CourseTaskStatus } from '@client/modules/AutoTest/types'; import { tabsRenderer } from './renderers'; export interface StatusTabsProps { statuses: CourseTaskStatus[]; activeTab?: CourseTaskStatus; onTabChange: (tab: CourseTaskStatus) => void; } const StatusTabs = ({ statuses, activeTab, onTabChange }: StatusTabsProps) => { const tabs = useMemo(() => tabsRenderer(statuses, activeTab), [statuses, activeTab]); const handleTabChange = (selectedTab: string) => { onTabChange(selectedTab as CourseTaskStatus); }; return ; }; export default StatusTabs; ================================================ FILE: client/src/modules/AutoTest/components/StatusTabs/renderers.tsx ================================================ import { CourseTaskStatus, COURSE_TASK_STATUSES } from '@client/modules/AutoTest/types'; import { LabelItem, tabRenderer } from '@client/components/TabsWithCounter/renderers'; export const tabsRenderer = (statuses: CourseTaskStatus[], activeTab?: string) => { return COURSE_TASK_STATUSES.reduce((acc, current) => { const { key, value } = current; const newItem = { label: value, key, count: statuses.filter(el => el === key).length || 0, }; return [...acc, newItem]; }, []).map(item => tabRenderer(item, activeTab)); }; ================================================ FILE: client/src/modules/AutoTest/components/TaskCard/TaskCard.test.tsx ================================================ import { render, screen } from '@testing-library/react'; import { TaskCard } from '..'; import { CheckerEnum } from '@client/api'; import { Course } from '@client/services/models'; import { CourseTaskState, CourseTaskVerifications } from '@client/modules/AutoTest/types'; const COURSE_MOCK = { alias: 'course-alias', id: 100 } as Course; describe('TaskCard', () => { it.each` prop | value ${'task name'} | ${'Course Task'} ${'start date'} | ${'Sep 10'} ${'end date'} | ${'Oct 10'} ${'state'} | ${'Missed'} ${'attempts count'} | ${'2 left'} ${'score'} | ${'–'} `('should render $prop', ({ value }: { value: string }) => { const courseTask = generateCourseTask(2); render(); const element = screen.getByText(new RegExp(value, 'i')); expect(element).toBeInTheDocument(); }); it('should render attempts count as "No limits" when max attempts was not provided', () => { const courseTask = generateCourseTask(); render(); const element = screen.getByText(/No limits/i); expect(element).toBeInTheDocument(); }); }); function generateCourseTask(maxAttemptsNumber?: number): CourseTaskVerifications { return { name: 'Course Task', studentStartDate: '2022-09-10T12:00:00.000Z', studentEndDate: '2022-10-10T12:00:00.000Z', checker: CheckerEnum.AutoTest, id: 10, descriptionUrl: 'description-url', state: CourseTaskState.Missed, publicAttributes: { maxAttemptsNumber, }, } as CourseTaskVerifications; } ================================================ FILE: client/src/modules/AutoTest/components/TaskCard/TaskCard.tsx ================================================ import { Button, Card, Col, Divider, Row, Tag, Typography } from 'antd'; import Link from 'next/link'; import { getAutoTestTaskRoute } from '@client/services/routes'; import { TaskCardColumn, TaskDeadlineDate } from '..'; import { Course } from '@client/services/models'; import { useAttemptsMessage } from '@client/modules/AutoTest/hooks'; import { CourseTaskState, CourseTaskVerifications } from '@client/modules/AutoTest/types'; const { Title, Paragraph } = Typography; export interface TaskCardProps { courseTask: CourseTaskVerifications; course: Course; } function getStatusTag(state: CourseTaskState) { switch (state) { case CourseTaskState.Completed: return {state}; case CourseTaskState.Missed: return {state}; default: return {CourseTaskState.Uncompleted}; } } function TaskCard({ courseTask, course }: TaskCardProps) { const { id, name, studentStartDate, studentEndDate, verifications, state, descriptionUrl } = courseTask; const { attemptsCount, explanation } = useAttemptsMessage(courseTask); const score = verifications?.[0]?.score ?? null; const columns = [ { label: 'Status', value: getStatusTag(state), }, { label: 'Attempts', value: isFinite(attemptsCount) ? `${attemptsCount} left` : 'No limits', }, { label: 'Score', value: score !== null ? score : <>–, }, ]; return ( {name} } extra={} > Task:{' '} {descriptionUrl} {explanation} {columns.map(item => ( ))} ); } export default TaskCard; ================================================ FILE: client/src/modules/AutoTest/components/TaskCardColumn/TaskCardColumn.tsx ================================================ import { Space, Typography } from 'antd'; import { ReactNode } from 'react'; type TaskCardColumnProps = { label: string; value: ReactNode; }; const { Text } = Typography; function TaskCardColumn({ label, value }: TaskCardColumnProps) { return ( {label} {value} ); } export default TaskCardColumn; ================================================ FILE: client/src/modules/AutoTest/components/TaskDeadlineDate/TaskDeadlineDate.test.tsx ================================================ import { render, screen } from '@testing-library/react'; import dayjs from 'dayjs'; import { TaskDeadlineDate, TaskDeadlineDateProps } from '..'; import { CourseTaskState } from '@client/modules/AutoTest/types'; describe('TaskDeadlineDate', () => { it.each` type | when | state | daysCount ${'secondary'} | ${'end date is not passed'} | ${CourseTaskState.Uncompleted} | ${9} ${'secondary'} | ${'end date is not passed'} | ${CourseTaskState.Completed} | ${9} ${'danger'} | ${'end date passed'} | ${CourseTaskState.Missed} | ${1} `( 'should render date as "$type" when $when', ({ type, state, daysCount }: { type: string; state: CourseTaskState; daysCount: number }) => { const date = dayjs(); const startDate = date.subtract(2, 'd'); const endDate = date.add(daysCount, 'd'); const props: TaskDeadlineDateProps = { startDate: startDate.format(), endDate: endDate.format(), state, }; render(); expect(screen.getByText(new RegExp(endDate.format('MMM DD'), 'i'))).toHaveClass(`ant-typography-${type}`); }, ); }); ================================================ FILE: client/src/modules/AutoTest/components/TaskDeadlineDate/TaskDeadlineDate.tsx ================================================ import { Space, Typography } from 'antd'; import { CalendarOutlined, SwapRightOutlined } from '@ant-design/icons'; import { memo, useMemo } from 'react'; import { dateWithTimeZoneRenderer } from '@client/shared/components/Table'; import { BaseType } from 'antd/lib/typography/Base'; import { CourseTaskState } from '@client/modules/AutoTest/types'; const { Text } = Typography; export type TaskDeadlineDateProps = { startDate: string; endDate: string; state: CourseTaskState; format?: string; }; function getTextType(state: CourseTaskState): BaseType { switch (state) { case CourseTaskState.Missed: return 'danger'; default: return 'secondary'; } } function TaskDeadlineDate({ startDate, endDate, state, format = 'MMM DD' }: TaskDeadlineDateProps): JSX.Element { const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; const type = useMemo(() => getTextType(state), [state]); const start = dateWithTimeZoneRenderer(timezone, format)(startDate); const end = dateWithTimeZoneRenderer(timezone, format)(endDate); return ( {start} {end} ); } export default memo(TaskDeadlineDate); ================================================ FILE: client/src/modules/AutoTest/components/TaskDescription/TaskDescription.tsx ================================================ import { Row, Col, Space, Typography, theme } from 'antd'; import { ArrowLeftOutlined } from '@ant-design/icons'; import { getAutoTestRoute } from '@client/services/routes'; import { TaskDeadlineDate } from '..'; import { CourseTaskVerifications } from '@client/modules/AutoTest/types'; const { Title, Text, Link } = Typography; type TaskDescriptionProps = { courseTask: CourseTaskVerifications; courseAlias: string; }; function TaskDescription({ courseAlias, courseTask }: TaskDescriptionProps) { const { descriptionUrl, name, studentStartDate, studentEndDate, state } = courseTask; const { token } = theme.useToken(); return ( <Space size={24}> <Link href={getAutoTestRoute(courseAlias)}> <ArrowLeftOutlined /> </Link> {name} </Space> {descriptionUrl ? ( Description: {descriptionUrl} ) : null} ); } export default TaskDescription; ================================================ FILE: client/src/modules/AutoTest/components/VerificationInformation/VerificationInformation.test.tsx ================================================ import { render, screen } from '@testing-library/react'; import { CourseTaskDetailedDtoTypeEnum, CheckerEnum } from '@client/api'; import { CourseTaskVerifications } from '@client/modules/AutoTest/types'; import VerificationInformation, { VerificationInformationProps } from './VerificationInformation'; function renderVerificationInformation({ type, studentEndDate = '2022-10-10 12:00', isTableVisible = true, }: { type: CourseTaskDetailedDtoTypeEnum; studentEndDate?: string; isTableVisible?: boolean; }) { const courseTask = { name: 'Course Task', studentStartDate: '2022-09-10 12:00', studentEndDate, checker: CheckerEnum.AutoTest, id: 10, descriptionUrl: 'description-url', githubRepoName: 'github-repo-name', type, publicAttributes: { maxAttemptsNumber: 2, }, } as CourseTaskVerifications; const props: VerificationInformationProps = { courseTask, isTableVisible, loading: false, reload: vi.fn(), startTask: vi.fn(), showAnswers: vi.fn(), }; return render(); } describe('VerificationInformation', () => { it.each` type ${CourseTaskDetailedDtoTypeEnum.Codewars} ${CourseTaskDetailedDtoTypeEnum.Codejam} ${CourseTaskDetailedDtoTypeEnum.Cvhtml} ${CourseTaskDetailedDtoTypeEnum.Cvmarkdown} ${CourseTaskDetailedDtoTypeEnum.Htmltask} ${CourseTaskDetailedDtoTypeEnum.Ipynb} ${CourseTaskDetailedDtoTypeEnum.Jstask} ${CourseTaskDetailedDtoTypeEnum.Kotlintask} ${CourseTaskDetailedDtoTypeEnum.Objctask} `( 'should not render "Show answers" button when task is $type', ({ type }: { type: CourseTaskDetailedDtoTypeEnum }) => { renderVerificationInformation({ type }); const answersButton = screen.queryByRole('button', { name: /show answers/i }); expect(answersButton).not.toBeInTheDocument(); }, ); it('should render "Show answers" button when task is SelfEducation', () => { renderVerificationInformation({ type: CourseTaskDetailedDtoTypeEnum.Selfeducation }); const answersButton = screen.getByRole('button', { name: /show answers/i }); expect(answersButton).toBeInTheDocument(); }); it('should render start and refresh buttons if table is visible', () => { renderVerificationInformation({ type: CourseTaskDetailedDtoTypeEnum.Jstask, isTableVisible: true }); const startTaskButton = screen.getByRole('button', { name: /start task/i }); const refreshButton = screen.getByRole('button', { name: /refresh/i }); expect(startTaskButton).toBeInTheDocument(); expect(refreshButton).toBeInTheDocument(); }); it('should not render start and refresh buttons if table is not visible', () => { renderVerificationInformation({ type: CourseTaskDetailedDtoTypeEnum.Jstask, isTableVisible: false }); const startTaskButton = screen.queryByRole('button', { name: /start task/i }); const refreshButton = screen.queryByRole('button', { name: /refresh/i }); expect(startTaskButton).not.toBeInTheDocument(); expect(refreshButton).not.toBeInTheDocument(); }); }); ================================================ FILE: client/src/modules/AutoTest/components/VerificationInformation/VerificationInformation.tsx ================================================ import { Alert, Button, Col, Row, Space, theme, Tooltip, Typography } from 'antd'; import { VerificationsTable } from '@client/modules/AutoTest/components'; import { useAttemptsMessage } from '@client/modules/AutoTest/hooks'; import { CourseTaskVerifications } from '@client/modules/AutoTest/types'; import ReloadOutlined from '@ant-design/icons/ReloadOutlined'; import { CourseTaskDetailedDtoTypeEnum } from '@client/api'; export type VerificationInformationProps = { courseTask: CourseTaskVerifications; loading: boolean; isTableVisible: boolean; startTask: () => void; reload: () => void; showAnswers: () => void; }; const { Text } = Typography; function VerificationInformation({ courseTask, loading, isTableVisible, startTask, reload, showAnswers, }: VerificationInformationProps): any { const { maxScore, verifications } = courseTask; const { explanation, attemptsLeftMessage, allowStartTask, allowCheckAnswers } = useAttemptsMessage(courseTask); const isSelfEducationTask = courseTask.type === CourseTaskDetailedDtoTypeEnum.Selfeducation; const { token } = theme.useToken(); return ( <> {attemptsLeftMessage && ( Attempts: {attemptsLeftMessage} )} {isTableVisible && ( {isSelfEducationTask && ( )} )} {isTableVisible && ( )} ); } export default VerificationInformation; ================================================ FILE: client/src/modules/AutoTest/components/VerificationsTable/VerificationsTable.test.tsx ================================================ import { render, screen } from '@testing-library/react'; import { VerificationsTable, VerificationsTableProps } from '..'; import { Verification } from '@client/services/course'; const PROPS_MOCK: VerificationsTableProps = { maxScore: 100, loading: false, verifications: [ { id: 1, score: 20, createdDate: '2022-12-10T00:00:00.000Z', details: 'Your accuracy: 40%.', }, ] as Verification[], }; describe('VerificationsTable', () => { it.each` item ${'Date / Time'} ${'Score / Max'} ${'Accuracy'} ${'Details'} ${'20 / 100'} ${'40%'} ${'Your accuracy: 40%.'} `('should render $item', ({ item }: { item: string }) => { render(); const element = screen.getByText(item); expect(element).toBeInTheDocument(); }); it('should render metadata when it was provided', () => { const verificationWithMetadata = { id: 1, score: 20, details: 'Accuracy: 30%.', metadata: [ { id: '1', name: 'Metadata name', }, ] as any, courseTask: { type: 'codewars', }, } as Verification; render(); const metadata = screen.getByText(/Metadata name/i); expect(metadata).toBeInTheDocument(); }); }); ================================================ FILE: client/src/modules/AutoTest/components/VerificationsTable/VerificationsTable.tsx ================================================ import { Table } from 'antd'; import { useMemo } from 'react'; import { Verification } from '@client/services/course'; import { getColumns } from './renderers'; export type VerificationsTableProps = { maxScore: number; verifications: Verification[]; loading: boolean; }; function VerificationsTable({ maxScore, verifications, loading }: VerificationsTableProps) { const columns = useMemo(() => getColumns(maxScore), [maxScore]); return ( ); } export default VerificationsTable; ================================================ FILE: client/src/modules/AutoTest/components/VerificationsTable/renderers.tsx ================================================ import { Space, Typography } from 'antd'; import { ColumnType } from 'antd/lib/table'; import { dateWithTimeZoneRenderer } from '@client/shared/components/Table'; import { CourseTaskDetailedDtoTypeEnum } from '@client/api'; import { CheckSquareTwoTone, CloseSquareTwoTone } from '@ant-design/icons'; import { Verification } from '@client/services/course'; const { Text, Link, Title } = Typography; type Breakpoint = ColumnType['responsive']; const DISPLAY_ALL: Breakpoint = ['sm']; const DISPLAY_ACCURACY: Breakpoint = ['md']; const DISPLAY_MOBILE: Breakpoint = ['xs']; type Metadata = { id: string; url: string; name: string; completed: boolean; }; export function getColumns(maxScore: number): ColumnType[] { return [ { key: 'date-time', title: 'Date / Time', dataIndex: 'createdDate', responsive: DISPLAY_ALL, render: renderDate, }, { key: 'score', title: 'Score / Max', dataIndex: 'score', responsive: DISPLAY_ALL, render: renderScore(maxScore), }, { key: 'accuracy', title: 'Accuracy', responsive: DISPLAY_ACCURACY, render: (_, row: Verification) => { const accuracyWordWithNumber = /accuracy:\s+(\d+%)/gi; const [, accuracyNumber] = accuracyWordWithNumber.exec(row.details) ?? []; return accuracyNumber ?? '–'; }, }, { key: 'details', title: 'Details', dataIndex: 'details', responsive: DISPLAY_ALL, render: renderDetails, }, { key: 'details', title: 'Details', render: renderMobileRow(maxScore), responsive: DISPLAY_MOBILE, }, ]; } function renderDetails(value: string, row: Verification) { if (row?.courseTask?.type === CourseTaskDetailedDtoTypeEnum.Codewars) { return ( <> {value} {(row?.metadata as Metadata[])?.map(({ id, url, name, completed }, index: number) => ( {completed ? ( ) : ( )} <> {' '} {index}.{name} ))} ); } return typeof value === 'string' ? value.split('\\n').map(str =>
{str}
) : value; } function renderScore(maxScore: number) { return (score: number) => ( {score ?? 0} / {maxScore} ); } function renderDate(createdDate: string) { const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; return dateWithTimeZoneRenderer(timezone, 'YYYY-MM-DD HH:mm')(createdDate); } function renderMobileRow(maxScore: number) { return (_: string, row: Verification) => ( {renderDate(row.createdDate)} {renderScore(maxScore)(row.score)} {renderDetails(row.details, row)} ); } ================================================ FILE: client/src/modules/AutoTest/components/index.tsx ================================================ export { default as TaskCard, type TaskCardProps } from './TaskCard/TaskCard'; export { default as TaskDescription } from './TaskDescription/TaskDescription'; export { default as TaskCardColumn } from './TaskCardColumn/TaskCardColumn'; export { default as TaskDeadlineDate, type TaskDeadlineDateProps } from './TaskDeadlineDate/TaskDeadlineDate'; export { default as VerificationsTable, type VerificationsTableProps } from './VerificationsTable/VerificationsTable'; export { default as Exercise } from './Exercise/Exercise'; export { default as Coding } from './Coding/Coding'; export { default as JupyterNotebook } from './JupyterNotebook/JupyterNotebook'; export { default as SelfEducation } from './SelfEducation/SelfEducation'; export { default as VerificationInformation } from './VerificationInformation/VerificationInformation'; export { default as StatusTabs } from './StatusTabs/StatusTabs'; export { default as AttemptsAnswers } from './AttemptsAnswers/AttemptsAnswers'; export { default as Question } from './Question/Question'; ================================================ FILE: client/src/modules/AutoTest/hooks/index.ts ================================================ export { useAttemptsMessage } from './useAttemptsMessage/useAttemptsMessage'; export { useCourseTaskSubmit } from './useCourseTaskSubmit/useCourseTaskSubmit'; export { useCourseTaskVerifications } from './useCourseTaskVerifications/useCourseTaskVerifications'; export { useVerificationsAnswers } from './useVerificationsAnswers/useVerificationsAnswers'; ================================================ FILE: client/src/modules/AutoTest/hooks/useAttemptsMessage/useAttemptsMessage.test.ts ================================================ import { renderHook } from '@testing-library/react'; import { SelfEducationPublicAttributes, Verification } from '@client/services/course'; import { CourseTaskDetailedDtoTypeEnum } from '@client/api'; import { useAttemptsMessage } from './useAttemptsMessage'; import { CourseTaskVerifications } from '@client/modules/AutoTest/types'; import dayjs from 'dayjs'; const MAX_ATTEMPTS = 4; function renderUseAttemptsMessage({ verificationsCount = 0, task, verificationCreatedDate, }: { verificationsCount?: number; task?: CourseTaskVerifications; verificationCreatedDate?: string; }) { const verifications = new Array(verificationsCount).fill({ createdDate: dayjs(verificationCreatedDate).add(2, 'h').format(), }) as Verification[]; let courseTask = { publicAttributes: { maxAttemptsNumber: MAX_ATTEMPTS, }, verifications, } as CourseTaskVerifications; if (task) { courseTask = { ...task, verifications }; } const { result: { current }, } = renderHook(() => useAttemptsMessage(courseTask)); return { ...current }; } describe('useAttemptsMessage', () => { it.each` verificationsCount | attemptsLeft ${0} | ${4} ${1} | ${3} ${4} | ${0} ${10} | ${0} `( `should return left attempts count equal to $attemptsLeft when verifications count is $verificationsCount and max attempts is ${MAX_ATTEMPTS}`, ({ verificationsCount, attemptsLeft }: { verificationsCount: number; attemptsLeft: number }) => { const { attemptsCount } = renderUseAttemptsMessage({ verificationsCount }); expect(attemptsCount).toBe(attemptsLeft); }, ); it('should return left attempts count equal to Infinity when max attempts was not provided', () => { const task = { type: CourseTaskDetailedDtoTypeEnum.Jstask, } as CourseTaskVerifications; const { attemptsCount } = renderUseAttemptsMessage({ task }); expect(attemptsCount).toBe(Infinity); }); it.each` publicAttributes | verificationsCount | expected ${{ strictAttemptsMode: true, maxAttemptsNumber: MAX_ATTEMPTS, tresholdPercentage: 90 }} | ${0} | ${'You must score at least 90% of points to pass. You have only 4 attempts.'} ${{ strictAttemptsMode: false, maxAttemptsNumber: MAX_ATTEMPTS, tresholdPercentage: 90 }} | ${0} | ${'You must score at least 90% of points to pass. You have only 4 attempts. After limit attempts is over you can get only half of a score.'} ${{ maxAttemptsNumber: MAX_ATTEMPTS, tresholdPercentage: 90, oneAttemptPerNumberOfHours: 1 }} | ${0} | ${'You must score at least 90% of points to pass. You have only 4 attempts. After limit attempts is over you can get only half of a score. You have only one attempt per 1 hours.'} ${{ maxAttemptsNumber: MAX_ATTEMPTS, tresholdPercentage: 90, oneAttemptPerNumberOfHours: 2 }} | ${1} | ${'You must score at least 90% of points to pass. You have only 4 attempts. After limit attempts is over you can get only half of a score. You have only one attempt per 2 hours. Next submit is possible in'} ${{ maxAttemptsNumber: undefined, tresholdPercentage: undefined }} | ${0} | ${'You can submit your solution as many times as you need before the deadline. Without fines. After the deadline, the submission will be closed.'} `( `should return explanation when ${JSON.stringify(`$publicAttributes`)}`, ({ publicAttributes, verificationsCount, expected, }: { publicAttributes: SelfEducationPublicAttributes; verificationsCount: number; expected: string; }) => { const task = { publicAttributes, } as CourseTaskVerifications; const { explanation } = renderUseAttemptsMessage({ task, verificationsCount }); expect(explanation).toMatch(new RegExp(expected, 'i')); }, ); it.each` type | strictAttemptsMode | verificationsCount | expected ${CourseTaskDetailedDtoTypeEnum.Jstask} | ${undefined} | ${3} | ${undefined} ${CourseTaskDetailedDtoTypeEnum.Selfeducation} | ${undefined} | ${3} | ${"Only 1 attempt left. Be careful, It's your last attempt!"} ${CourseTaskDetailedDtoTypeEnum.Selfeducation} | ${undefined} | ${2} | ${'2 attempts left.'} ${CourseTaskDetailedDtoTypeEnum.Selfeducation} | ${true} | ${4} | ${'You have no more attempts.'} ${CourseTaskDetailedDtoTypeEnum.Selfeducation} | ${undefined} | ${4} | ${'Limit of "free" attempts is over. Now you can get only half of a score.'} `( `should return left attempts count message when verifications count is $verificationsCount and max attempts is ${MAX_ATTEMPTS}`, ({ type, verificationsCount, strictAttemptsMode, expected, }: { type: CourseTaskDetailedDtoTypeEnum; verificationsCount: number; strictAttemptsMode: boolean; expected: string; }) => { const task = { type, publicAttributes: { maxAttemptsNumber: MAX_ATTEMPTS, strictAttemptsMode, }, } as CourseTaskVerifications; const { attemptsLeftMessage } = renderUseAttemptsMessage({ task, verificationsCount }); expect(attemptsLeftMessage).toBe(expected); }, ); describe('should allow to start task', () => { it('when strict mode is false and attempts count is 0', () => { const task = { publicAttributes: { maxAttemptsNumber: MAX_ATTEMPTS, strictAttemptsMode: false, }, } as CourseTaskVerifications; const { allowStartTask } = renderUseAttemptsMessage({ task, verificationsCount: MAX_ATTEMPTS }); expect(allowStartTask).toBeTruthy(); }); it('when deadline has not passed', () => { const task = { studentEndDate: dayjs().add(7, 'd').format(), } as CourseTaskVerifications; const { allowStartTask } = renderUseAttemptsMessage({ task, verificationsCount: MAX_ATTEMPTS }); expect(allowStartTask).toBeTruthy(); }); it('when attempts per hours are not over', () => { const task = { studentEndDate: dayjs().add(7, 'd').format(), publicAttributes: { oneAttemptPerNumberOfHours: 1, }, } as CourseTaskVerifications; const { allowStartTask } = renderUseAttemptsMessage({ task, verificationsCount: 1, verificationCreatedDate: dayjs().subtract(4, 'h').format(), }); expect(allowStartTask).toBeTruthy(); }); }); describe('should not allow to start task', () => { it('when strict mode is true and attempts count is 0', () => { const task = { publicAttributes: { maxAttemptsNumber: MAX_ATTEMPTS, strictAttemptsMode: true, }, } as CourseTaskVerifications; const { allowStartTask } = renderUseAttemptsMessage({ task, verificationsCount: MAX_ATTEMPTS }); expect(allowStartTask).toBeFalsy(); }); it('when deadline has passed', () => { const task = { studentEndDate: '1970-01-01T00:00:00.000Z', } as CourseTaskVerifications; const { allowStartTask } = renderUseAttemptsMessage({ task }); expect(allowStartTask).toBeFalsy(); }); it('when attempts per hours are over', () => { const task = { studentEndDate: dayjs().add(7, 'd').format(), publicAttributes: { oneAttemptPerNumberOfHours: 3, }, } as CourseTaskVerifications; const { allowStartTask } = renderUseAttemptsMessage({ task, verificationsCount: 1 }); expect(allowStartTask).toBeFalsy(); }); }); describe('should not allow to check answers', () => { it('when deadline is not passed', () => { const task = { studentEndDate: dayjs().add(7, 'd').format(), } as CourseTaskVerifications; const { allowCheckAnswers } = renderUseAttemptsMessage({ task }); expect(allowCheckAnswers).toBeFalsy(); }); it('when deadline is passed and attempts were not taken', () => { const task = { studentEndDate: dayjs().subtract(7, 'd').format(), publicAttributes: { maxAttemptsNumber: 5, }, } as CourseTaskVerifications; const { allowCheckAnswers } = renderUseAttemptsMessage({ task, verificationsCount: 0 }); expect(allowCheckAnswers).toBeFalsy(); }); }); describe('should allow to check answers', () => { it('when deadline is passed and attempts were taken', () => { const task = { studentEndDate: dayjs().subtract(7, 'd').format(), publicAttributes: { maxAttemptsNumber: 5, }, } as CourseTaskVerifications; const { allowCheckAnswers } = renderUseAttemptsMessage({ task, verificationsCount: 3 }); expect(allowCheckAnswers).toBeTruthy(); }); }); }); ================================================ FILE: client/src/modules/AutoTest/hooks/useAttemptsMessage/useAttemptsMessage.ts ================================================ import { useMemo } from 'react'; import { CourseTaskDetailedDtoTypeEnum } from '@client/api'; import { CourseTaskVerifications } from '@client/modules/AutoTest/types'; import dayjs from 'dayjs'; import utc from 'dayjs/plugin/utc'; dayjs.extend(utc); export function useAttemptsMessage(courseTask: CourseTaskVerifications) { const { publicAttributes, type, verifications, studentEndDate } = courseTask; const { maxAttemptsNumber, tresholdPercentage, strictAttemptsMode, oneAttemptPerNumberOfHours } = publicAttributes || {}; const attemptsCount = useMemo((): number => { if (maxAttemptsNumber) { const leftCount = maxAttemptsNumber - (verifications?.length || 0); return leftCount > 0 ? leftCount : 0; } return Infinity; }, [maxAttemptsNumber, verifications?.length]); const isDeadlinePassed = useMemo(() => { const now = dayjs(); const endDate = dayjs(studentEndDate); return now.isAfter(endDate); }, [studentEndDate]); const timeToNextSubmit = useMemo((): number => { const [lastAttempt] = verifications || []; const lastAttemptTime = lastAttempt?.createdDate; if (oneAttemptPerNumberOfHours && lastAttemptTime) { const diff = dayjs(lastAttemptTime).diff(dayjs().subtract(oneAttemptPerNumberOfHours, 'hour')); if (diff < 0) { return 0; } return diff; } return 0; }, [oneAttemptPerNumberOfHours, verifications]); const explanation = useMemo(() => { if (tresholdPercentage && maxAttemptsNumber) { let str = `You must score at least ${tresholdPercentage}% of points to pass. You have only ${maxAttemptsNumber} attempts.`; if (!strictAttemptsMode) { str += ' After limit attempts is over you can get only half of a score.'; } if (oneAttemptPerNumberOfHours) { str += ` You have only one attempt per ${oneAttemptPerNumberOfHours} hours.`; } if (timeToNextSubmit !== 0 && attemptsCount > 0) { str += ` Next submit is possible in ${dayjs.utc(timeToNextSubmit).format('HH:mm:ss')}`; } return str; } return 'You can submit your solution as many times as you need before the deadline. Without fines. After the deadline, the submission will be closed.'; }, [maxAttemptsNumber, tresholdPercentage, strictAttemptsMode, timeToNextSubmit]); const attemptsLeftMessage = useMemo((): string | undefined => { if (type !== CourseTaskDetailedDtoTypeEnum.Selfeducation || isDeadlinePassed) { return; } if (attemptsCount === 1) { return `Only 1 attempt left. Be careful, It's your last attempt!`; } if (attemptsCount > 1) { return `${attemptsCount} attempts left.`; } if (strictAttemptsMode) { return 'You have no more attempts.'; } return 'Limit of "free" attempts is over. Now you can get only half of a score.'; }, [attemptsCount, strictAttemptsMode]); const allowStartTask = useMemo(() => { if (isDeadlinePassed || !!timeToNextSubmit || (strictAttemptsMode && !attemptsCount)) { return false; } return true; }, [strictAttemptsMode, attemptsCount, isDeadlinePassed]); const allowCheckAnswers = useMemo( () => isDeadlinePassed && verifications?.length > 0, [isDeadlinePassed, verifications?.length], ); return { attemptsCount, explanation, attemptsLeftMessage, allowStartTask, allowCheckAnswers, }; } ================================================ FILE: client/src/modules/AutoTest/hooks/useCourseTaskSubmit/useCourseTaskSubmit.test.ts ================================================ import { renderHook } from '@testing-library/react'; import { CourseTaskDetailedDtoTypeEnum, CourseTaskVerificationsApi } from '@client/api'; import { IpynbFile, useCourseTaskSubmit } from './useCourseTaskSubmit'; import { FilesService } from '@client/services/files'; import { act } from 'react-dom/test-utils'; import { AxiosError } from 'axios'; import * as UserUtils from '@client/domain/user'; import { CourseTaskVerifications } from '@client/modules/AutoTest/types'; vi.mock('@client/services/files', () => ({ FilesService: vi.fn(), })); vi.mock('@client/domain/user'); vi.mock('@client/api', async importOriginal => { const actual = await importOriginal(); function MockCourseTaskVerificationsApi() {} MockCourseTaskVerificationsApi.prototype.createTaskVerification = vi.fn(); return { ...actual, CourseTaskVerificationsApi: MockCourseTaskVerificationsApi, }; }); const mockErrorNotification = vi.fn(); const mockSuccessNotification = vi.fn(); vi.mock('@client/hooks/useMessage', () => ({ useMessage: () => ({ notification: { error: mockErrorNotification, success: mockSuccessNotification, }, }), })); const uploadFileMock = vi.fn().mockImplementation(() => ({ s3Key: 'some-string' })); vi.mocked(FilesService).mockImplementation(function () { return { uploadFile: uploadFileMock }; }); const FILE_VALUE_MOCK = { upload: { file: { originFileObj: new File([new Blob(['blob-blob'])], 'file-name'), }, }, } as IpynbFile; const SELF_EDUCATION_MOCK = { ['answer-0']: 1, ['answer-1']: 2 }; const CODING_RESULT = { githubRepoName: 'github-repo-name', sourceGithubRepoUrl: 'source-github-repo-url', }; const CODEWARS_RESULT = { deadline: '2022-10-10T00:00.000Z' }; const SELF_EDUCATION_RESULT = [ { index: 0, value: 1 }, { index: 1, value: 2 }, ]; const IPYNB_RESULT = { s3Key: expect.any(String), taskName: 'course_task_ipynb' }; describe('useCourseTaskSubmit', () => { it.each` type | values | result ${CourseTaskDetailedDtoTypeEnum.Ipynb} | ${FILE_VALUE_MOCK} | ${IPYNB_RESULT} ${CourseTaskDetailedDtoTypeEnum.Selfeducation} | ${SELF_EDUCATION_MOCK} | ${SELF_EDUCATION_RESULT} ${CourseTaskDetailedDtoTypeEnum.Codewars} | ${{}} | ${CODEWARS_RESULT} ${CourseTaskDetailedDtoTypeEnum.Jstask} | ${{}} | ${CODING_RESULT} ${CourseTaskDetailedDtoTypeEnum.Kotlintask} | ${{}} | ${CODING_RESULT} ${CourseTaskDetailedDtoTypeEnum.Objctask} | ${{}} | ${CODING_RESULT} ${CourseTaskDetailedDtoTypeEnum.Cvmarkdown} | ${{}} | ${{}} ${CourseTaskDetailedDtoTypeEnum.Cvhtml} | ${{}} | ${{}} `( 'should post task verification for $type', async ({ type, values, result }: { type: CourseTaskDetailedDtoTypeEnum; values: any; result: any }) => { const createTaskVerificationMock = vi .spyOn(CourseTaskVerificationsApi.prototype, 'createTaskVerification') .mockResolvedValueOnce({ data: { courseTask: { type } } }); const courseTask = generateCourseTask(type); const { submit } = renderUseCourseTaskSubmit(courseTask); await act(async () => { await submit(values); }); expect(createTaskVerificationMock).toHaveBeenCalledWith(100, courseTask.id, result); }, ); it(`should trigger file upload when task is ${CourseTaskDetailedDtoTypeEnum.Ipynb}`, async () => { const courseTask = generateCourseTask(CourseTaskDetailedDtoTypeEnum.Ipynb); const { submit } = renderUseCourseTaskSubmit(courseTask); await act(async () => { await submit(FILE_VALUE_MOCK); }); expect(uploadFileMock).toHaveBeenCalled(); }); describe('when request failed', () => { beforeEach(() => { vi.clearAllMocks(); }); it.each` statusCode | _message ${401} | ${'Your authorization token has expired. You need to re-login in the application.'} ${429} | ${'Please wait. You will be able to submit your task again when the current verification is completed.'} ${423} | ${'Please reload page. This task was expired for submit.'} ${500} | ${'An error occurred. Please try later.'} `( 'and status code is $statusCode should trigger error notification', async ({ statusCode }: { statusCode: number }) => { const error = generateAxiosError(statusCode); const createTaskVerificationSpy = vi .spyOn(CourseTaskVerificationsApi.prototype, 'createTaskVerification') .mockRejectedValueOnce(error); const courseTask = generateCourseTask(); const { submit, finishTask, result } = renderUseCourseTaskSubmit(courseTask); await act(async () => { await submit({}); }); expect(createTaskVerificationSpy).toHaveBeenCalledTimes(1); expect(finishTask).not.toHaveBeenCalled(); expect(result.current.loading).toBe(false); }, ); it.each` isExpelled | perHour | _message ${true} | ${undefined} | ${'This task can only be submitted by active students.'} ${false} | ${undefined} | ${'You can submit this task only 4 times. For now your attempts limit is over!'} ${false} | ${1} | ${'You can submit this task only 4 times. You can submit this task not more than one time per 1 hours. For now your attempts limit is over!'} `( `and status code is 403 should trigger error notification`, async ({ isExpelled, perHour }: { isExpelled: boolean; perHour: number }) => { const error = generateAxiosError(403); vi.spyOn(UserUtils, 'isExpelledStudent').mockImplementationOnce(() => isExpelled); const createTaskVerificationSpy = vi .spyOn(CourseTaskVerificationsApi.prototype, 'createTaskVerification') .mockRejectedValueOnce(error); const courseTask = generateCourseTask(CourseTaskDetailedDtoTypeEnum.Jstask, perHour); const { submit, finishTask, result } = renderUseCourseTaskSubmit(courseTask); await act(async () => { await submit({}); }); expect(createTaskVerificationSpy).toHaveBeenCalledTimes(1); expect(finishTask).not.toHaveBeenCalled(); expect(result.current.loading).toBe(false); }, ); }); }); function renderUseCourseTaskSubmit(courseTask: CourseTaskVerifications) { const finishTask = vi.fn(); const { result: view } = renderHook(() => useCourseTaskSubmit(100, courseTask, finishTask)); return { ...view.current, result: view, finishTask }; } function generateCourseTask( type: CourseTaskDetailedDtoTypeEnum = CourseTaskDetailedDtoTypeEnum.Jstask, oneAttemptPerNumberOfHours?: number, ): CourseTaskVerifications { return { id: 10, name: `Course task ${type}`, type, checker: 'auto-test', studentEndDate: '2022-10-10T00:00.000Z', githubRepoName: 'github-repo-name', sourceGithubRepoUrl: 'source-github-repo-url', publicAttributes: { maxAttemptsNumber: 4, oneAttemptPerNumberOfHours, }, } as CourseTaskVerifications; } function generateAxiosError(code: number): AxiosError { return { isAxiosError: true, response: { status: code, }, } as AxiosError; } ================================================ FILE: client/src/modules/AutoTest/hooks/useCourseTaskSubmit/useCourseTaskSubmit.ts ================================================ import { Form } from 'antd'; import { CourseTaskDetailedDtoTypeEnum, CourseTaskVerificationsApi } from '@client/api'; import snakeCase from 'lodash/snakeCase'; import { useContext, useMemo, useState } from 'react'; import { FilesService } from '@client/services/files'; import { SelfEducationPublicAttributes } from '@client/services/course'; import { AxiosError } from 'axios'; import { isExpelledStudent } from '@client/domain/user'; import { SessionContext } from '@client/modules/Course/contexts'; import { InternalUploadFile } from 'antd/lib/upload/interface'; import { useBeforeUnload } from 'react-use'; import { CourseTaskVerifications } from '@client/modules/AutoTest/types'; import { useMessage } from '@client/hooks'; type SelfEducationValues = Record; export type IpynbFile = { upload: { file: InternalUploadFile } }; type FormValues = SelfEducationValues | IpynbFile; function isIpynbFile(item: unknown): item is IpynbFile { return !!item && typeof item === 'object' && 'upload' in item; } export function useCourseTaskSubmit(courseId: number, courseTask: CourseTaskVerifications, finishTask: () => void) { const { notification } = useMessage(); const session = useContext(SessionContext); const taskVerificationsApi = useMemo(() => new CourseTaskVerificationsApi(), []); const [loading, setLoading] = useState(false); const [isModified, setIsModified] = useState(false); const [form] = Form.useForm(); useBeforeUnload(isModified, 'You have changes in test. Do you really want to close this page?'); const uploadIpynbFile = async (values: FormValues): Promise => { if (isIpynbFile(values)) { const filesService = new FilesService(); const fileData = await readFile(values.upload.file); const { s3Key } = await filesService.uploadFile('', fileData); return s3Key; } }; const getSubmitData = async (values: FormValues) => { switch (courseTask.type) { case CourseTaskDetailedDtoTypeEnum.Ipynb: { const s3Key = await uploadIpynbFile(values); return { s3Key, taskName: snakeCase(courseTask.name), }; } case CourseTaskDetailedDtoTypeEnum.Selfeducation: { return Object.entries(values) .filter(([key]) => /answer/.test(key)) .map(([key, value]) => { const [, index = 0] = key.match(/answer-(.*)$/) || []; return { index: Number(index), value }; }); } case CourseTaskDetailedDtoTypeEnum.Codewars: { return { deadline: courseTask.studentEndDate, }; } case CourseTaskDetailedDtoTypeEnum.Jstask: case CourseTaskDetailedDtoTypeEnum.Kotlintask: case CourseTaskDetailedDtoTypeEnum.Objctask: return { githubRepoName: courseTask.githubRepoName, sourceGithubRepoUrl: courseTask.sourceGithubRepoUrl, }; case CourseTaskDetailedDtoTypeEnum.Cvmarkdown: case CourseTaskDetailedDtoTypeEnum.Cvhtml: return {}; default: return null; } }; const getError = (error: AxiosError): string => { switch (error.response?.status) { case 401: return 'Your authorization token has expired. You need to re-login in the application.'; case 429: return 'Please wait. You will be able to submit your task again when the current verification is completed.'; case 423: return 'Please reload page. This task was expired for submit.'; case 403: { if (isExpelledStudent(session, courseId)) { return 'This task can only be submitted by active students.'; } const { oneAttemptPerNumberOfHours, maxAttemptsNumber = 0 } = (courseTask?.publicAttributes ?? {}) as SelfEducationPublicAttributes; const timeLimitedAttempts = oneAttemptPerNumberOfHours ? `You can submit this task not more than one time per ${oneAttemptPerNumberOfHours} hours.` : ''; return `You can submit this task only ${maxAttemptsNumber} times. ${timeLimitedAttempts} For now your attempts limit is over!`; } default: return 'An error occurred. Please try later.'; } }; const submit = async (values: FormValues) => { if (loading) { return; } setLoading(true); const data = await getSubmitData(values); if (!data) { return; } try { const response = await taskVerificationsApi.createTaskVerification(courseId, courseTask.id, data); if (!response.data.id) { notification.success({ message: 'The task has been submitted.' }); } else { notification.success({ message: 'The task has been submitted for verification and it will be checked soon.' }); } finishTask(); setIsModified(false); } catch (e) { const error = e as AxiosError; const message = getError(error); notification.error({ message, // notification will never be closed automatically when status is 401 duration: error.response?.status === 401 ? false : undefined, }); } finally { setLoading(false); } }; const change = () => { setIsModified(true); }; return { form, loading, submit, change }; } function readFile(file: any) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsText(file?.originFileObj, 'utf-8'); reader.onload = ({ target }) => resolve(target ? (target.result as string) : ''); reader.onerror = e => reject(e); }); } ================================================ FILE: client/src/modules/AutoTest/hooks/useCourseTaskVerifications/useCourseTaskVerifications.ts ================================================ import { useRequest } from 'ahooks'; import { message } from 'antd'; import { CheckerEnum, CoursesTasksApi, CourseTaskDtoTypeEnum } from '@client/api'; import dayjs from 'dayjs'; import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'; import { mapTo } from '@client/modules/AutoTest/utils/map'; import { useEffect, useMemo, useState } from 'react'; import { CourseService } from '@client/services/course'; dayjs.extend(isSameOrAfter); export function useCourseTaskVerifications(courseId: number) { const [isExerciseVisible, setIsExerciseVisible] = useState(false); const { data } = useRequest(async () => { const { data } = await new CoursesTasksApi().getCourseTasksDetailed(courseId); const now = dayjs(); return data.filter( item => item.checker === CheckerEnum.AutoTest && item.type !== CourseTaskDtoTypeEnum.Test && now.isSameOrAfter(item.studentStartDate), ); }); const courseTasks = data; const courseService = useMemo(() => new CourseService(courseId), []); const { loading, data: allVerifications = [], error, run: reload, } = useRequest(async () => await courseService.getTaskVerifications()); const tasks = useMemo(() => courseTasks?.map(ct => mapTo(ct, allVerifications)), [courseTasks, allVerifications]); function startTask() { setIsExerciseVisible(true); } function finishTask() { reload(); setIsExerciseVisible(false); } useEffect(() => { if (error?.message) { message.error(error.message); } }, [error?.message]); return { tasks, loading, isExerciseVisible, startTask, finishTask, reload, }; } ================================================ FILE: client/src/modules/AutoTest/hooks/useVerificationsAnswers/useVerificationsAnswers.ts ================================================ import { message } from 'antd'; import { useState } from 'react'; import { CourseTaskVerificationsApi, TaskVerificationAttemptDto } from '@client/api'; import { AxiosError } from 'axios'; import { useLoading } from '@client/components/useLoading'; export function useVerificationsAnswers(courseId: number, courseTaskId: number) { const [answers, setAnswers] = useState(null); const [loading, withLoading] = useLoading(false, e => { const error = e as AxiosError; message.error(error.response?.data?.message || error?.message); }); const showAnswers = withLoading(async () => { const result = await new CourseTaskVerificationsApi().getAnswers(courseId, courseTaskId); setAnswers(result.data); }); const hideAnswers = () => { setAnswers(null); }; return { loading, answers, showAnswers, hideAnswers }; } ================================================ FILE: client/src/modules/AutoTest/pages/AutoTests/AutoTests.tsx ================================================ import { Col, Row } from 'antd'; import { ColProps } from 'antd/lib/grid'; import { PageLayout } from '@client/shared/components/PageLayout'; import { StatusTabs, TaskCard } from '@client/modules/AutoTest/components'; import { useCourseTaskVerifications } from '@client/modules/AutoTest/hooks'; import { CourseTaskStatus } from '@client/modules/AutoTest/types'; import { useActiveCourseContext } from '@client/modules/Course/contexts'; import { useMemo, useState } from 'react'; const RESPONSIVE_COLUMNS: ColProps = { sm: 24, md: 12, lg: 8, xl: 8, xxl: 6, }; function AutoTests() { const { course } = useActiveCourseContext(); const { tasks } = useCourseTaskVerifications(course.id); const [activeTab, setActiveTab] = useState(CourseTaskStatus.Available); const statuses = useMemo(() => tasks?.map(t => t.status) || [], [tasks]); const filteredTasks = useMemo(() => tasks?.filter(t => t.status === activeTab) || [], [tasks, activeTab]); return (
{filteredTasks.map(courseTask => ( ))} ); } export default AutoTests; ================================================ FILE: client/src/modules/AutoTest/pages/Task/Task.tsx ================================================ import { useContext } from 'react'; import { CoursePageProps } from '@client/services/models'; import { CourseTaskDetailedDto } from '@client/api'; import { PageLayout } from '@client/shared/components/PageLayout'; import { SessionContext, useActiveCourseContext } from '@client/modules/Course/contexts'; import { AttemptsAnswers, Exercise, TaskDescription, VerificationInformation, } from '@client/modules/AutoTest/components'; import { useCourseTaskVerifications, useVerificationsAnswers } from '@client/modules/AutoTest/hooks'; import { useRouter } from 'next/router'; import { theme } from 'antd'; export interface AutoTestTaskProps extends CoursePageProps { task: CourseTaskDetailedDto; } function Task() { const { course } = useActiveCourseContext(); const { githubId } = useContext(SessionContext); const { query } = useRouter(); const courseTaskId = Number(query.courseTaskId); const { loading, tasks, isExerciseVisible, startTask, finishTask, reload } = useCourseTaskVerifications(course.id); const { answers, showAnswers, hideAnswers } = useVerificationsAnswers(course.id, courseTaskId); const courseTask = tasks?.find(t => t.id === courseTaskId); const { token } = theme.useToken(); if (!courseTask) { return null; } return (
{!answers ? ( ) : ( )} {isExerciseVisible && ( )}
); } export default Task; ================================================ FILE: client/src/modules/AutoTest/pages/index.tsx ================================================ export { default as AutoTests } from './AutoTests/AutoTests'; export { default as Task, type AutoTestTaskProps } from './Task/Task'; ================================================ FILE: client/src/modules/AutoTest/types.ts ================================================ import { CourseTaskDetailedDto } from '@client/api'; import { SelfEducationPublicAttributes, Verification } from '@client/services/course'; export enum CourseTaskStatus { Available = 'Available', Missed = 'Missed', Done = 'Done', } export const COURSE_TASK_STATUSES = Object.entries(CourseTaskStatus).map(([key, value]) => ({ key, value })); export enum CourseTaskState { Uncompleted = 'Uncompleted', Missed = 'Missed', Completed = 'Completed', } export interface CourseTaskVerifications extends CourseTaskDetailedDto { publicAttributes: SelfEducationPublicAttributes; status: CourseTaskStatus; state: CourseTaskState; verifications: Verification[]; } ================================================ FILE: client/src/modules/AutoTest/utils/map.ts ================================================ import { CourseTaskDetailedDto } from '@client/api'; import dayjs from 'dayjs'; import { SelfEducationPublicAttributes, Verification } from '@client/services/course'; import { CourseTaskState, CourseTaskStatus, CourseTaskVerifications } from '../types'; function getState(courseTask: CourseTaskDetailedDto, verifications: Verification[]): CourseTaskState { const now = dayjs(); const end = dayjs(courseTask.studentEndDate); const attemptsCount = verifications?.length || 0; const publicAttributes = courseTask.publicAttributes as SelfEducationPublicAttributes; const isScorePassed = verifications?.some(v => v.score >= publicAttributes.tresholdPercentage) ?? false; if (isScorePassed) { return CourseTaskState.Completed; } if (now.isAfter(end) && !attemptsCount) { return CourseTaskState.Missed; } return CourseTaskState.Uncompleted; } function getStatus( { studentEndDate, maxScore, publicAttributes }: CourseTaskDetailedDto, verifications: Verification[], ): CourseTaskStatus { const attemptsCount = verifications?.length || 0; const now = dayjs(); const end = dayjs(studentEndDate); const publicAttr = publicAttributes as SelfEducationPublicAttributes; const isMaxAttemptsCount = attemptsCount >= publicAttr.maxAttemptsNumber; if (now.isAfter(end) && !attemptsCount) { return CourseTaskStatus.Missed; } if (publicAttr.strictAttemptsMode && isMaxAttemptsCount) { return CourseTaskStatus.Done; } if (maxScore === verifications[0]?.score || (now.isAfter(end) && attemptsCount)) { return CourseTaskStatus.Done; } if (isMaxAttemptsCount && maxScore / 2 === verifications[0]?.score) { return CourseTaskStatus.Done; } return CourseTaskStatus.Available; } // TODO: refactor nestjs models to return CourseTaskVerifications from server export function mapTo(courseTask: CourseTaskDetailedDto, verifications: Verification[]): CourseTaskVerifications { const taskVerifications = verifications.filter(v => v.courseTaskId === courseTask.id); return { ...courseTask, state: getState(courseTask, taskVerifications), status: getStatus(courseTask, taskVerifications), publicAttributes: courseTask.publicAttributes as SelfEducationPublicAttributes, verifications: taskVerifications, }; } ================================================ FILE: client/src/modules/Contributor/components/ContributorModal.tsx ================================================ import { Form, Input, message, Modal, Spin } from 'antd'; import { ContributorDto, ContributorsApi, UsersApi } from '@client/api'; import useRequest from 'ahooks/lib/useRequest'; import { UserSearch } from '@client/shared/components/UserSearch'; type Props = { contributorId: number | null; onClose: () => void; }; const api = new ContributorsApi(); const usersApi = new UsersApi(); type FormData = { user: { id: number }; description: string; }; export function ContributorModal(props: Props) { const [form] = Form.useForm(); const initial = useRequest, []>(async () => { if (!props.contributorId) { return {}; } const response = await api.getContributor(props.contributorId); return response.data; }); const submitForm = async () => { try { const value = await form.validateFields(); const record = { description: value.description, userId: value.user.id, }; if (props.contributorId) { await api.updateContributor(props.contributorId, record); } else { await api.createContributor(record); } props.onClose(); } catch { message.error('Something went wrong. Please try again later.'); } }; const loadUsers = async (searchText: string) => { const { data } = await usersApi.searchUsers(searchText); return data; }; const user = initial.data?.user ? { ...initial.data?.user, name: `${initial.data.user.firstName} ${initial.data.user.lastName}` } : null; return ( {initial.loading ? : null} {initial.data ? (
) : null}
); } ================================================ FILE: client/src/modules/Contributor/components/ContributorsTable.tsx ================================================ import { Button, Space, Table } from 'antd'; import { ContributorDto } from '@client/api'; import { DeleteOutlined, EditOutlined } from '@ant-design/icons'; import { GithubAvatar } from '@client/shared/components/GithubAvatar'; const { Column } = Table; type Props = { data: ContributorDto[]; handleUpdate: (record: ContributorDto) => void; handleDelete: (record: ContributorDto) => Promise; }; export const ContributorsTable = ({ data: disciplines, handleDelete, handleUpdate }: Props) => { return (
{ return ( <> {value} ); }} /> ( )} />
); }; ================================================ FILE: client/src/modules/Contributor/pages/ContributorPage.tsx ================================================ import { useRequest } from 'ahooks'; import { Button, Layout } from 'antd'; import { ContributorDto, ContributorsApi } from '@client/api'; import { AdminPageLayout } from '@client/shared/components/PageLayout'; import { useActiveCourseContext } from '@client/modules/Course/contexts'; import { ContributorModal } from '../components/ContributorModal'; import { ContributorsTable } from '../components/ContributorsTable'; import { useState } from 'react'; const api = new ContributorsApi(); export const ContributorPage = () => { const { courses } = useActiveCourseContext(); const [modalId, setModalId] = useState(); const response = useRequest(async () => { const { data } = await api.getContributors(); return data; }); const handleDelete = async (record: ContributorDto) => { await api.deleteContributor(record.id); response.run(); }; const handleUpdate = async (record: ContributorDto) => { setModalId(record.id); }; const handleClose = () => { setModalId(undefined); response.run(); }; return ( {modalId !== undefined ? : null} ); }; ================================================ FILE: client/src/modules/Course/components/CourseNoAccess.tsx ================================================ import { Button, Col, Result, Row } from 'antd'; export function CourseNoAccess() { return ( Go Home } /> ); } ================================================ FILE: client/src/modules/Course/components/NoSubmissionAvailable/index.tsx ================================================ import { Typography } from 'antd'; import Link from 'next/link'; export function NoSubmissionAvailable({ courseAlias }: { courseAlias: string }) { return ( <> No tasks available for submission now Check start dates in Schedule ); } ================================================ FILE: client/src/modules/Course/contexts/ActiveCourseContext.tsx ================================================ import { ProfileCourseDto } from '@client/api'; import { LoadingScreen } from '@client/shared/components/LoadingScreen'; import { useRouter } from 'next/router'; import React, { useCallback, useContext, useMemo, useState } from 'react'; import { useLocalStorage } from 'react-use'; import { UserService } from '@client/services/user'; import { WelcomeCard } from '@client/components/WelcomeCard'; import { Alert, Col, notification, Row } from 'antd'; import useRequest from 'ahooks/lib/useRequest'; import { AxiosError } from 'axios'; type ActiveCourseContextType = { course: ProfileCourseDto; courses: ProfileCourseDto[]; setCourse: (course: ProfileCourseDto) => void; refresh: () => void; }; const ActiveCourseContext = React.createContext({ course: {} as ProfileCourseDto, courses: [], setCourse: () => {}, refresh: () => {}, }); export const useActiveCourseContext = () => { return useContext(ActiveCourseContext); }; type Props = React.PropsWithChildren<{ publicRoutes: string[]; }>; export const ActiveCourseProvider = ({ children, publicRoutes }: Props) => { const router = useRouter(); // course alias const alias = router.query.course; const isPublicRoute = publicRoutes?.includes(router.pathname); const [storageCourseId, setStorageCourseId] = useLocalStorage('activeCourseId'); const [activeCourse, setActiveCourse] = useState(); const { data, loading, refresh } = useRequest(() => resolveCourse(alias, storageCourseId), { ready: router.isReady && !isPublicRoute, onSuccess: ([course]) => setCourse(course), onError: error => { const { pathname, search } = document.location; const redirectUrl = encodeURIComponent(`${pathname}${search}`); router.push('/login', { pathname: '/login', query: { url: redirectUrl } }); if ((error as AxiosError).status !== 401) { notification.error({ message: 'Error occurred during login', description: 'Please try again later or contact course manager', }); } }, }); const setCourse = useCallback((course: ProfileCourseDto | null) => { if (course) { setActiveCourse(course); setStorageCourseId(course.id.toString()); } }, []); const value = useMemo( () => (data && activeCourse ? { course: activeCourse, courses: data?.[1] ?? [], setCourse, refresh } : undefined), [activeCourse, data, setCourse, refresh], ); if (isPublicRoute && router.isReady) { return <>{children}; } if (alias && activeCourse && activeCourse.alias !== alias) { return ( ); } if (data && data[0] === null) { return ; } if (value) { return {children}; } return ; }; async function resolveCourse( alias: string | string[] | undefined, storageCourseId?: string, ): Promise<[ProfileCourseDto | null, ProfileCourseDto[]]> { const courses = await new UserService().getCourses(); const course = courses.find(course => course.alias === alias) ?? courses.find(course => String(course.id) === String(storageCourseId)) ?? courses[0] ?? null; return [course, courses] as const; } ================================================ FILE: client/src/modules/Course/contexts/SessionContext.test.tsx ================================================ import { render, screen } from '@testing-library/react'; import { SessionProvider } from './'; import Router from 'next/router'; import { useActiveCourseContext } from './ActiveCourseContext'; import useRequest from 'ahooks/lib/useRequest'; vi.mock('next/router', () => ({ default: { push: vi.fn() }, push: vi.fn() })); vi.mock('./ActiveCourseContext', () => ({ useActiveCourseContext: vi.fn(), })); vi.mock('ahooks/lib/useRequest'); describe('', () => { const mockChildren =
Child Component
; const mockSession = { isAdmin: true, courses: { 1: { roles: ['student'] } } }; const mockCourse = { id: 1 }; const mockActiveCourse = { course: mockCourse }; afterEach(() => { vi.clearAllMocks(); }); beforeEach(() => { vi.mocked(useActiveCourseContext).mockReturnValue(mockActiveCourse); }); it('should render loading screen', () => { vi.mocked(useRequest).mockReturnValue({ loading: true }); render({mockChildren}); expect(screen.getByText(/loading/i)).toBeInTheDocument(); }); it('should handle error and redirect to login', () => { vi.mocked(useRequest).mockReturnValue({ error: true }); render({mockChildren}); expect(Router.push).toHaveBeenCalledWith('/login', expect.anything()); }); it('should render children for admin user for admin-only pages', () => { vi.mocked(useRequest).mockReturnValue({ data: mockSession }); render({mockChildren}); expect(screen.getByText('Child Component')).toBeInTheDocument(); }); it('should render warning for non-admin user for admin-only pages', () => { vi.mocked(useRequest).mockReturnValue({ data: { ...mockSession, isAdmin: false } }); render({mockChildren}); expect(screen.getByText(/You don't have required role to access this page/)).toBeInTheDocument(); }); it('should render children for user with allowed roles', () => { vi.mocked(useRequest).mockReturnValue({ data: mockSession }); render({mockChildren}); expect(screen.getByText('Child Component')).toBeInTheDocument(); }); it('should render warning for user without allowed roles', () => { vi.mocked(useRequest).mockReturnValue({ data: { ...mockSession, isAdmin: false } }); render({mockChildren}); expect(screen.getByText(/You don't have required role to access this page/)).toBeInTheDocument(); }); }); ================================================ FILE: client/src/modules/Course/contexts/SessionContext.tsx ================================================ import useRequest from 'ahooks/lib/useRequest'; import { Button, Result } from 'antd'; import { type ProfileCourseDto, SessionApi } from '@client/api'; import { LoadingScreen } from '@client/shared/components/LoadingScreen'; import type { Session } from '@client/components/withSession'; import { hasRoleInAnyCourse } from '@client/domain/user'; import Router from 'next/router'; import React, { useEffect } from 'react'; import { CourseRole } from '@client/services/models'; import { useActiveCourseContext } from './ActiveCourseContext'; export const SessionContext = React.createContext({} as Session); type Props = React.PropsWithChildren<{ allowedRoles?: CourseRole[]; course?: ProfileCourseDto; adminOnly?: boolean; anyCoursePowerUser?: boolean; hirerOnly?: boolean; }>; export const AccessDeniedWarning = () => ( window.history.back()}> Go Back } /> ); const sessionApi = new SessionApi(); export function SessionProvider(props: Props) { const { allowedRoles, anyCoursePowerUser } = props; const activeCourse = useActiveCourseContext().course; const course = props.course ?? activeCourse; const { data: session, loading, error, } = useRequest( async () => { const response = await sessionApi.getSession(); return response.data; }, { cacheKey: 'session', staleTime: 1000 * 60 * 15, }, ); useEffect(() => { if (!error) { return; } const { pathname, search } = document.location; const redirectUrl = encodeURIComponent(`${pathname}${search}`); Router.push('/login', { pathname: '/login', query: { url: redirectUrl } }); }, [error]); if (session && props.adminOnly && !session.isAdmin) { return ; } if (session && props.hirerOnly && !session.isHirer && !session.isAdmin) { return ; } if (session && allowedRoles && course) { const { courses, isAdmin } = session; const id = course.id; if (!isAdmin) { const roles = courses?.[id]?.roles ?? []; const hasRoleInCurrentCourse = allowedRoles.some(role => roles.includes(role)); const isAnyCoursePowerUser = anyCoursePowerUser && allowedRoles.some(role => hasRoleInAnyCourse(session, role)); if (!hasRoleInCurrentCourse && !isAnyCoursePowerUser) { return ; } } } if (session) { return {props.children}; } return ; } ================================================ FILE: client/src/modules/Course/contexts/index.ts ================================================ export * from './SessionContext'; export * from './ActiveCourseContext'; ================================================ FILE: client/src/modules/Course/pages/CouseNoAccess/index.tsx ================================================ import { CourseNoAccess } from '../../components/CourseNoAccess'; export function CouseNoAccessPage() { return ; } ================================================ FILE: client/src/modules/Course/pages/Student/CrossCheckSubmit/index.tsx ================================================ import { useRequest } from 'ahooks'; import { Alert, Button, Checkbox, Col, Form, Input, Modal, Result, Row } from 'antd'; import { CheckboxChangeEvent } from 'antd/lib/checkbox'; import { Rule } from 'antd/lib/form'; import { CoursesTasksApi, CrossCheckFeedbackDto, CrossCheckMessageDtoRoleEnum, CrossCheckStatusEnum, } from '@client/api'; import { CourseTaskSelect, ScoreInput } from '@client/shared/components/Forms'; import { PageLayout } from '@client/shared/components/PageLayout'; import { NoSubmissionAvailable } from '@client/modules/Course/components/NoSubmissionAvailable'; import { SessionContext, useActiveCourseContext } from '@client/modules/Course/contexts'; import { CriteriaForm } from '@client/modules/CrossCheck/components/CriteriaForm'; import { SolutionReview } from '@client/modules/CrossCheck/components/SolutionReview'; import { SolutionReviewSettingsPanel } from '@client/modules/CrossCheck/components/SolutionReviewSettingsPanel'; import { SubmittedStatus } from '@client/modules/CrossCheck/components/SubmittedStatus'; import { useSolutionReviewSettings } from '@client/modules/CrossCheck/hooks'; import { useRouter } from 'next/router'; import { useContext, useEffect, useMemo, useState } from 'react'; import { CourseService, CrossCheckComment, CrossCheckCriteria, CrossCheckReview, TaskSolution, } from '@client/services/course'; import { githubPrUrl, privateRsRepoPattern, urlWithIpPattern } from '@client/services/validators'; import { getQueryString } from '@client/shared/utils/queryParams-utils'; import { useMessage } from '@client/hooks'; const colSizes = { xs: 24, sm: 18, md: 12, lg: 10 }; const createGithubInUrlRule = (githubId: string): Rule => { return { message: 'Your GitHub Username should be in the URL', required: true, pattern: new RegExp(`${githubId}`, 'i'), }; }; const validUrlRule: Rule = { required: true, pattern: urlWithIpPattern, message: 'Please provide a valid link (must start with `http://` or `https://`)', }; const githubPrInUrlRule: Rule = { required: true, pattern: githubPrUrl, message: 'Link should be a valid GitHub Pull Request URL', }; const notPrivateRsRepoRule: Rule = { validator: (_, value) => { if (privateRsRepoPattern.test(value)) { return Promise.reject("Please provide another link. Students can't see Pull Requests of private RS School repos"); } return Promise.resolve(); }, }; export function CrossCheckSubmit() { const session = useContext(SessionContext); const { course } = useActiveCourseContext(); const [form] = Form.useForm(); const courseService = useMemo(() => new CourseService(course.id), [course.id]); const teamDistributionApi = useMemo(() => new CoursesTasksApi(), []); const solutionReviewSettings = useSolutionReviewSettings(); const [feedback, setFeedback] = useState(null); const [submittedSolution, setSubmittedSolution] = useState(null as TaskSolution | null); const router = useRouter(); const queryTaskId = router.query.taskId ? +router.query.taskId : null; const [courseTaskId, setCourseTaskId] = useState(queryTaskId); const [criteria, setCriteria] = useState([] as CrossCheckCriteria[]); const [comments, setComments] = useState([] as CrossCheckComment[]); const [newComments, setNewComments] = useState([] as CrossCheckComment[]); const [isModalVisible, setIsModalVisible] = useState(false); const [buttonDisabled, setButtonDisabled] = useState(true); const [submitDeadlinePassed, setSubmitDeadlinePassed] = useState(false); const { message } = useMessage(); const [authorId, setAuthorId] = useState(null); const { data: courseTasks = [], loading } = useRequest(() => courseService.getCourseCrossCheckTasks('started'), { refreshDeps: [course.id], }); useEffect(() => { if (loading) return; if (queryTaskId) { handleTaskChange(queryTaskId); } }, [loading, queryTaskId]); const handleSubmit = async (values: { url: string; review: CrossCheckReview[] }) => { if (!courseTaskId) { return; } try { await courseService.postTaskSolution(session.githubId, courseTaskId, values.url, values.review, newComments); message.success('The task solution has been submitted'); form.resetFields(); setComments([]); setCourseTaskId(null); } catch { message.error('An error occured. Please try later.'); } }; const handleCancellation = async () => { if (!courseTaskId) { return; } try { await courseService.deleteTaskSolution(session.githubId, courseTaskId); message.success('The task submission has been canceled'); setIsModalVisible(false); form.resetFields(); setSubmittedSolution(null); setCourseTaskId(null); } catch { message.error('An error occurred. Please try later.'); } setButtonDisabled(true); }; const showModal = () => { setIsModalVisible(true); }; const cancelModal = () => { setIsModalVisible(false); setButtonDisabled(true); }; const cancellationChange = (e: CheckboxChangeEvent) => setButtonDisabled(!e.target.checked); function selectTask(value: number) { const query = { ...router.query, taskId: value }; const url = `${router.route}${getQueryString(query)}`; router.replace(url); } const handleTaskChange = async (value: number) => { setFeedback(null); const courseTaskId = Number(value); const courseTask = courseTasks.find(t => t.id === courseTaskId); if (courseTask == null) { return; } const [{ data: feedback }, submittedSolution, taskDetails] = await Promise.all([ teamDistributionApi.getMyCrossCheckFeedbacks(course.id, courseTask.id), courseService.getCrossCheckTaskSolution(session.githubId, courseTask.id).catch(() => null), courseService.getCrossCheckTaskDetails(courseTask.id), ]); const review = submittedSolution?.review ?? []; const criteria = taskDetails?.criteria ?? []; const endDate = taskDetails?.studentEndDate ? new Date(taskDetails.studentEndDate) : null; const submitDeadlinePassed = Date.now() > (endDate ? endDate.getTime() : 0); form.setFieldsValue({ review }); form.setFieldsValue({ score: calculateFinalScore(review, criteria) }); form.setFieldsValue({ url: submittedSolution?.url }); setFeedback(feedback); setSubmittedSolution(submittedSolution); setCourseTaskId(courseTask.id); setCriteria(criteria); setSubmitDeadlinePassed(submitDeadlinePassed); setComments(submittedSolution?.comments ?? []); setAuthorId(submittedSolution?.studentId ?? null); }; const handleReviewChange = (review: CrossCheckReview[], comments: CrossCheckComment[]) => { form.setFieldsValue({ score: calculateFinalScore(review, criteria) }); setNewComments(comments); }; const task = courseTasks.find(task => task.id === courseTaskId); const maxScore = task?.maxScore; const taskExists = !!task; const submitAllowed = taskExists && !submitDeadlinePassed; const newCrossCheck = criteria.length > 0; const isCrossCheckCompleted = task?.crossCheckStatus === CrossCheckStatusEnum.Completed; const isCrossCheckOngoing = task?.crossCheckStatus === CrossCheckStatusEnum.Distributed; const hasReviews = !!feedback?.reviews?.length; return (
{courseTasks.length > 0 && ( )} {courseTasks.length === 0 && !loading && } {submitAllowed && ( <> {task.submitText ? : null} )} {submitAllowed && newCrossCheck && ( )} {submitAllowed && newCrossCheck && } {submitAllowed && ( {submittedSolution && ( Being of sound mind and body, do hereby declare that I want to cancel my submission )} )}
{submittedSolution && !hasReviews && (isCrossCheckCompleted || isCrossCheckOngoing) && ( Check if you are eligible to appeal here. ) } /> )} {hasReviews && ( )} {feedback?.reviews?.map((review, index) => ( ))}
); } function calculateFinalScore( review: { percentage: number; criteriaId: string }[], criteria: CrossCheckCriteria[] = [], ) { return review.reduce((acc, r) => { const max = criteria.find(c => c.criteriaId === r.criteriaId)?.max ?? 0; return acc + Math.round(max * r.percentage); }, 0); } ================================================ FILE: client/src/modules/CourseManagement/components/CertificateCriteriaModal/CertificateCriteriaModal.test.tsx ================================================ import { render, screen, fireEvent } from '@testing-library/react'; import { CERTIFICATE_ALERT_MESSAGE, CertificateCriteriaModal, FormValues, hasValidCriteria, } from './CertificateCriteriaModal'; import userEvent from '@testing-library/user-event'; import * as ReactUse from 'react-use'; const props = { courseId: 1, onSubmit: vi.fn(), onClose: vi.fn(), isModalOpen: true, }; const renderCertificateCriteriaModal = () => { render(); }; describe('CertificateCriteriaModal', () => { beforeAll(() => { // mock CoursesTasksApi call vi.spyOn(ReactUse, 'useAsync').mockReturnValue({ value: [ { name: 'course 1', id: 1, }, ], loading: false, }); }); afterEach(() => { vi.clearAllMocks(); }); const user = userEvent.setup(); test('should render modal title', async () => { renderCertificateCriteriaModal(); const title = await screen.findByText('Certificate Criteria'); expect(title).toBeInTheDocument(); }); test('should render alert message', async () => { renderCertificateCriteriaModal(); const alert = await screen.findByText(CERTIFICATE_ALERT_MESSAGE); expect(alert).toBeInTheDocument(); }); test.each` label ${'Tasks'} ${'Minimum Score Per Task'} ${'Minimum Total Score'} `('should render field with $label label', async ({ label }) => { renderCertificateCriteriaModal(); const field = await screen.findByText(label); expect(field).toBeInTheDocument(); }); test('should render "cancel" button', async () => { renderCertificateCriteriaModal(); const button = await screen.findByRole('button', { name: /cancel/i }); expect(button).toBeInTheDocument(); }); test('should render "issue certificates" button', async () => { renderCertificateCriteriaModal(); const button = await screen.findByRole('button', { name: /issue certificates/i }); expect(button).toBeInTheDocument(); expect(button).toBeDisabled(); }); test('should enable "issue certificates" button on valid criteria', async () => { renderCertificateCriteriaModal(); const button = await screen.findByRole('button', { name: /issue certificates/i }); expect(button).toBeDisabled(); const minTotalScoreInput = await screen.findByLabelText('Minimum Total Score'); fireEvent.change(minTotalScoreInput, { target: { value: 5, }, }); expect(button).toBeEnabled(); }); test('should call "onClose" function on "cancel" button click', async () => { renderCertificateCriteriaModal(); const button = await screen.findByRole('button', { name: /cancel/i }); await user.click(button); expect(props.onClose).toHaveBeenCalled(); }); test('should call "onSubmit" function on "issue certificates" button click', async () => { renderCertificateCriteriaModal(); // Enable "issue certificates" button const minTotalScoreInput = await screen.findByLabelText('Minimum Total Score'); fireEvent.change(minTotalScoreInput, { target: { value: 5, }, }); const button = await screen.findByRole('button', { name: /issue certificates/i }); await user.click(button); expect(props.onSubmit).toHaveBeenCalled(); }); }); describe('hasValidCriteria', () => { afterEach(() => { vi.clearAllMocks(); }); test('should return "false" on empty values', () => { expect(hasValidCriteria({} as FormValues)).toBe(false); }); describe('tasksCriteriaValid (with minTotalScore > 0)', () => { test('should return "true" on empty courseTaskIds array', () => { const values = { minScore: 0, courseTaskIds: [], minTotalScore: 5, }; expect(hasValidCriteria(values)).toBe(true); }); test('should return "true" on not empty courseTaskIds array & minScore per task > 0', () => { const values = { minScore: 5, courseTaskIds: [1, 2], minTotalScore: 5, }; expect(hasValidCriteria(values)).toBe(true); }); test('should return "false" on not empty courseTaskIds array & minScore per task = 0', () => { const values = { minScore: 0, courseTaskIds: [1, 2], minTotalScore: 5, }; expect(hasValidCriteria(values)).toBe(false); }); }); describe('minTotalScore (with truthy tasksCriteriaValid)', () => { test('should return "false" on minTotalScore = 0', () => { const values = { minScore: 5, courseTaskIds: [1, 2], minTotalScore: 0, }; expect(hasValidCriteria(values)).toBe(false); }); test('should return "true" on minTotalScore > 0', () => { const values = { minScore: 5, courseTaskIds: [1, 2], minTotalScore: 5, }; expect(hasValidCriteria(values)).toBe(true); }); }); }); ================================================ FILE: client/src/modules/CourseManagement/components/CertificateCriteriaModal/CertificateCriteriaModal.tsx ================================================ import { Alert, Button, Col, Form, InputNumber, Modal, Row, Space } from 'antd'; import { useState } from 'react'; import { SelectCourseTasks } from '@client/modules/CourseManagement/components'; export type FormValues = { courseTaskIds: number[]; minScore: number; minTotalScore: number; }; type Criteria = Partial; type Props = { courseId: number; onSubmit: (criteria: Criteria) => void; onClose: () => void; isModalOpen: boolean; }; export const CERTIFICATE_ALERT_MESSAGE = 'Certificates will be issued to all students meeting the criteria down below.'; export function CertificateCriteriaModal({ courseId, onSubmit, onClose, isModalOpen }: Props) { const [form] = Form.useForm(); const [okEnabled, setOkEnabled] = useState(false); return (
{ setOkEnabled(hasValidCriteria(values)); }} onFinish={onSubmit} >
); } export function hasValidCriteria({ minScore, courseTaskIds, minTotalScore }: FormValues) { const tasksCriteriaValid = !courseTaskIds?.length || !!minScore; return tasksCriteriaValid && !!minTotalScore; } ================================================ FILE: client/src/modules/CourseManagement/components/CourseEventModal/formState.ts ================================================ import { message } from 'antd'; import { CreateCourseEventDto, CreateEventDto, EventDto, EventsApi } from '@client/api'; import { EVENT_TYPES } from '@client/data/eventTypes'; import omit from 'lodash/omit'; import { CourseEvent, CourseService } from '@client/services/course'; import dayjs from 'dayjs'; import utc from 'dayjs/plugin/utc'; import timezone from 'dayjs/plugin/timezone'; dayjs.extend(utc); dayjs.extend(timezone); const eventsApi = new EventsApi(); const createRecord = (eventTemplateId: number, values: any): CreateCourseEventDto => { const record = { eventId: eventTemplateId, special: values.special ? values.special.join(',') : '', dateTime: values.dateTime ? dayjs(values.dateTime).tz(values.timeZone, true).format() : undefined, endTime: values.endTime ? dayjs(values.endTime).tz(values.timeZone, true).format() : undefined, place: values.place || null, organizer: values.taskOwner?.id ? { id: values.taskOwner?.id } : undefined, }; return record; }; const submitTemplateEvent = async (values: any, eventTemplate?: EventDto) => { const templateEventData = { name: eventTemplate ? eventTemplate?.name : values.event, type: values.type, descriptionUrl: values.descriptionUrl, description: values.description, disciplineId: values.disciplineId, } as CreateEventDto; if (!eventTemplate) { try { const res = await eventsApi.createEvent(templateEventData); return res.data.id; } catch { message.error('Failed to create event template. Please try later.'); } } else { try { const res = await eventsApi.updateEvent(eventTemplate.id, templateEventData); return res.data.id; } catch { message.error('Failed to update event template. Please try later.'); } } }; export async function submitEvent( values: any, eventTemplates: EventDto[], courseId: number, editableRecord?: Partial, ): Promise { const currentEventTemplate = editableRecord?.event ?? eventTemplates.find(el => el.id === +values.event); const eventTemplateId = await submitTemplateEvent(values, currentEventTemplate as EventDto); if (!eventTemplateId) return; const serviceCourse = new CourseService(courseId); const record = createRecord(eventTemplateId, values); if (editableRecord?.id) { try { await serviceCourse.updateCourseEvent(editableRecord.id, omit(record, 'eventId')); } catch { message.error('Failed to update event. Please try later.'); } } else { try { await serviceCourse.createCourseEvent(record); } catch { message.error('Failed to create event. Please try later.'); } } } export function getInitialValues(modalData: Partial) { const timeZone = 'UTC'; return { ...modalData, type: EVENT_TYPES.find(event => event.id === modalData.event?.type)?.id ?? null, disciplineId: modalData?.event?.discipline?.id || modalData?.event?.disciplineId, descriptionUrl: modalData.event?.descriptionUrl ? modalData.event.descriptionUrl : '', description: modalData.event?.description ? modalData.event.description : '', dateTime: dayjs.utc(modalData.dateTime ?? undefined), endTime: dayjs.utc(modalData.endTime ?? undefined), taskOwner: modalData.organizer ? { id: modalData.organizer.id } : undefined, special: modalData.special ? modalData.special.split(',') : [], timeZone, }; } ================================================ FILE: client/src/modules/CourseManagement/components/CourseEventModal/index.tsx ================================================ import { Col, DatePicker, Form, Input, Row, Select, Typography } from 'antd'; import { DisciplinesApi, EventsApi } from '@client/api'; import { ModalForm } from '@client/shared/components/Forms'; import { UserSearch } from '@client/shared/components/UserSearch'; import { TIMEZONES } from '@client/configs/timezones'; import { EVENT_TYPES } from '@client/data/eventTypes'; import { SPECIAL_ENTITY_TAGS } from '@client/modules/Schedule/constants'; import { useCallback } from 'react'; import { useAsync } from 'react-use'; import { CourseEvent } from '@client/services/course'; import { UserService } from '@client/services/user'; import { urlPattern } from '@client/services/validators'; import { getInitialValues, submitEvent } from './formState'; const { Option } = Select; const { Title } = Typography; const { TextArea } = Input; type Props = { data: Partial; onCancel: () => void; onSubmit: () => void; courseId: number; }; const eventsApi = new EventsApi(); const disciplineApi = new DisciplinesApi(); const userService = new UserService(); export function CourseEventModal({ data, onCancel, courseId, onSubmit }: Props) { const [form] = Form.useForm(); const loadUsers = (searchText: string) => { return userService.searchUser(searchText); }; const loadData = async () => { const [{ data: events = [] }, { data: disciplines = [] }] = await Promise.all([ eventsApi.getEvents(), disciplineApi.getDisciplines(), ]); return { events, disciplines, }; }; const { loading, value: { events = [], disciplines = [] } = {} } = useAsync(loadData, []); const filterOption = useCallback( (input: string, option?: { value: string }): boolean => { if (!input) { return false; } const event = events.find(e => e.id === Number(option?.value)); return event?.name.toLowerCase().includes(input.toLowerCase()) ?? false; }, [events], ); const handleModalSubmit = async (values: any) => { await submitEvent(values, events, courseId, data); onSubmit(); }; const typesList = EVENT_TYPES; const entityTypes = typesList.map(tag => { return ( ); }); const onEventChange = (value: any) => { const currentEvent = value.at(-1); form.setFieldValue('event', currentEvent); const currentEventTemplate = events.find(el => el.id === +currentEvent && el.name !== currentEvent); form.setFieldValue('description', currentEventTemplate?.description); form.setFieldValue('descriptionUrl', currentEventTemplate?.descriptionUrl); form.setFieldValue('type', currentEventTemplate?.type); }; return ( {data.event?.id ? ( {data.event.name} ) : ( )}
); }; ================================================ FILE: client/src/modules/CrossCheck/CriteriaActions.tsx ================================================ import { FC } from 'react'; import { CriteriaDto } from '../../api'; import { Popconfirm, Typography, Space } from 'antd'; interface CriteriaActionsProps { editing: boolean; record: CriteriaDto; editingKey: string; save: (key: string) => void; remove: (key: string) => void; cancel: () => void; edit: (record: CriteriaDto) => void; } export const CriteriaActions: FC = ({ editing, cancel, remove, edit, save, record, editingKey, }) => editing ? ( save(record.key)}>Save Cancel ) : ( edit(record)}> Edit remove(record.key)} > Delete ); ================================================ FILE: client/src/modules/CrossCheck/CriteriaTypeSelect.tsx ================================================ import { Select } from 'antd'; import { FC } from 'react'; import { TaskType } from './constants'; import { SelectProps } from 'antd/lib'; const options = Object.entries(TaskType).map(([label, value]) => ({ label, value })); type CriteriaTypeSelectProps = SelectProps; export const CriteriaTypeSelect: FC = props => ( ); ================================================ FILE: client/src/modules/CrossCheck/DeleteAllCrossCheckCriteriaButton.tsx ================================================ import DeleteOutlined from '@ant-design/icons/DeleteOutlined'; import { Button, Popconfirm } from 'antd'; import { CriteriaDto } from '@client/api'; interface Props { setDataCriteria: (criteria: CriteriaDto[]) => void; } export function DeleteAllCrossCheckCriteriaButton({ setDataCriteria }: Props) { const deleteAllCrossCheckCriteria = () => { setDataCriteria([]); }; return (
); } ================================================ FILE: client/src/modules/CrossCheck/EditableCellForCrossCheck.tsx ================================================ import { CriteriaDto } from '@client/api'; import React from 'react'; import { EditableCriteriaInput } from './EditableCriteriaInput'; import { EditableTableColumnsDataIndex } from './constants'; import { theme } from 'antd'; interface EditableCellProps extends React.HTMLAttributes { editing: boolean; dataIndex: EditableTableColumnsDataIndex; record: CriteriaDto; index: number; children: React.ReactNode; onSelectChange: (value: string) => void; } export const EditableCellForCrossCheck: React.FC = ({ editing, dataIndex, children, record, onSelectChange, ...props }) => { const hasMax = record?.max !== 0; const { token } = theme.useToken(); return ( {editing ? ( ) : ( children )} ); }; ================================================ FILE: client/src/modules/CrossCheck/EditableCriteriaInput.tsx ================================================ import { Form, Input, InputNumber } from 'antd'; import { FC } from 'react'; import { CriteriaTypeSelect } from './CriteriaTypeSelect'; import { EditableTableColumnsDataIndex, TaskType } from './constants'; import { CriteriaDtoTypeEnum } from '@client/api'; interface EditableCriteriaInputProps { type?: CriteriaDtoTypeEnum; dataIndex: EditableTableColumnsDataIndex; onSelectChange: (value: string) => void; } export const EditableCriteriaInput: FC = ({ dataIndex, onSelectChange, type }) => { switch (dataIndex) { case EditableTableColumnsDataIndex.Max: return type !== TaskType.Title ? ( ) : null; case EditableTableColumnsDataIndex.Type: return ( ); case EditableTableColumnsDataIndex.Text: return ( ); default: return null; } }; ================================================ FILE: client/src/modules/CrossCheck/EditableTableForCrossCheck.tsx ================================================ import { Form } from 'antd'; import type { DragEndEvent } from '@dnd-kit/core'; import { DndContext } from '@dnd-kit/core'; import { restrictToVerticalAxis } from '@dnd-kit/modifiers'; import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable'; import { Dispatch, SetStateAction, useState } from 'react'; import { EditableCellForCrossCheck } from './EditableCellForCrossCheck'; import { CriteriaDto, CriteriaDtoTypeEnum } from '@client/api'; import { CriteriaActions } from './CriteriaActions'; import { EditableTableColumnsDataIndex } from './constants'; import { DragSortTable } from './components/DragSortTable'; import { arrayMoveImmutable } from './utils/arrayMoveImmutable'; import { DragHandle } from './components/DragHandle'; interface IEditableTableProps { dataCriteria: CriteriaDto[]; setDataCriteria: Dispatch>; } export const EditableTable = ({ dataCriteria, setDataCriteria }: IEditableTableProps) => { const [form] = Form.useForm(); const [editingKey, setEditingKey] = useState(''); const [originData, setOriginData] = useState([]); const isEditing = (record: CriteriaDto) => record.key === editingKey; const edit = (record: CriteriaDto) => { setOriginData(dataCriteria); form.setFieldsValue(record); setEditingKey(record.key); }; const remove = (key: string) => { setDataCriteria(dataCriteria.filter(item => item.key !== key)); }; const save = async (key: string) => { const formData = await form.validateFields(); const newData = dataCriteria.map(criteria => (criteria.key === key ? { ...criteria, ...formData } : criteria)); setDataCriteria(newData); setEditingKey(''); }; const cancel = () => { setDataCriteria(originData); setEditingKey(''); }; const changeTaskType = async (value: string) => { const newData = dataCriteria.map(criteria => criteria.key === editingKey ? { ...criteria, type: value as CriteriaDtoTypeEnum, max: value === CriteriaDtoTypeEnum.Title ? undefined : criteria.max, } : criteria, ); setDataCriteria(newData); }; const onDragEnd = ({ active, over }: DragEndEvent) => { if (active.id !== over?.id) { setDataCriteria(previous => { const activeIndex = previous.findIndex(i => i.key === active.id); const overIndex = previous.findIndex(i => i.key === over?.id); return arrayMoveImmutable(previous, activeIndex, overIndex); }); } }; const columns = [ { title: '⇅', dataIndex: 'drag', width: 40, align: 'center' as const, render: (_: unknown, record: CriteriaDto) => , }, { title: 'Type', dataIndex: EditableTableColumnsDataIndex.Type, width: '18%', editable: true, }, { title: 'Max', dataIndex: EditableTableColumnsDataIndex.Max, width: '10%', editable: true, }, { title: 'Text', dataIndex: EditableTableColumnsDataIndex.Text, width: '52%', editable: true, }, { title: 'Actions', dataIndex: EditableTableColumnsDataIndex.Actions, width: '20%', render: (_: unknown, record: CriteriaDto) => ( ), }, ]; const mergedColumns = columns.map(col => { if (!col.editable) { return col; } return { ...col, onCell: (record: CriteriaDto) => ({ record, dataIndex: col.dataIndex, title: col.title, editing: isEditing(record), onSelectChange: changeTaskType, }), }; }); return (
i.key)} strategy={verticalListSortingStrategy}>
); }; ================================================ FILE: client/src/modules/CrossCheck/ExportJSONButton.tsx ================================================ import FileOutlined from '@ant-design/icons/FileOutlined'; import { Button } from 'antd'; import { CriteriaDto } from '@client/api'; import omit from 'lodash/omit'; import { CriteriaJSONType } from './UploadCriteriaJSON'; import { TaskType } from './constants'; interface Props { dataCriteria: CriteriaDto[]; } export function ExportJSONButton({ dataCriteria }: Props) { const transformCriteriaData = (criteria: CriteriaDto[]) => { const transformedCriteria = criteria.map(item => { let editedItem: Partial = { ...item }; if (editedItem.type === TaskType.Title) { editedItem.title = editedItem.text; delete editedItem.text; } editedItem = omit(editedItem, ['key', 'index']); return editedItem; }); return { criteria: transformedCriteria }; }; const criteriaStringify = encodeURIComponent(JSON.stringify(transformCriteriaData(dataCriteria))); const href = `data:text/json;charset=utf-8,${criteriaStringify}`; return (
); } ================================================ FILE: client/src/modules/CrossCheck/UploadCriteriaJSON.tsx ================================================ import { Button, Upload } from 'antd'; import { UploadOutlined } from '@ant-design/icons'; import { UploadChangeParam, UploadFile } from 'antd/lib/upload'; import { CriteriaDto } from '@client/api'; import { CrossCheckCriteriaType } from '@client/services/course'; import { TaskType } from './constants'; import { useMessage } from '@client/hooks'; interface IUploadCriteriaJSON { onLoad: (data: CriteriaDto[]) => void; } export type CriteriaJSONType = { type: CrossCheckCriteriaType; max?: number; text?: string; title?: string; }; export const UploadCriteriaJSON = ({ onLoad }: IUploadCriteriaJSON) => { const { message } = useMessage(); const handleChange = (info: UploadChangeParam>) => { if (info.file.status === 'done') { const fileReader = new FileReader(); fileReader.readAsText(info.file.originFileObj as Blob, 'UTF-8'); fileReader.onload = (e: Event) => { const target = e.target as Element & { result: string }; const { criteria } = JSON.parse(target.result) as { criteria: CriteriaDto[] }; const transformedCriteria = criteria?.map((item: CriteriaJSONType) => { if (item.type === TaskType.Title) { return { type: item.type, text: item.title }; } return item; }); if (!transformedCriteria?.length) { message.warning(`There is no criteria for downloading`); return; } message.success(`${info.file.name} file uploaded successfully`); onLoad(transformedCriteria as CriteriaDto[]); }; } }; return ( opts.onSuccess?.(null)} > ); }; ================================================ FILE: client/src/modules/CrossCheck/__tests__/AddCriteriaForCrossCheck.test.tsx ================================================ import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { AddCriteriaForCrossCheck } from '../AddCriteriaForCrossCheck'; import userEvent from '@testing-library/user-event'; const addCriteria = vi.fn(); describe('AddCriteriaForCrossCheck', () => { test('should match shapshot', () => { const view = render(); expect(view).toMatchSnapshot(); }); test('should render "Add New Criteria" button', () => { render(); const element = screen.getByText(/Add New Criteria/i); expect(element).toBeInTheDocument(); }); test('should call addCriteria when "Add new criteria" button was clicked', async () => { render(); const selectCriteriaType = screen.getByRole('combobox'); fireEvent.mouseDown(selectCriteriaType); const optionTitle = screen.getByTestId('Title'); fireEvent.click(optionTitle); const descriptionInput = screen.getByPlaceholderText('Add description'); fireEvent.change(descriptionInput, { target: { value: 'test' } }); const button = screen.getByRole('button', { name: /Add New Criteria/i }); fireEvent.click(button); await waitFor(() => { expect(addCriteria).toHaveBeenCalledTimes(1); }); }); test('should render textarea', () => { render(); const textarea = screen.getByPlaceholderText('Add description'); expect(textarea).toBeInTheDocument(); }); test('should change textarea value on typing', async () => { const expectedString = 'test value'; render(); const textarea = screen.getByPlaceholderText('Add description'); await userEvent.type(textarea, expectedString); expect(textarea.value).toEqual(expectedString); }); test('should select criteria', async () => { render(); const selectCriteriaType = screen.getByRole('combobox'); expect(selectCriteriaType).toBeInTheDocument(); fireEvent.mouseDown(selectCriteriaType); const element = screen.getByRole('option', { name: 'Subtask' }); expect(element).toBeInTheDocument(); }); test('input with adding max score renders only after user select criteria type Subtask', async () => { render(); const selectCriteriaType = screen.getByRole('combobox'); const inputMaxScore = screen.queryByLabelText('Add Max Score'); expect(inputMaxScore).not.toBeInTheDocument(); fireEvent.mouseDown(selectCriteriaType); const optionSubtask = screen.getByTestId('Subtask'); fireEvent.click(optionSubtask); expect(screen.getByText('Add Max Score')).toBeInTheDocument(); }); }); ================================================ FILE: client/src/modules/CrossCheck/__tests__/ExportJSONButton.test.tsx ================================================ import { render, screen } from '@testing-library/react'; import { CriteriaDto, CriteriaDtoTypeEnum } from '@client/api'; import { ExportJSONButton } from '../ExportJSONButton'; const dataCriteria = [ { key: '0', index: 0, type: CriteriaDtoTypeEnum.Title, text: 'Its title', }, { key: '1', index: 1, type: CriteriaDtoTypeEnum.Subtask, text: 'Its subtask', max: 10, }, { key: '2', index: 2, type: CriteriaDtoTypeEnum.Penalty, text: 'Its penalty', max: -5, }, ] as CriteriaDto[]; describe('ExportJSONButton', () => { test('contains following text', () => { render(); expect(screen.getByText('Export JSON')).toBeInTheDocument(); }); test('should render correctly with empty dataCriteria', () => { render(); const link = screen.getByRole('link'); expect(link).toHaveAttribute('download', 'crossCheckCriteria.json'); }); }); ================================================ FILE: client/src/modules/CrossCheck/__tests__/UploadCriteriaJSON.test.tsx ================================================ import { UploadCriteriaJSON } from '../UploadCriteriaJSON'; import { fireEvent, render, screen, waitFor } from '@testing-library/react'; const onLoad = vi.fn(); describe('UploadCriteriaJSON', () => { test('contains following element', () => { render(); const element = screen.getByText('Click to Upload Criteria (JSON)'); expect(element).toBeInTheDocument(); }); test('upload file', async () => { render(); global.URL.createObjectURL = vi.fn(); const file = new File(['{test: 1}'], 'test.json', { type: 'application/json' }); const input = screen.getByTestId('uploader') as HTMLInputElement; fireEvent.change(input, { target: { files: [file] } }); await waitFor(() => { expect(input.files).toHaveLength(1); }); }); }); ================================================ FILE: client/src/modules/CrossCheck/__tests__/__snapshots__/AddCriteriaForCrossCheck.test.tsx.snap ================================================ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`AddCriteriaForCrossCheck > should match shapshot 1`] = ` { "asFragment": [Function], "baseElement":
Select type