Repository: polkassembly/polkassembly
Pull request #2355: Feat/nested votes api
Author: login: kanishkarajput01
Created: 2025-03-21T07:12:59Z | Merged: 2025-03-25T09:03:32Z



<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
	- Introduced a new API endpoint that retrieves nested voting details with comprehensive information such as balance, decision, and delegation data.
	- Added a query to aggregate detailed conviction voting data for proposals, offering complete insights into vote counts and delegation.
	- Enhanced data structures to ensure consistent and accurate representation of nested vote information across the system.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/pages/api/v1/votes/nestedVotes.ts b/pages/api/v1/votes/nestedVotes.ts
new file mode 100644
index 0000000000..fd330f2f63
--- /dev/null
+++ b/pages/api/v1/votes/nestedVotes.ts
@@ -0,0 +1,79 @@
+// Copyright 2019-2025 @polkassembly/polkassembly authors & contributors
+// This software may be modified and distributed under the terms
+// of the Apache-2.0 license. See the LICENSE file for details.
+
+import { NextApiRequest, NextApiResponse } from 'next';
+import withErrorHandling from '~src/api-middlewares/withErrorHandling';
+import { isValidNetwork } from '~src/api-utils';
+import { VoteType, voteTypes } from '~src/global/proposalType';
+import { GET_ALL_NESTED_VOTES } from '~src/queries';
+import { INestedVotesRes } from '~src/types';
+import fetchSubsquid from '~src/util/fetchSubsquid';
+
+async function handler(req: NextApiRequest, res: NextApiResponse) {
+	if (req.method !== 'GET') {
+		return res.status(405).json({ error: 'Method not allowed' });
+	}
+	try {
+		const { postId, voteType = VoteType.REFERENDUM_V2 } = req.query;
+
+		const network = String(req.headers['x-network']);
+		if (!network || !isValidNetwork(network)) {
+			return res.status(400).json({ error: 'Invalid network in request header' });
+		}
+
+		const strVoteType = String(voteType);
+		if (!voteTypes.includes(strVoteType)) {
+			return res.status(400).json({ error: `The voteType "${voteType}" is invalid.` });
+		}
+
+		const numPostId = Number(postId);
+		if (isNaN(numPostId) || numPostId < 0) {
+			return res.status(400).json({ error: `The postId "${postId}" is invalid.` });
+		}
+
+		const variables = {
+			index_eq: numPostId,
+			type_eq: voteType
+		};
+
+		const subsqidResponse = await fetchSubsquid({
+			network,
+			query: GET_ALL_NESTED_VOTES,
+			variables
+		});
+
+		const allVotes = subsqidResponse?.data?.convictionVotes || [];
+		const totalCount = subsqidResponse?.data?.convictionVotesConnection?.totalCount || 0;
+
+		const result: INestedVotesRes = {
+			totalCount: totalCount || 0,
+			votes:
+				allVotes?.map((vote: any) => ({
+					balance: vote?.balance?.value || vote?.balance?.abstain || '0',
+					createdAt: vote?.createdAt,
+					decision: vote?.decision || null,
+					delegatedTo: vote?.delegatedTo || '',
+					delegatedVotes:
+						vote?.delegatedVotes?.map((delegatedVote: any) => ({
+							voter: delegatedVote?.voter,
+							votingPower: delegatedVote?.votingPower || '0'
+						})) || [],
+					delegatedVotingPower: vote?.delegatedVotingPower || '0',
+					delegatorsCount: vote?.delegatedVotes?.length || 0,
+					extrinsicIndex: vote?.extrinsicIndex,
+					isDelegatedVote: false,
+					lockPeriod: Number(vote?.lockPeriod) || 0.1,
+					selfVotingPower: vote?.selfVotingPower || '0',
+					voter: vote?.voter
+				})) || []
+		};
+
+		return res.status(200).json(result);
+	} catch (error) {
+		console.error(error);
+		return res.status(500).json({ error: 'Internal server error' });
+	}
+}
+
+export default withErrorHandling(handler);
diff --git a/src/queries.ts b/src/queries.ts
index aadd9d5a9a..880d35506e 100644
--- a/src/queries.ts
+++ b/src/queries.ts
@@ -3434,3 +3434,34 @@ query MyQuery ($index_in: [Int!] , $type: ProposalType ){
   }
 }
 `;
+
+export const GET_ALL_NESTED_VOTES = `query GET_ALL_NESTED_VOTES($index_eq: Int, $type_eq: VoteType = ReferendumV2) {
+  convictionVotesConnection(orderBy: id_ASC, where: {type_eq: $type_eq, proposal: {index_eq: $index_eq}, removedAtBlock_isNull: true}) {
+    totalCount
+  }
+  convictionVotes(orderBy: createdAtBlock_DESC, where: {type_eq: $type_eq, proposal: {index_eq: $index_eq}, removedAtBlock_isNull: true}) {
+    id
+    decision
+    voter
+    balance {
+      ... on StandardVoteBalance {
+        value
+      }
+      ... on SplitVoteBalance {
+        aye
+        nay
+        abstain
+      }
+    }
+    createdAt
+    lockPeriod
+    selfVotingPower
+    totalVotingPower
+    delegatedVotingPower
+    delegatedVotes(orderBy: votingPower_DESC, where: {removedAtBlock_isNull: true}) {
+      voter
+      votingPower
+    }
+  }
+}
+`;
diff --git a/src/types.ts b/src/types.ts
index 147613a97e..10ff1fd866 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1332,3 +1332,22 @@ export interface TreasuryData {
 		usdc: string;
 	};
 }
+
+export interface INestedVotes {
+	balance: string;
+	createdAt: Date;
+	decision: string;
+	delegatedTo: string;
+	delegatedVotes: { voter: string; votingPower: string }[];
+	delegatorsCount: number;
+	extrinsicIndex: string;
+	isDelegatedVote: boolean;
+	lockPeriod: number;
+	selfVotingPower: string;
+	voter: string;
+	delegatedVotingPower: string;
+}
+export interface INestedVotesRes {
+	totalCount: number;
+	votes: INestedVotes[];
+}


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 3**

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>pages/api/v1/votes/nestedVotes.ts (1)</summary><blockquote>

`65-66`: **Unusual default for `lockPeriod`.**

The `lockPeriod` defaults to 0.1 if not present, which seems like an unusual default value.


Consider using 0 as the default or providing a comment explaining why 0.1 is chosen:

```diff
- lockPeriod: Number(vote?.lockPeriod) || 0.1,
+ lockPeriod: Number(vote?.lockPeriod) || 0, // Default to 0 if no lock period is specified
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between c3fd974a6a44c705bee99897efe232c72713c8e3 and c873fe0d57a718f095a06a30bd126cfd7d3ce6e3.

