-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* Add kusama society space, #1034 * Check social member, #1034 * fix: space icon, #1034 * fix: proposal threshold check, #1034 * refactor, #1034 * Update, #1034 * refactor request param check, #1034 * Refactor society proposal vote, #1034 * refactor vote, #1034 * Update, #1034 * fix: remove debug comment, #1034 * fix: check society member, #1034 * Show society member warning, #1034 * Show society member check info, #1034 * fix, #1034 * Disable button if not society member, #1034 * fix, #1034 * Mock test account as society member, #1034
- Loading branch information
Showing
87 changed files
with
1,559 additions
and
698 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
const Accessibility = { | ||
PUBLIC: "public", | ||
SOCIETY: "society", | ||
}; | ||
|
||
module.exports = { | ||
Accessibility, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
backend/packages/backend/src/features/proposals/checkProposalContent.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
const { HttpError } = require("../../exc"); | ||
const { ContentType, PostTitleLengthLimitation } = require("../../constants"); | ||
|
||
function checkProposalContent(data) { | ||
const { title, content, contentType } = data; | ||
|
||
if (!title) { | ||
throw new HttpError(400, { title: ["Title is missing"] }); | ||
} | ||
|
||
if (title.length > PostTitleLengthLimitation) { | ||
throw new HttpError(400, { | ||
title: ["Title must be no more than %d characters"], | ||
}); | ||
} | ||
|
||
if (!content) { | ||
throw new HttpError(400, { content: ["Content is missing"] }); | ||
} | ||
|
||
if (!contentType) { | ||
throw new HttpError(400, { contentType: ["Content type is missing"] }); | ||
} | ||
|
||
if ( | ||
contentType !== ContentType.Markdown && | ||
contentType !== ContentType.Html | ||
) { | ||
throw new HttpError(400, { contentType: ["Invalid content type"] }); | ||
} | ||
} | ||
|
||
module.exports = { | ||
checkProposalContent, | ||
}; |
4 changes: 2 additions & 2 deletions
4
backend/packages/backend/src/features/proposals/commonRoutes.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
const Router = require("koa-router"); | ||
const proposalController = require("./proposal.controller"); | ||
const { getProposalById } = require("./getProposalById"); | ||
|
||
const router = new Router(); | ||
router.get("/proposal/:proposalId", proposalController.getProposalById); | ||
router.get("/proposal/:proposalId", getProposalById); | ||
|
||
module.exports = router; |
259 changes: 259 additions & 0 deletions
259
backend/packages/backend/src/features/proposals/createProposal.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,259 @@ | ||
const { HttpError } = require("../../exc"); | ||
const proposalService = require("../../services/proposal.service"); | ||
const { ChoiceType } = require("../../constants"); | ||
const isEmpty = require("lodash.isempty"); | ||
const { spaces: spaceServices } = require("../../spaces"); | ||
const { Accessibility } = require("../../consts/space"); | ||
const { checkProposalContent } = require("./checkProposalContent"); | ||
const isEqual = require("lodash.isequal"); | ||
const pick = require("lodash.pick"); | ||
const { getLatestHeight } = require("../../services/chain.service"); | ||
|
||
function checkProposalChoices(data) { | ||
const { space, choiceType, choices } = data; | ||
|
||
if (!choiceType) { | ||
throw new HttpError(400, { content: ["Choice type is missing"] }); | ||
} | ||
|
||
if (![ChoiceType.Single, ChoiceType.Multiple].includes(choiceType)) { | ||
throw new HttpError(400, { choiceType: ["Unknown choice type"] }); | ||
} | ||
|
||
if (!choices) { | ||
throw new HttpError(400, { choices: ["Choices is missing"] }); | ||
} | ||
|
||
if ( | ||
!Array.isArray(choices) || | ||
choices.length < 2 || | ||
choices.some((item) => typeof item !== "string") | ||
) { | ||
throw new HttpError(400, { | ||
choices: ["Choices must be array of string with at least 2 items"], | ||
}); | ||
} | ||
|
||
if (new Set(choices).size < choices.length) { | ||
throw new HttpError(400, { choices: ["All choices should be different"] }); | ||
} | ||
|
||
const uniqueChoices = Array.from(new Set(choices)); | ||
if (uniqueChoices.length < 2) { | ||
throw new HttpError(400, { | ||
choices: ["There must be at least 2 different choices"], | ||
}); | ||
} | ||
|
||
const spaceService = spaceServices[space]; | ||
const maxOptionsCount = spaceService.maxOptionsCount || 10; | ||
if (choices.length > spaceService.maxOptionsCount) { | ||
throw new HttpError( | ||
400, | ||
`Too many options, support up to ${maxOptionsCount} options`, | ||
); | ||
} | ||
} | ||
|
||
function checkProposalDate(data) { | ||
const { startDate, endDate } = data; | ||
|
||
if (!startDate) { | ||
throw new HttpError(400, { content: ["Start date is missing"] }); | ||
} | ||
|
||
if (!endDate) { | ||
throw new HttpError(400, { content: ["End date is missing"] }); | ||
} | ||
|
||
if (endDate <= startDate) { | ||
throw new HttpError(400, "Start date should not be later than end date"); | ||
} | ||
|
||
const now = new Date(); | ||
|
||
if (endDate < now.getTime()) { | ||
throw new HttpError( | ||
400, | ||
"End date should not be earlier than current time", | ||
); | ||
} | ||
} | ||
|
||
async function checkSnapshotHeights(data) { | ||
const { space, snapshotHeights } = data; | ||
|
||
if (!snapshotHeights) { | ||
throw new HttpError(400, { content: ["Snapshot height is missing"] }); | ||
} | ||
|
||
const spaceService = spaceServices[space]; | ||
|
||
// Check if the snapshot heights is matching the space configuration | ||
const snapshotNetworks = Object.keys(snapshotHeights || {}); | ||
if ( | ||
snapshotNetworks.length === 0 || | ||
snapshotNetworks.length !== spaceService.networks.length | ||
) { | ||
throw new HttpError(400, { | ||
snapshotHeights: [ | ||
"The snapshot heights must match the space configuration", | ||
], | ||
}); | ||
} | ||
|
||
for (const spaceNetwork of spaceService.networks) { | ||
if (snapshotNetworks.includes(spaceNetwork.network)) { | ||
continue; | ||
} | ||
|
||
throw new HttpError(400, { | ||
snapshotHeights: [`Missing snapshot height of ${spaceNetwork.network}`], | ||
}); | ||
} | ||
|
||
await Promise.all( | ||
Object.keys(snapshotHeights).map(async (chain) => { | ||
const lastHeight = await getLatestHeight(chain); | ||
if (lastHeight && snapshotHeights[chain] > lastHeight) { | ||
throw new HttpError( | ||
400, | ||
`Snapshot height should not be higher than the current finalized height: ${chain}`, | ||
); | ||
} | ||
}), | ||
); | ||
} | ||
|
||
function checkNetworkConfig(data) { | ||
const { space, networksConfig } = data; | ||
|
||
if (isEmpty(networksConfig)) { | ||
throw new HttpError(400, { | ||
networksConfig: ["Networks config is missing"], | ||
}); | ||
} | ||
|
||
const spaceService = spaceServices[space]; | ||
if ( | ||
!isEqual(networksConfig, { | ||
...pick(spaceService, [ | ||
"symbol", | ||
"decimals", | ||
"networks", | ||
"accessibility", | ||
]), | ||
strategies: spaceService.weightStrategy, | ||
...pick(spaceService, ["quorum", "version"]), | ||
}) | ||
) { | ||
throw new HttpError(400, { | ||
networksConfig: [ | ||
"The proposal networks config is not matching the space config", | ||
], | ||
}); | ||
} | ||
} | ||
|
||
async function checkProposalSpace(data) { | ||
const { space } = data; | ||
|
||
if (!space) { | ||
throw new HttpError(400, { space: ["Space is missing"] }); | ||
} | ||
|
||
const spaceConfig = spaceServices[space]; | ||
if (!spaceConfig) { | ||
throw new HttpError(400, { space: ["Unknown space"] }); | ||
} | ||
} | ||
|
||
async function checkProposalOptions(data) { | ||
const { proposerNetwork } = data; | ||
|
||
if (!proposerNetwork) { | ||
throw new HttpError(400, { | ||
proposerNetwork: ["Proposer network is missing"], | ||
}); | ||
} | ||
|
||
checkProposalSpace(data); | ||
|
||
checkNetworkConfig(data); | ||
|
||
await checkSnapshotHeights(data); | ||
|
||
checkProposalContent(data); | ||
|
||
checkProposalDate(data); | ||
|
||
checkProposalChoices(data); | ||
} | ||
|
||
async function createProposal(ctx) { | ||
const { data, address, signature } = ctx.request.body; | ||
const { | ||
space, | ||
networksConfig, | ||
title, | ||
content, | ||
contentType, | ||
choiceType, | ||
choices, | ||
startDate, | ||
endDate, | ||
snapshotHeights, | ||
realProposer, | ||
proposerNetwork, | ||
banner, | ||
} = data; | ||
|
||
await checkProposalOptions(data); | ||
|
||
const spaceConfig = spaceServices[space]; | ||
|
||
if (spaceConfig.accessibility === Accessibility.SOCIETY) { | ||
ctx.body = await proposalService.createSocietyProposal({ | ||
space, | ||
networksConfig, | ||
title, | ||
content, | ||
contentType, | ||
choiceType, | ||
choices, | ||
startDate, | ||
endDate, | ||
snapshotHeights, | ||
realProposer, | ||
proposerNetwork, | ||
banner, | ||
data, | ||
address, | ||
signature, | ||
}); | ||
return; | ||
} | ||
|
||
ctx.body = await proposalService.createProposal({ | ||
space, | ||
networksConfig, | ||
title, | ||
content, | ||
contentType, | ||
choiceType, | ||
choices, | ||
startDate, | ||
endDate, | ||
snapshotHeights, | ||
realProposer, | ||
proposerNetwork, | ||
banner, | ||
data, | ||
address, | ||
signature, | ||
}); | ||
} | ||
|
||
module.exports = { | ||
createProposal, | ||
}; |
11 changes: 11 additions & 0 deletions
11
backend/packages/backend/src/features/proposals/getAddressVote.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
const proposalService = require("../../services/proposal.service"); | ||
|
||
async function getAddressVote(ctx) { | ||
const { proposalCid, address } = ctx.params; | ||
|
||
ctx.body = await proposalService.getAddressVote(proposalCid, address); | ||
} | ||
|
||
module.exports = { | ||
getAddressVote, | ||
}; |
17 changes: 17 additions & 0 deletions
17
backend/packages/backend/src/features/proposals/getComments.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
const proposalService = require("../../services/proposal.service"); | ||
const { extractPage } = require("../../utils"); | ||
|
||
async function getComments(ctx) { | ||
const { page, pageSize } = extractPage(ctx); | ||
if (pageSize === 0 || page < 1) { | ||
ctx.status = 400; | ||
return; | ||
} | ||
|
||
const { proposalCid } = ctx.params; | ||
ctx.body = await proposalService.getComments(proposalCid, page, pageSize); | ||
} | ||
|
||
module.exports = { | ||
getComments, | ||
}; |
11 changes: 11 additions & 0 deletions
11
backend/packages/backend/src/features/proposals/getProposalById.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
const proposalService = require("../../services/proposal.service"); | ||
|
||
async function getProposalById(ctx) { | ||
const { proposalId } = ctx.params; | ||
|
||
ctx.body = await proposalService.getProposalById(proposalId); | ||
} | ||
|
||
module.exports = { | ||
getProposalById, | ||
}; |
Oops, something went wrong.