Skip to content

Commit

Permalink
chore: add data to user projects endpoint (#1880)
Browse files Browse the repository at this point in the history
* chore: add personnel and coauthors to proposal project

* chore: use contract method to get vesting total

* chore: remove unused types
  • Loading branch information
1emu authored Jul 25, 2024
1 parent 841394f commit 3aefe65
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 58 deletions.
6 changes: 2 additions & 4 deletions src/clients/VestingData.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ChainId } from '@dcl/schemas'
import { JsonRpcProvider } from '@ethersproject/providers'
import { BigNumber, ethers } from 'ethers'
import { ethers } from 'ethers'

import { VestingStatus } from '../entities/Grant/types'
import { ErrorService } from '../services/ErrorService'
Expand Down Expand Up @@ -155,9 +155,7 @@ async function getVestingContractDataV2(

const released = parseContractValue(await vestingContract.getReleased())
const releasable = parseContractValue(await vestingContract.getReleasable())
const vestedPerPeriod: BigNumber[] = await vestingContract.getVestedPerPeriod()

const total = vestedPerPeriod.map(parseContractValue).reduce((acc, curr) => acc + curr, 0)
const total = parseContractValue(await vestingContract.getTotal())

let status = getInitialVestingStatus(start_at, finish_at)
const isRevoked = await vestingContract.getIsRevoked()
Expand Down
34 changes: 28 additions & 6 deletions src/entities/Proposal/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { toLower } from 'lodash'
import isEthereumAddress from 'validator/lib/isEthereumAddress'
import isUUID from 'validator/lib/isUUID'

import PersonnelModel from '../../models/Personnel'
import ProjectModel from '../../models/Project'
import Time from '../../utils/date/Time'
import { UnpublishedBidStatus } from '../Bid/types'
Expand Down Expand Up @@ -71,16 +72,31 @@ export default class ProposalModel extends Model<ProposalAttributes> {
}
}

static parseProposalWithProject(proposal: any): ProposalWithProject {
return {
...proposal,
coAuthors: proposal.coauthors,
snapshot_proposal: JSON.parse(proposal.snapshot_proposal),
}
}

static async getProposalWithProject(id: string): Promise<ProposalWithProject> {
if (!isUUID(id || '')) {
throw new Error(`Not found proposal: "${id}"`)
}

const query = SQL`
SELECT p.*, pr.id as "project_id", pr.status as "project_status"
SELECT p.*,
pr.id as "project_id",
pr.status as "project_status",
COALESCE(json_agg(DISTINCT to_jsonb(pe.*)) FILTER (WHERE pe.id IS NOT NULL), '[]') as personnel,
COALESCE(array_agg(co.address) FILTER (WHERE co.address IS NOT NULL), '{}') AS coauthors
FROM ${table(ProposalModel)} p
LEFT JOIN ${table(ProjectModel)} pr ON p.id = pr.proposal_id
LEFT JOIN ${table(PersonnelModel)} pe ON pr.id = pe.project_id AND pe.deleted = false
LEFT JOIN ${table(CoauthorModel)} co ON p.id = co.proposal_id AND co.status = ${CoauthorStatus.APPROVED}
WHERE p.id = ${id} AND p.deleted = false
GROUP BY p.id, pr.id
`

const result = await this.namedQuery('get_proposal_with_project', query)
Expand All @@ -89,7 +105,7 @@ export default class ProposalModel extends Model<ProposalAttributes> {
}

return {
...this.parse(result[0]),
...this.parseProposalWithProject(result[0]),
}
}

Expand Down Expand Up @@ -460,17 +476,23 @@ export default class ProposalModel extends Model<ProposalAttributes> {
const proposals = await this.namedQuery(
'get_project_list',
SQL`
SELECT prop.*, proj.id as project_id
SELECT prop.*,
proj.id as project_id,
COALESCE(json_agg(DISTINCT to_jsonb(pe.*)) FILTER (WHERE pe.id IS NOT NULL), '[]') as personnel,
COALESCE(array_agg(co.address) FILTER (WHERE co.address IS NOT NULL), '{}') AS coauthors
FROM ${table(ProposalModel)} prop
LEFT OUTER JOIN ${table(ProjectModel)} proj on prop.id = proj.proposal_id
WHERE "deleted" = FALSE
LEFT JOIN ${table(PersonnelModel)} pe ON proj.id = pe.project_id AND pe.deleted = false
LEFT JOIN ${table(CoauthorModel)} co ON prop.id = co.proposal_id AND co.status = ${CoauthorStatus.APPROVED}
WHERE prop."deleted" = FALSE
AND prop."type" IN (${join(types)})
AND prop."status" IN (${join(status)})
ORDER BY "created_at" DESC
GROUP BY prop.id, proj.id
ORDER BY prop."created_at" DESC
`
)

return proposals.map(this.parse)
return proposals.map(this.parseProposalWithProject)
}