</details>

<details>
<summary>📒 Files selected for processing (3)</summary>

* `pages/api/v1/votes/nestedVotes.ts` (1 hunks)
* `src/queries.ts` (1 hunks)
* `src/types.ts` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Definitions (1)</summary>

<details>
<summary>pages/api/v1/votes/nestedVotes.ts (2)</summary><blockquote>

<details>
<summary>src/queries.ts (1) (1)</summary>

* `GET_ALL_NESTED_VOTES` (3438-3467)

</details>
<details>
<summary>src/types.ts (1) (1)</summary>

* `INestedVote` (1336-1351)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>🔇 Additional comments (10)</summary><blockquote>

<details>
<summary>pages/api/v1/votes/nestedVotes.ts (9)</summary>

`1-12`: **Clean imports and proper organization.**

The file starts with appropriate copyright notices and imports necessary dependencies. The organization is clean and follows best practices.

---

`13-16`: **Method validation is properly implemented.**

The handler correctly validates that only GET requests are allowed and returns an appropriate 405 status code for other methods.

---

`17-23`: **Network validation is properly implemented.**

The code appropriately validates the network from request headers and returns a clear error message if invalid.

---

`25-28`: **Vote type validation is well-handled.**

The code validates the vote type against a predefined list of valid types, returning an appropriate error if invalid.

