-
-
Notifications
You must be signed in to change notification settings - Fork 282
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
Add datasets importing functionality #201
base: master
Are you sure you want to change the base?
Changes from 1 commit
de78f6b
acc103f
e60449e
56670ee
b272cc3
c0b695e
870e0f0
cf4680e
203dc05
90be205
0de46fa
3ad38bf
8de2735
53b0b6f
61657de
0344296
4513011
08b4d0a
1426f1f
2854bff
913c54b
208fc7e
49639b8
2f78a55
21915cd
37ae6a2
ae662d0
06e8219
45fc92d
f5fba4b
fdc8f17
48bd1b4
8d738f7
e563583
2c25f13
b68bbab
8ba3a82
560d24b
2598bdf
7c07651
0374e40
5fd6e9a
cab5b6a
b263307
a5b18db
0987520
c8cb4d0
e450dec
762ccd9
26d82a7
18fc335
29e3ada
154f76c
84989c7
18199d5
35ea8b0
2afd166
b15a144
b61c993
f8bb16c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
/* | ||
* Copyright (C) 2018 Shivam Tripathi | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; either version 2 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License along | ||
* with this program; if not, write to the Free Software Foundation, Inc., | ||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
*/ | ||
|
||
import * as achievement from '../../helpers/achievement'; | ||
import * as error from '../../helpers/error'; | ||
import * as propHelpers from '../../../client/helpers/props'; | ||
import * as search from '../../helpers/search'; | ||
import {escapeProps, generateProps} from '../../helpers/props'; | ||
import Layout from '../../../client/containers/layout'; | ||
import React from 'react'; | ||
import ReactDOMServer from 'react-dom/server'; | ||
import _ from 'lodash'; | ||
import {entityEditorMarkup} from '../../helpers/entityRouteUtils'; | ||
import {entityToFormState} from './transform-import'; | ||
import {generateImportEntityProps} from '../../helpers/importEntityRouteUtils'; | ||
import {getEntityUrl} from '../../../client/helpers/entity'; | ||
import {getImportUrl} from '../../../client/helpers/import-entity'; | ||
import {getValidator} from '../../../client/entity-editor/helpers'; | ||
import importEntityPages from | ||
'../../../client/components/pages/import-entities'; | ||
import {transformForm} from './transform-form'; | ||
import uuid from 'uuid'; | ||
|
||
|
||
export function displayImport(req, res) { | ||
const {importEntity} = res.locals; | ||
|
||
// Get unique identifier types for display | ||
const identifierTypes = importEntity.identifierSet && | ||
_.uniqBy( | ||
_.map(importEntity.identifierSet.identifiers, 'type'), | ||
(type) => type.id | ||
); | ||
|
||
const {type} = importEntity; | ||
const ImportEntityComponent = importEntityPages[type]; | ||
if (ImportEntityComponent) { | ||
const props = generateProps(req, res, {identifierTypes}); | ||
const markup = ReactDOMServer.renderToString( | ||
<Layout {...propHelpers.extractLayoutProps(props)}> | ||
<ImportEntityComponent | ||
{...propHelpers.extractImportEntityProps(props)} | ||
/> | ||
</Layout> | ||
); | ||
|
||
res.render('target', { | ||
markup, | ||
props: escapeProps(props), | ||
script: '/js/import-entity/import-entity.js' | ||
}); | ||
} | ||
else { | ||
throw new Error( | ||
`Component was not found for the following import: ${type}` | ||
); | ||
} | ||
} | ||
|
||
export function displayDiscardImportEntity(req, res) { | ||
const {importEntity} = res.locals; | ||
const importUrl = getImportUrl(importEntity); | ||
|
||
if (importEntity.hasVoted) { | ||
res.redirect(importUrl); | ||
} | ||
|
||
if (!req.session.token) { | ||
req.session.token = uuid(); | ||
} | ||
const {token} = req.session; | ||
const props = generateProps(req, res, {token}); | ||
const {DiscardImportEntityPage} = importEntityPages; | ||
const markup = ReactDOMServer.renderToString( | ||
<Layout {...propHelpers.extractLayoutProps(props)}> | ||
<DiscardImportEntityPage | ||
importEntity={props.importEntity} | ||
token={props.token} | ||
/> | ||
</Layout> | ||
); | ||
|
||
res.render('target', { | ||
markup, | ||
props: escapeProps(props), | ||
script: '/js/import-entity/discard-import-entity.js' | ||
}); | ||
} | ||
|
||
export function handleDiscardImportEntity(req, res) { | ||
const {headers} = req; | ||
const token = headers['x-auth-token']; | ||
|
||
if (token !== req.session.token) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The token as implemented doesn't seem to be useful. I think we should remove it until we work out a better system and identify the exact problem that needs to be solved. |
||
res.status(401).send({error: 'No token provided.'}); | ||
} | ||
|
||
const {orm} = req.app.locals; | ||
const editorId = req.session.passport.user.id; | ||
const {importEntity} = res.locals; | ||
orm.bookshelf.transaction(async transacting => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use parentheses around the parameter to an arrow function |
||
try { | ||
await orm.func.imports.castDiscardVote( | ||
transacting, importEntity.importId, editorId | ||
); | ||
// To-do: Add code to remove importEntity from the search index | ||
res.status(200).send(); | ||
} | ||
catch (err) { | ||
res.status(400).send({error: err}); | ||
} | ||
}); | ||
} | ||
|
||
export async function approveImportEntity(req, res) { | ||
const {orm} = res.app.locals; | ||
const editorId = req.session.passport.user.id; | ||
const {importEntity} = res.locals; | ||
const entity = await orm.bookshelf.transaction(transacting => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same with arrow function parentheses here - eslint should have caught this. |
||
orm.func.imports.approveImport( | ||
{editorId, importEntity, orm, transacting} | ||
)); | ||
const entityUrl = getEntityUrl(entity); | ||
|
||
/* Add code to remove import and add the newly created entity to the elastic | ||
search index and remove delete import */ | ||
|
||
// Update editor achievement | ||
entity.alert = (await achievement.processEdit( | ||
orm, editorId, entity.revisionId | ||
)).alert; | ||
|
||
// Cleanup search indexing | ||
search.indexEntity(entity); | ||
// Todo: Add functionality to remove imports from ES index upon deletion | ||
|
||
res.redirect(entityUrl); | ||
} | ||
|
||
export function editImportEntity(req, res) { | ||
const {importEntity} = res.locals; | ||
const initialState = entityToFormState(importEntity); | ||
const importEntityProps = generateImportEntityProps( | ||
req, res, initialState, {} | ||
); | ||
const {markup, props} = entityEditorMarkup(importEntityProps); | ||
return res.render('target', { | ||
markup, | ||
props: escapeProps(props), | ||
script: '/js/entity-editor.js', | ||
title: 'Edit Work Import' | ||
}); | ||
} | ||
|
||
export async function approveImportPostEditing(req, res) { | ||
const {orm} = req.app.locals; | ||
const {importEntity} = res.locals; | ||
const editorId = req.session.passport.user.id; | ||
const {importId, type} = importEntity; | ||
const formData = req.body; | ||
|
||
const validateForm = getValidator(type.toLowerCase()); | ||
|
||
if (!validateForm(formData)) { | ||
const err = new error.FormSubmissionError(); | ||
error.sendErrorAsJSON(res, err); | ||
} | ||
|
||
const entityData = transformForm[type](formData); | ||
|
||
const entity = await orm.bookshelf.transaction(async (transacting) => { | ||
await orm.func.imports.deleteImport( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need to swap the order of these, and pass the new entity BBID to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to pass the new entity BBID to the deleteImport function? Also how does it matter we keep deletion before creation or creation before deletion, as it all happens in the same transaction? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The deleteImport function needs to know the new entity BBID to update the link_import table doesn't it? https://github.com/bookbrainz/bookbrainz-data-js/blob/master/src/func/imports/delete-import.js |
||
transacting, importId | ||
); | ||
return orm.func.createEntity( | ||
{editorId, entityData, orm, transacting} | ||
); | ||
}); | ||
|
||
// Update editor achievement | ||
entity.alert = (await achievement.processEdit( | ||
orm, editorId, entity.revisionId | ||
)).alert; | ||
|
||
// Cleanup search indexing | ||
await search.indexEntity(entity); | ||
// To-do: Add code to remove importEntity from the search index | ||
|
||
res.send(entity); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to add copyright for me in 2016 (some of the entity display code adapted here).