private static parseTimeframe(timeFrame?: string | null) {
Expand Down
29 changes: 5 additions & 24 deletions src/entities/Proposal/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
/* eslint-disable @typescript-eslint/ban-types */
import { SQLStatement } from 'decentraland-gatsby/dist/entities/Database/utils'

import { SnapshotProposal } from '../../clients/SnapshotTypes'
import { CommitteeName } from '../../clients/Transparency'
import { Vesting } from '../../clients/VestingData'
import { PersonnelAttributes } from '../../models/Personnel'
import { UnpublishedBidInfo } from '../Bid/types'
import {
CategoryAssessmentQuestions,
Expand Down Expand Up @@ -74,6 +74,8 @@ export type ProposalAttributes<C extends Record<string, unknown> = any> = {
export interface ProposalWithProject extends ProposalAttributes {
project_id?: string | null
project_status?: ProjectStatus | null
personnel: PersonnelAttributes[]
coAuthors?: string[]
}

export type ProposalListFilter = {
Expand Down Expand Up @@ -122,9 +124,6 @@ export enum SortingOrder {
DESC = 'DESC',
}

export type GovernanceProcessType = ProposalType.Poll | ProposalType.Draft | ProposalType.Governance
export type BiddingProcessType = ProposalType.Pitch | ProposalType.Tender | ProposalType.Bid

export enum PoiType {
AddPOI = 'add_poi',
RemovePOI = 'remove_poi',
Expand Down Expand Up @@ -178,24 +177,6 @@ export function isSortingOrder(value: string | null | undefined): boolean {
}
}

export function getPoiTypeAction(poiType: PoiType) {
return poiType.split('_')[0] // "add" | "remove"
}

export function isHiringType(value: string | null | undefined): boolean {
switch (value) {
case HiringType.Add:
case HiringType.Remove:
return true
default:
return false
}
}

export function toHiringType<T>(value: string | null | undefined, orElse: () => T): HiringType | T {
return isHiringType(value) ? (value as HiringType) : orElse()
}

function requiredVotingPower(value: string | undefined | null, defaultValue: number) {
if (value === undefined || value === null) {
return defaultValue
Expand Down Expand Up @@ -828,6 +809,8 @@ export type ProposalProject = {
status: ProjectStatus
title: string
user: string
coAuthors?: string[]
personnel: PersonnelAttributes[]
size: number
type: ProposalType
about: string
Expand All @@ -845,8 +828,6 @@ export type ProposalProjectWithUpdate = ProposalProject & {
update_timestamp?: number
}

export type PendingProposalsQuery = { start: Date; end: Date; fields: (keyof SnapshotProposal)[]; limit: number }

export enum PriorityProposalType {
ActiveGovernance = 'active_governance',
OpenPitch = 'open_pitch',
Expand Down
26 changes: 13 additions & 13 deletions src/routes/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
ProjectLinkInCreationSchema,
ProjectMilestoneInCreationSchema,
} from '../entities/Project/types'
import { ProposalProjectWithUpdate } from '../entities/Proposal/types'
import PersonnelModel, { PersonnelAttributes } from '../models/Personnel'
import ProjectLinkModel, { ProjectLink } from '../models/ProjectLink'
import ProjectMilestoneModel, { ProjectMilestone } from '../models/ProjectMilestone'
Expand All @@ -30,15 +31,15 @@ export default routes((route) => {
route.get('/projects/tenders-total', handleJSON(getOpenTendersTotal))
})

type ProjectsReturnType = Awaited<ReturnType<typeof ProjectService.getProposalProjects>>

function filterProjectsByDate(projects: ProjectsReturnType, from?: Date, to?: Date): ProjectsReturnType {
return {
data: projects.data.filter((project) => {
const createdAt = new Date(project.created_at)
return (!from || createdAt >= from) && (!to || createdAt < to)
}),
}
function filterProjectsByDate(
projects: ProposalProjectWithUpdate[],
from?: Date,
to?: Date
): ProposalProjectWithUpdate[] {
return projects.filter((project) => {
const createdAt = new Date(project.created_at)
return (!from || createdAt >= from) && (!to || createdAt < to)
})
}

async function getProjects(req: Request) {
Expand All @@ -50,21 +51,20 @@ async function getProjects(req: Request) {
}

const cacheKey = `projects`
const cachedProjects = CacheService.get<ProjectsReturnType>(cacheKey)
const cachedProjects = CacheService.get<ProposalProjectWithUpdate[]>(cacheKey)
if (cachedProjects) {
return filterProjectsByDate(cachedProjects, from, to)
return { data: filterProjectsByDate(cachedProjects, from, to) }
}
const projects = await ProjectService.getProposalProjects()
CacheService.set(cacheKey, projects, TTL_1_HS)
return filterProjectsByDate(projects, from, to)
return { data: filterProjectsByDate(projects, from, to) }
}

async function getProject(req: Request<{ project: string }>) {
const id = validateId(req.params.project)
try {
return await ProjectService.getUpdatedProject(id)
} catch (e) {
console.log(`Error getting project: ${e}`) //TODO: remove before merging projects to main
throw new RequestError(`Project "${id}" not found`, RequestError.NotFound)
}
}
Expand Down
14 changes: 8 additions & 6 deletions src/routes/proposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export default routes((route) => {
route.post('/proposals/bid', withAuth, handleAPI(createProposalBid))
route.post('/proposals/hiring', withAuth, handleAPI(createProposalHiring))
route.get('/proposals/priority/:address?', handleJSON(getPriorityProposals))
route.get('/proposals/grants/:address', handleAPI(getGrantsByUser))
route.get('/proposals/grants/:address', handleAPI(getProjectsByUser)) //TODO: move to project routes
route.get('/proposals/:proposal', handleAPI(getProposalWithProject))
route.patch('/proposals/:proposal', withAuth, handleAPI(updateProposalStatus))
route.delete('/proposals/:proposal', withAuth, handleAPI(removeProposal))
Expand Down Expand Up @@ -591,22 +591,24 @@ async function validateSubmissionThreshold(user: string, submissionThreshold?: s
}
}

async function getGrantsByUser(req: Request) {
async function getProjectsByUser(req: Request) {
const address = validateAddress(req.params.address)

const coauthoring = await CoauthorModel.findProposals(address, CoauthorStatus.APPROVED)
const coauthoringProposalIds = new Set(coauthoring.map((coauthoringAttributes) => coauthoringAttributes.proposal_id))

const projects = await ProjectService.getProposalProjects()
const filteredGrants = projects.data.filter(
const filteredProposalProjectsWithUpdates = projects.filter(
(project) =>
project.type === ProposalType.Grant &&
(isSameAddress(project.user, address) || coauthoringProposalIds.has(project.id))
(isSameAddress(project.user, address) ||
coauthoringProposalIds.has(project.id) ||
project.personnel.some((person) => isSameAddress(person.address, address)))
)

return {
data: filteredGrants,
total: filteredGrants.length,
data: filteredProposalProjectsWithUpdates,
total: filteredProposalProjectsWithUpdates.length,
}
}

Expand Down
8 changes: 3 additions & 5 deletions src/services/ProjectService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ function newestVestingFirst(a: TransparencyVesting, b: TransparencyVesting): num

export class ProjectService {
public static async getProposalProjects() {
const data = await ProposalModel.getProjectList()
const proposalWithProjects = await ProposalModel.getProjectList()
const vestings = await VestingService.getAllVestings()
const projects: ProposalProjectWithUpdate[] = []

await Promise.all(
data.map(async (proposal) => {
proposalWithProjects.map(async (proposal) => {
try {
const proposalVestings = vestings.filter((item) => item.proposal_id === proposal.id).sort(newestVestingFirst)
const prioritizedVesting: TransparencyVesting | undefined =
Expand Down Expand Up @@ -80,9 +80,7 @@ export class ProjectService {
})
)

return {
data: projects,
}
return projects
}

private static getUpdateData(update: (UpdateAttributes & { index: number }) | null) {
Expand Down
2 changes: 2 additions & 0 deletions src/utils/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ export function createProposalProject(proposal: ProposalWithProject, vesting?: T
status,
title: proposal.title,
user: proposal.user,
personnel: proposal.personnel,
coAuthors: proposal.coAuthors,
about: proposal.configuration.abstract,
type: proposal.type,
size: proposal.configuration.size || proposal.configuration.funding,
Expand Down

0 comments on commit 3aefe65

Please sign in to comment.