gitlab-api-async-iterator
Advanced tools
+20
| # Change Log | ||
| All notable changes to this project will be documented in this file. | ||
| See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. | ||
| # 1.1.0 (2020-08-25) | ||
| ### Bug Fixes | ||
| - **gitlab-api-async-iterator:** Deal with non-node environments ([6de110b](https://gitlab.com/leipert-projects/npm-packages/commit/6de110b5c1e55b1dead3673fad656f52f6b94e96)) | ||
| ### Features | ||
| - **gitlab-api-async-iterator:** Add Retry-After header to retry error handling ([ff18714](https://gitlab.com/leipert-projects/npm-packages/commit/ff18714c42e26515fb89822e3b7009cfc0de4603)) | ||
| # 1.0.0 (2020-08-23) | ||
| ### Features | ||
| - **gitlab-api-async-iterator:** Initial implementation ([133a4d1](https://gitlab.com/leipert-projects/npm-packages/commit/133a4d16ed257768a7f9cc8f9a91ee98005141eb)) |
+41
-22
@@ -17,2 +17,3 @@ const axios = require("axios"); | ||
| const waitBeforeRetry = async (error, retries) => { | ||
| // Be forgiving if we hit server errors or rate limits and retry after some time | ||
| if ( | ||
@@ -23,18 +24,37 @@ error && | ||
| ) { | ||
| if (error.response.headers && error.response.headers["ratelimit-reset"]) { | ||
| const wait = | ||
| parseInt(error.response.headers["ratelimit-reset"], 10) * 1000 - | ||
| new Date().getTime(); | ||
| let wait; | ||
| console.warn( | ||
| `Got a status code of ${error.response.status} and Retry-After. Waiting until ${error.response.headers["ratelimit-resettime"]} (${wait} ms)` | ||
| ); | ||
| await timeout(wait); | ||
| } else { | ||
| const wait = 2 ** retries * 100; | ||
| console.warn( | ||
| `Got a status code of ${error.response.status}. Retry #${retries}. Waiting ${wait} ms` | ||
| ); | ||
| await timeout(wait); | ||
| const log = [`Got a status code of ${error.response.status}.`]; | ||
| if (error.response.headers) { | ||
| try { | ||
| const { | ||
| "ratelimit-reset": rateLimitReset, | ||
| "ratelimit-resettime": rateLimitResetTime, | ||
| "retry-after": retryAfter, | ||
| } = error.response.headers; | ||
| if (rateLimitReset) { | ||
| wait = parseInt(rateLimitReset, 10) * 1000 - new Date().getTime(); | ||
| log.push( | ||
| `Found a ratelimit-reset header, waiting until ${rateLimitResetTime}.` | ||
| ); | ||
| } | ||
| if (retryAfter) { | ||
| wait = parseInt(retryAfter, 10) * 1000; | ||
| log.push(`Found a retry-after header (${retryAfter}).`); | ||
| } | ||
| } catch (e) {} | ||
| } | ||
| if (!wait) { | ||
| wait = 2 ** retries * 100; | ||
| } | ||
| log.push(`Waiting ${wait} ms`); | ||
| console.warn(log.join(" ")); | ||
| await timeout(wait); | ||
| return retries + 1; | ||
@@ -46,2 +66,7 @@ } else { | ||
| const getTokenFromProcess = () => | ||
| process && | ||
| process.env && | ||
| (process.env.GITLAB_TOKEN || process.env.DANGER_GITLAB_API_TOKEN); | ||
| const GitLabAPIFactory = (options = {}) => { | ||
@@ -55,6 +80,3 @@ const { | ||
| if ( | ||
| !privateToken && | ||
| !(process.env.GITLAB_TOKEN || process.env.DANGER_GITLAB_API_TOKEN) | ||
| ) { | ||
| if (!privateToken && !getTokenFromProcess()) { | ||
| throw new Error( | ||
@@ -72,6 +94,3 @@ "Please provide a GitLab token as an ENV variable via GITLAB_TOKEN or DANGER_GITLAB_API_TOKEN" | ||
| ...headers, | ||
| "PRIVATE-TOKEN": | ||
| privateToken || | ||
| process.env.GITLAB_TOKEN || | ||
| process.env.DANGER_GITLAB_API_TOKEN, | ||
| "PRIVATE-TOKEN": privateToken || getTokenFromProcess(), | ||
| }, | ||
@@ -78,0 +97,0 @@ }); |
+8
-5
| { | ||
| "name": "gitlab-api-async-iterator", | ||
| "version": "1.0.0", | ||
| "description": "Async iterator for GitLab API", | ||
| "version": "1.1.0", | ||
| "description": "Async iterator for GitLab API based on axios", | ||
| "keywords": [ | ||
| "gitlab" | ||
| "gitlab", | ||
| "api", | ||
| "async", | ||
| "iterator" | ||
| ], | ||
@@ -19,3 +22,3 @@ "author": "Lukas Eipert <git@leipert.io>", | ||
| "url": "https://gitlab.com/leipert-projects/npm-packages.git", | ||
| "directory": "scoped/gitlab-spec-failures" | ||
| "directory": "packages/gitlab-api-async-iterator" | ||
| }, | ||
@@ -31,3 +34,3 @@ "scripts": { | ||
| }, | ||
| "gitHead": "e1df79b5d47c0841628a74c6c883bf34d3b1bcca" | ||
| "gitHead": "3bc01ef8097e96e0a72521d49e3b4426fd0a45de" | ||
| } |
+107
-2
| # `gitlab-api-async-iterator` | ||
| > TODO: description | ||
| > Async iterator for GitLab API based on axios | ||
| ## Usage | ||
| This module exposes a factory for creating async iterators for GitLab APIs. | ||
| This allows to use APIs pretty efficiently (code-wise). | ||
| It has a built-in retry feature in case you hit one of the following API errors: [429, 500, 502, 503, 504] | ||
| ### Instantiation | ||
| ```js | ||
| // es6 / webpack | ||
| import factory from "gitlab-api-async-iterator"; | ||
| // node | ||
| const factory = require("gitlab-api-async-iterator"); | ||
| const { GitlabAPI, GitLabAPIIterator } = factory({ | ||
| //default values | ||
| baseURL: "https://gitlab.com/api/v4/", | ||
| // If no token is defined, it tries to read process.env.GITLAB_TOKEN || process.env.DANGER_GITLAB_API_TOKEN | ||
| privateToken: null, | ||
| // How often certain failing requests are retried | ||
| maxRetries: 5, | ||
| // You can supply other options which would be provided to axios.create: | ||
| // https://github.com/axios/axios#request-config | ||
| }); | ||
| ``` | ||
| // TODO: DEMONSTRATE API | ||
| ### Usage of the API | ||
| ```js | ||
| let response; | ||
| // The GitLabAPI object is a normal instance of axios.create, so you could do: | ||
| response = await GitlabAPI.get("/user"); // Retrieve current user | ||
| response = await GitlabAPI.get("/version"); // Retrieve current GitLab version | ||
| response = await GitlabAPI.post("/projects", { name: "foobar" }); // Create a project | ||
| ``` | ||
| ### Usage of the API Iterator | ||
| ```js | ||
| // The real powerhouse is the GitLabAPIIterator: | ||
| const groupIterator = new GitLabAPIIterator("/groups"); | ||
| // This will paginate through _all_ groups, and you have normal loop controls | ||
| for await (const group of groupIterator) { | ||
| //Skip groups starting with 'A' | ||
| if (group.name.startsWith("A")) { | ||
| continue; | ||
| } | ||
| // Stop looping as soon as we hit a group starting with C | ||
| if (group.name.startsWith("C")) { | ||
| break; | ||
| } | ||
| console.log("Group Details:", group); | ||
| } | ||
| // You can provide options to the group iterator: | ||
| const aliceIterator = new GitLabAPIIterator("/users", { | ||
| // General parameters: | ||
| page: 2, // Start with page two | ||
| maxPages: Number.MAX_SAFE_INTEGER, // how many pages to receive at a maximum | ||
| per_page: 20, //Pagination size (GitLab default is 20) | ||
| // Parameters specific to the endpoint | ||
| search: "Alice", | ||
| }); | ||
| for await (const alice of aliceIterator) { | ||
| console.log("Found a Alice", alice); | ||
| } | ||
| ``` | ||
| ### Subclassing the API Iterator: | ||
| ```js | ||
| class PipelineIterator extends GitLabAPIIterator { | ||
| constructor(projectId, options) { | ||
| super(`/projects/${projectId}/pipelines`, options); | ||
| } | ||
| } | ||
| class PipelineJobIterator extends GitLabAPIIterator { | ||
| constructor(projectId, pipelineId, options) { | ||
| super(`/projects/${projectId}/pipelines/${pipelineId}/jobs`, options); | ||
| } | ||
| } | ||
| const projectId = "foobar"; | ||
| const pipelines = new PipelineIterator(projectId); | ||
| for await (const pipeline of pipelines) { | ||
| console.log(pipeline); | ||
| const jobs = new PipelineIterator(projectId, pipeline.id); | ||
| let found = false; | ||
| for await (const job of jobs) { | ||
| if (job.name.includes("jest")) { | ||
| found = true; | ||
| break; | ||
| } | ||
| } | ||
| if (found) { | ||
| console.log(`${pipeline.web_url} is the first Pipeline with a jest job`); | ||
| break; | ||
| } | ||
| } | ||
| ``` |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
8500
97.9%6
20%134
8.94%115
1050%4
-20%