-
-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: add credits consuming * test: fix existing tests * test: add int tests * test: add unit tests * feat: support credits headers * fix: fix tests * test: add more tests * refactor: rate limiter * docs: update openapi * fix: header tests * fix: required defaults to false * docs: update descriptions * fix: stricter check * fix: simplify and improve requiredPoints calculation * feat: add credits-required headers * docs: update headers docs * feat: separate limits for authenticated and anon users * fix: location limits sum <= global limit --------- Co-authored-by: Martin Kolárik <[email protected]>
- Loading branch information
1 parent
0598c16
commit 5328364
Showing
15 changed files
with
621 additions
and
114 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
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
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,19 +1,38 @@ | ||
components: | ||
headers: | ||
CreditsConsumed: | ||
description: The number of credits consumed by the request. Returned only when credits were successfully consumed. | ||
required: false | ||
schema: | ||
type: integer | ||
CreditsRequired: | ||
description: The number of credits required to process the request. Returned only when the credits in your account were not sufficient, and the request was rejected. | ||
required: false | ||
schema: | ||
type: integer | ||
CreditsRemaining: | ||
description: The number of credits remaining. Returned only when an attempt to use credits was made (requests with a valid token exceeding the hourly rate limit). | ||
required: false | ||
schema: | ||
type: integer | ||
MeasurementLocation: | ||
description: A link to the newly created measurement. | ||
required: true | ||
schema: | ||
type: string | ||
format: uri | ||
RateLimitLimit: | ||
description: The number of requests available in a given time window. | ||
required: true | ||
schema: | ||
type: integer | ||
RateLimitRemaining: | ||
description: The number of requests remaining in the current time window. | ||
required: true | ||
schema: | ||
type: integer | ||
RateLimitReset: | ||
description: The number of seconds until the limit is reset. | ||
required: true | ||
schema: | ||
type: integer |
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
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,45 @@ | ||
import type { Knex } from 'knex'; | ||
import { client } from './sql/client.js'; | ||
|
||
export const CREDITS_TABLE = 'gp_credits'; | ||
const ER_CONSTRAINT_FAILED_CODE = 4025; | ||
|
||
export class Credits { | ||
constructor (private readonly sql: Knex) {} | ||
|
||
async consume (userId: string, credits: number): Promise<{ isConsumed: boolean, remainingCredits: number }> { | ||
let numberOfUpdates = null; | ||
|
||
try { | ||
numberOfUpdates = await this.sql(CREDITS_TABLE).where({ user_id: userId }).update({ amount: this.sql.raw('amount - ?', [ credits ]) }); | ||
} catch (error) { | ||
if (error && (error as Error & {errno?: number}).errno === ER_CONSTRAINT_FAILED_CODE) { | ||
const remainingCredits = await this.getRemainingCredits(userId); | ||
return { isConsumed: false, remainingCredits }; | ||
} | ||
|
||
throw error; | ||
} | ||
|
||
if (numberOfUpdates === 0) { | ||
return { isConsumed: false, remainingCredits: 0 }; | ||
} | ||
|
||
const remainingCredits = await this.getRemainingCredits(userId); | ||
return { isConsumed: true, remainingCredits }; | ||
} | ||
|
||
async getRemainingCredits (userId: string): Promise<number> { | ||
const result = await this.sql(CREDITS_TABLE).where({ user_id: userId }).select<[{ amount: number }]>('amount'); | ||
|
||
const remainingCredits = result[0]?.amount; | ||
|
||
if (remainingCredits || remainingCredits === 0) { | ||
return remainingCredits; | ||
} | ||
|
||
throw new Error('Credits data for the user not found.'); | ||
} | ||
} | ||
|
||
export const credits = new Credits(client); |
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
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
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
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
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
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
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
Oops, something went wrong.