startgg-helper 2.3.0 → 2.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/readme.md +95 -9
- package/src/jsUtil.js +42 -10
- package/src/query.js +61 -30
- package/src/queryLimiter.js +17 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "startgg-helper",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.2",
|
|
4
4
|
"description": "A set of functions and classes useful to communicate with the start.gg API, using any client (YOU NEED TO PROVIDE A CLIENT YOURSELF, SEE README)",
|
|
5
5
|
"main": "main.js",
|
|
6
6
|
"module": "main.js",
|
package/readme.md
CHANGED
|
@@ -1,16 +1,102 @@
|
|
|
1
1
|
# start.gg Helper
|
|
2
2
|
|
|
3
|
-
A set of functions and classes useful to interact with the start.gg GraphQL API.
|
|
3
|
+
A set of functions and classes useful to interact with the [start.gg GraphQL API](https://developer.start.gg/docs/intro/). It does NOT provide abstractions for the actual data retrieved from the API (events, players, sets, etc), only eases making the queries. An understanding of [GraphQL](https://graphql.org/learn/introduction/), and [start.gg's GraphQL Schema](https://smashgg-schema.netlify.app/reference/query.doc.html) are necessary to leverage the API through this package.
|
|
4
4
|
|
|
5
|
-
## You need to provide a client to interact with the API
|
|
6
|
-
|
|
5
|
+
## You need to provide a client to interact with the API
|
|
6
|
+
tl;dr : you're probably looking for `startgg-helper-node` or `startgg-helper-browser`.
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
To interact with the API, the functions in this package need (and take as argument) a "client" object, able to send requests to the GraphQL API. Such a client **is not provided by this package**. This is to ensure that this package is usable not only in a node ecosystem, but also in browser front-end code : sending requests to an API is done differently in browser-oriented code and usual NodeJS code, meaning that providing a client in this package would make it unfit for at least some purposes.
|
|
9
|
+
Two packages exist to solve that issue :
|
|
10
|
+
- `startgg-helper-node`, which includes this one and provides a client using the `graphql` package : this is for your NodeJS projects
|
|
11
|
+
- `startgg-helper-browser`, which includes this ont and provides a simple client relying on the `fetch` API : this is for your web projects, to be run by a browser (using a tool like `browserify` to make your node package usable on browser)
|
|
9
12
|
|
|
10
|
-
|
|
13
|
+
### I still want to use this package and provide my own client
|
|
14
|
+
OK ! A client is actually a very simple thing : all it needs is a `request(schema, variables)` method, taking a GraphQL schema as a string and a collection of variables as an object. As long as your object exposes this method, and it correctly returns the result of the desired GraphQL request, it can be passed to `startgg-helper` functions.
|
|
11
15
|
|
|
12
|
-
|
|
16
|
+
## Quick Doc
|
|
17
|
+
[Full focumentation here](./doc.md)
|
|
13
18
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
The basic feature of this package is the `Query` object, which represents a GraphQL Schema/Document. Its methods allow to execute the query (i.e. make a GraphQL request with the defined schema) with different variables, with automatic retries in case of failure.
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
const schema = `
|
|
23
|
+
query Test($slug: String, $page: Int, $perPage: Int){
|
|
24
|
+
event(slug: $slug) {
|
|
25
|
+
sets(page: $page, perPage: $perPage){
|
|
26
|
+
nodes {
|
|
27
|
+
id
|
|
28
|
+
state
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
`
|
|
34
|
+
|
|
35
|
+
const query = new Query(schema, 3); //3 is the default number of retries
|
|
36
|
+
|
|
37
|
+
let res = await Promise.all([
|
|
38
|
+
"tournament/my-tournament-1/event/ult-singles",
|
|
39
|
+
"tournament/my-tournament-2/event/ult-singles",
|
|
40
|
+
].map(async slug => await query.execute(client, {slug, page: 1})))
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Paginated collections and queries
|
|
44
|
+
Paginated collections are collections not represented by a GraphQL Array, but a page system, where each query needs to specify the index and size of the page it's fecthing. See the start.gg API for more information.
|
|
45
|
+
A paginated collection of type T is represented by a field with a `page` and `perPage` parameter returning a Connection-style type :
|
|
46
|
+
```graphql
|
|
47
|
+
field(page: Int, perPage: Int): TConnection
|
|
48
|
+
|
|
49
|
+
type TConnection {
|
|
50
|
+
pageInfo: PageInfo
|
|
51
|
+
|
|
52
|
+
nodes: [T]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
type PageInfo {
|
|
56
|
+
total: Int
|
|
57
|
+
totalPages: Int
|
|
58
|
+
page: Int
|
|
59
|
+
perPage: Int
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The `Query.executePaginated` method can be used to query entire paginated collections, by qerying repeatedly while increasing a certain parameter of the query, which must be used as the page argument of paginated collection field in the query, which you need to point to using the path parameter, and agregating the elements of each page into one single array. The page argument will be controlled entirely by the loop and doesn't need to be included with other graphQL variables. If the pageInfo.totalPages field is included in your schema, it will be used to determine the last page ; if not, this method will stop once it receives an empty page.
|
|
64
|
+
|
|
65
|
+
Example :
|
|
66
|
+
|
|
67
|
+
```graphql
|
|
68
|
+
query Example($page: Int){
|
|
69
|
+
topField {
|
|
70
|
+
field2(page: $page){
|
|
71
|
+
pageInfo {
|
|
72
|
+
totalPages
|
|
73
|
+
}
|
|
74
|
+
nodes {
|
|
75
|
+
...
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
```js
|
|
82
|
+
query.executePaginated(client, {}, "topfield.field2")
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Limiters : dealing with the API rate limit
|
|
86
|
+
The start.gg API has a rate limit of (as of writing this) 80 requests per minute per API key. To avoid exceeding this limit, `startgg-helper` provide a client-side rate-limiting mechanism, that comes in the form of objects you can pass to request-sending functions.
|
|
87
|
+
|
|
88
|
+
```js
|
|
89
|
+
const limiter = new StartGGDelayQueryLimiter(); //it is important to only create and use one in the entire program.
|
|
90
|
+
|
|
91
|
+
for (let i = 0; i < 200; i++){
|
|
92
|
+
query.execute(client, {slug: `tournament/my-tournament-${i}/event/ult-singles`}, limiter);
|
|
93
|
+
}
|
|
94
|
+
//queries will be delayed to avoid exceeding start.gg's rate limit
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
there are a handful of limiter classes but only a few are useful to you (some others are here only for legacy)
|
|
98
|
+
- DelayQueryLimiter(rpm) : allows up to `rpm` request per minute
|
|
99
|
+
- StartGGDelayQueryLimiter : allows up to 60 request per minute. This is intentionally lower than the actual server-side limit, to prevent network timing mishaps from making us accidentally going over.
|
|
100
|
+
|
|
101
|
+
### Etc
|
|
102
|
+
This package also provides lots of utility functions ; see the [full doc](./doc.md)
|
package/src/jsUtil.js
CHANGED
|
@@ -12,11 +12,30 @@ function processObjectPath(path){
|
|
|
12
12
|
}//jsutil
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
+
* Traverses nested object following a path and returns what's at the end, without throwing an error if an intermediary object-property is not found.
|
|
16
|
+
* The "path" argument works like a JS object access expression, starting with a property from the initial object (first parameter)
|
|
17
|
+
* ```js
|
|
18
|
+
* const obj = {a: {b: {c: 12}}};
|
|
19
|
+
* obj.a.b.c; //value : 12
|
|
20
|
+
* deep_get(obj, "a.b.c"); //returns 12
|
|
15
21
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
22
|
+
* obj.a.d.c; //ERROR : Cannot read properties of undefined (reading 'c')
|
|
23
|
+
* deep_get(obj, "a.d.c"); //returns null, no error
|
|
24
|
+
* deep_get(obj, "a.d.c", 15);//returns 15, default value
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* This function supports numbers as property names, which **works with arrays**.
|
|
28
|
+
* ```js
|
|
29
|
+
* const obj = {a: [{}, {}, {b: 12}]};
|
|
30
|
+
*
|
|
31
|
+
* obj.a[2].b; value: 12
|
|
32
|
+
* obj.a.2.b; Syntax ERROR
|
|
33
|
+
* deep_get(obj, "a.2.b"); //returns 12
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @param {Object | Array} obj Object or array
|
|
37
|
+
* @param {string} path See above
|
|
38
|
+
* @param {*} def Value returned if the path cannot be followed to the end
|
|
20
39
|
*/
|
|
21
40
|
export function deep_get(obj, path, def = null){
|
|
22
41
|
//https://stackoverflow.com/a/8817473
|
|
@@ -29,6 +48,13 @@ export function deep_get(obj, path, def = null){
|
|
|
29
48
|
return obj;
|
|
30
49
|
};
|
|
31
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Traverses nest objects following a path and sets the final property to a given vallue.
|
|
53
|
+
* Traversing works the same as with deep_get()
|
|
54
|
+
* @param {{}} obj
|
|
55
|
+
* @param {string} path
|
|
56
|
+
* @param {*} value
|
|
57
|
+
*/
|
|
32
58
|
export function deep_set(obj, path, value){
|
|
33
59
|
path = processObjectPath(path);
|
|
34
60
|
|
|
@@ -49,8 +75,8 @@ export function generateUniqueID(){
|
|
|
49
75
|
}//jsutil
|
|
50
76
|
|
|
51
77
|
/**
|
|
52
|
-
*
|
|
53
|
-
* @param {(()
|
|
78
|
+
* Returns an array containing the results of an array of functions, called without parameters. Any "undefined" result is ignored, meaning the resulting array can be smaller than the function array.
|
|
79
|
+
* @param {(()=>any)[]} fArray
|
|
54
80
|
*/
|
|
55
81
|
export function fResultsArray(fArray){
|
|
56
82
|
let result = [];
|
|
@@ -65,7 +91,7 @@ export function fResultsArray(fArray){
|
|
|
65
91
|
}
|
|
66
92
|
|
|
67
93
|
/**
|
|
68
|
-
*
|
|
94
|
+
* Returns an array containing the results of all parameters, treated as functions, called without parameters. See fResultsArray
|
|
69
95
|
* @param {...(() => any)} functions
|
|
70
96
|
*/
|
|
71
97
|
export function fResults(...functions){
|
|
@@ -73,20 +99,26 @@ export function fResults(...functions){
|
|
|
73
99
|
}
|
|
74
100
|
|
|
75
101
|
/**
|
|
76
|
-
*
|
|
102
|
+
* Serializes a value to JSON text like JSON.stringify does. (this function is just a very thin wrapper only useful for the "pretty" parameter)
|
|
77
103
|
* @param {any} data
|
|
78
|
-
* @param {boolean} pretty
|
|
104
|
+
* @param {boolean} pretty If true, the resulting JSON will be made to be human-readable, with 4-space indentation
|
|
79
105
|
*/
|
|
80
106
|
export function toJSON(data, pretty){
|
|
81
107
|
return JSON.stringify(data, null, pretty ? 4 : undefined);
|
|
82
108
|
}//jsutil
|
|
83
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Take a wild guess
|
|
112
|
+
* @param {any} n
|
|
113
|
+
*/
|
|
84
114
|
export function isNumber(n){
|
|
85
115
|
return typeof n == "number";
|
|
86
116
|
}
|
|
87
117
|
|
|
88
118
|
/**
|
|
89
|
-
*
|
|
119
|
+
* Converts a data to a UNIX timestamp, i.e. number of seconds since 1/1/1970 00:00. Accepts :
|
|
120
|
+
* - JS Date objects
|
|
121
|
+
* - strings and numbers : will be converted inta a Date like `new Date(d)` does. (number treated as UNIX timestamps with milisecond granularity, more complicated for strings)
|
|
90
122
|
* @param {number | Date | string} d
|
|
91
123
|
*/
|
|
92
124
|
export function toUNIXTimestamp(d){
|
package/src/query.js
CHANGED
|
@@ -5,14 +5,16 @@ function isConnection(val){
|
|
|
5
5
|
return val instanceof Object && val.nodes instanceof Array
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* A GraphQL Document/Schema. Allows easily batch-requesting with the same schema but different parameters.
|
|
10
|
+
*/
|
|
8
11
|
export class Query {
|
|
9
12
|
#schema;
|
|
10
13
|
#maxTries;
|
|
11
14
|
|
|
12
15
|
/**
|
|
13
|
-
*
|
|
14
|
-
* @param {
|
|
15
|
-
* @param {number?} maxTries
|
|
16
|
+
* @param {string} schema The GraphQL [Schema]
|
|
17
|
+
* @param {number?} maxTries How many retries will be performed when requesting using this query
|
|
16
18
|
*/
|
|
17
19
|
constructor (schema, maxTries = null){
|
|
18
20
|
this.#schema = schema;
|
|
@@ -25,9 +27,11 @@ export class Query {
|
|
|
25
27
|
* @param {{[varName: string]: value}} params
|
|
26
28
|
* @returns
|
|
27
29
|
*/
|
|
28
|
-
#getLog(logName, params){
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
#getLog(logName, params, logsOverride = null){
|
|
31
|
+
let log;
|
|
32
|
+
if (logsOverride) log = logsOverride[logName];
|
|
33
|
+
if (this.log) log = this.log[logName] ?? log;
|
|
34
|
+
|
|
31
35
|
if (log){
|
|
32
36
|
if (typeof log == "string"){
|
|
33
37
|
return log;
|
|
@@ -38,20 +42,24 @@ export class Query {
|
|
|
38
42
|
return null;
|
|
39
43
|
}
|
|
40
44
|
|
|
45
|
+
#getPaginatedLogOverride(params){
|
|
46
|
+
if (!this.paginatedLog) return null;
|
|
47
|
+
return this.paginatedLog(params);
|
|
48
|
+
}
|
|
49
|
+
|
|
41
50
|
/**
|
|
42
51
|
*
|
|
43
52
|
* @param {GraphQLClient} client
|
|
44
53
|
* @param {{[varName: string]: value}} params
|
|
45
54
|
* @param {number} tries How many tries in are we
|
|
46
55
|
* @param {TimedQuerySemaphore} limiter
|
|
47
|
-
* @param {boolean} silentErrors
|
|
56
|
+
* @param {boolean} silentErrors legacy parameter, does nothing
|
|
48
57
|
* @param {number} maxTries Overrides this.#maxTries
|
|
49
|
-
* @returns
|
|
50
58
|
*/
|
|
51
|
-
async #execute_(client, params, tries, limiter = null, silentErrors = false, maxTries = null){
|
|
59
|
+
async #execute_(client, params, tries, limiter = null, silentErrors = false, maxTries = null, logsOverride = null){
|
|
52
60
|
maxTries = maxTries || this.#maxTries || 1
|
|
53
61
|
|
|
54
|
-
console.log((this.#getLog("query", params) || "Querying ...") + " Try " + (tries + 1));
|
|
62
|
+
console.log((this.#getLog("query", params, logsOverride) || "Querying ...") + " Try " + (tries + 1));
|
|
55
63
|
try {
|
|
56
64
|
let data = await ( limiter ? limiter.execute(client, this.#schema, params) : client.request(this.#schema, params));
|
|
57
65
|
|
|
@@ -62,22 +70,21 @@ export class Query {
|
|
|
62
70
|
console.error("Maximum number of tries reached. Throwing.", e);
|
|
63
71
|
throw e;
|
|
64
72
|
}
|
|
65
|
-
console.error((this.#getLog("error", params) || "Request failed.") + ` Retrying (try ${tries + 1}). Error : `, e);
|
|
66
|
-
return this.#execute_(client, params, tries + 1, limiter, silentErrors, maxTries);
|
|
73
|
+
console.error((this.#getLog("error", params, logsOverride) || "Request failed.") + ` Retrying (try ${tries + 1}). Error : `, e);
|
|
74
|
+
return this.#execute_(client, params, tries + 1, limiter, silentErrors, maxTries, logsOverride);
|
|
67
75
|
}
|
|
68
76
|
}
|
|
69
77
|
|
|
70
78
|
/**
|
|
71
|
-
*
|
|
72
|
-
* @param {
|
|
73
|
-
* @param {
|
|
74
|
-
* @param {
|
|
75
|
-
* @param {
|
|
76
|
-
* @param {number} maxTries Overrides the default maximum tries count for this query
|
|
79
|
+
* @param {GraphQLClient} client A client object ; not provided by this package, either use startgg-helper-node (or-browser) or refer to README.md
|
|
80
|
+
* @param {{[varName: string]: value}} params GraphQL variables
|
|
81
|
+
* @param {TimedQuerySemaphore} limiter A request limiter object; see TimedQuerySemaphore
|
|
82
|
+
* @param {boolean} silentErrors No effect, exists only for legacy purposes
|
|
83
|
+
* @param {number} maxTries Overrides the default maximum tries count for this query.
|
|
77
84
|
* @returns
|
|
78
85
|
*/
|
|
79
|
-
async execute(client, params, limiter = null, silentErrors = false, maxTries = null){
|
|
80
|
-
return await this.#execute_(client, params, 0, limiter, silentErrors, maxTries);
|
|
86
|
+
async execute(client, params, limiter = null, silentErrors = false, maxTries = null, logsOverride = null){
|
|
87
|
+
return await this.#execute_(client, params, 0, limiter, silentErrors, maxTries, logsOverride);
|
|
81
88
|
}
|
|
82
89
|
|
|
83
90
|
static IWQModes = {
|
|
@@ -88,15 +95,23 @@ export class Query {
|
|
|
88
95
|
}
|
|
89
96
|
|
|
90
97
|
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
* @param {
|
|
96
|
-
* @param {{
|
|
97
|
-
* @param {
|
|
98
|
+
* Queries a whole paginated collection (of a *Connection type). See the start.gg API doc or this package's documentation for more info about pagniated collections. This is done through executing the query repeatedly while increasing the page index each time.
|
|
99
|
+
* The target collection must be pointed to by the "path" argument, and will be agregated in a single array. The schema must have a variable (whose name can be specified in parameters) that is used as the page index of the paginated field.
|
|
100
|
+
*
|
|
101
|
+
*
|
|
102
|
+
* @param {GraphQLClient} client A client object ; not provided by this package, either use startgg-helper-node (or-browser) or refer to README.md
|
|
103
|
+
* @param {{[varName: string]: value}} params GraphQL variables ; does not include the page index variable.
|
|
104
|
+
* @param {string} connectionPathInQuery JSON path to the paginated collection that must be aggregated in the query (JSON path : property names separated by dots, see deep_get())
|
|
105
|
+
* @param {TimedQuerySemaphore} limiter A request limiter object; see TimedQuerySemaphore
|
|
106
|
+
* @param {{pageParamName?: string, perPageParamName?: string, perPage?: number, delay?: number, maxElements?: number, includeWholeQuery?: number, callback: (localResult: T[]) => T[]?}} config
|
|
107
|
+
* @param config.pageParamName name of the variable representing the page index. This variable must exist in your query, and be used as an argument in a paginated collection field
|
|
108
|
+
* @param config.delay number of miliseconds to wait for between each query. No delay if absent.
|
|
109
|
+
* @param config.maxElements if present, queries will stop once this many elements have been fetched
|
|
110
|
+
* @param config.includeWholeQuery controls the structure of the result. If 0, only an array is returned, if 1 the whole query is returned with the `nodes` field replaced with the full array, if 3 this functions returns a tuple containing the aggregated collection and the rest of the query, 2 does both 1 and 3.
|
|
111
|
+
* @param config.callback a callback function called for each fetched page, with the page elements and the page index. It should return an array itself, which will be treated as the actual page (allowing users to transform the pages on the fly) ; if it does not, the paginated execution is stopped.
|
|
112
|
+
* @param {boolean} silentErrors No effect, exists only for legacy purposes
|
|
98
113
|
* @param {number} maxTries
|
|
99
|
-
* @returns
|
|
114
|
+
* @returns See config.includeWholeQuery
|
|
100
115
|
*/
|
|
101
116
|
async executePaginated(client, params, connectionPathInQuery, limiter = null, config = {}, silentErrors = false, maxTries = null){
|
|
102
117
|
let result = [];
|
|
@@ -113,14 +128,16 @@ export class Query {
|
|
|
113
128
|
params[pageParamName] = currentPage;
|
|
114
129
|
params[perPageParamName] = perPage;
|
|
115
130
|
|
|
131
|
+
const logsOverride = this.#getPaginatedLogOverride(params);
|
|
132
|
+
|
|
116
133
|
let data;
|
|
117
134
|
while (true){
|
|
118
135
|
if (result.length >= maxElements) break;
|
|
119
136
|
|
|
120
137
|
console.log("Querying page", params[pageParamName], `(${result.length} elements loaded)`);
|
|
121
|
-
data = await this.execute(client, params, limiter, silentErrors, maxTries);
|
|
138
|
+
data = await this.execute(client, params, limiter, silentErrors, maxTries, logsOverride);
|
|
122
139
|
|
|
123
|
-
if (!data) throw (this.#getLog("error", params) ?? "Request failed.") + "(in paginated execution, at page " + params[pageParamName] + ")";
|
|
140
|
+
if (!data) throw (this.#getLog("error", params, logsOverride) ?? "Request failed.") + "(in paginated execution, at page " + params[pageParamName] + ")";
|
|
124
141
|
|
|
125
142
|
let connection = deep_get(data, connectionPathInQuery);
|
|
126
143
|
|
|
@@ -132,9 +149,15 @@ export class Query {
|
|
|
132
149
|
if (!isConnection(connection)) throw "The given path does not point to a connection type";
|
|
133
150
|
|
|
134
151
|
let localResult = connection.nodes;
|
|
152
|
+
|
|
135
153
|
if (connection.pageInfo && connection.pageInfo.totalPages){
|
|
136
154
|
let totalPages = connection.pageInfo.totalPages;
|
|
137
155
|
if (!totalPages || currentPage >= totalPages) {
|
|
156
|
+
if (config.callback){
|
|
157
|
+
let cbRes = config.callback(localResult, currentPage);
|
|
158
|
+
if (!cbRes) break;
|
|
159
|
+
localResult = cbRes;
|
|
160
|
+
}
|
|
138
161
|
result = result.concat(localResult);
|
|
139
162
|
break;
|
|
140
163
|
}
|
|
@@ -142,6 +165,12 @@ export class Query {
|
|
|
142
165
|
if (localResult.length < 1) break;
|
|
143
166
|
}
|
|
144
167
|
|
|
168
|
+
if (config.callback){
|
|
169
|
+
let cbRes = config.callback(localResult, currentPage);
|
|
170
|
+
if (!cbRes) break;
|
|
171
|
+
localResult = cbRes;
|
|
172
|
+
}
|
|
173
|
+
|
|
145
174
|
result = result.concat(localResult);
|
|
146
175
|
currentPage++;
|
|
147
176
|
params[pageParamName] = currentPage;
|
|
@@ -168,6 +197,8 @@ export class Query {
|
|
|
168
197
|
}
|
|
169
198
|
|
|
170
199
|
/**
|
|
200
|
+
* Deprecated, use executePaginated instead.
|
|
201
|
+
*
|
|
171
202
|
* Executes a query containing a paginated collection, repeatedly, increasing the page index each time until nothing is returned, returning an aggregation of all the pages.
|
|
172
203
|
* @param {GraphQLClient} client
|
|
173
204
|
* @param {{[varName: string]: value}} params
|
package/src/queryLimiter.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
//i am a god of JS
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Provides timing control for requests. If passed to a Query method, it will delay its execution to stay withing a defined limit.
|
|
5
|
+
*
|
|
6
|
+
* This is implemented like a semaphore automatically releasing tokens after a certain delay
|
|
7
|
+
*/
|
|
5
8
|
export class TimedQuerySemaphore {
|
|
6
9
|
/**
|
|
7
10
|
* @typedef {{client: any, schema: string,
|
|
@@ -25,8 +28,8 @@ export class TimedQuerySemaphore {
|
|
|
25
28
|
|
|
26
29
|
/**
|
|
27
30
|
*
|
|
28
|
-
* @param {number} size Semaphore counter initial value
|
|
29
|
-
* @param {number} delay
|
|
31
|
+
* @param {number} size Semaphore counter initial value : how many requests we can send before having to wait
|
|
32
|
+
* @param {number} delay Delay before each taken token is released ; how much time we wait before making new requests
|
|
30
33
|
*/
|
|
31
34
|
constructor(size, delay){
|
|
32
35
|
this.#counter = size;
|
|
@@ -67,7 +70,8 @@ export class TimedQuerySemaphore {
|
|
|
67
70
|
}
|
|
68
71
|
|
|
69
72
|
/**
|
|
70
|
-
* Executes the given query with the given GraphQL Client
|
|
73
|
+
* Executes the given query with the given GraphQL Client.
|
|
74
|
+
* Note that this method probably shouldn't be called by anything else than Query.execute
|
|
71
75
|
* @param {any} client
|
|
72
76
|
* @param {string} schema
|
|
73
77
|
* @param {{[varName: string]: value}} params
|
|
@@ -97,6 +101,9 @@ export class TimedQuerySemaphore {
|
|
|
97
101
|
}
|
|
98
102
|
}
|
|
99
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Stops the timing manager ; always call this at the end of your program, if you dont node will think there is still work to be done and won't exit
|
|
106
|
+
*/
|
|
100
107
|
stop(){
|
|
101
108
|
for (let timer of this.#timers){
|
|
102
109
|
clearTimeout(timer);
|
|
@@ -104,19 +111,22 @@ export class TimedQuerySemaphore {
|
|
|
104
111
|
}
|
|
105
112
|
}
|
|
106
113
|
|
|
114
|
+
/**TimedQuerySemaphore that allows one request before waiting. You should use the DelayQueryLimiter instead.*/
|
|
107
115
|
export class ClockQueryLimiter extends TimedQuerySemaphore {
|
|
108
|
-
/** @param {number} rpm */
|
|
116
|
+
/** @param {number} rpm Requests per minute*/
|
|
109
117
|
constructor(rpm){
|
|
110
118
|
super(1, 60000 / rpm);
|
|
111
119
|
}
|
|
112
120
|
}
|
|
113
121
|
|
|
122
|
+
/** ClockQueryLimiter configured to stay within the start.gg API requests limit (60 per minute, so 1 second delay). You should use the StartGGDelayQueryLimiter instead*/
|
|
114
123
|
export class StartGGClockQueryLimiter extends ClockQueryLimiter {
|
|
115
124
|
constructor(){
|
|
116
125
|
super(60);
|
|
117
126
|
}
|
|
118
127
|
}
|
|
119
128
|
|
|
129
|
+
/**TimedQuerySemaphore that allows x requests before waiting, and waits for one minute. */
|
|
120
130
|
export class DelayQueryLimiter extends TimedQuerySemaphore {
|
|
121
131
|
/** @param {number} rpm */
|
|
122
132
|
constructor(rpm){
|
|
@@ -124,6 +134,7 @@ export class DelayQueryLimiter extends TimedQuerySemaphore {
|
|
|
124
134
|
}
|
|
125
135
|
}
|
|
126
136
|
|
|
137
|
+
/** ClockQueryLimiter configured to stay within the start.gg API requests limit (60 per minute)*/
|
|
127
138
|
export class StartGGDelayQueryLimiter extends DelayQueryLimiter {
|
|
128
139
|
constructor(){
|
|
129
140
|
super(60)
|