---

`30-33`: **Post ID validation looks good.**

The code ensures that the post ID is a valid non-negative number before proceeding.

---

`35-44`: **API request is properly structured.**

The variables for the GraphQL query are correctly built and passed to the fetchSubsquid utility.

---

`46-47`: **Safe data extraction with defaults.**

The code safely extracts data from the response with default values in case of undefined results.

---

`71-75`: **Error handling looks good.**

The error handling captures and logs errors while returning a generic error message to the client, which is good for security.

---

`77-79`: **Appropriate use of error handling middleware.**

The handler is wrapped with the `withErrorHandling` middleware, which is a good practice for consistent error handling across API routes.

</details>
<details>
<summary>src/queries.ts (1)</summary>

`3438-3466`: **Well-structured GraphQL query for nested votes.**

The `GET_ALL_NESTED_VOTES` query is well-constructed to retrieve comprehensive voting data, including total count, individual votes, and delegated votes information. It properly excludes removed votes with the `removedAtBlock_isNull: true` filter.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-21T07:21:34Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>pages/api/v1/votes/nestedVotes.ts (1)</summary><blockquote>

`49-70`: _🛠️ Refactor suggestion_

**Hardcoded `isDelegatedVote` value.**

The `isDelegatedVote` field is hardcoded to `false` without considering whether the vote is actually delegated.


Determine this value based on actual data:

```diff
-					isDelegatedVote: false,
+					isDelegatedVote: !!vote?.delegatedTo,
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between c873fe0d57a718f095a06a30bd126cfd7d3ce6e3 and e1e90875314226e903ad14d9b28b6b6bba15e829.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `pages/api/v1/votes/nestedVotes.ts` (1 hunks)
* `src/types.ts` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Definitions (1)</summary>

<details>
<summary>pages/api/v1/votes/nestedVotes.ts (2)</summary><blockquote>

<details>
<summary>src/queries.ts (1) (1)</summary>

* `GET_ALL_NESTED_VOTES` (3438-3467)

</details>
<details>
<summary>src/types.ts (1) (1)</summary>

* `INestedVotes` (1336-1351)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>🔇 Additional comments (5)</summary><blockquote>

<details>
<summary>pages/api/v1/votes/nestedVotes.ts (5)</summary>

`5-11`: **LGTM: Good import organization.**

The imports are well-organized and include all necessary dependencies for the API endpoint.

---

`13-38`: **Good parameter validation.**

The request validation logic is thorough, checking for valid HTTP method, network, vote type, and post ID parameters.

---

`40-47`: **LGTM: Well-structured data fetching.**

The implementation correctly uses the fetchSubsquid utility with proper parameters and handles the response data appropriately.

---

`72-76`: **LGTM: Proper error handling.**

The error handling is well-implemented, with appropriate status codes and error messages.

---

`79-79`: **LGTM: Good middleware usage.**

The endpoint is wrapped with the error handling middleware, which is a good practice for consistent error responses.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-21T08:07:27Z
user: login: alphainfinitus
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-21T08:13:36Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>pages/api/v1/votes/nestedVotes.ts (1)</summary><blockquote>

`49-70`: _🛠️ Refactor suggestion_

**Fix the hardcoded `isDelegatedVote` value.**

The `isDelegatedVote` field is currently hardcoded to `false`, but it should be determined based on whether the vote has been delegated.


```diff
-					isDelegatedVote: false,
+					isDelegatedVote: !!vote?.delegatedTo,
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>pages/api/v1/votes/nestedVotes.ts (1)</summary><blockquote>

`73-76`: **Improve error logging for better debugging.**

The current error logging only uses `console.error` which may not be sufficient for debugging complex issues in production.


