mirror of https://github.com/nocodb/nocodb
8 changed files with 456 additions and 153 deletions
@ -0,0 +1,45 @@
|
||||
import { Controller, Get, Param, Req, UseGuards } from '@nestjs/common'; |
||||
import { Request } from 'express'; |
||||
import { GlobalGuard } from '~/guards/global/global.guard'; |
||||
import { DataApiLimiterGuard } from '~/guards/data-api-limiter.guard'; |
||||
import { CalendarDatasService } from '~/services/calendar-datas.service'; |
||||
import Acl from '~/utils/acl'; |
||||
|
||||
@Controller() |
||||
@UseGuards(DataApiLimiterGuard, GlobalGuard) |
||||
export class CalendarDatasController { |
||||
constructor(private readonly calendarDatasService: CalendarDatasService) {} |
||||
|
||||
@Get(['/api/v1/db/calendar-data/:orgs/:baseName/:tableName/views/:viewName']) |
||||
@Acl('dataList') |
||||
async dataList(@Req() req: Request, @Param('viewName') viewId: string) { |
||||
return await this.calendarDatasService.getCalendarDataList({ |
||||
viewId: viewId, |
||||
query: req.query, |
||||
}); |
||||
} |
||||
|
||||
@Get([ |
||||
'/api/v1/db/calendar-data/:orgs/:baseName/:tableName/countByDate/', |
||||
'/api/v1/db/calendar-data/:orgs/:baseName/:tableName/views/:viewName/countByDate/', |
||||
]) |
||||
@Acl('dataList') |
||||
async calendarDataCount( |
||||
@Req() req: Request, |
||||
@Res() res: Response, |
||||
@Param('baseName') baseName: string, |
||||
@Param('tableName') tableName: string, |
||||
@Param('viewName') viewName: string, |
||||
) { |
||||
const startTime = process.hrtime(); |
||||
|
||||
const data = await this.calendarDatasService.getCalendarRecordCount({ |
||||
query: req.query, |
||||
viewId: viewName, |
||||
}); |
||||
|
||||
const elapsedSeconds = parseHrtimeToMilliSeconds(process.hrtime(startTime)); |
||||
res.setHeader('xc-db-response', elapsedSeconds); |
||||
res.json(data); |
||||
} |
||||
} |
@ -0,0 +1,205 @@
|
||||
import { Injectable, Logger } from '@nestjs/common'; |
||||
import { ViewTypes } from 'nocodb-sdk'; |
||||
import { nocoExecute } from 'nc-help'; |
||||
import dayjs from 'dayjs'; |
||||
import type { CalendarRangeType, FilterType } from 'nocodb-sdk'; |
||||
import { CalendarRange, Model, Source, View } from '~/models'; |
||||
import { NcBaseError, NcError } from '~/helpers/catchError'; |
||||
import getAst from '~/helpers/getAst'; |
||||
import { PagedResponseImpl } from '~/helpers/PagedResponse'; |
||||
import NcConnectionMgrv2 from '~/utils/common/NcConnectionMgrv2'; |
||||
import { DatasService } from '~/services/datas.service'; |
||||
|
||||
@Injectable() |
||||
export class CalendarDatasService { |
||||
protected logger = new Logger(CalendarDatasService.name); |
||||
|
||||
constructor(protected datasService: DatasService) {} |
||||
|
||||
async getCalendarDataList(param: { viewId: string; query: any }) { |
||||
const { viewId, query } = param; |
||||
const from_date = query.from_date; |
||||
const to_date = query.to_date; |
||||
|
||||
if (!from_date || !to_date) |
||||
NcError.badRequest('from_date and to_date are required'); |
||||
|
||||
const view = await View.get(viewId); |
||||
|
||||
if (!view) NcError.notFound('View not found'); |
||||
|
||||
if (view.type !== ViewTypes.CALENDAR) |
||||
NcError.badRequest('View is not a calendar view'); |
||||
|
||||
const calendarRange = await CalendarRange.read(view.id); |
||||
|
||||
if (!calendarRange?.ranges?.length) NcError.badRequest('No ranges found'); |
||||
|
||||
const filterArr = await this.buildFilterArr({ |
||||
viewId, |
||||
from_date, |
||||
to_date, |
||||
}); |
||||
|
||||
query.filterArr = [...(query.filterArr ? query.filterArr : []), filterArr]; |
||||
|
||||
const model = await Model.getByIdOrName({ |
||||
id: view.fk_model_id, |
||||
}); |
||||
|
||||
const source = await Source.get(model.source_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: await NcConnectionMgrv2.get(source), |
||||
}); |
||||
|
||||
const { ast, dependencyFields } = await getAst({ |
||||
model, |
||||
query, |
||||
view, |
||||
}); |
||||
|
||||
const listArgs: any = dependencyFields; |
||||
try { |
||||
listArgs.filterArr = JSON.parse(listArgs.filterArrJson); |
||||
} catch (e) {} |
||||
try { |
||||
listArgs.sortArr = JSON.parse(listArgs.sortArrJson); |
||||
} catch (e) {} |
||||
|
||||
const [count, data] = await Promise.all([ |
||||
baseModel.count(listArgs, false), |
||||
(async () => { |
||||
let data = []; |
||||
try { |
||||
data = await nocoExecute( |
||||
ast, |
||||
await baseModel.list(listArgs, { |
||||
ignoreViewFilterAndSort: false, |
||||
}), |
||||
{}, |
||||
listArgs, |
||||
); |
||||
} catch (e) { |
||||
if (e instanceof NcBaseError) throw e; |
||||
this.logger.error(e); |
||||
NcError.internalServerError( |
||||
'Please check server log for more details', |
||||
); |
||||
} |
||||
return data; |
||||
})(), |
||||
]); |
||||
return new PagedResponseImpl(data, { |
||||
...query, |
||||
count, |
||||
}); |
||||
} |
||||
|
||||
async getCalendarRecordCount(param: { viewId: string; query: any }) { |
||||
const { viewId, query } = param; |
||||
const from_date = query.from_date; |
||||
const to_date = query.to_date; |
||||
|
||||
if (!from_date || !to_date) |
||||
NcError.badRequest('from_date and to_date are required'); |
||||
|
||||
const view = await View.get(viewId); |
||||
|
||||
if (!view) NcError.notFound('View not found'); |
||||
|
||||
if (view.type !== ViewTypes.CALENDAR) |
||||
NcError.badRequest('View is not a calendar view'); |
||||
|
||||
const { ranges } = await CalendarRange.read(view.id); |
||||
|
||||
if (!ranges.length) NcError.badRequest('No ranges found'); |
||||
|
||||
const filterArr = await this.buildFilterArr({ |
||||
viewId, |
||||
from_date, |
||||
to_date, |
||||
}); |
||||
|
||||
query.filterArr = [...(query.filterArr ? query.filterArr : []), filterArr]; |
||||
|
||||
const model = await Model.getByIdOrName({ |
||||
id: view.fk_model_id, |
||||
}); |
||||
|
||||
const data = await this.datasService.getDataList({ |
||||
model, |
||||
view, |
||||
query, |
||||
}); |
||||
|
||||
if (!data) NcError.notFound('Data not found'); |
||||
|
||||
const dates: Array<string> = []; |
||||
|
||||
ranges.forEach((range: CalendarRangeType) => { |
||||
const fromCol = model.columns.find( |
||||
(c) => c.id === range.fk_from_column_id, |
||||
)?.title; |
||||
|
||||
data.list.forEach((date) => { |
||||
const fromDt = dayjs(date[fromCol]); |
||||
|
||||
if (fromCol && fromDt.isValid()) { |
||||
dates.push(fromDt.format('YYYY-MM-DD HH:mm:ssZ')); |
||||
} |
||||
}); |
||||
}); |
||||
|
||||
return { |
||||
count: dates.length, |
||||
dates: Array.from(new Set(dates)), |
||||
}; |
||||
} |
||||
|
||||
async buildFilterArr({ |
||||
viewId, |
||||
from_date, |
||||
to_date, |
||||
}: { |
||||
viewId: string; |
||||
from_date: string; |
||||
to_date: string; |
||||
}) { |
||||
const calendarRange = await CalendarRange.read(viewId); |
||||
if (!calendarRange?.ranges?.length) NcError.badRequest('No ranges found'); |
||||
|
||||
const filterArr: FilterType = { |
||||
is_group: true, |
||||
logical_op: 'and', |
||||
children: [], |
||||
}; |
||||
|
||||
calendarRange.ranges.forEach((range: CalendarRange) => { |
||||
const fromColumn = range.fk_from_column_id; |
||||
let rangeFilter: any = []; |
||||
if (fromColumn) { |
||||
rangeFilter = [ |
||||
{ |
||||
fk_column_id: fromColumn, |
||||
comparison_op: 'lt', |
||||
comparison_sub_op: 'exactDate', |
||||
value: to_date as string, |
||||
}, |
||||
{ |
||||
fk_column_id: fromColumn, |
||||
comparison_op: 'gt', |
||||
comparison_sub_op: 'exactDate', |
||||
value: from_date as string, |
||||
}, |
||||
]; |
||||
} |
||||
|
||||
if (rangeFilter.length > 0) filterArr.children.push(rangeFilter); |
||||
}); |
||||
|
||||
return filterArr; |
||||
} |
||||
} |
Loading…
Reference in new issue