Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SW-162: Capture publishing date #52

Merged
merged 5 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"dependencies": {
"connect-redis": "^7.1.1",
"cookie-parser": "^1.4.6",
"date-fns": "^4.1.0",
"dotenv": "^16.4.5",
"ejs": "^3.1.10",
"express": "^4.21.0",
Expand Down
12 changes: 2 additions & 10 deletions src/@types/express-session/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
import 'express-session';
import { DatasetDTO, RevisionDTO } from '../../dtos/dataset';
import { ViewErrDTO } from '../../dtos/view-dto';
import { SourceAssignmentDTO } from '../../dtos/source-assignment-dto';
import { FactTableDto } from '../../dtos/fact-table';
import { ViewError } from '../../dtos/view-error';

declare module 'express-session' {
interface SessionData {
currentDataset: DatasetDTO | undefined;
currentRevision: RevisionDTO | undefined;
currentImport: FactTableDto | undefined;
errors: ViewErrDTO | undefined;
dimensionCreationRequest: SourceAssignmentDTO[];
currentTitle: string | undefined;
errors: ViewError[] | undefined;
}
}
167 changes: 117 additions & 50 deletions src/controllers/publish.ts

Large diffs are not rendered by default.

20 changes: 0 additions & 20 deletions src/dtos/processed-csv.ts

This file was deleted.

9 changes: 6 additions & 3 deletions src/dtos/task-list-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@ export interface TaskListState {
relevant_topics: TaskStatus;
};

publishing: {
when: TaskStatus;
translation: {
export: TaskStatus;
import: TaskStatus;
submit: TaskStatus;
};

publishing: {
organisation: TaskStatus;
when: TaskStatus;
};
}
9 changes: 0 additions & 9 deletions src/dtos/view-dto.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { Readable } from 'stream';

import { SourceType } from '../enums/source-type';

import { ViewError } from './view-error';
Expand All @@ -19,14 +17,12 @@ export interface PageInfo {
}

export interface ViewErrDTO {
success: boolean;
status: number;
errors: ViewError[];
dataset_id: string | undefined;
}

export interface ViewDTO {
success: boolean;
dataset: DatasetDTO;
fact_table: FactTableDto;
current_page: number;
Expand All @@ -37,8 +33,3 @@ export interface ViewDTO {
headers: CSVHeader[];
data: string[][];
}

export interface ViewStream {
success: boolean;
stream: Readable;
}
6 changes: 3 additions & 3 deletions src/dtos/view-error.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export interface ViewError {
field?: string;
tag: {
name: string;
field: string;
message?: {
key: string;
params?: object;
};
}
51 changes: 49 additions & 2 deletions src/middleware/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,49 @@
"topics": {
"heading": "Which topics are relevant to this dataset?"
},
"schedule": {
"heading": "When should this dataset be published?",
"form": {
"date": {
"label": "Date",
"hint": "For example, 10 05 2024",
"error": {
"invalid": "The date this dataset should be published must be a real date",
"past": "The date this dataset should be published must be in the future"
}
},
"day": {
"label": "Day",
"error": "The date this dataset should be published must include a day"
},
"month": {
"label": "Month",
"error": "The date this dataset should be published must include a month"
},
"year": {
"label": "Year",
"error": "The date this dataset should be published must include a year"
},
"time": {
"label": "Time",
"hint": "This will be 09:30 by default. Only change this if a different publication time is needed. Use 24 hour clock format, for example 15:00.",
"error": {
"invalid": "The time this dataset should be published must be a real time"
}
},
"hour": {
"label": "Hour",
"error": "The time this dataset should be published must include an hour"
},
"minute": {
"label": "Minute",
"error": "The time this dataset should be published must include a minute"
}
},
"error": {
"saving": "Could not save the publish date, please try later"
}
},
"upload": {
"title": "Upload the data table",
"note": "The file should be in a CSV format"
Expand Down Expand Up @@ -305,11 +348,15 @@
"designation": "Designation",
"relevant_topics": "Relevant topics"
},
"translation": {
"subheading": "Translation",
"export": "Export text fields for translation",
"import": "Import translations"
},
"publishing": {
"subheading": "Publishing",
"organisation": "Publisher organisation and contact",
"when": "When this dataset should be published",
"export": "Export text fields for translation",
"import": "Import translations",
"submit": "Submit for approval"
}
},
Expand Down
4 changes: 2 additions & 2 deletions src/routes/dataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const dataset = Router();
dataset.get('/', async (req: Request, res: Response, next: NextFunction) => {
try {
const datasets: DatasetListItemDTO[] = await req.swapi.getActiveDatasetList();
res.render('view/list', datasets);
res.render('dataset/list', datasets);
} catch (err) {
next(err);
}
Expand All @@ -41,7 +41,7 @@ dataset.get('/:datasetId', fetchDataset, async (req: Request, res: Response, nex

// eslint-disable-next-line require-atomic-updates
res.locals.pagination = generateSequenceForNumber(datasetView?.current_page, datasetView?.total_pages);
res.render('view/data', datasetView);
res.render('dataset/data', datasetView);
});

dataset.get(
Expand Down
6 changes: 5 additions & 1 deletion src/routes/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import {
provideRelatedLinks,
provideUpdateFrequency,
provideDesignation,
provideTopics
provideTopics,
providePublishDate
} from '../controllers/publish';

export const publish = Router();
Expand Down Expand Up @@ -72,3 +73,6 @@ publish.post('/:datasetId/designation', fetchDataset, upload.none(), provideDesi

publish.get('/:datasetId/topics', fetchDataset, provideTopics);
publish.post('/:datasetId/topics', fetchDataset, upload.none(), provideTopics);

publish.get('/:datasetId/schedule', fetchDataset, providePublishDate);
publish.post('/:datasetId/schedule', fetchDataset, upload.none(), providePublishDate);
16 changes: 12 additions & 4 deletions src/services/stats-wales-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@ export class StatsWalesApi {
throw new ViewException(error.message, error.status, [
{
field: 'csv',
tag: {
name: 'errors.upload.no_csv_data',
message: {
key: 'errors.upload.no_csv_data',
params: {}
}
}
Expand Down Expand Up @@ -256,8 +256,8 @@ export class StatsWalesApi {
throw new ViewException(error.message, error.status, [
{
field: 'csv',
tag: {
name: 'errors.upload.no_csv_data',
message: {
key: 'errors.upload.no_csv_data',
params: {}
}
}
Expand Down Expand Up @@ -287,4 +287,12 @@ export class StatsWalesApi {
(response) => response.json() as unknown as DatasetDTO
);
}

public async setPublishDate(datasetId: string, revisionId: string, publishDate: string): Promise<DatasetDTO> {
return this.fetch({
url: `dataset/${datasetId}/revision/by-id/${revisionId}/publish-at`,
method: HttpMethod.Patch,
json: { publish_at: publishDate }
}).then((response) => response.json() as unknown as DatasetDTO);
}
}
8 changes: 4 additions & 4 deletions src/services/stats-wales.api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,8 @@ describe('StatsWalesApi', () => {
new ViewException('Bad Request', 400, [
{
field: 'csv',
tag: {
name: 'errors.upload.no_csv_data',
message: {
key: 'errors.upload.no_csv_data',
params: {}
}
}
Expand Down Expand Up @@ -365,8 +365,8 @@ describe('StatsWalesApi', () => {
new ViewException('Bad Request', 400, [
{
field: 'csv',
tag: {
name: 'errors.upload.no_csv_data',
message: {
key: 'errors.upload.no_csv_data',
params: {}
}
}
Expand Down
22 changes: 15 additions & 7 deletions src/utils/generate-view-errors.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import { FieldValidationError } from 'express-validator';

import { ViewErrDTO } from '../dtos/view-dto';
import { ViewError } from '../dtos/view-error';

export function generateViewErrors(datasetID: string | undefined, statusCode: number, errors: ViewError[]): ViewErrDTO {
return {
success: false,
status: statusCode,
errors,
dataset_id: datasetID
} as ViewErrDTO;
export function generateViewErrors(dataset_id: string | undefined, status: number, errors: ViewError[]): ViewErrDTO {
return { dataset_id, status, errors };
}

export function generatePageErrors(errors: FieldValidationError[], i18nPrefix: string): ViewError[] {
return errors.map((error: FieldValidationError) => {
return {
field: error.path,
message: {
key: `${i18nPrefix}.form.${error.path}.error`
}
};
});
}
36 changes: 35 additions & 1 deletion src/validators/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Request } from 'express';
import { body, param, ValidationChain } from 'express-validator';
import { body, FieldValidationError, param, ValidationChain } from 'express-validator';
import { ResultWithContext } from 'express-validator/lib/chain/context-runner';

import { Designation } from '../enums/designation';
import { DurationUnit } from '../enums/duration-unit';
Expand All @@ -8,6 +9,33 @@ export const hasError = async (validator: ValidationChain, req: Request) => {
return !(await validator.run(req)).isEmpty();
};

export async function getErrors(
validators: ValidationChain | ValidationChain[],
req: Request
): Promise<FieldValidationError[]> {
if (!Array.isArray(validators)) {
// eslint-disable-next-line no-param-reassign
validators = [validators];
}

let errors: FieldValidationError[] = [];

for (const validator of validators) {
const result: ResultWithContext = await validator.run(req);
if (!result.isEmpty()) {
const fieldErrors = result.array().filter((err) => err.type === 'field');
errors = errors.concat(fieldErrors);
}
}

return errors;
}

export async function getErrorFields(validators: ValidationChain | ValidationChain[], req: Request): Promise<string[]> {
const errors = await getErrors(validators, req);
return errors.map((error) => error.path);
}

export const datasetIdValidator = () => param('datasetId').trim().notEmpty().isUUID(4);
export const revisionIdValidator = () => param('revisionId').trim().notEmpty().isUUID(4);
export const factTableIdValidator = () => param('factTableId').trim().notEmpty().isUUID(4);
Expand Down Expand Up @@ -37,3 +65,9 @@ export const designationValidator = () => body('designation').trim().isIn(Object
export const providerIdValidator = () => body('provider_id').trim().notEmpty().isUUID(4);

export const topicIdValidator = () => body('topics').isArray({ min: 1 });

export const dayValidator = () => body('day').isInt({ min: 1, max: 31, allow_leading_zeroes: true });
export const monthValidator = () => body('month').isInt({ min: 1, max: 12, allow_leading_zeroes: true });
export const yearValidator = () => body('year').isInt({ min: new Date().getFullYear(), allow_leading_zeroes: true });
export const hourValidator = () => body('hour').isInt({ min: 0, max: 23, allow_leading_zeroes: true });
export const minuteValidator = () => body('minute').isInt({ min: 0, max: 59, allow_leading_zeroes: true });
File renamed without changes.
File renamed without changes.
8 changes: 4 additions & 4 deletions src/views/partials/error-handler.ejs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
<% if (locals?.errors) { %>
<% if (locals?.errors && locals.errors.length > 0) { %>
<div class="govuk-error-summary" data-module="govuk-error-summary">
<div role="alert">
<h2 class="govuk-error-summary__title">
<%= t('errors.problem') %>
</h2>
<div class="govuk-error-summary__body">
<ul class="govuk-list govuk-error-summary__list">
<% locals?.errors.errors.forEach(function(error) { %>
<% locals?.errors?.forEach(function(error) { %>
<li>
<a href="#<%= error.field %>"><%= t(error.tag.name, error.params) %></a>
<a href="#<%= error.field %>"><%= t(error.message.key, error.message.params) %></a>
</li>
<% }); %>
</ul>
</div>
</div>
</div>
<% } %>
<% } %>
Loading
Loading