[META] Drop version numbers from api routes

This commit is contained in:
Santeri Rajamäki
2026-04-14 14:41:52 +03:00
parent 539ed1e3b8
commit 54f363be6e
21 changed files with 73 additions and 73 deletions
+9 -9
View File
@@ -92,17 +92,17 @@ const clickAway = () => {
const getCourseSearchHooksForSingleYear = code => { const getCourseSearchHooksForSingleYear = code => {
cy.intercept({ cy.intercept({
pathname: '/api/v2/coursesmulti', pathname: '/api/coursesmulti',
query: { code }, query: { code },
}).as('courses') }).as('courses')
cy.intercept({ cy.intercept({
pathname: '/api/v3/courseyearlystats', pathname: '/api/courseyearlystats',
query: { 'codes[]': code }, query: { 'codes[]': code },
}).as('yearlystats') }).as('yearlystats')
cy.intercept({ cy.intercept({
pathname: '/api/v3/populationstatisticsbycourse', pathname: '/api/populationstatisticsbycourse',
}).as('coursestats') }).as('coursestats')
} }
@@ -263,17 +263,17 @@ describe('Basic user', () => {
it('Searching multiple courses having substitution mappings shows course statistics', () => { it('Searching multiple courses having substitution mappings shows course statistics', () => {
cy.intercept({ cy.intercept({
pathname: '/api/v2/coursesmulti', pathname: '/api/coursesmulti',
query: { code: 'TKT' }, query: { code: 'TKT' },
}).as('courses') }).as('courses')
cy.intercept({ cy.intercept({
pathname: '/api/v3/courseyearlystats', pathname: '/api/courseyearlystats',
query: { 'codes[]': 'TKT20001', 'codes[]': 'TKT10002' }, query: { 'codes[]': 'TKT20001', 'codes[]': 'TKT10002' },
}).as('yearlystats') }).as('yearlystats')
cy.intercept({ cy.intercept({
pathname: '/api/v3/populationstatisticsbycourse', pathname: '/api/populationstatisticsbycourse',
}).as('coursestats') }).as('coursestats')
cy.cs('select-multiple-courses-toggle').should('not.have.class', 'Mui-checked') cy.cs('select-multiple-courses-toggle').should('not.have.class', 'Mui-checked')
@@ -309,17 +309,17 @@ describe('Basic user', () => {
it('On searches with multiple courses, has correct links on the Course tab', () => { it('On searches with multiple courses, has correct links on the Course tab', () => {
cy.intercept({ cy.intercept({
pathname: '/api/v2/coursesmulti', pathname: '/api/coursesmulti',
query: { code: 'BSCS' }, query: { code: 'BSCS' },
}).as('courses') }).as('courses')
cy.intercept({ cy.intercept({
pathname: '/api/v3/courseyearlystats', pathname: '/api/courseyearlystats',
query: { 'codes[]': 'BSCS1003', 'codes[]': 'BSCS1001' }, query: { 'codes[]': 'BSCS1003', 'codes[]': 'BSCS1001' },
}).as('yearlystats') }).as('yearlystats')
cy.intercept({ cy.intercept({
pathname: '/api/v3/populationstatisticsbycourse', pathname: '/api/populationstatisticsbycourse',
}).as('coursestats') }).as('coursestats')
cy.cs('select-multiple-courses-toggle').should('not.have.class', 'Mui-checked') cy.cs('select-multiple-courses-toggle').should('not.have.class', 'Mui-checked')
+1 -1
View File
@@ -269,7 +269,7 @@ describe('Degree programme overview', () => {
beforeEach(() => { beforeEach(() => {
cy.intercept( cy.intercept(
'GET', 'GET',
'api/v2/studyprogrammes/KH50_001/studytrackstats?special_groups=SPECIAL_INCLUDED&combined_programme=' 'api/studyprogrammes/KH50_001/studytrackstats?special_groups=SPECIAL_INCLUDED&combined_programme='
).as('stQuery') ).as('stQuery')
cy.init('/study-programme') cy.init('/study-programme')
cy.contains('a', 'Matemaattisten tieteiden kandiohjelma').click() cy.contains('a', 'Matemaattisten tieteiden kandiohjelma').click()
+3 -3
View File
@@ -27,8 +27,8 @@ describe('Population statistics tests', () => {
describe('When using basic user', () => { describe('When using basic user', () => {
beforeEach(() => { beforeEach(() => {
cy.init('/populations') cy.init('/populations')
cy.intercept('/api/v3/populationstatistics/studyprogrammes').as('studyprogrammes') cy.intercept('/api/populationstatistics/studyprogrammes').as('studyprogrammes')
cy.intercept('/api/v2/studyprogrammes/**/studytracks').as('studytracks') cy.intercept('/api/studyprogrammes/**/studytracks').as('studytracks')
}) })
describe('Population search', () => { describe('Population search', () => {
@@ -251,7 +251,7 @@ describe('Population statistics tests', () => {
it('Is displayed and link to individual course stats page works', () => { it('Is displayed and link to individual course stats page works', () => {
cy.visit(pathToMathBSc2020) cy.visit(pathToMathBSc2020)
cy.contains('Courses of class').click() cy.contains('Courses of class').click()
cy.intercept('/api/v3/courseyearlystats**').as('coursePage') cy.intercept('/api/courseyearlystats**').as('coursePage')
cy.cs('toggle-group-module-MAT110').click() cy.cs('toggle-group-module-MAT110').click()
cy.contains('td', 'MAT11001').siblings().find('[data-testid="NorthEastIcon"]').click() cy.contains('td', 'MAT11001').siblings().find('[data-testid="NorthEastIcon"]').click()
cy.wait('@coursePage') cy.wait('@coursePage')
+1 -1
View File
@@ -72,7 +72,7 @@ describe('Users tests', () => {
}) })
it("only the mocked user's programmes are visible", () => { it("only the mocked user's programmes are visible", () => {
cy.intercept('/api/v3/populationstatistics/studyprogrammes').as('studyprogrammes') cy.intercept('/api/populationstatistics/studyprogrammes').as('studyprogrammes')
cy.visit('/populations') cy.visit('/populations')
cy.wait('@studyprogrammes').its('response.statusCode').should('be.oneOf', [200, 304]) cy.wait('@studyprogrammes').its('response.statusCode').should('be.oneOf', [200, 304])
+1 -1
View File
@@ -91,6 +91,6 @@ All things listed below should work automatically, if new combinations are done.
## Study guidance groups ## Study guidance groups
- Backend: student data is fetched by bachelor degree programme code in `/v3/populationstatisticsbystudentnumbers`. Separation from code 'KHxx_xxx+MHxx_xxx' is done in backend. - Backend: student data is fetched by bachelor degree programme code in `/populationstatisticsbystudentnumbers`. Separation from code 'KHxx_xxx+MHxx_xxx' is done in backend.
- Frontend: things are done in the same way as with class stats. - Frontend: things are done in the same way as with class stats.
- **Need manual work**: `./oodikone/services/frontend/src/redux/elementdetails.js` add combined programme codes (both bachelor and master) to `combinedProgrammeCodes` and modify `combinedOptions` list accordingly in `useFilterAndFormattedElementDetails()` function. - **Need manual work**: `./oodikone/services/frontend/src/redux/elementdetails.js` add combined programme codes (both bachelor and master) to `combinedProgrammeCodes` and modify `combinedOptions` list accordingly in `useFilterAndFormattedElementDetails()` function.
+2 -2
View File
@@ -52,8 +52,8 @@ const routes = (app: Express, url: string) => {
app.use(`${url}/semesters/codes`, semesters) app.use(`${url}/semesters/codes`, semesters)
app.use(url, tags) app.use(url, tags)
app.use(url, programmeModules) app.use(url, programmeModules)
app.use(`${url}/v3/course-exclusions`, courseExclusions) app.use(`${url}/course-exclusions`, courseExclusions)
app.use(`${url}/v2/studyprogrammes`, studyProgramme) app.use(`${url}/studyprogrammes`, studyProgramme)
app.use(`${url}/programme-criteria`, studyProgrammeCriteria) app.use(`${url}/programme-criteria`, studyProgrammeCriteria)
app.use(`${url}/openunisearch`, auth.roles(['openUniSearch']), customOpenUniSearch) app.use(`${url}/openunisearch`, auth.roles(['openUniSearch']), customOpenUniSearch)
app.use(`${url}/changelog`, changelog) app.use(`${url}/changelog`, changelog)
+2 -2
View File
@@ -22,7 +22,7 @@ import {
const router = Router() const router = Router()
router.get<never, CanError<CoursesMultiResBody>, CoursesMultiReqBody, CoursesMultiQuery>( router.get<never, CanError<CoursesMultiResBody>, CoursesMultiReqBody, CoursesMultiQuery>(
'/v2/coursesmulti', '/coursesmulti',
async (req, res) => { async (req, res) => {
const { name, code, includeSpecial } = req.query const { name, code, includeSpecial } = req.query
if (!(validateParamLength(name, 5) || validateParamLength(code, 2))) { if (!(validateParamLength(name, 5) || validateParamLength(code, 2))) {
@@ -54,7 +54,7 @@ router.get<never, CanError<CoursesMultiResBody>, CoursesMultiReqBody, CoursesMul
type CourseYearlyStatsResBody = Awaited<ReturnType<typeof getCourseYearlyStats>> type CourseYearlyStatsResBody = Awaited<ReturnType<typeof getCourseYearlyStats>>
router.get<never, CanError<CourseYearlyStatsResBody>, CourseYearlyStatsReqBody, CourseYearlyStatsQuery>( router.get<never, CanError<CourseYearlyStatsResBody>, CourseYearlyStatsReqBody, CourseYearlyStatsQuery>(
'/v3/courseyearlystats', '/courseyearlystats',
async (req, res) => { async (req, res) => {
const { codes, combineSubstitutions, separate } = req.query const { codes, combineSubstitutions, separate } = req.query
+5 -5
View File
@@ -98,7 +98,7 @@ const obfuscateStuff = ({
} }
router.get<never, CanError<PopulationstatisticsResBody>, PopulationstatisticsReqBody, PopulationstatisticsQuery>( router.get<never, CanError<PopulationstatisticsResBody>, PopulationstatisticsReqBody, PopulationstatisticsQuery>(
'/v3/populationstatistics', '/populationstatistics',
async (req, res) => { async (req, res) => {
const { id: userId, roles: userRoles, programmeRights: userProgrammeRights, studentsUserCanAccess } = req.user const { id: userId, roles: userRoles, programmeRights: userProgrammeRights, studentsUserCanAccess } = req.user
const { programme, combinedProgramme } = req.query const { programme, combinedProgramme } = req.query
@@ -168,7 +168,7 @@ router.get<
CanError<PopulationstatisticsbycourseResBody>, CanError<PopulationstatisticsbycourseResBody>,
PopulationstatisticsbycourseReqBody, PopulationstatisticsbycourseReqBody,
PopulationstatisticsbycourseParams PopulationstatisticsbycourseParams
>('/v3/populationstatisticsbycourse', async (req, res) => { >('/populationstatisticsbycourse', async (req, res) => {
const { const {
id: userId, id: userId,
roles: userRoles, roles: userRoles,
@@ -218,7 +218,7 @@ router.get<
// Used in custom population and single study guidance groups // Used in custom population and single study guidance groups
router.post<never, GetCustomPopulationResBody, CustomPopulationQuery>( router.post<never, GetCustomPopulationResBody, CustomPopulationQuery>(
'/v3/populationstatisticsbystudentnumbers', '/populationstatisticsbystudentnumbers',
async (req, res) => { async (req, res) => {
const { studentNumbers, tags } = req.body const { studentNumbers, tags } = req.body
const { id: userId, roles, studentsUserCanAccess } = req.user const { id: userId, roles, studentsUserCanAccess } = req.user
@@ -248,7 +248,7 @@ router.post<never, GetCustomPopulationResBody, CustomPopulationQuery>(
) )
router.get<never, PopulationstatisticsStudyprogrammesResBody>( router.get<never, PopulationstatisticsStudyprogrammesResBody>(
'/v3/populationstatistics/studyprogrammes', '/populationstatistics/studyprogrammes',
async (req, res) => { async (req, res) => {
const { roles, programmeRights } = req.user const { roles, programmeRights } = req.user
const programmes = await getDegreeProgrammesOfOrganization(rootOrgId, false) const programmes = await getDegreeProgrammesOfOrganization(rootOrgId, false)
@@ -274,7 +274,7 @@ router.get<
PopulationstatisticsMaxYearsToCreatePopulationFormResBody, PopulationstatisticsMaxYearsToCreatePopulationFormResBody,
never, never,
PopulationstatisticsMaxYearsToCreatePopulationFormQuery PopulationstatisticsMaxYearsToCreatePopulationFormQuery
>('/v3/populationstatistics/maxYearsToCreatePopulationFrom', async (req, res) => { >('/populationstatistics/maxYearsToCreatePopulationFrom', async (req, res) => {
const courseCodes = JSON.parse(req.query.courseCodes) as string[] const courseCodes = JSON.parse(req.query.courseCodes) as string[]
const maxYearsToCreatePopulationFromOpen = await maxYearsToCreatePopulationFrom(courseCodes, Unification.OPEN) const maxYearsToCreatePopulationFromOpen = await maxYearsToCreatePopulationFrom(courseCodes, Unification.OPEN)
const maxYearsToCreatePopulationFromUni = await maxYearsToCreatePopulationFrom(courseCodes, Unification.REGULAR) const maxYearsToCreatePopulationFromUni = await maxYearsToCreatePopulationFrom(courseCodes, Unification.REGULAR)
@@ -4,13 +4,13 @@ import { getCoursesAndModules, getCurriculumOptions } from '../services/programm
const router = Router() const router = Router()
router.get('/v3/curriculum-options/:code', async (req: Request, res: Response) => { router.get('/curriculum-options/:code', async (req: Request, res: Response) => {
const { code } = req.params const { code } = req.params
const result = await getCurriculumOptions(code) const result = await getCurriculumOptions(code)
res.json(result) res.json(result)
}) })
router.get('/v3/curriculum/:code/:periodIds', async (req: Request, res: Response) => { router.get('/curriculum/:code/:periodIds', async (req: Request, res: Response) => {
const { code, periodIds } = req.params const { code, periodIds } = req.params
const { defaultProgrammeCourses, secondProgrammeCourses } = await getCoursesAndModules(code, periodIds) const { defaultProgrammeCourses, secondProgrammeCourses } = await getCoursesAndModules(code, periodIds)
+4 -4
View File
@@ -26,14 +26,14 @@ const refreshProgrammesByList = (programmeCodes: string[]) => {
return 'Added jobs for refreshing programme' return 'Added jobs for refreshing programme'
} }
router.get('/update/v2/meta', async (_req: Request, res: Response) => { router.get('/update/meta', async (_req: Request, res: Response) => {
const response = await updateSISMetadata() const response = await updateSISMetadata()
if (response) { if (response) {
res.status(200).json('Update SIS meta scheduled') res.status(200).json('Update SIS meta scheduled')
} }
}) })
router.get('/update/v2/students', async (_req: Request, res: Response) => { router.get('/update/students', async (_req: Request, res: Response) => {
const response = await updateSISStudents() const response = await updateSISStudents()
if (response) { if (response) {
res.status(200).json('Update SIS students scheduled') res.status(200).json('Update SIS students scheduled')
@@ -47,7 +47,7 @@ interface PostCustomListRequest extends Request {
} }
} }
router.post('/update/v2/customlist/:type', async (req: PostCustomListRequest, res: Response) => { router.post('/update/customlist/:type', async (req: PostCustomListRequest, res: Response) => {
const { type } = req.params const { type } = req.params
const list = req.body const list = req.body
const typeToJob = { const typeToJob = {
@@ -65,7 +65,7 @@ router.post('/update/v2/customlist/:type', async (req: PostCustomListRequest, re
} }
}) })
router.get('/update/v2/programmes', async (_req: Request, res: Response) => { router.get('/update/programmes', async (_req: Request, res: Response) => {
const response = await updateSISProgrammes() const response = await updateSISProgrammes()
if (response) { if (response) {
res.status(200).json('Update SIS programmes scheduled') res.status(200).json('Update SIS programmes scheduled')
@@ -8,39 +8,39 @@ const client = axios.create({ baseURL: SIS_UPDATER_URL })
const params = { params: { token: SECRET_TOKEN } } const params = { params: { token: SECRET_TOKEN } }
export const abortUpdate = async () => { export const abortUpdate = async () => {
const response = await client.get('/v1/abort', params) const response = await client.get('/abort', params)
return response.data?.message return response.data?.message
} }
export const updateSISCoursesByCourseCode = async (coursecodes: string[]) => { export const updateSISCoursesByCourseCode = async (coursecodes: string[]) => {
const data = { coursecodes } const data = { coursecodes }
const response = await client.post('/v1/courses', data, params) const response = await client.post('/courses', data, params)
return response.data return response.data
} }
export const updateSISMetadata = async () => { export const updateSISMetadata = async () => {
const response = await client.get('/v1/meta', params) const response = await client.get('/meta', params)
return response.data return response.data
} }
export const updateSISProgrammes = async () => { export const updateSISProgrammes = async () => {
const response = await client.get('/v1/programmes', params) const response = await client.get('/programmes', params)
return response.data return response.data
} }
export const updateSISRedisCache = async () => { export const updateSISRedisCache = async () => {
const response = await client.get('/v1/rediscache', params) const response = await client.get('/rediscache', params)
return response.data return response.data
} }
export const updateSISStudents = async () => { export const updateSISStudents = async () => {
const response = await client.get('/v1/students', params) const response = await client.get('/students', params)
return response.data return response.data
} }
export const updateSISStudentsByStudentNumber = async (studentnumbers: string[]) => { export const updateSISStudentsByStudentNumber = async (studentnumbers: string[]) => {
const data = { studentnumbers } const data = { studentnumbers }
const response = await client.post('/v1/students', data, params) const response = await client.post('/students', data, params)
return response.data return response.data
} }
@@ -54,6 +54,6 @@ export const updateSISStudyPlans = async (days: number) => {
limit: 1000, limit: 1000,
}) })
const studentnumbers = result.map(r => r.studentnumber) const studentnumbers = result.map(r => r.studentnumber)
const response = await client.post('v1/studyplans', { studentnumbers }, params) const response = await client.post('studyplans', { studentnumbers }, params)
return response.data return response.data
} }
+1 -1
View File
@@ -24,7 +24,7 @@ const populationUrl = (
semesters: ('SPRING' | 'FALL')[] = [], semesters: ('SPRING' | 'FALL')[] = [],
students: string[] = [] students: string[] = []
) => { ) => {
return `/v3/populationstatistics?${programme && 'programme=' + programme}${years.map(year => '&years=' + year).join('')}${semesters.map(sem => '&semesters=' + sem).join('')}${students.map(s => '&studentStatuses=' + s).join('')}` return `/populationstatistics?${programme && 'programme=' + programme}${years.map(year => '&years=' + year).join('')}${semesters.map(sem => '&semesters=' + sem).join('')}${students.map(s => '&studentStatuses=' + s).join('')}`
} }
void describe('Population statistics', async () => { void describe('Population statistics', async () => {
@@ -28,7 +28,7 @@ export const StudentInfoCard = ({ student }: { student: StudentPageStudent }) =>
) )
const updateStudent = async () => { const updateStudent = async () => {
await callApi('/updater/update/v2/customlist/students', 'post', [student.studentNumber]) await callApi('/updater/update/customlist/students', 'post', [student.studentNumber])
} }
return ( return (
@@ -37,11 +37,11 @@ export const Updater = () => {
} }
} }
const updateSISMeta = () => void apiCall('/updater/update/v2/meta') const updateSISMeta = () => void apiCall('/updater/update/meta')
const updateSISStudents = () => void apiCall('/updater/update/v2/students') const updateSISStudents = () => void apiCall('/updater/update/students')
const updateSISProgrammes = () => void apiCall('/updater/update/v2/programmes') const updateSISProgrammes = () => void apiCall('/updater/update/programmes')
const updateSISCustomList = () => const updateSISCustomList = () =>
void apiCall(`/updater/update/v2/customlist/${type}`, 'post', customList.trim().split('\n')) void apiCall(`/updater/update/customlist/${type}`, 'post', customList.trim().split('\n'))
const refreshTeacherLeaderboardForCurrentAndPreviousYear = () => const refreshTeacherLeaderboardForCurrentAndPreviousYear = () =>
void apiCall('/updater/refresh-teacher-leaderboard', 'post') void apiCall('/updater/refresh-teacher-leaderboard', 'post')
const abortSisUpdater = () => void apiCall('/updater/abort') const abortSisUpdater = () => void apiCall('/updater/abort')
@@ -6,14 +6,14 @@ const courseExclusionsApi = RTKApi.injectEndpoints({
endpoints: builder => ({ endpoints: builder => ({
setCourseExclusion: builder.mutation<ExcludedCoursesResBody, ExcludedCoursesReqBody>({ setCourseExclusion: builder.mutation<ExcludedCoursesResBody, ExcludedCoursesReqBody>({
query: ({ courseCodes, curriculumVersion, programmeCode }) => ({ query: ({ courseCodes, curriculumVersion, programmeCode }) => ({
url: `/v3/course-exclusions/`, url: `/course-exclusions/`,
method: 'POST', method: 'POST',
body: { courseCodes, curriculumVersion, programmeCode }, body: { courseCodes, curriculumVersion, programmeCode },
}), }),
}), }),
removeCourseExclusion: builder.mutation<ExcludedCoursesResBody, ExcludedCoursesReqBody>({ removeCourseExclusion: builder.mutation<ExcludedCoursesResBody, ExcludedCoursesReqBody>({
query: ({ courseCodes, curriculumVersion, programmeCode }) => ({ query: ({ courseCodes, curriculumVersion, programmeCode }) => ({
url: `/v3/course-exclusions/`, url: `/course-exclusions/`,
method: 'DELETE', method: 'DELETE',
body: { courseCodes, curriculumVersion, programmeCode }, body: { courseCodes, curriculumVersion, programmeCode },
}), }),
+1 -1
View File
@@ -5,7 +5,7 @@ const courseSearchApi = RTKApi.injectEndpoints({
endpoints: builder => ({ endpoints: builder => ({
getCourseSearchResult: builder.query<GetCourseSearchResultResponse, GetCourseSearchResultRequest>({ getCourseSearchResult: builder.query<GetCourseSearchResultResponse, GetCourseSearchResultRequest>({
query: ({ name, code, includeSpecial }) => ({ query: ({ name, code, includeSpecial }) => ({
url: '/v2/coursesmulti', url: '/coursesmulti',
params: { name, code, includeSpecial }, params: { name, code, includeSpecial },
}), }),
}), }),
+1 -1
View File
@@ -13,7 +13,7 @@ const courseStatsApi = RTKApi.injectEndpoints({
separate?: boolean separate?: boolean
combineSubstitutions?: boolean combineSubstitutions?: boolean
}) => ({ }) => ({
url: '/v3/courseyearlystats', url: '/courseyearlystats',
params: { codes, separate, combineSubstitutions }, params: { codes, separate, combineSubstitutions },
}), }),
transformResponse: ( transformResponse: (
+2 -2
View File
@@ -4,10 +4,10 @@ import { CurriculumOption, CurriculumDetails } from '@oodikone/shared/types'
const curriculumsApi = RTKApi.injectEndpoints({ const curriculumsApi = RTKApi.injectEndpoints({
endpoints: builder => ({ endpoints: builder => ({
getCurriculumOptions: builder.query<CurriculumOption[], { code: string }>({ getCurriculumOptions: builder.query<CurriculumOption[], { code: string }>({
query: ({ code }) => `/v3/curriculum-options/${code}`, query: ({ code }) => `/curriculum-options/${code}`,
}), }),
getCurriculums: builder.query<CurriculumDetails, { code: string; periodIds: string[] }>({ getCurriculums: builder.query<CurriculumDetails, { code: string; periodIds: string[] }>({
query: ({ code, periodIds }) => `/v3/curriculum/${code}/${periodIds.join(',')}`, query: ({ code, periodIds }) => `/curriculum/${code}/${periodIds.join(',')}`,
}), }),
}), }),
}) })
+5 -5
View File
@@ -16,7 +16,7 @@ const populationApi = RTKApi.injectEndpoints({
endpoints: builder => ({ endpoints: builder => ({
getPopulationStatistics: builder.query<Output<PopulationstatisticsResBody>, PopulationQuery>({ getPopulationStatistics: builder.query<Output<PopulationstatisticsResBody>, PopulationQuery>({
query: params => ({ query: params => ({
url: '/v3/populationstatistics/', url: '/populationstatistics/',
method: 'GET', method: 'GET',
params, params,
}), }),
@@ -24,7 +24,7 @@ const populationApi = RTKApi.injectEndpoints({
}), }),
getCustomPopulation: builder.query<Output<GetCustomPopulationResBody>, CustomPopulationQuery>({ getCustomPopulation: builder.query<Output<GetCustomPopulationResBody>, CustomPopulationQuery>({
query: ({ studentNumbers, tags }) => ({ query: ({ studentNumbers, tags }) => ({
url: '/v3/populationstatisticsbystudentnumbers', url: '/populationstatisticsbystudentnumbers',
method: 'POST', method: 'POST',
body: { body: {
studentNumbers, studentNumbers,
@@ -38,7 +38,7 @@ const populationApi = RTKApi.injectEndpoints({
PopulationstatisticsbycourseParams PopulationstatisticsbycourseParams
>({ >({
query: ({ coursecodes, from, to, separate, unifyCourses }) => ({ query: ({ coursecodes, from, to, separate, unifyCourses }) => ({
url: '/v3/populationstatisticsbycourse', url: '/populationstatisticsbycourse',
params: { coursecodes, from, to, separate, unifyCourses }, params: { coursecodes, from, to, separate, unifyCourses },
}), }),
transformResponse: formatPopulationData<PopulationstatisticsbycourseResBody>, transformResponse: formatPopulationData<PopulationstatisticsbycourseResBody>,
@@ -48,12 +48,12 @@ const populationApi = RTKApi.injectEndpoints({
PopulationstatisticsMaxYearsToCreatePopulationFormQuery PopulationstatisticsMaxYearsToCreatePopulationFormQuery
>({ >({
query: ({ courseCodes }) => ({ query: ({ courseCodes }) => ({
url: '/v3/populationstatistics/maxYearsToCreatePopulationFrom', url: '/populationstatistics/maxYearsToCreatePopulationFrom',
params: { courseCodes }, params: { courseCodes },
}), }),
}), }),
getProgrammes: builder.query<PopulationstatisticsStudyprogrammesResBody, void>({ getProgrammes: builder.query<PopulationstatisticsStudyprogrammesResBody, void>({
query: () => '/v3/populationstatistics/studyprogrammes', query: () => '/populationstatistics/studyprogrammes',
keepUnusedDataFor: 60 * 60, keepUnusedDataFor: 60 * 60,
}), }),
}), }),
@@ -20,20 +20,20 @@ const studyProgrammeApi = RTKApi.injectEndpoints({
{ id: string; yearType: YearType; specialGroups: SpecialGroups; combinedProgramme: string } { id: string; yearType: YearType; specialGroups: SpecialGroups; combinedProgramme: string }
>({ >({
query: ({ id, yearType, specialGroups, combinedProgramme }) => query: ({ id, yearType, specialGroups, combinedProgramme }) =>
`/v2/studyprogrammes/${id}/basicstats?year_type=${yearType}&special_groups=${specialGroups}&combined_programme=${combinedProgramme}`, `/studyprogrammes/${id}/basicstats?year_type=${yearType}&special_groups=${specialGroups}&combined_programme=${combinedProgramme}`,
}), }),
getColorizedTableCourseStats: builder.query< getColorizedTableCourseStats: builder.query<
any, // TODO: Type any, // TODO: Type
{ id: string } { id: string }
>({ >({
query: ({ id }) => `/v2/studyprogrammes/${id}/colorizedtablecoursestats`, query: ({ id }) => `/studyprogrammes/${id}/colorizedtablecoursestats`,
}), }),
getCreditStats: builder.query< getCreditStats: builder.query<
CreditStatsPayload, CreditStatsPayload,
{ codes: string[]; specialGroups: SpecialGroups; yearType: YearType } { codes: string[]; specialGroups: SpecialGroups; yearType: YearType }
>({ >({
query: ({ codes, specialGroups, yearType }) => query: ({ codes, specialGroups, yearType }) =>
`/v2/studyprogrammes/creditstats?codes=${JSON.stringify( `/studyprogrammes/creditstats?codes=${JSON.stringify(
codes codes
)}&yearType=${yearType}&specialGroups=${specialGroups}`, )}&yearType=${yearType}&specialGroups=${specialGroups}`,
}), }),
@@ -42,38 +42,38 @@ const studyProgrammeApi = RTKApi.injectEndpoints({
{ id: string; yearType: YearType; specialGroups: SpecialGroups; combinedProgramme: string } { id: string; yearType: YearType; specialGroups: SpecialGroups; combinedProgramme: string }
>({ >({
query: ({ id, yearType, specialGroups, combinedProgramme }) => query: ({ id, yearType, specialGroups, combinedProgramme }) =>
`/v2/studyprogrammes/${id}/graduationstats?year_type=${yearType}&special_groups=${specialGroups}&combined_programme=${combinedProgramme}`, `/studyprogrammes/${id}/graduationstats?year_type=${yearType}&special_groups=${specialGroups}&combined_programme=${combinedProgramme}`,
}), }),
getStudyTrackStats: builder.query< getStudyTrackStats: builder.query<
StudyTrackStats, StudyTrackStats,
{ id: string; specialGroups: SpecialGroups; combinedProgramme: string } { id: string; specialGroups: SpecialGroups; combinedProgramme: string }
>({ >({
query: ({ id, specialGroups, combinedProgramme }) => query: ({ id, specialGroups, combinedProgramme }) =>
`/v2/studyprogrammes/${id}/studytrackstats?special_groups=${specialGroups}&combined_programme=${combinedProgramme}`, `/studyprogrammes/${id}/studytrackstats?special_groups=${specialGroups}&combined_programme=${combinedProgramme}`,
}), }),
getProgrammeCoursesStats: builder.query< getProgrammeCoursesStats: builder.query<
StudyProgrammeCourse[], StudyProgrammeCourse[],
{ id: string; yearType: YearType; combinedProgramme: string } { id: string; yearType: YearType; combinedProgramme: string }
>({ >({
query: ({ id, yearType, combinedProgramme }) => query: ({ id, yearType, combinedProgramme }) =>
`/v2/studyprogrammes/${id}/coursestats?yearType=${yearType}&combinedProgramme=${combinedProgramme}`, `/studyprogrammes/${id}/coursestats?yearType=${yearType}&combinedProgramme=${combinedProgramme}`,
}), }),
getStudyTracks: builder.query<Record<string, Name | string>, { id: string }>({ getStudyTracks: builder.query<Record<string, Name | string>, { id: string }>({
query: ({ id }) => `/v2/studyprogrammes/${id}/studytracks`, query: ({ id }) => `/studyprogrammes/${id}/studytracks`,
}), }),
updateBasicView: builder.query< updateBasicView: builder.query<
any, // TODO: Type any, // TODO: Type
{ id: string; combinedProgramme: string } { id: string; combinedProgramme: string }
>({ >({
query: ({ id, combinedProgramme }) => query: ({ id, combinedProgramme }) =>
`/v2/studyprogrammes/${id}/update_basicview?combined_programme=${combinedProgramme}`, `/studyprogrammes/${id}/update_basicview?combined_programme=${combinedProgramme}`,
}), }),
updateStudyTrackView: builder.query< updateStudyTrackView: builder.query<
any, // TODO: Type any, // TODO: Type
{ id: string; combinedProgramme: string } { id: string; combinedProgramme: string }
>({ >({
query: ({ id, combinedProgramme }) => query: ({ id, combinedProgramme }) =>
`/v2/studyprogrammes/${id}/update_studytrackview?combined_programme=${combinedProgramme}`, `/studyprogrammes/${id}/update_studytrackview?combined_programme=${combinedProgramme}`,
}), }),
}), }),
overrideExisting: false, overrideExisting: false,
+9 -9
View File
@@ -42,7 +42,7 @@ const app = express()
app.use(express.json()) app.use(express.json())
app.use(message) app.use(message)
app.get('/v1/healthcheck', async (_, res) => { app.get('/healthcheck', async (_, res) => {
const latestMessage = await redisClient.get(REDIS_LATEST_MESSAGE_RECEIVED) const latestMessage = await redisClient.get(REDIS_LATEST_MESSAGE_RECEIVED)
const threshold = new Date().getTime() - 1000 * 60 * 60 * 6 // 6 hours ago const threshold = new Date().getTime() - 1000 * 60 * 60 * 6 // 6 hours ago
if (!latestMessage || new Date(latestMessage).getTime() < threshold) { if (!latestMessage || new Date(latestMessage).getTime() < threshold) {
@@ -53,20 +53,20 @@ app.get('/v1/healthcheck', async (_, res) => {
app.use(auth) app.use(auth)
app.get('/v1/meta', async (_, res) => { app.get('/meta', async (_, res) => {
await scheduleMeta() await scheduleMeta()
res.locals.msg('Scheduled meta') res.locals.msg('Scheduled meta')
}) })
app.get('/v1/students', async (_, res) => { app.get('/students', async (_, res) => {
await scheduleStudents() await scheduleStudents()
logger.info('Scheduled students') logger.info('Scheduled students')
res.locals.msg('Scheduled students') res.locals.msg('Scheduled students')
}) })
app.post('/v1/studyplans', async (req, res) => { app.post('/studyplans', async (req, res) => {
const { studentnumbers } = req.body const { studentnumbers } = req.body
const msg = `Scheduling update of ${studentnumbers.length} students whose studyplan has not been updated recently` const msg = `Scheduling update of ${studentnumbers.length} students whose studyplan has not been updated recently`
logger.info(msg) logger.info(msg)
@@ -75,14 +75,14 @@ app.post('/v1/studyplans', async (req, res) => {
res.locals.msg('Shceduled studyplans update') res.locals.msg('Shceduled studyplans update')
}) })
app.get('/v1/programmes', async (_, res) => { app.get('/programmes', async (_, res) => {
await scheduleProgrammes() await scheduleProgrammes()
logger.info('Scheduled programmes') logger.info('Scheduled programmes')
res.locals.msg('Scheduled programmes') res.locals.msg('Scheduled programmes')
}) })
app.post('/v1/students', async (req, res) => { app.post('/students', async (req, res) => {
const studentnumbers = req.body.studentnumbers.map(n => (n[0] === '0' ? n : `0${n}`)) const studentnumbers = req.body.studentnumbers.map(n => (n[0] === '0' ? n : `0${n}`))
logger.info(`Scheduling ${studentnumbers.length} custom studentnumbers`) logger.info(`Scheduling ${studentnumbers.length} custom studentnumbers`)
@@ -91,13 +91,13 @@ app.post('/v1/students', async (req, res) => {
res.locals.msg('Scheduled studentnumbers') res.locals.msg('Scheduled studentnumbers')
}) })
app.get('/v1/rediscache', async (req, res) => { app.get('/rediscache', async (req, res) => {
await queue.add('reload_redis') await queue.add('reload_redis')
logger.info('Scheduled redis cache reloading') logger.info('Scheduled redis cache reloading')
res.locals.msg('Scheduled redis cache reloading') res.locals.msg('Scheduled redis cache reloading')
}) })
app.get('/v1/abort', async (req, res) => { app.get('/abort', async (req, res) => {
const jobCountsBeforeDrain = await queue.getJobCounts() const jobCountsBeforeDrain = await queue.getJobCounts()
await queue.drain() await queue.drain()
const jobCountsAfterDrain = await queue.getJobCounts() const jobCountsAfterDrain = await queue.getJobCounts()
@@ -115,7 +115,7 @@ app.get('/v1/abort', async (req, res) => {
res.locals.msg(`Removed approximately ${differenceString} from queue`) res.locals.msg(`Removed approximately ${differenceString} from queue`)
}) })
app.post('/v1/courses', async (req, res) => { app.post('/courses', async (req, res) => {
await scheduleByCourseCodes(req.body.coursecodes) await scheduleByCourseCodes(req.body.coursecodes)
logger.info('Scheduled courses') logger.info('Scheduled courses')