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

Story map admin #105

Draft
wants to merge 35 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
76f6d20
Adapt storymap api for admin console
gdozot2 Apr 10, 2024
52b6937
Get data modifications
gdozot2 Apr 15, 2024
115bde7
Standardize stories DB name
gdozot2 Apr 15, 2024
c92a6f4
Log error
gdozot2 Apr 15, 2024
31f7c47
adapt story and chapters
gdozot2 Apr 16, 2024
d42c87b
Update DB + fix order
gdozot2 Apr 24, 2024
ea297ab
Rework story + add tests
gdozot2 May 8, 2024
26ccb9d
update tests
gdozot2 May 8, 2024
f636914
update tests + update update method
gdozot2 May 14, 2024
aa48e95
Refactor chapter and add tests
gdozot2 May 28, 2024
0275ea6
update db storymap
gdozot2 May 13, 2024
afb47fe
revert code
gdozot2 May 14, 2024
9f41e61
Add type
gdozot2 May 14, 2024
783f709
add news table migration
zweiro Apr 9, 2024
649cb45
wip: add news feature
zweiro Apr 15, 2024
523977f
test: add news /GET tests
zweiro Apr 16, 2024
9eb904c
refactor: add img_alt field to news schema
zweiro Apr 22, 2024
fca3af1
refactor: switch created_at news field to published at
zweiro Apr 26, 2024
177751f
test: update news pagination test values to be hardcoded
zweiro Apr 26, 2024
1cccfe7
Add langFallback ENV variable to show english when local lang does no…
zweiro May 22, 2024
fd14b9c
Get data modifications
gdozot2 Apr 15, 2024
1663646
Standardize stories DB name
gdozot2 Apr 15, 2024
b13c242
Rework story + add tests
gdozot2 May 8, 2024
47e1752
update tests + update update method
gdozot2 May 14, 2024
6aea0e0
Add some tests, fix some issues, add some fixtures
gdozot2 Jun 10, 2024
597c04a
Improve code
gdozot2 Jun 10, 2024
719d95c
fix join sequelize
gdozot2 Jun 10, 2024
0209156
update code to add owner + nbChapters + update all tests and addd mis…
gdozot2 Jun 20, 2024
b820ffb
Story creation and retrieve base on owner management
gdozot2 Jun 24, 2024
d6e748d
Fix tests with user authorization
gdozot2 Jun 24, 2024
128b5b2
Add not authorized user tests
gdozot2 Jun 26, 2024
bc42979
Manage owner right for addition, supression and modification of story…
gdozot2 Jun 26, 2024
6d4a053
improve
gdozot2 Aug 22, 2024
d5f8922
Fix latest test error
gdozot2 Sep 6, 2024
4b8c21b
Merge branch 'main' into story-map-admin
gdozot2 Sep 9, 2024
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
8 changes: 7 additions & 1 deletion app/api/chapters/chapter.openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
application/json:
schema:
$ref: "#/components/schemas/ChaptersResponse"

'403':
$ref: "#/components/responses/UnauthorizedError"


/stories/{storyId}/chapters/{id}:
Expand Down Expand Up @@ -74,7 +77,8 @@
application/json:
schema:
$ref: "#/components/schemas/ChaptersResponse"

'403':
$ref: "#/components/responses/UnauthorizedError"
'404':
$ref: "#/components/responses/NotFoundError"

Expand All @@ -99,3 +103,5 @@
application/json:
schema:
$ref: "#/components/schemas/SuccessMessageResponse"
'403':
$ref: "#/components/responses/UnauthorizedError"
21 changes: 13 additions & 8 deletions app/api/chapters/chapters.controller.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
const models = require("../../models");
const { notFoundError } = require('../../utils/errors');
const { validateStoryRight } = require('../../utils/story');
const { route } = require("../../utils/express");

const getChapterById = async (req, res) => {
const getChapterById = route(async (req, res) => {
const { id } = req.params;
const chapter = await models.stories_chapters.findByPk(id);
if (chapter) {
res.json(chapter);
} else {
throw notFoundError(req);
}
};
});