```diff
-		console.error(error);
+		console.error('Error fetching nested votes:', {
+			error: error instanceof Error ? error.message : String(error),
+			postId,
+			voteType,
+			network
+		});
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between e1e90875314226e903ad14d9b28b6b6bba15e829 and ae4045ef419e0ffe3e0f147d7cca46ca8c5162ff.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `pages/api/v1/votes/nestedVotes.ts` (1 hunks)
* `src/types.ts` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (4)</summary><blockquote>

<details>
<summary>src/types.ts (1)</summary>

`1336-1353`: **New interfaces for nested votes look good.**

The `INestedVotes` and `INestedVotesRes` interfaces are well-structured and provide clear typing for the nested votes API. The inclusion of the `delegatedVotingPower` field addresses a past review comment.

</details>
<details>
<summary>pages/api/v1/votes/nestedVotes.ts (3)</summary>

`13-33`: **API endpoint validation looks good.**

The API endpoint correctly validates the request method, network, vote type, and post ID parameters before processing the request.

---

`35-44`: **GraphQL query implementation is correct.**

The variables are properly structured for the GraphQL query, and the `fetchSubsquid` utility is used appropriately to fetch data.

---

`66-67`: **Review the default `lockPeriod` value.**

The `lockPeriod` defaults to 0.1 when not provided. Consider whether this is the appropriate fallback value or if it should be 0 or another value based on business requirements.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-21T08:35:00Z

Review comments:
diff_hunk: @@ -0,0 +1,78 @@
+// Copyright 2019-2025 @polkassembly/polkassembly authors & contributors
+// This software may be modified and distributed under the terms
+// of the Apache-2.0 license. See the LICENSE file for details.
+
+import { NextApiRequest, NextApiResponse } from 'next';
+import withErrorHandling from '~src/api-middlewares/withErrorHandling';
+import { isValidNetwork } from '~src/api-utils';
+import { VoteType, voteTypes } from '~src/global/proposalType';
+import { GET_ALL_NESTED_VOTES } from '~src/queries';
+import { INestedVote } from '~src/types';
+import fetchSubsquid from '~src/util/fetchSubsquid';
+
+async function handler(req: NextApiRequest, res: NextApiResponse) {
+	if (req.method !== 'GET') {
+		return res.status(405).json({ error: 'Method not allowed' });
+	}
+	try {
+		const { postId, voteType = VoteType.REFERENDUM_V2 } = req.query;
+
+		const network = String(req.headers['x-network']);
+		if (!network || !isValidNetwork(network)) {
+			return res.status(400).json({ error: 'Invalid network in request header' });
+		}
+
+		const strVoteType = String(voteType);
+		if (!voteTypes.includes(strVoteType)) {
+			return res.status(400).json({ error: `The voteType "${voteType}" is invalid.` });
+		}
+
+		const numPostId = Number(postId);
+		if (isNaN(numPostId) || numPostId < 0) {
+			return res.status(400).json({ error: `The postId "${postId}" is invalid.` });
+		}
+
+		const variables = {
+			index_eq: numPostId,
+			type_eq: voteType
+		};
+
+		const subsqidResponse = await fetchSubsquid({
+			network,
+			query: GET_ALL_NESTED_VOTES,
+			variables
+		});
+
+		const allVotes = subsqidResponse?.data?.convictionVotes || [];
+		const totalCount = subsqidResponse?.data?.convictionVotesConnection?.totalCount || 0;
+
+		const result: INestedVote = {
+			totalCount: totalCount || 0,
+			votes: allVotes?.map((vote: any) => ({
+				balance: vote?.balance?.value || vote?.balance?.abstain || '0',
+				createdAt: vote?.createdAt,
+				decision: vote?.decision || null,
+				delegatedTo: vote?.delegatedTo || '',
+				delegatedVotes:
+					vote?.delegatedVotes?.map((delegatedVote: any) => ({
+						voter: delegatedVote?.voter,
+						votingPower: delegatedVote?.votingPower || '0'
+					})) || [],
+				delegatedVotingPower: vote?.delegatedVotingPower || '0',
+				delegatorsCount: vote?.delegatedVotes?.length || 0,
+				extrinsicIndex: vote?.extrinsicIndex,
+				isDelegatedVote: false,
+				lockPeriod: Number(vote?.lockPeriod) || 0.1,
path: pages/api/v1/votes/nestedVotes.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Hardcoded `isDelegatedVote` value.**

The `isDelegatedVote` field is hardcoded to `false` without any condition. This might not accurately reflect whether a vote is delegated or not.


Consider determining this value based on actual data:

```diff
- isDelegatedVote: false,
+ isDelegatedVote: !!vote?.delegatedTo,
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
				isDelegatedVote: !!vote?.delegatedTo,
				lockPeriod: Number(vote?.lockPeriod) || 0.1,
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-21T07:21:33Z
author_association: NONE
diff_hunk: @@ -0,0 +1,78 @@
+// Copyright 2019-2025 @polkassembly/polkassembly authors & contributors
+// This software may be modified and distributed under the terms
+// of the Apache-2.0 license. See the LICENSE file for details.
+
+import { NextApiRequest, NextApiResponse } from 'next';
+import withErrorHandling from '~src/api-middlewares/withErrorHandling';
+import { isValidNetwork } from '~src/api-utils';
+import { VoteType, voteTypes } from '~src/global/proposalType';
+import { GET_ALL_NESTED_VOTES } from '~src/queries';
+import { INestedVote } from '~src/types';
+import fetchSubsquid from '~src/util/fetchSubsquid';
+
+async function handler(req: NextApiRequest, res: NextApiResponse) {
+	if (req.method !== 'GET') {
+		return res.status(405).json({ error: 'Method not allowed' });
+	}
+	try {
+		const { postId, voteType = VoteType.REFERENDUM_V2 } = req.query;
+
+		const network = String(req.headers['x-network']);
+		if (!network || !isValidNetwork(network)) {
+			return res.status(400).json({ error: 'Invalid network in request header' });
+		}
+
+		const strVoteType = String(voteType);
+		if (!voteTypes.includes(strVoteType)) {
+			return res.status(400).json({ error: `The voteType "${voteType}" is invalid.` });
+		}
+
+		const numPostId = Number(postId);
+		if (isNaN(numPostId) || numPostId < 0) {
+			return res.status(400).json({ error: `The postId "${postId}" is invalid.` });
+		}
+
+		const variables = {
+			index_eq: numPostId,
+			type_eq: voteType
+		};
+
+		const subsqidResponse = await fetchSubsquid({
+			network,
+			query: GET_ALL_NESTED_VOTES,
+			variables
+		});
+
+		const allVotes = subsqidResponse?.data?.convictionVotes || [];
+		const totalCount = subsqidResponse?.data?.convictionVotesConnection?.totalCount || 0;
+
+		const result: INestedVote = {
+			totalCount: totalCount || 0,
+			votes: allVotes?.map((vote: any) => ({
+				balance: vote?.balance?.value || vote?.balance?.abstain || '0',
+				createdAt: vote?.createdAt,
+				decision: vote?.decision || null,
+				delegatedTo: vote?.delegatedTo || '',
+				delegatedVotes:
+					vote?.delegatedVotes?.map((delegatedVote: any) => ({
+						voter: delegatedVote?.voter,
+						votingPower: delegatedVote?.votingPower || '0'
+					})) || [],
+				delegatedVotingPower: vote?.delegatedVotingPower || '0',
+				delegatorsCount: vote?.delegatedVotes?.length || 0,
+				extrinsicIndex: vote?.extrinsicIndex,
+				isDelegatedVote: false,
+				lockPeriod: Number(vote?.lockPeriod) || 0.1,
+				selfVotingPower: vote?.selfVotingPower || '0',
+				voter: vote?.voter
+			}))
+		};
path: pages/api/v1/votes/nestedVotes.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Type mismatch between interface and implementation.**

There appears to be a discrepancy between the `INestedVote` interface and how it's used here. The interface defines `votes` as an object, but the implementation treats it as an array.

Additionally, the `delegatedVotingPower` field is used in the mapping but isn't defined in the interface shown in the context.


Please reconcile the interface definition with the implementation. Consider updating the interface to reflect that `votes` should be an array or adjust the implementation accordingly:

```diff
export interface INestedVote {
	totalCount: number;
-	votes: {
+	votes: Array<{
		balance: string;
		createdAt: Date;
		decision: string;
		delegatedTo: string;
		delegatedVotes: { voter: string; votingPower: string }[];
		delegatorsCount: number;
		extrinsicIndex: string;
		isDelegatedVote: boolean;
		lockPeriod: number;
		selfVotingPower: string;
		voter: string;
+		delegatedVotingPower?: string;
-	};
+	}>;
}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
export interface INestedVote {
	totalCount: number;
	votes: Array<{
		balance: string;
		createdAt: Date;
		decision: string;
		delegatedTo: string;
		delegatedVotes: { voter: string; votingPower: string }[];
		delegatorsCount: number;
		extrinsicIndex: string;
		isDelegatedVote: boolean;
		lockPeriod: number;
		selfVotingPower: string;
		voter: string;
		delegatedVotingPower?: string;
	}>;
}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-21T07:21:33Z
author_association: NONE
diff_hunk: @@ -1332,3 +1332,20 @@ export interface TreasuryData {
 		usdc: string;
 	};
 }
+
+export interface INestedVote {
+	totalCount: number;
+	votes: {
+		balance: string;
+		createdAt: Date;
+		decision: string;
+		delegatedTo: string;
+		delegatedVotes: { voter: string; votingPower: string }[];
+		delegatorsCount: number;
+		extrinsicIndex: string;
+		isDelegatedVote: boolean;
+		lockPeriod: number;
+		selfVotingPower: string;
+		voter: string;
+	};
+}
path: src/types.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Interface structure inconsistency.**

The `INestedVote` interface defines `votes` as a single object, but the implementation in `nestedVotes.ts` treats it as an array of objects. This mismatch could lead to type errors or runtime issues.


Update the interface to correctly reflect that `votes` is an array:

```diff
export interface INestedVote {
	totalCount: number;
-	votes: {
+	votes: Array<{
		balance: string;
		createdAt: Date;
		decision: string;
		delegatedTo: string;
		delegatedVotes: { voter: string; votingPower: string }[];
		delegatorsCount: number;
		extrinsicIndex: string;
		isDelegatedVote: boolean;
		lockPeriod: number;
		selfVotingPower: string;
		voter: string;
+		delegatedVotingPower?: string; // Add this field used in the implementation
-	};
+	}>;
}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
export interface INestedVote {
	totalCount: number;
	votes: Array<{
		balance: string;
		createdAt: Date;
		decision: string;
		delegatedTo: string;
		delegatedVotes: { voter: string; votingPower: string }[];
		delegatorsCount: number;
		extrinsicIndex: string;
		isDelegatedVote: boolean;
		lockPeriod: number;
		selfVotingPower: string;
		voter: string;
		delegatedVotingPower?: string; // Add this field used in the implementation
	}>;
}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit ae4045e
created_at: 2025-03-21T07:21:33Z
author_association: NONE
diff_hunk: @@ -1332,3 +1332,20 @@ export interface TreasuryData {
 		usdc: string;
 	};
 }
+
+export interface INestedVotes {
+	totalCount: number;
+	votes: {
+		balance: string;
+		createdAt: Date;
+		decision: string;
+		delegatedTo: string;
+		delegatedVotes: { voter: string; votingPower: string }[];
+		delegatorsCount: number;
+		extrinsicIndex: string;
+		isDelegatedVote: boolean;
+		lockPeriod: number;
+		selfVotingPower: string;
+		voter: string;
+	}[];
+}
path: src/types.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Missing property in INestedVotes interface.**

The `INestedVotes` interface is well-structured but is missing the `delegatedVotingPower` property that is used in the implementation. This could lead to type errors.

Apply this diff to add the missing property:

```diff
export interface INestedVotes {
	totalCount: number;
	votes: {
		balance: string;
		createdAt: Date;
		decision: string;
		delegatedTo: string;
		delegatedVotes: { voter: string; votingPower: string }[];
		delegatorsCount: number;
		extrinsicIndex: string;
		isDelegatedVote: boolean;
		lockPeriod: number;
		selfVotingPower: string;
		voter: string;
+		delegatedVotingPower: string;
	}[];
}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
export interface INestedVotes {
	totalCount: number;
	votes: {
		balance: string;
		createdAt: Date;
		decision: string;
		delegatedTo: string;
		delegatedVotes: { voter: string; votingPower: string }[];
		delegatorsCount: number;
		extrinsicIndex: string;
		isDelegatedVote: boolean;
		lockPeriod: number;
		selfVotingPower: string;
		voter: string;
		delegatedVotingPower: string;
	}[];
}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-21T08:07:26Z
author_association: NONE

PR comments:
user: login: vercel[bot]
created_at: 2025-03-21T07:13:02Z
body: [vc]: #c35QX2zq6eUm/RRCNRKnWv4+JfQLZVQgmOn7vHeMP58=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJwb2xrYXNzZW1ibHkiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoicG9sa2Fzc2VtYmx5LWdpdC1mZWF0LW5lc3RlZC12b3Rlcy1hcGktcG9sa2Fzc2VtYmx5LW5leHQudmVyY2VsLmFwcCJ9LCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vcG9sa2Fzc2VtYmx5LW5leHQvcG9sa2Fzc2VtYmx5L0V0eVBYdXB3UzN0cDMzRTFNWHRXNWJqbVZzM2UiLCJwcmV2aWV3VXJsIjoicG9sa2Fzc2VtYmx5LWdpdC1mZWF0LW5lc3RlZC12b3Rlcy1hcGktcG9sa2Fzc2VtYmx5LW5leHQudmVyY2VsLmFwcCIsIm5leHRDb21taXRTdGF0dXMiOiJERVBMT1lFRCIsInJvb3REaXJlY3RvcnkiOm51bGx9XX0=
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **polkassembly** | ✅ Ready ([Inspect](https://vercel.com/polkassembly-next/polkassembly/EtyPXupwS3tp33E1MXtW5bjmVs3e)) | [Visit Preview](https://polkassembly-git-feat-nested-votes-api-polkassembly-next.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/polkassembly-git-feat-nested-votes-api-polkassembly-next.vercel.app?via=pr-comment-feedback-link) | Mar 21, 2025 8:35am |


author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-03-21T07:13:05Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
A new API endpoint has been implemented to retrieve nested votes associated with a specific post. The endpoint validates request parameters and utilizes a utility to fetch vote data from a back-end service. Additionally, a new GraphQL query has been added to retrieve detailed nested conviction vote information, and corresponding TypeScript interfaces have been defined to structure the vote data.

## Changes

| File(s)                                 | Summary                                                                                                                                                      |
|-----------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `pages/api/v1/votes/nestedVotes.ts`       | Introduces a GET API endpoint to retrieve nested votes; validates request method, network, voteType, and postId; returns formatted vote data.               |
| `src/queries.ts`                        | Adds the `GET_ALL_NESTED_VOTES` GraphQL query which fetches comprehensive nested conviction vote details and delegation information.                        |
| `src/types.ts`                          | Defines the `INestedVotes` interface describing the structure for nested vote data including balance, creation date, decision, delegation details, etc.      |

## Sequence Diagram(s)
```mermaid
sequenceDiagram
  participant Client
  participant API
  participant FetchUtil
  Client->>API: GET /api/v1/votes/nestedVotes?voteType=...&postId=...
  API->>API: Validate method, network, voteType, postId
  API->>FetchUtil: Execute nested votes query with parameters
  FetchUtil-->>API: Return nested votes data
  API->>Client: Respond with 200 OK and formatted vote data
```

## Suggested reviewers
- orphic-code  
- This-Is-Prince

## Poem
> I'm a coding rabbit, hopping with glee,  
> New endpoints and queries as fresh as can be.  
> Nested votes neatly arranged like carrots in a row,  
> Data and validation helping our app grow.  
> With a twitch of my nose, I celebrate this flow!  
> 🐇✨

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=polkassembly/polkassembly&utm_content=2355):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE