@@ -17,8 +17,9 @@ import DataAccessError from '../../errors/data-access.error.js';
1717
1818/**
1919 * SuggestionGrantCollection - Manages SuggestionGrant records (suggestion_grants table).
20- * Table is insert-only; inserts happen via grant_suggestions RPC. This collection
21- * provides read-only lookup by suggestion IDs.
20+ * Inserts happen via the grant_suggestions RPC and deletes via the revoke_suggestion_grant
21+ * RPC. This collection provides read-only lookup by suggestion IDs as well as grant and
22+ * revoke operations.
2223 *
2324 * @class SuggestionGrantCollection
2425 * @extends BaseCollection
@@ -119,51 +120,6 @@ class SuggestionGrantCollection extends BaseCollection {
119120 return grantedIds . length > 0 ;
120121 }
121122
122- /**
123- * Invokes the revoke_suggestion_grant RPC. Deletes suggestion_grants rows for the given
124- * grant ID and decrements tokens.used by 1.
125- * RPC name and parameter shape live in this collection (suggestion_grants).
126- *
127- * @async
128- * @param {string } grantId - Grant ID to revoke.
129- * @returns {Promise<{ data: Array|null, error: object|null }> }
130- */
131- async invokeRevokeSuggestionGrantRpc ( grantId ) {
132- return this . postgrestService . rpc ( 'revoke_suggestion_grant' , {
133- p_grant_id : grantId ,
134- } ) ;
135- }
136-
137- /**
138- * Revokes a suggestion grant by grant ID. Calls the revoke_suggestion_grant RPC to
139- * atomically delete suggestion_grants rows and refund the consumed token.
140- *
141- * @async
142- * @param {string } grantId - The grant ID to revoke.
143- * @returns {Promise<{ success: boolean, reason?: string, revokedCount?: number }> }
144- * @throws {DataAccessError } - On missing inputs or RPC failure.
145- */
146- async revokeSuggestionGrant ( grantId ) {
147- if ( ! hasText ( grantId ) ) {
148- throw new DataAccessError ( 'revokeSuggestionGrant: grantId is required' , this ) ;
149- }
150-
151- const rpcResult = await this . invokeRevokeSuggestionGrantRpc ( grantId ) ;
152- const { data, error } = rpcResult ;
153-
154- if ( error ) {
155- this . log . error ( 'revokeSuggestionGrant: RPC failed' , error ) ;
156- throw new DataAccessError ( 'Failed to revoke suggestion grant (revoke_suggestion_grant)' , this , error ) ;
157- }
158-
159- const row = Array . isArray ( data ) && data . length > 0 ? data [ 0 ] : null ;
160- if ( ! row || ! row . success ) {
161- return { success : false , reason : row ?. reason || 'rpc_no_result' } ;
162- }
163-
164- return { success : true , revokedCount : row . revoked_count } ;
165- }
166-
167123 /**
168124 * Grants one or more suggestions by consuming a single token for the given token type.
169125 * Resolves the current cycle token via TokenCollection#findBySiteIdAndTokenType
@@ -217,6 +173,55 @@ class SuggestionGrantCollection extends BaseCollection {
217173
218174 return { success : true , grantedSuggestions : row . granted_suggestions } ;
219175 }
176+
177+ /**
178+ * Invokes the revoke_suggestion_grant RPC. Deletes suggestion_grants rows for the given
179+ * grant ID and decrements tokens.used by 1.
180+ * RPC name and parameter shape live in this collection (suggestion_grants).
181+ *
182+ * @async
183+ * @param {string } grantId - Grant ID to revoke.
184+ * @returns {Promise<{ data: Array|null, error: object|null }> }
185+ * @throws {DataAccessError } - On missing grantId.
186+ */
187+ async invokeRevokeSuggestionGrantRpc ( grantId ) {
188+ if ( ! hasText ( grantId ) ) {
189+ throw new DataAccessError ( 'invokeRevokeSuggestionGrantRpc: grantId is required' , this ) ;
190+ }
191+ return this . postgrestService . rpc ( 'revoke_suggestion_grant' , {
192+ p_grant_id : grantId ,
193+ } ) ;
194+ }
195+
196+ /**
197+ * Revokes a suggestion grant by grant ID. Calls the revoke_suggestion_grant RPC to
198+ * atomically delete suggestion_grants rows and refund the consumed token.
199+ *
200+ * @async
201+ * @param {string } grantId - The grant ID to revoke.
202+ * @returns {Promise<{ success: boolean, reason?: string, revokedCount?: number }> }
203+ * @throws {DataAccessError } - On missing inputs or RPC failure.
204+ */
205+ async revokeSuggestionGrant ( grantId ) {
206+ if ( ! hasText ( grantId ) ) {
207+ throw new DataAccessError ( 'revokeSuggestionGrant: grantId is required' , this ) ;
208+ }
209+
210+ const rpcResult = await this . invokeRevokeSuggestionGrantRpc ( grantId ) ;
211+ const { data, error } = rpcResult ;
212+
213+ if ( error ) {
214+ this . log . error ( 'revokeSuggestionGrant: RPC failed' , error ) ;
215+ throw new DataAccessError ( 'Failed to revoke suggestion grant (revoke_suggestion_grant)' , this , error ) ;
216+ }
217+
218+ const row = Array . isArray ( data ) && data . length > 0 ? data [ 0 ] : null ;
219+ if ( ! row || ! row . success ) {
220+ return { success : false , reason : row ?. reason || 'rpc_no_result' } ;
221+ }
222+
223+ return { success : true , revokedCount : row . revoked_count } ;
224+ }
220225}
221226
222227export default SuggestionGrantCollection ;
0 commit comments