//Add a chapter to the db
const addChapter = async (req, res) => {
const addChapter = route(async (req, res) => {
await validateStoryRight(req, res, req.params.storyId);
const { storyId } = req.params;
gdozot2 marked this conversation as resolved.
Show resolved Hide resolved
const { title, type, picture_id, url_media, description, zoom, indexinstory, view_custom } = req.body;
const newChapter = await models.stories_chapters.create({
Expand All @@ -27,11 +30,12 @@ const addChapter = async (req, res) => {
view_custom
});
res.status(201).json(newChapter); // Return the ID of the newly created chapter
};
});


//Update a chapter
const updateChapter = async (req, res) => {
const updateChapter = route(async (req, res) => {
await validateStoryRight(req, res, req.params.storyId);
const { storyId, id } = req.params;
gdozot2 marked this conversation as resolved.
Show resolved Hide resolved
const { title, type, picture_id, url_media, description, zoom, indexinstory, view_custom } = req.body;
const updatedChapter = await models.stories_chapters.update({
Expand All @@ -49,16 +53,17 @@ const updateChapter = async (req, res) => {
where: {id: id}, returning: true, plain: true
});
res.status(200).json(updatedChapter[1]); // Return the ID of the newly created chapter
};
});

const deleteChapter = async (req, res) => {
const deleteChapter = route(async (req, res) => {
await validateStoryRight(req, res, req.params.storyId);

await models.stories_chapters.destroy({where: {id:req.params.id}});
gdozot2 marked this conversation as resolved.
Show resolved Hide resolved
res.send({
message: "The story was deleted."
});

}
});

module.exports = {
getChapterById,
Expand Down
110 changes: 109 additions & 1 deletion app/api/chapters/chapters.delete.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,59 @@ describe('DELETE /stories/:storyId/chapters/:id', () => {
({ app } = createApplicationWithMocks());
});

it('Delete a story with user without the right', async () => {
const user = await createUser({ roles: [ 'volunteer' ] });
const token = await generateJwtFor(user);
const [ owner1 ] = await generate(1, createOwner);
const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000);
const col1 = await createCollection({ date_publi: yesterday, is_owner_challenge: true, owner: owner1 });
const image = await createImage({ collection: col1, state: 'initial' });
const story = await createStory({
title: "Mon titre",
logo_link: "http://localhost",
description_preview: "abc",
description: "efg",
owner_id: owner1.id
});
const chapter = await createChapter({
title: 'titre',
type: 'IMAGE',
picture_id: image.id,
url_media: '',
description: 'description',
zoom: 14,
story_id: story.id,
indexinstory: 0,
view_custom: {
transparency: 0.5,
showBuilding: true,
buildingsSlider: 10,
depthSlider: 15
},
})

const initialState = await loadInitialState();

const req = {
method: 'DELETE',
path: `/stories/${story.id}/chapters/${chapter.id}`,
headers: {
Authorization: `Bearer ${token}`
}
};

expect(req).to.matchRequestDocumentation();

const res = await testHttpRequest(app, req);

expect(res)
.to.have.status(403)
.and.to.matchResponseDocumentation();

await expectNoSideEffects(app, initialState);

});

it('Delete a story', async () => {
const user = await createUser({ roles: [ 'owner_admin' ] });
const token = await generateJwtFor(user);
Expand All @@ -37,7 +90,7 @@ describe('DELETE /stories/:storyId/chapters/:id', () => {
logo_link: "http://localhost",
description_preview: "abc",
description: "efg",
owner_id: owner1.id
owner_id: user.owner_id
});
const initialState = await loadInitialState();
const chapter = await createChapter({
Expand Down Expand Up @@ -80,4 +133,59 @@ describe('DELETE /stories/:storyId/chapters/:id', () => {

});

it('Delete a story without owner_right', async () => {
const user = await createUser({ roles: [ 'owner_admin' ] });
const token = await generateJwtFor(user);
const [ owner1 ] = await generate(1, createOwner);
const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000);
const col1 = await createCollection({ date_publi: yesterday, is_owner_challenge: true, owner: owner1 });
const image = await createImage({ collection: col1, state: 'initial' });
const story = await createStory({
title: "Mon titre",
logo_link: "http://localhost",
description_preview: "abc",
description: "efg",
owner_id: owner1.id
});

const chapter = await createChapter({
title: 'titre',
type: 'IMAGE',
picture_id: image.id,
url_media: '',
description: 'description',
zoom: 14,
story_id: story.id,
indexinstory: 0,
view_custom: {
transparency: 0.5,
showBuilding: true,
buildingsSlider: 10,
depthSlider: 15
},
})

const currentCounts = await countDatabaseRows();
expect(currentCounts['stories_chapters']).to.be.equals(1);
const initialState = await loadInitialState();
const req = {
method: 'DELETE',
path: `/stories/${story.id}/chapters/${chapter.id}`,
headers: {
Authorization: `Bearer ${token}`
}
};

expect(req).to.matchRequestDocumentation();

const res = await testHttpRequest(app, req);

expect(res)
.to.have.status(403)
.and.to.matchResponseDocumentation();

await expectNoSideEffects(app, initialState);

});

});
142 changes: 139 additions & 3 deletions app/api/chapters/chapters.post.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,97 @@ describe('POST /stories/:id/chapters', () => {
({ app } = createApplicationWithMocks());
});

describe('Post chapter when stories has no chapters and there is no view_custom', () => {
describe('Post chapter when user has not the right to create a chapter', () => {
it('Add a chapter', async () => {
const user = await createUser({ roles: [ 'volunteer' ] });
const token = await generateJwtFor(user);
const [ owner1 ] = await generate(1, createOwner);
const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000);
const col1 = await createCollection({ date_publi: yesterday, is_owner_challenge: true, owner: owner1 });
const image = await createImage({ collection: col1, state: 'initial' });
const story = await createStory({
title: "Mon titre",
logo_link: "http://localhost",
description_preview: "abc",
description: "efg",
owner_id: user.owner_id
});
const chapter = {
title: 'titre',
type: 'IMAGE',
picture_id: image.id,
url_media: '',
description: 'description',
zoom: 14,
story_id: story.id,
indexinstory: 0,
view_custom: null,
}

const req = {
method: 'POST',
path: `/stories/${story.id}/chapters`,
body: chapter,
headers: {
Authorization: `Bearer ${token}`
}
};

expect(req).to.matchRequestDocumentation();

const res = await testHttpRequest(app, req);

expect(res)
.to.have.status(403)
.and.to.matchResponseDocumentation();

});
it('Add a chapter', async () => {
const user = await createUser({ roles: [ 'volunteer' ] });
const token = await generateJwtFor(user);
const [ owner1 ] = await generate(1, createOwner);
const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000);
const col1 = await createCollection({ date_publi: yesterday, is_owner_challenge: true, owner: owner1 });
const image = await createImage({ collection: col1, state: 'initial' });
const story = await createStory({
title: "Mon titre",
logo_link: "http://localhost",
description_preview: "abc",
description: "efg",
owner_id: user.owner_id
});
const chapter = {
title: 'titre',
type: 'IMAGE',
picture_id: image.id,
url_media: '',
description: 'description',
zoom: 14,
story_id: story.id,
indexinstory: 0,
view_custom: null,
}

const req = {
method: 'POST',
path: `/stories/${story.id}/chapters`,
body: chapter,
headers: {
Authorization: `Bearer ${token}`
}
};

expect(req).to.matchRequestDocumentation();

const res = await testHttpRequest(app, req);

expect(res)
.to.have.status(403)
.and.to.matchResponseDocumentation();

});

it('Add a chapter without owner right', async () => {
const user = await createUser({ roles: [ 'owner_admin' ] });
const token = await generateJwtFor(user);
const [ owner1 ] = await generate(1, createOwner);
Expand Down Expand Up @@ -62,6 +151,53 @@ describe('POST /stories/:id/chapters', () => {

const res = await testHttpRequest(app, req);

expect(res)
.to.have.status(403)
.and.to.matchResponseDocumentation();

});
});

describe('Post chapter when stories has no chapters and there is no view_custom', () => {
it('Add a chapter', async () => {
const user = await createUser({ roles: [ 'owner_admin' ] });
const token = await generateJwtFor(user);
const [ owner1 ] = await generate(1, createOwner);
const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000);
const col1 = await createCollection({ date_publi: yesterday, is_owner_challenge: true, owner: owner1 });
const image = await createImage({ collection: col1, state: 'initial' });
const story = await createStory({
title: "Mon titre",
logo_link: "http://localhost",
description_preview: "abc",
description: "efg",
owner_id: user.owner_id
});
const chapter = {
title: 'titre',
type: 'IMAGE',
picture_id: image.id,
url_media: '',
description: 'description',
zoom: 14,
story_id: story.id,
indexinstory: 0,
view_custom: null,
}

const req = {
method: 'POST',
path: `/stories/${story.id}/chapters`,
body: chapter,
headers: {
Authorization: `Bearer ${token}`
}
};

expect(req).to.matchRequestDocumentation();

const res = await testHttpRequest(app, req);

chapter.id = 1;

expect(res)
Expand All @@ -85,7 +221,7 @@ describe('POST /stories/:id/chapters', () => {
logo_link: "http://localhost",
description_preview: "abc",
description: "efg",
owner_id: owner1.id
owner_id: user.owner_id
});

const chapter = {
Expand Down Expand Up @@ -141,7 +277,7 @@ describe('POST /stories/:id/chapters', () => {
logo_link: "http://localhost",
description_preview: "abc",
description: "efg",
owner_id: owner1.id
owner_id: user.owner_id
});
await createChapter({
title: 'titre',
Expand Down
Loading