├── .editorconfig ├── .github ├── CODE_OF_CONDUCT.md └── CONTRIBUTING.md ├── .gitignore ├── .npmignore ├── .vscode ├── launch.json └── settings.json ├── LICENSE ├── MIGRATING.md ├── README.md ├── circle.yml ├── demo ├── index.html ├── index.js ├── index.ts ├── package.json └── yarn.lock ├── package.json ├── renovate.json ├── rollup.config.ts ├── src ├── __mocks__ │ └── client.ts ├── api │ ├── actions.ts │ ├── artifacts.ts │ ├── builds.ts │ ├── cache.ts │ ├── checkout-keys.ts │ ├── env.ts │ ├── index.ts │ ├── metadata.ts │ ├── misc.ts │ ├── projects.ts │ └── user.ts ├── circleci.ts ├── client.ts ├── index.ts ├── types │ ├── api.ts │ ├── index.ts │ ├── json │ │ ├── artifacts.sample.json │ │ ├── artifacts.schema.json │ │ ├── build.sample.json │ │ ├── build.schema.json │ │ ├── buildAction.sample.json │ │ ├── buildAction.schema.json │ │ ├── followNewProject.sample.json │ │ ├── followNewProject.schema.json │ │ ├── me.sample.json │ │ ├── me.schema.json │ │ ├── project.sample.json │ │ └── project.schema.json │ └── lib.ts └── util.ts ├── test ├── __mocks__ │ └── axios.ts ├── api │ ├── actions.test.ts │ ├── artifacts.test.ts │ ├── builds.test.ts │ ├── cache.test.ts │ ├── checkout-keys.test.ts │ ├── env.test.ts │ ├── metadata.test.ts │ ├── misc.test.ts │ ├── projects.test.ts │ └── user.test.ts ├── circleci.test.ts ├── client.test.ts ├── index.test.ts └── util.test.ts ├── tools └── gh-pages-publish.ts ├── tsconfig.base.json ├── tsconfig.json ├── tsconfig.test.json ├── tslint.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | #root = true 2 | 3 | [*] 4 | indent_style = space 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | max_line_length = 80 10 | indent_size = 2 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at jordon.dehoog@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | First of all, thanks for your interest in contributing to the circleci-api! 🎉 4 | 5 | PRs are the preferred way to spike ideas and address issues, if you have time. If you plan on contributing frequently, please feel free to ask to become a maintainer; the more the merrier. 🤙 6 | 7 | ## Technical overview 8 | 9 | This library uses following libraries for development: 10 | 11 | - [typescript](http://www.typescriptlang.org/) for typed JavaScript and transpilation 12 | - [prettier](https://prettier.io/) for formating the code 13 | - [jest](https://jestjs.io/) for unit testing 14 | - run `yarn test:dev` during development 15 | - [rollup](https://rollupjs.org/guide/en) for creating UMD bundles 16 | - [yarn](https://yarnpkg.com/lang/en/) for package management 17 | - [husky](https://github.com/typicode/husky) for git hooks (pre-commit, pre-push) 18 | 19 | ### 🧪 Tests 20 | 21 | Test are written and run via Jest 💪 22 | 23 | ```sh 24 | # Run whole test suite once 25 | yarn test 26 | # Run test in watch mode 27 | yarn test:watch 28 | # Lint lib and tests, the run tests with coverage 29 | yarn test:prod 30 | ``` 31 | 32 | ### 💅 Style guides 33 | 34 | Style guides are enforced by robots _(I meant prettier and tslint of course 🤖 )_, so they'll let you know if you screwed something, but most of the time, they'll autofix things for you. Magic right? 35 | 36 | Lint and format codebase via npm-script: 37 | 38 | ```sh 39 | # Lint and autofix using tslint 40 | yarn lint 41 | 42 | # Format code with prettier 43 | yarn format 44 | ``` 45 | 46 | #### Commit conventions (via commitizen) 47 | 48 | - this is preferred way how to create conventional-changelog valid commits 49 | - if you prefer your custom tool we provide a commit hook linter which will error out, it you provide invalid commit message 50 | - if you are in rush and just wanna skip commit message validation just prefix your message with `WIP: something done` ( if you do this please squash your work when you're done with proper commit message so semantic-release can create Changelog and bump version of your library appropriately ) 51 | 52 | > Use the great [commitizen CLI](https://github.com/commitizen/cz-cli) to create commits 53 | 54 | ```sh 55 | # invoke commitizen 56 | yarn commit 57 | ``` 58 | 59 | ### 📖 Documentation 60 | 61 | ```sh 62 | # Build the docs 63 | yarn docs 64 | 65 | # Build the docs and watch for file changes 66 | yarn docs:watch 67 | ``` 68 | 69 | ## Getting started 70 | 71 | ### Creating a Pull Request 72 | 73 | If you've never submitted a Pull request before please visit http://makeapullrequest.com/ to learn everything you need to know. 74 | 75 | #### Setup 76 | 77 | 1. Fork the repo. 78 | 1. `git clone` your fork. 79 | 1. Make a `git checkout -b branch-name` branch for your change. 80 | 1. Run `yarn install` (make sure you have node and yarn installed first) 81 | 82 | #### Updates 83 | 84 | 1. Make sure to add unit tests. 85 | 1. If there is a `*.test.ts` file, update it to include a test for your change, if needed. If this file doesn't exist, please create it. 86 | 1. Run `yarn test` or `yarn test:dev` to make sure all tests are working, regardless if a test was added. 87 | 1. When your work is done run `yarn test --coverage` to ensure your changes are covered. 88 | 89 | #### Commiting 90 | 91 | 1. Run `yarn commit`. 92 | 1. Follow all the prompts and create a meaningful commit. 93 | 1. Push to your branch. 94 | 1. Create PR. 95 | 96 | --- 97 | 98 | ## 🚀 Publishing 99 | 100 | > releases are handled by awesome [semantic-release](https://github.com/semantic-release/semantic-release) 101 | 102 | Whenever a commit is pushed to the `master` branch, the CI server will validate the commit, then run `semantic-release`. 103 | 104 | If `semantic-release` decides that the commit is worthy of a new release it will: 105 | 106 | - bump package version and git tag 107 | - push to github master branch + push tags 108 | - publish build packages to npm 109 | 110 | ## License 111 | 112 | By contributing your code to the circleci-api GitHub Repository, you agree to license your contribution under the MIT license. 113 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | .nyc_output 4 | .DS_Store 5 | *.log 6 | .idea 7 | dist 8 | compiled 9 | .awcache 10 | .rpt2_cache 11 | docs 12 | demo/node_modules 13 | demo/dist 14 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .rpt2_cache/ 2 | .vscode/ 3 | coverage/ 4 | demo/ 5 | docs/ 6 | src/ 7 | test/ 8 | tools/ 9 | .editorconfig 10 | .travis.yml 11 | circle.yml 12 | rollup.config.ts 13 | tsconfig.* 14 | tslint.json 15 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "launch", 7 | "name": "Tests", 8 | "program": "${workspaceRoot}/node_modules/jest/bin/jest.js", 9 | "args": ["--runInBand", "--no-cache"], 10 | "cwd": "${workspaceRoot}", 11 | "sourceMaps": true, 12 | "smartStep": true, 13 | "skipFiles": ["/**"] 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.enable": false, 3 | "search.exclude": { 4 | "**/node_modules": true, 5 | "**/bower_components": true, 6 | "**/dist": true 7 | }, 8 | "editor.tabSize": 2, 9 | "eslint.autoFixOnSave": true, 10 | "typescript.referencesCodeLens.enabled": false, 11 | "tslint.ignoreDefinitionFiles": false, 12 | "tslint.autoFixOnSave": true, 13 | "tslint.exclude": "**/node_modules/**/*", 14 | "editor.formatOnSave": true 15 | } 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 WorldTurtleMedia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MIGRATING.md: -------------------------------------------------------------------------------- 1 | # v3.x.x to v4.0.0 2 | 3 | Version 4 includes the following changes: 4 | 5 | 1. **BREAKING** The ability to supply a custom url for the CircleCI API, to support self-hosted solutions. 6 | - This required restructuring some of the method calls, so that the last parameter was an object, instead of being in the middle. 7 | 2. Deprecation of `circleGet/Post/Delete` from `client.ts 8 | - Will be removed in the _next_ version 9 | 10 | ## Parameter Restructure 11 | 12 | ### Example 13 | 14 | Before the restructure: 15 | 16 | ```typescript 17 | getBuildArtifacts( 18 | "my-token", 19 | { circleHost: "https://mycircleci.com/", owner: "foo", repo: "bar" }, 20 | 42 21 | ); 22 | ``` 23 | 24 | The `buildNumber` parameter is at the end, and if the line is formatted: 25 | 26 | ```typescript 27 | getBuildArtifacts( 28 | "my-token", 29 | { 30 | circleHost: "https://mycircleci.com/", 31 | owner: "foo", 32 | repo: "bar" 33 | }, 34 | 42 35 | ); 36 | ``` 37 | 38 | It can look a bit weird since the final parameter is at the end. The restructuring allows for better readability: 39 | 40 | ```typescript 41 | getBuildArtifacts("my-token", 42, { 42 | circleHost: "https://mycircleci.com/", 43 | owner: "foo", 44 | repo: "bar" 45 | }); 46 | ``` 47 | 48 | You are **not** affected if you use the `CircleCI` class, only if you use the standalone functions. 49 | 50 | ### Functions affected: 51 | 52 | ```typescript 53 | // Old 54 | getFullBuild(token: string, vcs: GitInfo, buildNumber: number) 55 | // New 56 | getFullBuild(token: string, buildNumber: number, options: GitInfo & CircleOptions) 57 | 58 | // Old 59 | getBuildArtifacts(token: string, vcs: GitInfo, buildNumber: number) 60 | // New 61 | getFullBuild(token: string, buildNumber: number, options: GitInfo & CircleOptions) 62 | 63 | // Old 64 | addEnv(token: string, vcs: GitInfo, payload: EnvVariable) 65 | // New 66 | addEnv(token: string, payload: EnvVariable, options: GitInfo & CircleOptions) 67 | 68 | // Old 69 | getEnvVar(token: string, vcs: GitInfo, envName: string) 70 | // New 71 | getEnvVar(token: string, envName: string, options: GitInfo & CircleOptions) 72 | 73 | // Old 74 | deleteEnvVar(token: string, vcs: GitInfo, envName: string) 75 | // New 76 | deleteEnvVar(token: string, envName: string, options: GitInfo & CircleOptions) 77 | 78 | // Old 79 | createCheckoutKey(token: string, vcs: GitInfo, key: CheckoutKey) 80 | // New 81 | createCheckoutKey(token: string, key: CheckoutKey, options: GitInfo & CircleOptions) 82 | 83 | // Old 84 | createCheckoutKey(token: string, vcs: GitInfo, fingerprint: string) 85 | // New 86 | createCheckoutKey(token: string, fingerprint: string, options: GitInfo & CircleOptions) 87 | 88 | // Old 89 | deleteCheckoutKey(token: string, vcs: GitInfo, fingerprint: string) 90 | // New 91 | deleteCheckoutKey(token: string, fingerprint: string, options: GitInfo & CircleOptions) 92 | 93 | // Old 94 | getTestMetadata(token: string, vcs: GitInfo, buildNumber: number) 95 | // New 96 | getTestMetadata(token: string, buildNumber: number, options: GitInfo & CircleOptions) 97 | 98 | // Old 99 | postBuildActions( 100 | token: string, 101 | buildNumber: number, 102 | vcs: GitInfo, 103 | action: BuildAction, 104 | // New 105 | postBuildActions( 106 | token: string, 107 | buildNumber: number, 108 | action: BuildAction, 109 | { circleHost, ...vcs }: GitInfo & CircleOptions 110 | ) 111 | ``` 112 | 113 | ## Deprecation of http helpers 114 | 115 | The following functions from `src/client.ts` have been deprecated: 116 | 117 | 1. `circleGet` 118 | 1. `circlePost` 119 | 1. `circleDelete` 120 | 121 | They were just wrappers for `client("token").get(...)` and seem like unnecessary bloat. They have been deprecated and will be removed in the next major update. 122 | 123 | You just need to replace them with calls to the `client`: 124 | 125 | ```typescript 126 | // Old 127 | circleGet("my-token", "/projects", { 128 | timeout: 1000, 129 | baseURL: "https://my-circleci.com" 130 | }); 131 | 132 | // New 133 | client("my-token", "https://my-circleci.com").get("/projects", { 134 | timeout: 1000 135 | }); 136 | ``` 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CircleCi API Client 2 | 3 | A Node and Browser client for the CircleCI API, written in TypeScript. 4 | 5 | [![CircleCI branch](https://img.shields.io/circleci/project/github/worldturtlemedia/circleci-api/master.svg?label=release%20build)](https://circleci.com/gh/worldturtlemedia/circleci-api/tree/master) [![CircleCI (all branches)](https://img.shields.io/circleci/project/github/worldturtlemedia/circleci-api.svg)](https://circleci.com/gh/worldturtlemedia/circleci-api) [![Coverage Status](https://coveralls.io/repos/github/worldturtlemedia/circleci-api/badge.svg?branch=master)](https://coveralls.io/github/worldturtlemedia/circleci-api?branch=master) 6 | 7 | [![npm version](https://badge.fury.io/js/circleci-api.svg)](https://www.npmjs.com/package/circleci-api) [![Downloads](https://img.shields.io/npm/dw/circleci-api)](https://www.npmjs.com/package/circleci-api) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/circleci-api.svg) ![License](https://img.shields.io/github/license/worldturtlemedia/circleci-api.svg) 8 | 9 | [![Renovate enabled](https://img.shields.io/badge/renovate-enabled-brightgreen.svg)](https://renovatebot.com/) 10 | 11 | API wrapper for [CircleCi API](https://circleci.com/docs/api/v1-reference/), usable in node and the browser. If used in a TypeScript project, you will get types, and auto-complete for all of the api responses. You will no longer need to tab back and fourth to the API documentation. 12 | 13 | I recommend using this library if you are writing a tool or website in TypeScript. I have created definitions for each of the CircleCI endpoints. There may still be some errors, but I am open to contributions on making them better. 14 | 15 | If there are any features you would like, please feel free to open up an issue. 16 | 17 | ## Notes 18 | 19 | ### Types 20 | 21 | I did my best to correctly add types for all of the supported endpoints. However if you notice an incorrect payload type, or some missing properties, **please** open up an issue, or submit a pull request. 22 | 23 | ### CircleCI API v2 24 | 25 | CircleCI is going to be rolling out a new version of their api (see [here](https://github.com/CircleCI-Public/api-preview-docs)). Currently this library **does not** support v2, but I will update it in the future, see [#228](https://github.com/worldturtlemedia/circleci-api/issues/228) for updates. 26 | 27 | ## Migrating from v3 to v4 28 | 29 | There have been some breaking changes, please see [MIGRATING.md](https://github.com/worldturtlemedia/circleci-api/blob/master/MIGRATING.md) for more info. 30 | 31 | ## Installation 32 | 33 | Add using yarn or npm 34 | 35 | ```bash 36 | yarn add circleci-api 37 | 38 | ## or 39 | 40 | npm install circleci-api 41 | ``` 42 | 43 | ## Usage 44 | 45 | Get your API token from [CircleCi](https://circleci.com/account/api) 46 | 47 | There are two ways to use this library. 48 | 49 | ## 1. CircleCi class 50 | 51 | Get instance of the factory. 52 | 53 | ```typescript 54 | // Module 55 | import { CircleCI, GitType, CircleCIOptions } from "circleci-api"; 56 | 57 | // Configure the factory with some defaults 58 | const options: CircleCIOptions = { 59 | // Required for all requests 60 | token: "", // Set your CircleCi API token 61 | 62 | // Optional 63 | // Anything set here can be overriden when making the request 64 | 65 | // Git information is required for project/build/etc endpoints 66 | vcs: { 67 | type: GitType.GITHUB, // default: github 68 | owner: "worldturtlemedia", 69 | repo: "circleci-api" 70 | } 71 | 72 | // Optional query params for requests 73 | options: { 74 | branch: "master", // default: master 75 | filter: "completed" 76 | } 77 | } 78 | 79 | // Create the api object 80 | const api = new CircleCI(options) 81 | 82 | // Use the api 83 | 84 | /** 85 | * Grab the latest artifacts from a successful build on a certain branch 86 | * @param [branch="master"] - Artifacts for certain branch 87 | * @return List of successfully built artifact objects 88 | */ 89 | export async function getLatestArtifacts(branch: string = "master"): Promise { 90 | try { 91 | // Will use the repo defined in the options above 92 | const result: Aritfact[] = await api.latestArtifacts({ branch, filter: "successful" }) 93 | console.log(`Found ${result.length} artifacts`) 94 | return result 95 | } catch (error) { 96 | console.log("No build artifacts found") 97 | } 98 | 99 | return [] 100 | } 101 | 102 | getLatestArtifacts("develop") 103 | .then(artifacts => { 104 | artifacts 105 | .forEach(({ path, url }: Artifact) => console.log(`${path} -> ${url}`)) 106 | }) 107 | 108 | // Or override settings set above 109 | api 110 | .latestArtifacts( 111 | { branch: "develop" }, 112 | { 113 | vcs: { repo: "awesome-repo" }, 114 | options: { filter: "successful" } 115 | } 116 | ) 117 | .then((artifacts: Artifact[]) => console.log(`Found ${artifacts.length} artifacts`)) 118 | .catch(error => console.error(error)) 119 | ``` 120 | 121 | ## 2. Manually 122 | 123 | The individual functions can also be imported if you only need one or two. To help with tree-shaking. 124 | 125 | ```typescript 126 | import { getMe, getLatestArtifacts } from "circleci-api"; 127 | 128 | const CIRCLECI_TOKEN: string = "circle-ci-token"; 129 | 130 | getMe(CIRCLECI_TOKEN) 131 | .then(me => console.log("token is valid")) 132 | .catch(error => console.error("invalid token")); 133 | 134 | getLatestArtifacts(CIRCLECI_TOKEN, { 135 | vcs: { 136 | owner: "billyBob", 137 | repo: "super-cool-app" 138 | }, 139 | options: { 140 | filter: "failed", 141 | branch: "feature-smith2" 142 | } 143 | }) 144 | .then(result => console.log(`Found ${result.length} artifacts`)) 145 | .catch(error => console.error(error)); 146 | ``` 147 | 148 | ## Self-hosted CircleCI 149 | 150 | To override the default API base url `https://circleci.com/api/v1.1`, you can pass a `circleHost` to the `CircleCI` constructor, or to the standalone functions. 151 | 152 | ```typescript 153 | // Using the CircleCi class 154 | new CircleCI({ 155 | token: "my-token", 156 | vcs: { owner: "worldturtlemedia", repo = "circleci-api" }, 157 | circleHost: "https://my-selfhosted-circleci.com/" 158 | }).getLatestArtifacts() 159 | 160 | // Using the standalone functions 161 | getLatestArtifacts("my-token", { 162 | ... 163 | circleHost: "https://my-selfhosted-circleci.com/" 164 | }) 165 | ``` 166 | 167 | While all of the standalone functions support a custom `circleHost` property. Using the `CircleCI` class you _must_ specify it in the constructor. 168 | 169 | ## Demo 170 | 171 | There are three similar demos are available in the `demo` folder. 172 | 173 | **Note:** I recommend [VSCode](https://code.visualstudio.com/) for viewing and editing the examples. It will give you great intellisense about the library. 174 | 175 | For the TypeScript & JavaScript follow the steps below: 176 | 177 | ```bash 178 | # Step 1 - Change into demo folder and install dependencies 179 | cd demo 180 | yarn 181 | 182 | # Javascript example: 183 | node ./index.js 184 | 185 | # Typescript example: 186 | npx ts-node --project ../tsconfig.base.json ./index.ts 187 | 188 | # To view Browser example, first build project 189 | yarn build 190 | 191 | # Then open `index.html` in your browser 192 | ``` 193 | 194 | ## Supported endpoints 195 | 196 | Using factory: 197 | 198 | Optional properties: 199 | 200 | ```typescript 201 | export interface CircleRequest { 202 | token?: string; 203 | vcs?: GitInfo; 204 | options?: Options; 205 | } 206 | ``` 207 | 208 | Any function with an _optional_ paramater of `CircleRequest` can override any of the values you assigned when using the `circleci()` factory. 209 | 210 | | Name | Required | Optional | Returns | 211 | | --------------------- | :-------------------------: | :---------------------------------: | --------------------------: | 212 | | `me()` | none | none | `Me` | 213 | | `projects()` | none | `CircleRequest` | `Project[]` | 214 | | `followProject()` | `{ vcs: GitInfo }` | none | `FollowNewResult` | 215 | | `recentBuilds()` | none | `{ limit: number, offset: number }` | `BuildSummary[]` | 216 | | `builds()` | none | `{ limit: number, offset: number }` | `BuildSummary[]` | 217 | | `buildsFor()` | `branch: string = "master"` | `{ limit: number, offset: number }` | `BuildSummary[]` | 218 | | `build()` | `buildNumber: number` | `CircleRequest` | `BuildWithSteps` | 219 | | `artifacts()` | `buildNumber: number` | `CircleRequest` | `Artifact[]` | 220 | | `latestArtifacts()` | none | `CircleRequest` | `Artifact[]` | 221 | | `retry()` | `buildNumber: number` | `CircleRequest` | `BuildSummary` | 222 | | `cancel()` | `buildNumber: number` | `CircleRequest` | `BuildSummary` | 223 | | `triggerBuild()` | none | `CircleRequest` | `Build` | 224 | | `triggerBuildFor()` | `branch: string = "master"` | `CircleRequest` | `Build` | 225 | | `clearCache()` | none | `CircleRequest` | `ClearCacheResponse` | 226 | | `listEnvVars()` | none | `CircleRequest` | `EnvVariable[]` | 227 | | `addEnvVar()` | `variable: EnvVariable` | `CircleRequest` | `EnvVariable` | 228 | | `getEnvVar()` | `envName: string` | `CircleRequest` | `EnvVariable` | 229 | | `deleteEnvVar()` | `envName: string` | `CircleRequest` | `MessageResponse` | 230 | | `checkoutKeys()` | none | `CircleRequest` | `CheckoutKeyResponse` | 231 | | `createCheckoutKey()` | `type: CheckoutType` | `CircleRequest` | `CheckoutKeyResponse` | 232 | | `checkoutKey()` | `fingerprint: string` | `CircleRequest` | `CheckoutKeyResponse` | 233 | | `deleteCheckoutKey()` | `fingerprint: string` | `CircleRequest` | `DeleteCheckoutKeyResponse` | 234 | | `getTestMetadata()` | `buildNumber: number` | `CircleRequest` | `TestMetadataResponse` | 235 | | `addSSHKey()` | `key: SSHKey` | `CircleRequest` | none | 236 | | `addHerokuKey()` | `key: HerokuKey` | `CircleRequest` | none | 237 | 238 | ## Missing endpoint 239 | 240 | The last remaining endpoint probably won't be added unless there is demand. 241 | 242 | | Name | Link | 243 | | ----------------- | :-------------------------------------------------------------: | 244 | | Add user to build | [ref](https://circleci.com/docs/api/v1-reference/#add-user-ssh) | 245 | 246 | ## Contributing 247 | 248 | See [CONTRIBUTING](.github/CONTRIBUTING.md). 249 | 250 | ## License 251 | 252 | ```text 253 | MIT License 254 | 255 | Copyright (c) 2019 WorldTurtleMedia 256 | 257 | Permission is hereby granted, free of charge, to any person obtaining a copy 258 | of this software and associated documentation files (the "Software"), to deal 259 | in the Software without restriction, including without limitation the rights 260 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 261 | copies of the Software, and to permit persons to whom the Software is 262 | furnished to do so, subject to the following conditions: 263 | 264 | The above copyright notice and this permission notice shall be included in all 265 | copies or substantial portions of the Software. 266 | 267 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 268 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 269 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 270 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 271 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 272 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 273 | SOFTWARE. 274 | ``` 275 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | - image: circleci/node:latest 6 | environment: 7 | - NODE_ENV: TEST 8 | working_directory: ~/repo 9 | steps: 10 | - checkout 11 | - run: yarn versions 12 | - restore_cache: 13 | name: Restore Yarn Package Cache 14 | keys: 15 | - yarn-packages-{{ checksum "yarn.lock" }} 16 | - run: 17 | name: Install Dependencies 18 | command: yarn install 19 | - save_cache: 20 | name: Save Yarn Package Cache 21 | key: yarn-packages-{{ checksum "yarn.lock" }} 22 | paths: 23 | - ~/.cache/yarn 24 | - run: 25 | name: Lint & Test 26 | command: yarn test:prod 27 | - run: 28 | name: Build 29 | command: yarn build 30 | - run: 31 | name: Run demo test 32 | command: cd demo && yarn && yarn test && cd .. 33 | - run: 34 | name: Report Coverage 35 | command: yarn report-coverage 36 | - store_artifacts: 37 | path: ./dist 38 | destination: dist 39 | - store_artifacts: 40 | path: ./coverage 41 | destination: coverage 42 | - store_artifacts: 43 | path: ./docs 44 | destination: docs 45 | - store_test_results: 46 | path: ./coverage 47 | release: 48 | docker: 49 | - image: circleci/node:latest 50 | steps: 51 | - checkout 52 | - restore_cache: 53 | name: Restore Yarn Package Cache 54 | keys: 55 | - yarn-packages-{{ checksum "yarn.lock" }} 56 | - run: 57 | name: Install Dependencies 58 | command: yarn install 59 | - save_cache: 60 | name: Save Yarn Package Cache 61 | key: yarn-packages-{{ checksum "yarn.lock" }} 62 | paths: 63 | - ~/.cache/yarn 64 | - run: 65 | name: Deploy release 66 | command: yarn semantic-release 67 | - run: 68 | name: Deploy docs 69 | command: yarn deploy-docs 70 | - store_artifacts: 71 | path: ./dist 72 | destination: dist 73 | - store_artifacts: 74 | path: ./docs 75 | destination: docs 76 | 77 | workflows: 78 | version: 2 79 | build_and_release: 80 | jobs: 81 | - build 82 | - release: 83 | requires: 84 | - build 85 | filters: 86 | branches: 87 | only: master 88 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | CircleCI-API 7 | 8 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 |
25 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /demo/index.js: -------------------------------------------------------------------------------- 1 | const { CircleCI } = require("circleci-api"); 2 | 3 | const api = new CircleCI({ 4 | token: " ", // Not needed for latest artifacts endpoint 5 | vcs: { 6 | owner: "worldturtlemedia", 7 | repo: "circleci-api" 8 | } 9 | }); 10 | 11 | console.log("Getting latest artifacts"); 12 | api 13 | .latestArtifacts() 14 | .then(x => { 15 | if (x.length) x.forEach(y => console.log(`found ${y.url}`)); 16 | else console.log("No artifacts!"); 17 | }) 18 | .catch(err => console.error(err)); 19 | -------------------------------------------------------------------------------- /demo/index.ts: -------------------------------------------------------------------------------- 1 | import { CircleCI } from "circleci-api"; 2 | 3 | const api = new CircleCI({ 4 | token: " ", // Not needed for latest artifacts endpoint 5 | vcs: { 6 | owner: "worldturtlemedia", 7 | repo: "circleci-api", 8 | }, 9 | }); 10 | 11 | api 12 | .latestArtifacts() 13 | .then((x) => { 14 | console.log(`Found ${x.length} artifacts!`); 15 | process.exit(0); 16 | }) 17 | .catch((err) => { 18 | console.error(err); 19 | process.exit(1); 20 | }); 21 | -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "tsc --outDir dist/ --module commonjs --moduleResolution node --target es5 --strict true index.ts", 8 | "pretest": "npm run build", 9 | "test": "node dist/" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "circleci-api": "../" 15 | }, 16 | "devDependencies": { 17 | "ts-node": "9.0.0", 18 | "typescript": "4.0.3" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /demo/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | arg@^4.1.0: 6 | version "4.1.3" 7 | resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" 8 | integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== 9 | 10 | axios@^0.21.1: 11 | version "0.21.4" 12 | resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" 13 | integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== 14 | dependencies: 15 | follow-redirects "^1.14.0" 16 | 17 | buffer-from@^1.0.0: 18 | version "1.1.1" 19 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" 20 | integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== 21 | 22 | circleci-api@../: 23 | version "1.0.0" 24 | dependencies: 25 | axios "^0.21.1" 26 | 27 | diff@^4.0.1: 28 | version "4.0.2" 29 | resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" 30 | integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== 31 | 32 | follow-redirects@^1.14.0: 33 | version "1.14.8" 34 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc" 35 | integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA== 36 | 37 | make-error@^1.1.1: 38 | version "1.3.6" 39 | resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" 40 | integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== 41 | 42 | source-map-support@^0.5.17: 43 | version "0.5.19" 44 | resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" 45 | integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== 46 | dependencies: 47 | buffer-from "^1.0.0" 48 | source-map "^0.6.0" 49 | 50 | source-map@^0.6.0: 51 | version "0.6.1" 52 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" 53 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== 54 | 55 | ts-node@9.0.0: 56 | version "9.0.0" 57 | resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.0.0.tgz#e7699d2a110cc8c0d3b831715e417688683460b3" 58 | integrity sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg== 59 | dependencies: 60 | arg "^4.1.0" 61 | diff "^4.0.1" 62 | make-error "^1.1.1" 63 | source-map-support "^0.5.17" 64 | yn "3.1.1" 65 | 66 | typescript@4.0.3: 67 | version "4.0.3" 68 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.3.tgz#153bbd468ef07725c1df9c77e8b453f8d36abba5" 69 | integrity sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg== 70 | 71 | yn@3.1.1: 72 | version "3.1.1" 73 | resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" 74 | integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== 75 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "circleci-api", 3 | "version": "1.0.0", 4 | "organization": "worldturtlemedia", 5 | "description": "", 6 | "keywords": [ 7 | "circleci", 8 | "ci", 9 | "api", 10 | "typescript" 11 | ], 12 | "main": "dist/circleci-api.cjs.js", 13 | "module": "dist/circleci-api.es5.js", 14 | "browser": "dist/circleci-api.umd.min.js", 15 | "typings": "dist/types/src/index.d.ts", 16 | "sideEffects": false, 17 | "files": [ 18 | "dist" 19 | ], 20 | "author": "Jordon de Hoog ", 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/worldturtlemedia/circleci-api" 24 | }, 25 | "license": "MIT", 26 | "engines": { 27 | "node": ">=6.0.0" 28 | }, 29 | "scripts": { 30 | "lint:lib": "tslint --project ./tsconfig.json -t codeFrame 'src/**/*.ts'", 31 | "lint:tests": "tslint --project ./tsconfig.test.json -t codeFrame 'test/**/*.ts'", 32 | "lint": "npm run lint:lib && npm run lint:tests", 33 | "format": "prettier --write '{src,test}/**/*.{ts,json}' '*.md'", 34 | "clean": "rimraf dist", 35 | "predocs": "rimraf docs", 36 | "docs": "typedoc --out docs --exclude '**/__mocks__/**' --excludeExternals --target es6 --includeDeclarations --excludePrivate --mode modules src", 37 | "docs:watch": "watch 'yarn docs' ./src", 38 | "tsc": "tsc --module commonjs", 39 | "rollup": "rollup -c rollup.config.ts", 40 | "prebuild": "npm run clean", 41 | "build": "npm run tsc && npm run rollup", 42 | "postbuild": "npm run docs", 43 | "start": "rollup -c rollup.config.ts -w", 44 | "test": "jest", 45 | "test:watch": "jest --watch", 46 | "test:prod": "npm run lint && npm run test -- --coverage --no-cache", 47 | "deploy-docs": "ts-node tools/gh-pages-publish", 48 | "report-coverage": "cat ./coverage/lcov.info | coveralls", 49 | "commit": "git-cz", 50 | "semantic-release": "semantic-release", 51 | "precommit": "lint-staged", 52 | "prepublishOnly": "npm run build" 53 | }, 54 | "dependencies": { 55 | "axios": "^0.21.1" 56 | }, 57 | "devDependencies": { 58 | "@commitlint/cli": "11.0.0", 59 | "@commitlint/config-conventional": "11.0.0", 60 | "@rollup/plugin-commonjs": "15.1.0", 61 | "@rollup/plugin-json": "4.1.0", 62 | "@rollup/plugin-node-resolve": "9.0.0", 63 | "@types/jest": "26.0.15", 64 | "@types/node": "11.15.33", 65 | "colors": "1.4.0", 66 | "commitizen": "4.2.2", 67 | "coveralls": "3.1.0", 68 | "cross-env": "7.0.2", 69 | "cz-conventional-changelog": "3.3.0", 70 | "greenkeeper-lockfile": "1.15.1", 71 | "husky": "4.3.0", 72 | "jest": "26.6.0", 73 | "lint-staged": "10.4.2", 74 | "lodash.camelcase": "4.3.0", 75 | "prettier": "2.1.2", 76 | "prompt": "1.0.0", 77 | "replace-in-file": "6.1.0", 78 | "rimraf": "3.0.2", 79 | "rollup": "2.32.1", 80 | "rollup-plugin-node-builtins": "2.1.2", 81 | "rollup-plugin-node-globals": "1.4.0", 82 | "rollup-plugin-sourcemaps": "0.6.3", 83 | "rollup-plugin-terser": "7.0.2", 84 | "rollup-plugin-typescript2": "0.28.0", 85 | "semantic-release": "19.0.3", 86 | "source-map-support": "0.5.19", 87 | "ts-jest": "26.4.1", 88 | "ts-node": "9.0.0", 89 | "tslib": "2.0.3", 90 | "tslint": "6.1.3", 91 | "tslint-config-prettier": "1.18.0", 92 | "tslint-config-standard": "9.0.0", 93 | "typedoc": "0.19.2", 94 | "typescript": "4.0.3", 95 | "watch": "1.0.2" 96 | }, 97 | "husky": { 98 | "hooks": { 99 | "pre-commit": "lint-staged", 100 | "pre-push": "npm run lint", 101 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" 102 | } 103 | }, 104 | "lint-staged": { 105 | "{src,test}/**/*.ts": [ 106 | "prettier --write" 107 | ] 108 | }, 109 | "config": { 110 | "commitizen": { 111 | "path": "node_modules/cz-conventional-changelog" 112 | } 113 | }, 114 | "commitlint": { 115 | "extends": [ 116 | "@commitlint/config-conventional" 117 | ] 118 | }, 119 | "jest": { 120 | "globals": { 121 | "ts-jest": { 122 | "tsConfig": "./tsconfig.test.json", 123 | "diagnostics": true 124 | } 125 | }, 126 | "collectCoverageFrom": [ 127 | "src/**/*.{js,jsx,ts,tsx}", 128 | "!src/**/*.d.ts" 129 | ], 130 | "coverageDirectory": "coverage/", 131 | "coverageReporters": [ 132 | "lcov", 133 | "html", 134 | "text-summary" 135 | ], 136 | "moduleFileExtensions": [ 137 | "ts", 138 | "tsx", 139 | "js" 140 | ], 141 | "roots": [ 142 | "/src/", 143 | "/test/" 144 | ], 145 | "testMatch": [ 146 | "/test/**/?(*.)(spec|test).ts?(x)" 147 | ], 148 | "transformIgnorePatterns": [ 149 | "[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$" 150 | ], 151 | "preset": "ts-jest" 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ], 5 | "baseBranches": [ 6 | "next" 7 | ], 8 | "automerge": true, 9 | "major": { 10 | "automerge": false 11 | } 12 | } -------------------------------------------------------------------------------- /rollup.config.ts: -------------------------------------------------------------------------------- 1 | import resolve from "@rollup/plugin-node-resolve"; 2 | import commonjs from "@rollup/plugin-commonjs"; 3 | import sourceMaps from "rollup-plugin-sourcemaps"; 4 | import camelCase from "lodash.camelcase"; 5 | import typescript from "rollup-plugin-typescript2"; 6 | import json from "@rollup/plugin-json"; 7 | import builtins from "rollup-plugin-node-builtins"; 8 | import nodeGlobals from "rollup-plugin-node-globals"; 9 | import { terser } from "rollup-plugin-terser"; 10 | 11 | const pkg = require("./package.json"); 12 | 13 | const libraryName = pkg.name; 14 | 15 | const browserPlugins = [ 16 | resolve({ browser: true }), 17 | commonjs(), 18 | json(), 19 | nodeGlobals(), 20 | builtins(), 21 | typescript({ 22 | useTsconfigDeclarationDir: true, 23 | exclude: ["src/__mocks__/*.ts"], 24 | }), 25 | sourceMaps(), 26 | ]; 27 | 28 | const nodePlugins = [ 29 | json(), 30 | typescript({ useTsconfigDeclarationDir: true }), 31 | commonjs(), 32 | resolve(), 33 | sourceMaps(), 34 | ]; 35 | 36 | const commonConfig = { 37 | input: `src/index.ts`, 38 | external: [], 39 | watch: { 40 | include: "src/**", 41 | }, 42 | }; 43 | 44 | const browserConfig = { 45 | ...commonConfig, 46 | output: { 47 | file: pkg.browser.replace(".min.js", ".js"), 48 | name: camelCase(libraryName), 49 | format: "umd", 50 | sourcemap: true, 51 | }, 52 | plugins: [...browserPlugins, sourceMaps()], 53 | }; 54 | 55 | const minifiedBrowserConfig = { 56 | ...commonConfig, 57 | output: { 58 | ...browserConfig.output, 59 | file: pkg.browser, 60 | }, 61 | plugins: [...browserPlugins, terser(), sourceMaps()], 62 | }; 63 | 64 | const nodeConfig = { 65 | ...commonConfig, 66 | output: [ 67 | { file: pkg.module, format: "es", sourcemap: true }, 68 | { file: pkg.main, format: "cjs", sourcemap: true }, 69 | ], 70 | plugins: nodePlugins, 71 | external: [ 72 | "os", 73 | "http", 74 | "https", 75 | "url", 76 | "assert", 77 | "stream", 78 | "tty", 79 | "util", 80 | "zlib", 81 | ], 82 | }; 83 | 84 | export default [browserConfig, minifiedBrowserConfig, nodeConfig]; 85 | -------------------------------------------------------------------------------- /src/__mocks__/client.ts: -------------------------------------------------------------------------------- 1 | const mockClient: any = jest.genMockFromModule("./client"); 2 | 3 | let mockResponse: any | null; 4 | let mockErrorResponse: any | null; 5 | 6 | export function __reset() { 7 | mockResponse = null; 8 | mockErrorResponse = null; 9 | } 10 | 11 | /* tslint:disable-next-line:function-name */ 12 | export function __setResponse(result: any) { 13 | mockResponse = result; 14 | } 15 | 16 | /* tslint:disable-next-line:function-name */ 17 | export function __setError(result: any) { 18 | mockErrorResponse = result; 19 | } 20 | 21 | function handleResponse() { 22 | if (mockResponse !== null) { 23 | return Promise.resolve(mockResponse); 24 | } 25 | return Promise.reject(mockErrorResponse || mockResponse); 26 | } 27 | 28 | /* tslint:disable-next-line:variable-name */ 29 | export const __getMock = jest.fn(() => handleResponse()); 30 | 31 | /* tslint:disable-next-line:variable-name */ 32 | export const __postMock = jest.fn(() => handleResponse()); 33 | 34 | /* tslint:disable-next-line:variable-name */ 35 | export const __deleteMock = jest.fn(() => handleResponse()); 36 | 37 | export const client = jest.fn((token: string) => ({ 38 | get: __getMock, 39 | post: __postMock, 40 | delete: __deleteMock 41 | })); 42 | 43 | export default mockClient; 44 | -------------------------------------------------------------------------------- /src/api/actions.ts: -------------------------------------------------------------------------------- 1 | import { 2 | GitInfo, 3 | BuildActionResponse, 4 | TriggerBuildResponse, 5 | GitRequiredRequest, 6 | BuildAction, 7 | createVcsUrl, 8 | CircleOptions 9 | } from "../types"; 10 | import { client } from "../client"; 11 | 12 | /** 13 | * Commit a build action, returns a summary of the new build. 14 | * 15 | * Retry a build 16 | * @see https://circleci.com/docs/api/v1-reference/#retry-build 17 | * @example POST - /project/:vcs-type/:username/:project/:build_num/retry 18 | * 19 | * Cancel a build 20 | * @see https://circleci.com/docs/api/v1-reference/#cancel-build 21 | * @example POST - /project/:vcs-type/:username/:project/:build_num/cancel 22 | * 23 | * @param token - CircleCI API token 24 | * @param buildNumber - Target build number to retry 25 | * @param action - Action to perform on the build 26 | * @param circleHost Provide custom url for CircleCI 27 | * @param vcs - Project's git information that you'd like to retry 28 | */ 29 | export function postBuildActions( 30 | token: string, 31 | buildNumber: number, 32 | action: BuildAction, 33 | { circleHost, ...vcs }: GitInfo & CircleOptions 34 | ): Promise { 35 | const url = `${createVcsUrl(vcs)}/${buildNumber}/${action}`; 36 | return client(token, circleHost).post(url); 37 | } 38 | 39 | /** 40 | * Triggers a new build, returns a summary of the build. 41 | * @see https://circleci.com/docs/api/v1-reference/#new-build 42 | * @example /project/:vcs-type/:username/:project 43 | * 44 | * Triggers a new build, returns a summary of the build. 45 | * @see https://circleci.com/docs/api/v1-reference/#new-build-branch 46 | * @example /project/:vcs-type/:username/:project/tree/:branch 47 | */ 48 | export function postTriggerNewBuild( 49 | token: string, 50 | { 51 | circleHost, 52 | vcs, 53 | options: { branch = "", newBuildOptions = {} } = {} 54 | }: GitRequiredRequest 55 | ): Promise { 56 | const url = `${createVcsUrl(vcs)}${branch ? `/tree/${branch}` : ""}`; 57 | return client(token, circleHost).post( 58 | url, 59 | newBuildOptions 60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /src/api/artifacts.ts: -------------------------------------------------------------------------------- 1 | import { 2 | GitInfo, 3 | ArtifactResponse, 4 | GitRequiredRequest, 5 | createVcsUrl, 6 | CircleOptions 7 | } from "../types"; 8 | import { client } from "../client"; 9 | import { queryParams } from "../util"; 10 | 11 | /** 12 | * Get artifacts for single project build 13 | * 14 | * @see https://circleci.com/docs/api/v1-reference/#build-artifacts 15 | * @example /project/:vcs-type/:username/:project/:build_num/artifacts 16 | * 17 | * @param token - CircleCI API token 18 | * @param buildNumber - Target build number 19 | * @param circleHost Provide custom url for CircleCI 20 | * @param vcs - Project's git information 21 | * @returns Promise of an array of build artifacs 22 | */ 23 | export function getBuildArtifacts( 24 | token: string, 25 | buildNumber: number, 26 | { circleHost, ...vcs }: GitInfo & CircleOptions 27 | ): Promise { 28 | const url = `${createVcsUrl(vcs)}/${buildNumber}/artifacts`; 29 | return client(token, circleHost).get(url); 30 | } 31 | 32 | /** 33 | * Get the latest build artifacts for a project 34 | * 35 | * @example branch - The branch you would like to look in for the latest build. Returns artifacts for latest build in entire project if omitted. 36 | * @example filter - Restricts which builds are returned. Set to "completed", "successful", "failed", "running" 37 | * 38 | * @see https://circleci.com/docs/api/v1-reference/#build-artifacts-latest 39 | * @example GET - /project/:vcs-type/:username/:project/latest/artifacts?branch=:branch&filter=:filter 40 | * @param token - CircleCI API token 41 | * @param vcs - Project's git information 42 | * @param options - Query parameters 43 | * @param circleHost Provide custom url for CircleCI 44 | */ 45 | export function getLatestArtifacts( 46 | token: string, 47 | { vcs, options = {}, circleHost }: GitRequiredRequest 48 | ): Promise { 49 | const { branch, filter } = options; 50 | const url = `${createVcsUrl(vcs)}/latest/artifacts${queryParams({ 51 | branch, 52 | filter 53 | })}`; 54 | return client(token, circleHost).get(url); 55 | } 56 | -------------------------------------------------------------------------------- /src/api/builds.ts: -------------------------------------------------------------------------------- 1 | import { 2 | GitInfo, 3 | FetchBuildResponse, 4 | Options, 5 | BuildSummaryResponse, 6 | GitRequiredRequest, 7 | API_RECENT_BUILDS, 8 | createVcsUrl, 9 | CircleOptions 10 | } from "../types"; 11 | import { client } from "../client"; 12 | import { queryParams } from "../util"; 13 | 14 | /** 15 | * Get all recent builds for CircleCI user 16 | * 17 | * @see https://circleci.com/docs/api/v1-reference/#recent-builds 18 | * @example GET - /recent-builds?limit=10&offset=5 19 | * 20 | * @param token - CircleCI API token 21 | * @param limit - optional - Limit the number of builds returned, max=100 22 | * @param offset - optional -builds starting from this offset 23 | * @param circleHost Provide custom url for CircleCI 24 | * @returns List of recent build summaries 25 | */ 26 | export function getRecentBuilds( 27 | token: string, 28 | { limit, offset, circleHost }: Options & CircleOptions = {} 29 | ): Promise { 30 | const url = `${API_RECENT_BUILDS}${queryParams({ limit, offset })}`; 31 | return client(token, circleHost).get(url); 32 | } 33 | 34 | /** 35 | * Get recent build summaries for a project 36 | * 37 | * Supported query parameters: 38 | * 39 | * @example limit - The number of builds to return. Maximum 100, defaults to 30. 40 | * @example offset - builds starting from this offset, defaults to 0. 41 | * @example filter -Restricts which builds are returned. Set to "completed", "successful", "failed", "running" 42 | * 43 | * @see https://circleci.com/docs/api/v1-reference/#recent-builds-project 44 | * @example GET - /project/:vcs-type/:username/:project?circle-token=:token&limit=20&offset=5&filter=completed 45 | * 46 | * Get recent builds for a project and branch 47 | * @see https://circleci.com/docs/api/v1-reference/#recent-builds-project-branch 48 | * @example GET - /project/:vcs-type/:username/:project/tree/:branch 49 | * 50 | * @see FullRequest 51 | * @param token - CircleCI API token 52 | * @param vcs - Get builds for this project 53 | * @param options - Optional - Query parameters 54 | * @param circleHost Provide custom url for CircleCI 55 | * @returns A list of build summaries 56 | */ 57 | export function getBuildSummaries( 58 | token: string, 59 | { vcs, options = {}, circleHost }: GitRequiredRequest 60 | ): Promise { 61 | const { limit, offset, filter, branch } = options; 62 | const url = `${createVcsUrl(vcs)}${branch ? `/tree/${branch}` : ""}`; 63 | const params = queryParams({ limit, offset, filter }); 64 | return client(token, circleHost).get(`${url}${params}`); 65 | } 66 | 67 | /** 68 | * Get full build details for a single build 69 | * 70 | * @see https://circleci.com/docs/api/v1-reference/#build 71 | * @example /project/:vcs-type/:username/:project/:build_num 72 | * 73 | * @param token - CircleCI API token 74 | * @param buildNumber - Target build number 75 | * @param circleHost Provide custom url for CircleCI 76 | * @param vcs - Project's git information 77 | * @returns Full build details including build steps 78 | */ 79 | export function getFullBuild( 80 | token: string, 81 | buildNumber: number, 82 | { circleHost, ...vcs }: GitInfo & CircleOptions 83 | ): Promise { 84 | const url = `${createVcsUrl(vcs)}/${buildNumber}`; 85 | return client(token, circleHost).get(url); 86 | } 87 | -------------------------------------------------------------------------------- /src/api/cache.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ClearCacheResponse, 3 | GitInfo, 4 | createVcsUrl, 5 | CircleOptions 6 | } from "../types"; 7 | import { client } from "../client"; 8 | 9 | /** 10 | * Clear project cache 11 | * 12 | * @see https://circleci.com/docs/api/v1-reference/#clear-cache 13 | * @example DELETE : https://circleci.com/api/v1.1/project/:vcs-type/:username/:project/build-cache 14 | * 15 | * @param token CircleCI API token 16 | * @param circleHost Provide custom url for CircleCI 17 | * @param vcs Git info for project 18 | * @returns status message of request 19 | */ 20 | export function clearCache( 21 | token: string, 22 | { circleHost, ...vcs }: GitInfo & CircleOptions 23 | ): Promise { 24 | const url = `${createVcsUrl(vcs)}/build-cache`; 25 | return client(token, circleHost).delete(url); 26 | } 27 | -------------------------------------------------------------------------------- /src/api/checkout-keys.ts: -------------------------------------------------------------------------------- 1 | import { 2 | GitInfo, 3 | CheckoutKeyResponse, 4 | createVcsUrl, 5 | CheckoutKey, 6 | DeleteCheckoutKeyResponse, 7 | CircleOptions 8 | } from "../types"; 9 | import { client } from "../client"; 10 | import { createJsonHeader } from "../util"; 11 | 12 | /** 13 | * Lists the checkout keys for a project 14 | * 15 | * @see https://circleci.com/docs/api/v1-reference/#list-checkout-keys 16 | * @example GET : https://circleci.com/api/v1.1/project/:vcs-type/:username/:project/checkout-key 17 | * 18 | * @param token CircleCI API token 19 | * @param circleHost Provide custom url for CircleCI 20 | * @param vcs Git information for project 21 | * @returns list of checkout keys for a specific project 22 | */ 23 | export function getCheckoutKeys( 24 | token: string, 25 | { circleHost, ...vcs }: GitInfo & CircleOptions 26 | ): Promise { 27 | return client(token, circleHost).get(createUrl(vcs)); 28 | } 29 | 30 | /** 31 | * Create a checkout key for a project 32 | * 33 | * @see https://circleci.com/docs/api/v1-reference/#new-checkout-key 34 | * @example POST : https://circleci.com/api/v1.1/project/:vcs-type/:username/:project/checkout-key 35 | * 36 | * @param token CircleCI API token 37 | * @param key Key to create for project 38 | * @param vcs Git information for project 39 | * @param circleHost Provide custom url for CircleCI 40 | * @returns New checkout key 41 | */ 42 | export function createCheckoutKey( 43 | token: string, 44 | key: CheckoutKey, 45 | { circleHost, ...vcs }: GitInfo & CircleOptions 46 | ): Promise { 47 | return client(token, circleHost).post( 48 | createUrl(vcs), 49 | key, 50 | createJsonHeader() 51 | ); 52 | } 53 | 54 | /** 55 | * Get a single checkout key from it's fingerprint 56 | * 57 | * @see https://circleci.com/docs/api/v1-reference/#get-checkout-key 58 | * @example POST : https://circleci.com/api/v1.1/project/:vcs-type/:username/:project/checkout-key 59 | * 60 | * @param token CircleCI API token 61 | * @param fingerprint Fingerprint of the key to fetch 62 | * @param circleHost Provide custom url for CircleCI 63 | * @param vcs Git information for project 64 | * @returns list of checkout keys for a specific project 65 | */ 66 | export function getCheckoutKey( 67 | token: string, 68 | fingerprint: string, 69 | { circleHost, ...vcs }: GitInfo & CircleOptions 70 | ): Promise { 71 | return client(token, circleHost).get( 72 | createUrl(vcs, fingerprint) 73 | ); 74 | } 75 | 76 | /** 77 | * Deletes a checkout key 78 | * 79 | * @see https://circleci.com/docs/api/v1-reference/#delete-checkout-key 80 | * @example DELETE : https://circleci.com/api/v1.1/project/:vcs-type/:username/:project/checkout-key/:fingerprint 81 | * 82 | * @param token CircleCI API token 83 | * @param fingerprint Fingerprint of the key to delete 84 | * @param circleHost Provide custom url for CircleCI 85 | * @param vcs Git information for project 86 | * @returns Status message of deletion 87 | */ 88 | export function deleteCheckoutKey( 89 | token: string, 90 | fingerprint: string, 91 | { circleHost, ...vcs }: GitInfo & CircleOptions 92 | ): Promise { 93 | return client(token, circleHost).delete( 94 | createUrl(vcs, fingerprint) 95 | ); 96 | } 97 | 98 | /** 99 | * Create a url for checkout-key operations 100 | * @private 101 | * @param vcs Git information for project 102 | * @param name Optional, Name of the environment variable 103 | */ 104 | function createUrl(vcs: GitInfo, fingerprint?: string): string { 105 | return `${createVcsUrl(vcs)}/checkout-key${ 106 | fingerprint ? `/${fingerprint}` : "" 107 | }`; 108 | } 109 | -------------------------------------------------------------------------------- /src/api/env.ts: -------------------------------------------------------------------------------- 1 | import { 2 | GitInfo, 3 | ListEnvVariablesResponse, 4 | createVcsUrl, 5 | EnvVariable, 6 | EnvVariableResponse, 7 | DeleteEnvVarResponse, 8 | CircleOptions 9 | } from "../types"; 10 | import { client } from "../client"; 11 | import { createJsonHeader } from "../util"; 12 | 13 | /** 14 | * List all of a projects environment variables, part of the 15 | * value will be masked with *'s 16 | * 17 | * @see getEnv for retrieving the hidden value of an env variable 18 | * 19 | * @see https://circleci.com/docs/api/v1-reference/#list-environment-variables 20 | * @example GET : https://circleci.com/api/v1.1/project/:vcs-type/:username/:project/envvar 21 | * 22 | * @param token CircleCI API token 23 | * @param circleHost Provide custom url for CircleCI 24 | * @param vcs Git information for project 25 | * @returns list of env variables for a specific project 26 | */ 27 | export function listEnv( 28 | token: string, 29 | { circleHost, ...vcs }: GitInfo & CircleOptions 30 | ): Promise { 31 | return client(token, circleHost).get( 32 | createUrl(vcs) 33 | ); 34 | } 35 | 36 | /** 37 | * Add environment variable to project 38 | * 39 | * @see https://circleci.com/docs/api/v1-reference/#add-environment-variable 40 | * @example POST : https://circleci.com/api/v1.1/project/:vcs-type/:username/:project/envvar 41 | * 42 | * @param token CircleCI API token 43 | * @param payload Environment variable object to add to project 44 | * @param vcs Git information for project 45 | * @param circleHost Provide custom url for CircleCI 46 | * @returns newly created environment variable 47 | */ 48 | export function addEnv( 49 | token: string, 50 | payload: EnvVariable, 51 | { circleHost, ...vcs }: GitInfo & CircleOptions 52 | ): Promise { 53 | return client(token, circleHost).post( 54 | createUrl(vcs), 55 | payload, 56 | createJsonHeader() 57 | ); 58 | } 59 | 60 | /** 61 | * Gets the hidden value of environment variable :name 62 | * 63 | * @example GET : https://circleci.com/api/v1.1/project/:vcs-type/:username/:project/envvar/:name 64 | * @see https://circleci.com/docs/api/v1-reference/#get-environment-variable 65 | * 66 | * @param token CircleCI API token 67 | * @param envName Name of variable to fetch value 68 | * @param circleHost Provide custom url for CircleCI 69 | * @param vcs Git information for project 70 | * @returns Full hidden value of environment variable 71 | */ 72 | export function getEnv( 73 | token: string, 74 | envName: string, 75 | { circleHost, ...vcs }: GitInfo & CircleOptions 76 | ): Promise { 77 | return client(token, circleHost).get(createUrl(vcs, envName)); 78 | } 79 | 80 | /** 81 | * Deletes the environment variable named ':name' 82 | * 83 | * @example DELETE : https://circleci.com/api/v1.1/project/:vcs-type/:username/:project/envvar/:name 84 | * @see https://circleci.com/docs/api/v1-reference/#delete-environment-variable 85 | * 86 | * @param token CircleCI API token 87 | * @param envName Name of variable to fetch value 88 | * @param circleHost Provide custom url for CircleCI 89 | * @param vcs Git information for project 90 | * @returns Status message result of operation 91 | */ 92 | export function deleteEnv( 93 | token: string, 94 | envName: string, 95 | { circleHost, ...vcs }: GitInfo & CircleOptions 96 | ): Promise { 97 | return client(token, circleHost).delete(createUrl(vcs, envName)); 98 | } 99 | 100 | /** 101 | * Create a url for env operations 102 | * @private 103 | * @param vcs Git information for project 104 | * @param name Optional, Name of the environment variable 105 | */ 106 | function createUrl(vcs: GitInfo, name: string = ""): string { 107 | return `${createVcsUrl(vcs)}/envvar${name ? `/${name}` : ""}`; 108 | } 109 | -------------------------------------------------------------------------------- /src/api/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./actions"; 2 | export * from "./artifacts"; 3 | export * from "./builds"; 4 | export * from "./projects"; 5 | export * from "./cache"; 6 | export * from "./checkout-keys"; 7 | export * from "./env"; 8 | export * from "./metadata"; 9 | export * from "./misc"; 10 | export * from "./user"; 11 | -------------------------------------------------------------------------------- /src/api/metadata.ts: -------------------------------------------------------------------------------- 1 | import { 2 | GitInfo, 3 | TestMetadataResponse, 4 | createVcsUrl, 5 | CircleOptions 6 | } from "../types"; 7 | import { client } from "../client"; 8 | 9 | /** 10 | * Provides test metadata for a build 11 | * 12 | * @see https://circleci.com/docs/api/v1-reference/#test-metadata 13 | * @example GET : https://circleci.com/api/v1.1/project/:vcs-type/:username/:project/:build_num/tests 14 | * 15 | * @param token CircleCI API token 16 | * @param buildNumber Build number to get metadata for 17 | * @param circleHost Provide custom url for CircleCI 18 | * @param vcs Git information for project 19 | * @returns metadata for tests 20 | */ 21 | export function getTestMetadata( 22 | token: string, 23 | buildNumber: number, 24 | { circleHost, ...vcs }: GitInfo & CircleOptions 25 | ): Promise { 26 | const url = `${createVcsUrl(vcs)}/${buildNumber}/tests`; 27 | return client(token, circleHost).get(url); 28 | } 29 | -------------------------------------------------------------------------------- /src/api/misc.ts: -------------------------------------------------------------------------------- 1 | import { 2 | GitInfo, 3 | SSHKey, 4 | AddSSHKeyResponse, 5 | createVcsUrl, 6 | HerokuKey, 7 | AddHerokuResponse, 8 | CircleOptions, 9 | API_USER 10 | } from "../types"; 11 | import { client } from "../client"; 12 | 13 | /** 14 | * Creates an ssh key that will be used to access the external system identified by 15 | * the hostname parameter for SSH key-based authentication. 16 | * 17 | * @see https://circleci.com/docs/api/v1-reference/#ssh-keys 18 | * @example POST - https://circleci.com/api/v1.1/project/:vcs-type/:username/:project/ssh-key 19 | * 20 | * @param token CircleCI API token 21 | * @param vcs Git information for project 22 | * @param key SSH key details to add to project 23 | * @param circleHost Provide custom url for CircleCI 24 | * @returns nothing unless error 25 | */ 26 | export function addSSHKey( 27 | token: string, 28 | vcs: GitInfo, 29 | key: SSHKey, 30 | { circleHost }: CircleOptions = {} 31 | ): Promise { 32 | const url = `${createVcsUrl(vcs)}/ssh-key`; 33 | return client(token, circleHost).post(url, key); 34 | } 35 | 36 | /** 37 | * Adds your Heroku API key to CircleCI 38 | * 39 | * @see https://circleci.com/docs/api/v1-reference/#ssh-keys 40 | * @example POST - https://circleci.com/api/v1.1/project/:vcs-type/:username/:project/ssh-key 41 | * 42 | * @param token CircleCI API token 43 | * @param key Heroku key to add to project 44 | * @param circleHost Provide custom url for CircleCI 45 | * @returns nothing unless error 46 | */ 47 | export function addHerokuKey( 48 | token: string, 49 | key: HerokuKey, 50 | { circleHost }: CircleOptions = {} 51 | ): Promise { 52 | const url = `${API_USER}/heroku-key`; 53 | return client(token, circleHost).post(url, key); 54 | } 55 | -------------------------------------------------------------------------------- /src/api/projects.ts: -------------------------------------------------------------------------------- 1 | import { client } from "../client"; 2 | import { 3 | AllProjectsResponse, 4 | GitRequiredRequest, 5 | FollowProjectResponse, 6 | API_ALL_PROJECTS, 7 | createVcsUrl, 8 | CircleOptions 9 | } from "../types"; 10 | 11 | /** 12 | * All followed projects: 13 | * @see https://circleci.com/docs/api/v1-reference/#projects 14 | * @example GET - /projects 15 | */ 16 | export function getAllProjects( 17 | token: string, 18 | { circleHost }: CircleOptions = {} 19 | ): Promise { 20 | return client(token, circleHost).get(API_ALL_PROJECTS); 21 | } 22 | 23 | /** 24 | * Follow a new project. CircleCI will then monitor the project for automatic building of commits. 25 | * @see https://circleci.com/docs/api/v1-reference/#follow-project 26 | * @example POST - /project/:vcs-type/:username/:project/follow 27 | */ 28 | export function postFollowNewProject( 29 | token: string, 30 | { vcs, circleHost }: GitRequiredRequest 31 | ): Promise { 32 | const url = `${createVcsUrl(vcs)}/follow`; 33 | return client(token, circleHost).post(url); 34 | } 35 | -------------------------------------------------------------------------------- /src/api/user.ts: -------------------------------------------------------------------------------- 1 | import { MeResponse, API_ME, CircleOptions } from "../types"; 2 | import { client } from "../client"; 3 | 4 | /** 5 | * Get authenticated user 6 | * @see https://circleci.com/docs/api/v1-reference/#getting-started 7 | * @example GET - /me 8 | */ 9 | export function getMe( 10 | token: string, 11 | { circleHost }: CircleOptions = {} 12 | ): Promise { 13 | return client(token, circleHost).get(API_ME); 14 | } 15 | -------------------------------------------------------------------------------- /src/circleci.ts: -------------------------------------------------------------------------------- 1 | import { validateVCSRequest } from "./util"; 2 | import { 3 | CircleRequest, 4 | CircleCIOptions, 5 | GitInfo, 6 | GitRequiredRequest, 7 | FullRequest, 8 | GitType, 9 | Options, 10 | BuildActionResponse, 11 | BuildAction, 12 | FilterRequestOptions, 13 | RequestOptions, 14 | ArtifactsRequestOptions, 15 | Me, 16 | Project, 17 | FollowNewResult, 18 | BuildSummary, 19 | BuildWithSteps, 20 | Artifact, 21 | Build, 22 | ListEnvVariablesResponse, 23 | EnvVariable, 24 | EnvVariableResponse, 25 | DeleteEnvVarResponse, 26 | ClearCacheResponse, 27 | CheckoutKeyResponse, 28 | CheckoutType, 29 | DeleteCheckoutKeyResponse, 30 | TestMetadataResponse, 31 | SSHKey, 32 | AddSSHKeyResponse, 33 | HerokuKey, 34 | AddHerokuResponse, 35 | CircleOptions 36 | } from "./types"; 37 | import { getAllProjects, postFollowNewProject } from "./api/projects"; 38 | import { getRecentBuilds, getBuildSummaries, getFullBuild } from "./api/builds"; 39 | import { getLatestArtifacts, getBuildArtifacts } from "./api/artifacts"; 40 | import { getMe } from "./api/user"; 41 | import { postBuildActions, postTriggerNewBuild } from "./api"; 42 | import { listEnv, addEnv, getEnv, deleteEnv } from "./api/env"; 43 | import { clearCache } from "./api/cache"; 44 | import { 45 | getCheckoutKeys, 46 | createCheckoutKey, 47 | getCheckoutKey, 48 | deleteCheckoutKey 49 | } from "./api/checkout-keys"; 50 | import { getTestMetadata } from "./api/metadata"; 51 | import { addSSHKey, addHerokuKey } from "./api/misc"; 52 | 53 | // TODO 54 | /* 55 | For the endpoints that accept filters/offset/etc 56 | modify factory functions to pass in only required options 57 | */ 58 | 59 | /** 60 | * CircleCI API Wrapper 61 | * A wrapper for all of the circleci api calls. 62 | * Most values can be overridden by individual methods 63 | * 64 | */ 65 | export class CircleCI { 66 | private token: string; 67 | private vcs: GitInfo; 68 | private options: Options; 69 | private circleOptions: CircleOptions; 70 | 71 | /** 72 | * 73 | * @param token CircleCI API token 74 | * @param vcs Default git information 75 | * @param vcs.type Git project type ex "github" | "bitbucket" 76 | * @param vcs.owner Owner of the git repository 77 | * @param vcs.repo Git repository name 78 | * @param options Additional query parameters 79 | * @returns {CircleCI} wrapper for CircleCI 80 | */ 81 | constructor({ 82 | token, 83 | vcs: { type = GitType.GITHUB, owner = "", repo = "" } = {}, 84 | options = {}, 85 | circleHost 86 | }: CircleCIOptions) { 87 | this.token = token; 88 | this.vcs = { type, owner, repo }; 89 | this.options = options; 90 | this.circleOptions = { circleHost }; 91 | } 92 | 93 | /** 94 | * Get the options used to create this instance 95 | */ 96 | defaults(): CircleRequest { 97 | return { token: this.token, vcs: this.vcs, options: this.options }; 98 | } 99 | 100 | /** 101 | * Adds the CircleCI token to a url 102 | * @param url URL to modify 103 | */ 104 | addToken(url: string): string { 105 | return `${url}?circle-token=${this.token}`; 106 | } 107 | 108 | /** 109 | * Get the currently authenticated user 110 | */ 111 | me(): Promise { 112 | return getMe(this.token, this.circleOptions); 113 | } 114 | 115 | /** 116 | * Get a list of all the projects the user follows 117 | */ 118 | projects(): Promise { 119 | return getAllProjects(this.token, this.circleOptions); 120 | } 121 | 122 | /** 123 | * Follow a new project. CircleCI will then monitor the project for automatic building of commits. 124 | * @param opts Project information 125 | */ 126 | followProject(opts: GitRequiredRequest): Promise { 127 | const { token, ...rest } = this.createRequest(opts); 128 | return postFollowNewProject(token, { ...rest, ...this.circleOptions }); 129 | } 130 | 131 | /** 132 | * Get all recent builds for CircleCI user 133 | * @param reqOptions Optional, Request options 134 | * @param reqOptions.options.limit Optional, Limit the number of builds returned, max=100 135 | * @param reqOptions.options.offset Optional, builds starting from this offset 136 | * @param opts Optional settings 137 | */ 138 | recentBuilds( 139 | reqOptions: RequestOptions = {}, 140 | opts?: CircleRequest 141 | ): Promise { 142 | const { token, options } = this.createRequest({ 143 | ...(opts || {}), 144 | options: { ...(opts ? opts.options : {}), ...reqOptions } 145 | }); 146 | return getRecentBuilds(token, { ...options, ...this.circleOptions }); 147 | } 148 | 149 | /** 150 | * Get recent build summaries for a project 151 | * @param reqOptions Optional, request options for filtering, limiting, etc 152 | * @param reqOptions.limit Optional, the number of builds to return. Maximum 100, defaults to 30. 153 | * @param reqOptions.offset Optional, builds starting from this offset, defaults to 0. 154 | * @param reqOptions.filter Optional, restricts which builds are returned. Set to "completed", "successful", "failed", "running" 155 | * @param opts Optional settings 156 | */ 157 | builds( 158 | reqOptions?: FilterRequestOptions, 159 | opts?: CircleRequest 160 | ): Promise { 161 | const { token, ...rest } = this.createRequest({ 162 | ...(opts || {}), 163 | options: { ...(opts ? opts.options : {}), ...(reqOptions || {}) } 164 | }); 165 | return getBuildSummaries(token, { ...rest, ...this.circleOptions }); 166 | } 167 | 168 | /** 169 | * Get recent builds for a project and branch 170 | * @param branch Target branch to fetch builds for 171 | * @param reqOptions Optional, request options for filtering, limiting, etc 172 | * @param reqOptions.limit Optional, the number of builds to return. Maximum 100, defaults to 30. 173 | * @param reqOptions.offset Optional, builds starting from this offset, defaults to 0. 174 | * @param reqOptions.filter Optional, restricts which builds are returned. Set to "completed", "successful", "failed", "running" 175 | * @param opts Optional settings 176 | */ 177 | buildsFor( 178 | branch: string = "master", 179 | reqOptions: FilterRequestOptions = {}, 180 | opts?: CircleRequest 181 | ): Promise { 182 | const { token, ...rest } = this.createRequest({ 183 | ...opts, 184 | options: { ...(opts ? opts.options : {}), ...reqOptions, branch } 185 | }); 186 | return getBuildSummaries(token, { ...rest, ...this.circleOptions }); 187 | } 188 | 189 | /** 190 | * Get full build details for a single build 191 | * @param buildNumber Target build number 192 | * @param opts Optional settings 193 | */ 194 | build( 195 | buildNumber: number, 196 | opts?: GitRequiredRequest 197 | ): Promise { 198 | const { token, vcs } = this.createRequest({ 199 | ...(opts || {}), 200 | options: { ...(opts ? opts.options : {}) } 201 | }); 202 | return getFullBuild(token, buildNumber, { ...vcs, ...this.circleOptions }); 203 | } 204 | 205 | /** 206 | * Get artifacts for single project build 207 | * @param buildNumber Target build number 208 | * @param opts Optional settings to override class defaults 209 | */ 210 | artifacts(buildNumber: number, opts?: CircleRequest): Promise { 211 | const { token, vcs } = this.createRequest(opts); 212 | return getBuildArtifacts(token, buildNumber, { 213 | ...vcs, 214 | ...this.circleOptions 215 | }); 216 | } 217 | 218 | /** 219 | * Get the latest build artifacts for a project 220 | * Pass a branch in the options to target a specific branch 221 | * @param reqOptions Optional, request options for filtering and specifying a branch 222 | * @param reqOptions.branch The branch you would like to look in for the latest build. Returns artifacts for latest build in entire project if omitted. 223 | * @param reqOptions.filter Restricts which builds are returned. Set to "completed", "successful", "failed", "running" 224 | * @param opts Optional settings 225 | */ 226 | latestArtifacts( 227 | reqOptions?: ArtifactsRequestOptions, 228 | opts: CircleRequest = {} 229 | ): Promise { 230 | const { token, ...rest } = this.createRequest({ 231 | ...opts, 232 | options: { ...opts.options, ...(reqOptions || {}) } 233 | }); 234 | return getLatestArtifacts(token, { ...rest, ...this.circleOptions }); 235 | } 236 | 237 | /** 238 | * Retries the build, returns a summary of the new build. 239 | * @param build Target build number 240 | * @param opts Optional settings 241 | */ 242 | retry(build: number, opts?: CircleRequest): Promise { 243 | return this.performAction( 244 | { ...this.createRequest(opts), ...this.circleOptions }, 245 | build, 246 | BuildAction.RETRY 247 | ); 248 | } 249 | 250 | /** 251 | * Cancels the build, returns a summary of the new build. 252 | * @param build Target build number 253 | * @param opts Optional settings 254 | */ 255 | cancel(build: number, opts?: CircleRequest): Promise { 256 | return this.performAction( 257 | { ...this.createRequest(opts), ...this.circleOptions }, 258 | build, 259 | BuildAction.CANCEL 260 | ); 261 | } 262 | 263 | /** 264 | * Triggers a new build, returns a summary of the build. 265 | * @see https://circleci.com/docs/api/v1-reference/#new-build 266 | * @param opts Optional settings 267 | * @param opts.options.newBuildOptions Additional build settings 268 | */ 269 | triggerBuild(opts?: CircleRequest): Promise { 270 | const { token, ...rest } = this.createRequest(opts); 271 | return postTriggerNewBuild(token, { ...rest, ...this.circleOptions }); 272 | } 273 | 274 | /** 275 | * Triggers a new build for a specific branch. 276 | * @see https://circleci.com/docs/api/v1-reference/#new-build-branch 277 | * @param branch Optional, branch to target, defaults to 'master' 278 | * @param opts Optional settings 279 | * @param opts.options.newBuildOptions Additional build settings 280 | */ 281 | triggerBuildFor( 282 | branch: string = "master", 283 | opts?: CircleRequest 284 | ): Promise { 285 | const { token, ...request } = this.createRequest({ 286 | ...opts, 287 | options: { ...(opts ? opts.options : {}), branch } 288 | }); 289 | return postTriggerNewBuild(token, { ...request, ...this.circleOptions }); 290 | } 291 | 292 | /* 293 | * Cache 294 | */ 295 | 296 | /** 297 | * Clear the cache for the project 298 | * @see clearCache for implementation 299 | * @see https://circleci.com/docs/api/v1-reference/#clear-cache 300 | * @param opts Optional settings 301 | */ 302 | clearCache(opts?: CircleRequest): Promise { 303 | const { token, vcs } = this.createRequest(opts); 304 | return clearCache(token, { ...vcs, ...this.circleOptions }); 305 | } 306 | 307 | /* 308 | * Environment Variables 309 | */ 310 | 311 | /** 312 | * List all of a projects environment variables, values will not be fully shown 313 | * @see getEnvVar for accessing full value 314 | * @see listEnv 315 | * @see https://circleci.com/docs/api/v1-reference/#list-environment-variables 316 | * @param opts Optional settings 317 | */ 318 | listEnvVars(opts?: CircleRequest): Promise { 319 | const { token, vcs } = this.createRequest(opts); 320 | return listEnv(token, { ...vcs, ...this.circleOptions }); 321 | } 322 | 323 | /** 324 | * Add environment variable to project 325 | * @see addEnv 326 | * @see https://circleci.com/docs/api/v1-reference/#add-environment-variable 327 | * @param variable Environment variable to add to project 328 | * @param opts Optional settings 329 | */ 330 | addEnvVar( 331 | variable: EnvVariable, 332 | opts?: CircleRequest 333 | ): Promise { 334 | const { token, vcs } = this.createRequest(opts); 335 | return addEnv(token, variable, { ...vcs, ...this.circleOptions }); 336 | } 337 | 338 | /** 339 | * Get the hidden value of an environment variable 340 | * @see getEnv 341 | * @see https://circleci.com/docs/api/v1-reference/#get-environment-variable 342 | * @param envName Name of the variable to fetch 343 | * @param opts Optional settings 344 | */ 345 | getEnvVar( 346 | envName: string, 347 | opts?: CircleRequest 348 | ): Promise { 349 | const { token, vcs } = this.createRequest(opts); 350 | return getEnv(token, envName, { ...vcs, ...this.circleOptions }); 351 | } 352 | 353 | /** 354 | * Delete an environment variable 355 | * @see deleteEnv 356 | * @see https://circleci.com/docs/api/v1-reference/#delete-environment-variable 357 | * @param envName Name of the variable to delete 358 | * @param opts Optional settings 359 | */ 360 | deleteEnvVar( 361 | envName: string, 362 | opts?: CircleRequest 363 | ): Promise { 364 | const { token, vcs } = this.createRequest(opts); 365 | return deleteEnv(token, envName, { ...vcs, ...this.circleOptions }); 366 | } 367 | 368 | /* 369 | * Checkout Keys 370 | */ 371 | 372 | /** 373 | * List all the checkout keys for the project 374 | * @see getCheckoutKeys 375 | * @see https://circleci.com/docs/api/v1-reference/#list-checkout-keys 376 | * @param opts Optional request settings 377 | */ 378 | listCheckoutKeys(opts?: CircleRequest): Promise { 379 | const { token, vcs } = this.createRequest(opts); 380 | return getCheckoutKeys(token, { ...vcs, ...this.circleOptions }); 381 | } 382 | 383 | /** 384 | * Create a new checkout key 385 | * @see createCheckoutKey 386 | * @see https://circleci.com/docs/api/v1-reference/#new-checkout-key 387 | * @param type Type of checkout key to create 388 | * @param opts Optional request settings 389 | */ 390 | addCheckoutKey( 391 | type: CheckoutType, 392 | opts?: CircleRequest 393 | ): Promise { 394 | const { token, vcs } = this.createRequest(opts); 395 | return createCheckoutKey( 396 | token, 397 | { type }, 398 | { ...vcs, ...this.circleOptions } 399 | ); 400 | } 401 | 402 | /** 403 | * Get a single checkout key from it's fingerprint 404 | * @see getCheckoutKey 405 | * @see https://circleci.com/docs/api/v1-reference/#get-checkout-key 406 | * @param fingerprint Fingerprint of the key to get 407 | * @param opts Optional request settings 408 | */ 409 | getCheckoutKey( 410 | fingerprint: string, 411 | opts?: CircleRequest 412 | ): Promise { 413 | const { token, vcs } = this.createRequest(opts); 414 | return getCheckoutKey(token, fingerprint, { 415 | ...vcs, 416 | ...this.circleOptions 417 | }); 418 | } 419 | 420 | /** 421 | * Delete a checkout key 422 | * @see deleteCheckoutKey 423 | * @see https://circleci.com/docs/api/v1-reference/#delete-checkout-key 424 | * @param fingerprint Fingerprint of the key to delete 425 | * @param opts Optional request settings 426 | */ 427 | deleteCheckoutKey( 428 | fingerprint: string, 429 | opts?: CircleRequest 430 | ): Promise { 431 | const { token, vcs } = this.createRequest(opts); 432 | return deleteCheckoutKey(token, fingerprint, { 433 | ...vcs, 434 | ...this.circleOptions 435 | }); 436 | } 437 | 438 | /** 439 | * Get test metadata for a build 440 | * @see getTestMetadata 441 | * @see https://circleci.com/docs/api/v1-reference/#test-metadata 442 | * @param buildNumber Build number to get metadata for 443 | * @param opts Optional request settings 444 | */ 445 | getTestMetadata( 446 | buildNumber: number, 447 | opts?: CircleRequest 448 | ): Promise { 449 | const { token, vcs } = this.createRequest(opts); 450 | return getTestMetadata(token, buildNumber, { 451 | ...vcs, 452 | ...this.circleOptions 453 | }); 454 | } 455 | 456 | /** 457 | * Creates an ssh key that will be used to access the external system identified by 458 | * the hostname parameter for SSH key-based authentication. 459 | * @see https://circleci.com/docs/api/v1-reference/#ssh-keys 460 | * @param token CircleCI API token 461 | * @param vcs Git information for project 462 | * @param key SSH key details to add to project 463 | */ 464 | addSSHKey(key: SSHKey, opts?: CircleRequest): Promise { 465 | const { token, vcs } = this.createRequest(opts); 466 | return addSSHKey(token, vcs, key, this.circleOptions); 467 | } 468 | 469 | /** 470 | * Adds your Heroku API key to CircleCI 471 | * @see https://circleci.com/docs/api/v1-reference/#ssh-keys 472 | * @param token CircleCI API token 473 | * @param key Heroku key to add to project 474 | */ 475 | addHerokuKey( 476 | key: HerokuKey, 477 | opts?: CircleRequest 478 | ): Promise { 479 | const { token } = this.createRequest(opts); 480 | return addHerokuKey(token, key, this.circleOptions); 481 | } 482 | 483 | /* 484 | * Private functions 485 | */ 486 | 487 | /** 488 | * Take a request object and merge it with the class properties. 489 | * Passed in options always take priority over the class properties 490 | * @param opts Optional, request options 491 | * @throws If missing a token, or VCS options 492 | * @returns Merged request object 493 | */ 494 | private createRequest(opts: CircleRequest = {}): FullRequest & CircleOptions { 495 | const request: FullRequest & CircleOptions = { 496 | token: opts.token || this.token, 497 | options: { ...this.options, ...opts.options }, 498 | vcs: { ...this.vcs, ...opts.vcs } 499 | }; 500 | 501 | validateVCSRequest(request); 502 | 503 | return request; 504 | } 505 | 506 | /** 507 | * Perform a build action on a build 508 | * @see BuildAction for list of actions 509 | * @see postBuildActions for implementation 510 | * @param request Request information 511 | * @param build Build number to perform action on 512 | * @param action Type of action to perform 513 | */ 514 | private performAction( 515 | request: FullRequest & CircleOptions, 516 | build: number, 517 | action: BuildAction 518 | ): Promise { 519 | const { token, vcs, circleHost } = request; 520 | return postBuildActions(token, build, action, { ...vcs, circleHost }); 521 | } 522 | } 523 | -------------------------------------------------------------------------------- /src/client.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosPromise, AxiosRequestConfig } from "axios"; 2 | import { API_BASE } from "./types"; 3 | import { addUserAgentHeader } from "./util"; 4 | 5 | function get( 6 | token: string, 7 | url: string, 8 | options: AxiosRequestConfig 9 | ): AxiosPromise { 10 | return axios.get(addTokenParam(token, url), options); 11 | } 12 | 13 | function post( 14 | token: string, 15 | url: string, 16 | body: T, 17 | options: AxiosRequestConfig 18 | ): AxiosPromise { 19 | return axios.post(addTokenParam(token, url), body, options); 20 | } 21 | 22 | function doDelete( 23 | token: string, 24 | url: string, 25 | options: AxiosRequestConfig 26 | ): AxiosPromise { 27 | return axios.delete(addTokenParam(token, url), options); 28 | } 29 | 30 | function addTokenParam(token: string, url: string): string { 31 | return `${url}${url.includes("?") ? "&" : "?"}circle-token=${token}`; 32 | } 33 | 34 | /** 35 | * Create a custom GET request for CircleCI 36 | * 37 | * @deprecated In favour of using the [client] instead. 38 | */ 39 | export function circleGet( 40 | token: string, 41 | url: string, 42 | options?: AxiosRequestConfig 43 | ): AxiosPromise { 44 | console.warn("circleGet is deprecated, use `client('token').get(...)`"); 45 | return client(token).get(url, addUserAgentHeader(options)); 46 | } 47 | 48 | /** 49 | * Create a custom POST request for CircleCI 50 | * 51 | * @deprecated In favour of using the [client] instead. 52 | */ 53 | export function circlePost( 54 | token: string, 55 | url: string, 56 | body?: any, 57 | options?: AxiosRequestConfig 58 | ): AxiosPromise { 59 | console.warn("circlePost is deprecated, use `client('token').post(...)`"); 60 | return client(token).post(url, body, addUserAgentHeader(options)); 61 | } 62 | 63 | /** 64 | * Create a custom DELETE request for CircleCI 65 | * 66 | * @deprecated In favour of using the [client] instead. 67 | */ 68 | export function circleDelete( 69 | token: string, 70 | url: string, 71 | options?: AxiosRequestConfig 72 | ): AxiosPromise { 73 | console.warn("circleDelete is deprecated, use `client('token').delete(...)`"); 74 | return client(token).delete(url, addUserAgentHeader(options)); 75 | } 76 | 77 | export interface ClientFactory { 78 | get: (url: string, options?: AxiosRequestConfig) => Promise; 79 | post: ( 80 | url: string, 81 | body?: any, 82 | options?: AxiosRequestConfig 83 | ) => Promise; 84 | delete: (url: string, options?: AxiosRequestConfig) => Promise; 85 | } 86 | 87 | /** 88 | * Create a client for interacting with the CircleCI API. 89 | * 90 | * @param token CircleCI API token 91 | * @param circleHost Custom host address for CircleCI 92 | */ 93 | export function client(token: string, circleHost: string = API_BASE) { 94 | const baseOptions: AxiosRequestConfig = { baseURL: circleHost }; 95 | const factory: ClientFactory = { 96 | get: async ( 97 | url: string, 98 | options: AxiosRequestConfig = {} 99 | ): Promise => { 100 | const config = addUserAgentHeader(mergeOptions(baseOptions, options)); 101 | return (await get(token, url, config)).data; 102 | }, 103 | post: async ( 104 | url: string, 105 | body?: any, 106 | options: AxiosRequestConfig = {} 107 | ): Promise => { 108 | const config = addUserAgentHeader(mergeOptions(baseOptions, options)); 109 | return (await post(token, url, body, config)).data; 110 | }, 111 | delete: async ( 112 | url: string, 113 | options: AxiosRequestConfig = {} 114 | ): Promise => { 115 | const config = addUserAgentHeader(mergeOptions(baseOptions, options)); 116 | return (await doDelete(token, url, config)).data; 117 | }, 118 | }; 119 | 120 | return factory; 121 | } 122 | 123 | function mergeOptions( 124 | base: AxiosRequestConfig, 125 | provided: AxiosRequestConfig 126 | ): AxiosRequestConfig { 127 | return { 128 | ...base, 129 | ...provided, 130 | }; 131 | } 132 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./types/api"; 2 | export * from "./types/lib"; 3 | export * from "./api"; 4 | export * from "./circleci"; 5 | export { getGitType } from "./util"; 6 | export { client as CircleCIFactory } from "./client"; 7 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./api"; 2 | export * from "./lib"; 3 | -------------------------------------------------------------------------------- /src/types/json/artifacts.sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "path": "apk/release/app-release.apk", 3 | "pretty_path": "apk/release/app-release.apk", 4 | "node_index": 0, 5 | "url": 6 | "https://000000000-gh.circle-artifacts.com/0/apk/release/app-release.apk" 7 | } 8 | -------------------------------------------------------------------------------- /src/types/json/artifacts.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "http://circleci.com/", 3 | "type": "object", 4 | "definitions": {}, 5 | "$schema": "http://json-schema.org/draft-07/schema#", 6 | "properties": { 7 | "path": { 8 | "$id": "/properties/path", 9 | "type": "string" 10 | }, 11 | "pretty_path": { 12 | "$id": "/properties/pretty_path", 13 | "type": "string" 14 | }, 15 | "node_index": { 16 | "$id": "/properties/node_index", 17 | "type": "integer" 18 | }, 19 | "url": { 20 | "$id": "/properties/url", 21 | "type": "string" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/types/json/build.sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "all_commit_details": [ 3 | { 4 | "author_date": "2018-07-13T14:51:43-04:00", 5 | "author_email": "", 6 | "author_login": "", 7 | "author_name": "", 8 | "body": "", 9 | "branch": "master", 10 | "commit": "d884761c978ebc92a64f703802d9e72e2fe2abc3", 11 | "commit_url": "", 12 | "committer_date": "2018-07-13T14:51:43-04:00", 13 | "committer_email": "", 14 | "committer_login": "", 15 | "committer_name": "", 16 | "subject": "Develop (#119)" 17 | } 18 | ], 19 | "all_commit_details_truncated": false, 20 | "author_date": "2018-07-13T14:51:43-04:00", 21 | "author_email": "", 22 | "author_name": "", 23 | "body": "", 24 | "branch": "master", 25 | "build_num": 698, 26 | "build_parameters": null, 27 | "build_time_millis": 325199, 28 | "build_url": "", 29 | "canceled": false, 30 | "canceler": null, 31 | "circle_yml": { 32 | "string": "" 33 | }, 34 | "committer_date": "2018-07-13T14:51:43-04:00", 35 | "committer_email": "", 36 | "committer_name": "", 37 | "compare": "", 38 | "dont_build": null, 39 | "fail_reason": null, 40 | "failed": false, 41 | "has_artifacts": true, 42 | "infrastructure_fail": false, 43 | "is_first_green_build": false, 44 | "job_name": null, 45 | "lifecycle": "finished", 46 | "messages": [], 47 | "no_dependency_cache": false, 48 | "node": null, 49 | "oss": false, 50 | "outcome": "success", 51 | "parallel": 1, 52 | "picard": { 53 | "build_agent": { 54 | "image": null, 55 | "properties": { 56 | "build_agent": "0.0.7400-c4f46116", 57 | "executor": "docker" 58 | } 59 | }, 60 | "executor": "docker", 61 | "resource_class": { 62 | "class": "medium", 63 | "cpu": 2, 64 | "ram": 4096 65 | } 66 | }, 67 | "platform": "2.0", 68 | "previous": { 69 | "build_num": 683, 70 | "build_time_millis": 402294, 71 | "status": "success" 72 | }, 73 | "previous_successful_build": { 74 | "build_num": 683, 75 | "build_time_millis": 402294, 76 | "status": "success" 77 | }, 78 | "queued_at": "2018-07-13T18:51:51.359Z", 79 | "reponame": "", 80 | "retries": null, 81 | "retry_of": null, 82 | "ssh_disabled": true, 83 | "ssh_users": [], 84 | "start_time": "2018-07-13T18:51:52.866Z", 85 | "status": "success", 86 | "steps": [ 87 | { 88 | "actions": [ 89 | { 90 | "allocation_id": "5b476105ea1b610013c28426-0-build/31727C2E", 91 | "background": false, 92 | "bash_command": null, 93 | "canceled": null, 94 | "continue": null, 95 | "end_time": "2018-07-12T14:10:47.277Z", 96 | "exit_code": null, 97 | "failed": null, 98 | "has_output": true, 99 | "index": 0, 100 | "infrastructure_fail": null, 101 | "insignificant": false, 102 | "name": "Spin up Environment", 103 | "output_url": "", 104 | "parallel": true, 105 | "run_time_millis": 6350, 106 | "start_time": "2018-07-12T14:10:40.927Z", 107 | "status": "success", 108 | "step": 0, 109 | "timedout": null, 110 | "truncated": false, 111 | "type": "test" 112 | } 113 | ], 114 | "name": "Spin up Environment" 115 | }, 116 | { 117 | "actions": [ 118 | { 119 | "allocation_id": "fgfg23432-0-build/31727C2E", 120 | "background": false, 121 | "bash_command": "", 122 | "canceled": null, 123 | "continue": null, 124 | "end_time": "2018-07-12T14:10:49.205Z", 125 | "exit_code": 0, 126 | "failed": null, 127 | "has_output": true, 128 | "index": 0, 129 | "infrastructure_fail": null, 130 | "insignificant": false, 131 | "name": "Checkout code", 132 | "output_url": "", 133 | "parallel": true, 134 | "run_time_millis": 1847, 135 | "start_time": "2018-07-12T14:10:47.358Z", 136 | "status": "success", 137 | "step": 101, 138 | "timedout": null, 139 | "truncated": false, 140 | "type": "test" 141 | } 142 | ], 143 | "name": "Checkout code" 144 | } 145 | ], 146 | "stop_time": "2018-07-13T18:57:18.065Z", 147 | "subject": "Develop (#119)", 148 | "timedout": false, 149 | "usage_queued_at": "2018-07-13T18:51:45.068Z", 150 | "user": { 151 | "avatar_url": "", 152 | "id": 528792, 153 | "is_user": true, 154 | "login": "", 155 | "name": "", 156 | "vcs_type": "github" 157 | }, 158 | "username": "", 159 | "vcs_revision": "d884761c978ebc92a64f703802d9e72e2fe2abc3", 160 | "vcs_tag": null, 161 | "vcs_type": "github", 162 | "vcs_url": "", 163 | "why": "github" 164 | } 165 | -------------------------------------------------------------------------------- /src/types/json/build.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "http://circleci.com/api/v1.1", 3 | "type": "object", 4 | "definitions": {}, 5 | "$schema": "http://json-schema.org/draft-07/schema#", 6 | "properties": { 7 | "all_commit_details": { 8 | "$id": "/properties/all_commit_details", 9 | "type": "array", 10 | "items": { 11 | "$id": "/properties/all_commit_details/items", 12 | "type": "object", 13 | "properties": { 14 | "author_date": { 15 | "$id": 16 | "/properties/all_commit_details/items/properties/author_date", 17 | "type": "string" 18 | }, 19 | "author_email": { 20 | "$id": 21 | "/properties/all_commit_details/items/properties/author_email", 22 | "type": "string" 23 | }, 24 | "author_login": { 25 | "$id": 26 | "/properties/all_commit_details/items/properties/author_login", 27 | "type": "string" 28 | }, 29 | "author_name": { 30 | "$id": 31 | "/properties/all_commit_details/items/properties/author_name", 32 | "type": "string" 33 | }, 34 | "body": { 35 | "$id": "/properties/all_commit_details/items/properties/body", 36 | "type": "string" 37 | }, 38 | "branch": { 39 | "$id": "/properties/all_commit_details/items/properties/branch", 40 | "type": "string" 41 | }, 42 | "commit": { 43 | "$id": "/properties/all_commit_details/items/properties/commit", 44 | "type": "string" 45 | }, 46 | "commit_url": { 47 | "$id": "/properties/all_commit_details/items/properties/commit_url", 48 | "type": "string" 49 | }, 50 | "committer_date": { 51 | "$id": 52 | "/properties/all_commit_details/items/properties/committer_date", 53 | "type": "string" 54 | }, 55 | "committer_email": { 56 | "$id": 57 | "/properties/all_commit_details/items/properties/committer_email", 58 | "type": "string" 59 | }, 60 | "committer_login": { 61 | "$id": 62 | "/properties/all_commit_details/items/properties/committer_login", 63 | "type": "string" 64 | }, 65 | "committer_name": { 66 | "$id": 67 | "/properties/all_commit_details/items/properties/committer_name", 68 | "type": "string" 69 | }, 70 | "subject": { 71 | "$id": "/properties/all_commit_details/items/properties/subject", 72 | "type": "string" 73 | } 74 | } 75 | } 76 | }, 77 | "all_commit_details_truncated": { 78 | "$id": "/properties/all_commit_details_truncated", 79 | "type": "boolean" 80 | }, 81 | "author_date": { 82 | "$id": "/properties/author_date", 83 | "type": "string" 84 | }, 85 | "author_email": { 86 | "$id": "/properties/author_email", 87 | "type": "string" 88 | }, 89 | "author_name": { 90 | "$id": "/properties/author_name", 91 | "type": "string" 92 | }, 93 | "body": { 94 | "$id": "/properties/body", 95 | "type": "string" 96 | }, 97 | "branch": { 98 | "$id": "/properties/branch", 99 | "type": "string" 100 | }, 101 | "build_num": { 102 | "$id": "/properties/build_num", 103 | "type": "integer" 104 | }, 105 | "build_parameters": { 106 | "$id": "/properties/build_parameters", 107 | "type": "null" 108 | }, 109 | "build_time_millis": { 110 | "$id": "/properties/build_time_millis", 111 | "type": "integer" 112 | }, 113 | "build_url": { 114 | "$id": "/properties/build_url", 115 | "type": "string" 116 | }, 117 | "canceled": { 118 | "$id": "/properties/canceled", 119 | "type": "boolean" 120 | }, 121 | "canceler": { 122 | "$id": "/properties/canceler", 123 | "type": "null" 124 | }, 125 | "circle_yml": { 126 | "$id": "/properties/circle_yml", 127 | "type": "object", 128 | "properties": { 129 | "string": { 130 | "$id": "/properties/circle_yml/properties/string", 131 | "type": "string" 132 | } 133 | } 134 | }, 135 | "committer_date": { 136 | "$id": "/properties/committer_date", 137 | "type": "string" 138 | }, 139 | "committer_email": { 140 | "$id": "/properties/committer_email", 141 | "type": "string" 142 | }, 143 | "committer_name": { 144 | "$id": "/properties/committer_name", 145 | "type": "string" 146 | }, 147 | "compare": { 148 | "$id": "/properties/compare", 149 | "type": "string" 150 | }, 151 | "dont_build": { 152 | "$id": "/properties/dont_build", 153 | "type": "null" 154 | }, 155 | "fail_reason": { 156 | "$id": "/properties/fail_reason", 157 | "type": "null" 158 | }, 159 | "failed": { 160 | "$id": "/properties/failed", 161 | "type": "boolean" 162 | }, 163 | "has_artifacts": { 164 | "$id": "/properties/has_artifacts", 165 | "type": "boolean" 166 | }, 167 | "infrastructure_fail": { 168 | "$id": "/properties/infrastructure_fail", 169 | "type": "boolean" 170 | }, 171 | "is_first_green_build": { 172 | "$id": "/properties/is_first_green_build", 173 | "type": "boolean" 174 | }, 175 | "job_name": { 176 | "$id": "/properties/job_name", 177 | "type": "null" 178 | }, 179 | "lifecycle": { 180 | "$id": "/properties/lifecycle", 181 | "type": "string" 182 | }, 183 | "messages": { 184 | "$id": "/properties/messages", 185 | "type": "array" 186 | }, 187 | "no_dependency_cache": { 188 | "$id": "/properties/no_dependency_cache", 189 | "type": "boolean" 190 | }, 191 | "node": { 192 | "$id": "/properties/node", 193 | "type": "null" 194 | }, 195 | "oss": { 196 | "$id": "/properties/oss", 197 | "type": "boolean" 198 | }, 199 | "outcome": { 200 | "$id": "/properties/outcome", 201 | "type": "string" 202 | }, 203 | "parallel": { 204 | "$id": "/properties/parallel", 205 | "type": "integer" 206 | }, 207 | "picard": { 208 | "$id": "/properties/picard", 209 | "type": "object", 210 | "properties": { 211 | "build_agent": { 212 | "$id": "/properties/picard/properties/build_agent", 213 | "type": "object", 214 | "properties": { 215 | "image": { 216 | "$id": 217 | "/properties/picard/properties/build_agent/properties/image", 218 | "type": "null" 219 | }, 220 | "properties": { 221 | "$id": 222 | "/properties/picard/properties/build_agent/properties/properties", 223 | "type": "object", 224 | "properties": { 225 | "build_agent": { 226 | "$id": 227 | "/properties/picard/properties/build_agent/properties/properties/properties/build_agent", 228 | "type": "string" 229 | }, 230 | "executor": { 231 | "$id": 232 | "/properties/picard/properties/build_agent/properties/properties/properties/executor", 233 | "type": "string" 234 | } 235 | } 236 | } 237 | } 238 | }, 239 | "executor": { 240 | "$id": "/properties/picard/properties/executor", 241 | "type": "string" 242 | }, 243 | "resource_class": { 244 | "$id": "/properties/picard/properties/resource_class", 245 | "type": "object", 246 | "properties": { 247 | "class": { 248 | "$id": 249 | "/properties/picard/properties/resource_class/properties/class", 250 | "type": "string" 251 | }, 252 | "cpu": { 253 | "$id": 254 | "/properties/picard/properties/resource_class/properties/cpu", 255 | "type": "integer" 256 | }, 257 | "ram": { 258 | "$id": 259 | "/properties/picard/properties/resource_class/properties/ram", 260 | "type": "integer" 261 | } 262 | } 263 | } 264 | } 265 | }, 266 | "platform": { 267 | "$id": "/properties/platform", 268 | "type": "string" 269 | }, 270 | "previous": { 271 | "$id": "/properties/previous", 272 | "type": "object", 273 | "properties": { 274 | "build_num": { 275 | "$id": "/properties/previous/properties/build_num", 276 | "type": "integer" 277 | }, 278 | "build_time_millis": { 279 | "$id": "/properties/previous/properties/build_time_millis", 280 | "type": "integer" 281 | }, 282 | "status": { 283 | "$id": "/properties/previous/properties/status", 284 | "type": "string" 285 | } 286 | } 287 | }, 288 | "previous_successful_build": { 289 | "$id": "/properties/previous_successful_build", 290 | "type": "object", 291 | "properties": { 292 | "build_num": { 293 | "$id": "/properties/previous_successful_build/properties/build_num", 294 | "type": "integer" 295 | }, 296 | "build_time_millis": { 297 | "$id": 298 | "/properties/previous_successful_build/properties/build_time_millis", 299 | "type": "integer" 300 | }, 301 | "status": { 302 | "$id": "/properties/previous_successful_build/properties/status", 303 | "type": "string" 304 | } 305 | } 306 | }, 307 | "queued_at": { 308 | "$id": "/properties/queued_at", 309 | "type": "string" 310 | }, 311 | "reponame": { 312 | "$id": "/properties/reponame", 313 | "type": "string" 314 | }, 315 | "retries": { 316 | "$id": "/properties/retries", 317 | "type": "null" 318 | }, 319 | "retry_of": { 320 | "$id": "/properties/retry_of", 321 | "type": "null" 322 | }, 323 | "ssh_disabled": { 324 | "$id": "/properties/ssh_disabled", 325 | "type": "boolean" 326 | }, 327 | "ssh_users": { 328 | "$id": "/properties/ssh_users", 329 | "type": "array" 330 | }, 331 | "start_time": { 332 | "$id": "/properties/start_time", 333 | "type": "string" 334 | }, 335 | "status": { 336 | "$id": "/properties/status", 337 | "type": "string" 338 | }, 339 | "steps": { 340 | "$id": "/properties/steps", 341 | "type": "array", 342 | "items": { 343 | "$id": "/properties/steps/items", 344 | "type": "object", 345 | "properties": { 346 | "actions": { 347 | "$id": "/properties/steps/items/properties/actions", 348 | "type": "array", 349 | "items": { 350 | "$id": "/properties/steps/items/properties/actions/items", 351 | "type": "object", 352 | "properties": { 353 | "allocation_id": { 354 | "$id": 355 | "/properties/steps/items/properties/actions/items/properties/allocation_id", 356 | "type": "string" 357 | }, 358 | "background": { 359 | "$id": 360 | "/properties/steps/items/properties/actions/items/properties/background", 361 | "type": "boolean" 362 | }, 363 | "bash_command": { 364 | "$id": 365 | "/properties/steps/items/properties/actions/items/properties/bash_command", 366 | "type": "null" 367 | }, 368 | "canceled": { 369 | "$id": 370 | "/properties/steps/items/properties/actions/items/properties/canceled", 371 | "type": "null" 372 | }, 373 | "continue": { 374 | "$id": 375 | "/properties/steps/items/properties/actions/items/properties/continue", 376 | "type": "null" 377 | }, 378 | "end_time": { 379 | "$id": 380 | "/properties/steps/items/properties/actions/items/properties/end_time", 381 | "type": "string" 382 | }, 383 | "exit_code": { 384 | "$id": 385 | "/properties/steps/items/properties/actions/items/properties/exit_code", 386 | "type": "null" 387 | }, 388 | "failed": { 389 | "$id": 390 | "/properties/steps/items/properties/actions/items/properties/failed", 391 | "type": "null" 392 | }, 393 | "has_output": { 394 | "$id": 395 | "/properties/steps/items/properties/actions/items/properties/has_output", 396 | "type": "boolean" 397 | }, 398 | "index": { 399 | "$id": 400 | "/properties/steps/items/properties/actions/items/properties/index", 401 | "type": "integer" 402 | }, 403 | "infrastructure_fail": { 404 | "$id": 405 | "/properties/steps/items/properties/actions/items/properties/infrastructure_fail", 406 | "type": "null" 407 | }, 408 | "insignificant": { 409 | "$id": 410 | "/properties/steps/items/properties/actions/items/properties/insignificant", 411 | "type": "boolean" 412 | }, 413 | "name": { 414 | "$id": 415 | "/properties/steps/items/properties/actions/items/properties/name", 416 | "type": "string" 417 | }, 418 | "output_url": { 419 | "$id": 420 | "/properties/steps/items/properties/actions/items/properties/output_url", 421 | "type": "string" 422 | }, 423 | "parallel": { 424 | "$id": 425 | "/properties/steps/items/properties/actions/items/properties/parallel", 426 | "type": "boolean" 427 | }, 428 | "run_time_millis": { 429 | "$id": 430 | "/properties/steps/items/properties/actions/items/properties/run_time_millis", 431 | "type": "integer" 432 | }, 433 | "start_time": { 434 | "$id": 435 | "/properties/steps/items/properties/actions/items/properties/start_time", 436 | "type": "string" 437 | }, 438 | "status": { 439 | "$id": 440 | "/properties/steps/items/properties/actions/items/properties/status", 441 | "type": "string" 442 | }, 443 | "step": { 444 | "$id": 445 | "/properties/steps/items/properties/actions/items/properties/step", 446 | "type": "integer" 447 | }, 448 | "timedout": { 449 | "$id": 450 | "/properties/steps/items/properties/actions/items/properties/timedout", 451 | "type": "null" 452 | }, 453 | "truncated": { 454 | "$id": 455 | "/properties/steps/items/properties/actions/items/properties/truncated", 456 | "type": "boolean" 457 | }, 458 | "type": { 459 | "$id": 460 | "/properties/steps/items/properties/actions/items/properties/type", 461 | "type": "string" 462 | } 463 | } 464 | } 465 | }, 466 | "name": { 467 | "$id": "/properties/steps/items/properties/name", 468 | "type": "string" 469 | } 470 | } 471 | } 472 | }, 473 | "stop_time": { 474 | "$id": "/properties/stop_time", 475 | "type": "string" 476 | }, 477 | "subject": { 478 | "$id": "/properties/subject", 479 | "type": "string" 480 | }, 481 | "timedout": { 482 | "$id": "/properties/timedout", 483 | "type": "boolean" 484 | }, 485 | "usage_queued_at": { 486 | "$id": "/properties/usage_queued_at", 487 | "type": "string" 488 | }, 489 | "user": { 490 | "$id": "/properties/user", 491 | "type": "object", 492 | "properties": { 493 | "avatar_url": { 494 | "$id": "/properties/user/properties/avatar_url", 495 | "type": "string" 496 | }, 497 | "id": { 498 | "$id": "/properties/user/properties/id", 499 | "type": "integer" 500 | }, 501 | "is_user": { 502 | "$id": "/properties/user/properties/is_user", 503 | "type": "boolean" 504 | }, 505 | "login": { 506 | "$id": "/properties/user/properties/login", 507 | "type": "string" 508 | }, 509 | "name": { 510 | "$id": "/properties/user/properties/name", 511 | "type": "string" 512 | }, 513 | "vcs_type": { 514 | "$id": "/properties/user/properties/vcs_type", 515 | "type": "string" 516 | } 517 | } 518 | }, 519 | "username": { 520 | "$id": "/properties/username", 521 | "type": "string" 522 | }, 523 | "vcs_revision": { 524 | "$id": "/properties/vcs_revision", 525 | "type": "string" 526 | }, 527 | "vcs_tag": { 528 | "$id": "/properties/vcs_tag", 529 | "type": "null" 530 | }, 531 | "vcs_type": { 532 | "$id": "/properties/vcs_type", 533 | "type": "string" 534 | }, 535 | "vcs_url": { 536 | "$id": "/properties/vcs_url", 537 | "type": "string" 538 | }, 539 | "why": { 540 | "$id": "/properties/why", 541 | "type": "string" 542 | } 543 | } 544 | } 545 | -------------------------------------------------------------------------------- /src/types/json/buildAction.sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "body": "", 3 | "branch": "master", 4 | "build_num": 23, 5 | "build_time_millis": 23505, 6 | "build_url": "https://circleci.com/gh/circleci/mongofinil/23", 7 | "canceled": false, 8 | "committer_email": "arohner@gmail.com", 9 | "committer_name": "Allen Rohner", 10 | "dont_build": null, 11 | "lifecycle": "queued", 12 | "outcome": null, 13 | "previous": { 14 | "build_num": 22, 15 | "status": "failed" 16 | }, 17 | "queued_at": "2013-04-12T21:33:30Z", 18 | "reponame": "mongofinil", 19 | "retry_of": 22, 20 | "start_time": "2013-04-12T21:33:38Z", 21 | "status": "queued", 22 | "stop_time": "2013-04-12T21:34:01Z", 23 | "subject": "Don't explode when the system clock shifts backwards", 24 | "username": "circleci", 25 | "vcs_revision": "1d231626ba1d2838e599c5c598d28e2306ad4e48", 26 | "vcs_url": "https://github.com/circleci/mongofinil", 27 | "why": "retry" 28 | } 29 | -------------------------------------------------------------------------------- /src/types/json/buildAction.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "http://circleci.com/api/v1.1", 3 | "type": "object", 4 | "definitions": {}, 5 | "$schema": "http://json-schema.org/draft-07/schema#", 6 | "properties": { 7 | "body": { 8 | "$id": "/properties/body", 9 | "type": "string" 10 | }, 11 | "branch": { 12 | "$id": "/properties/branch", 13 | "type": "string" 14 | }, 15 | "build_num": { 16 | "$id": "/properties/build_num", 17 | "type": "integer" 18 | }, 19 | "build_time_millis": { 20 | "$id": "/properties/build_time_millis", 21 | "type": "integer" 22 | }, 23 | "build_url": { 24 | "$id": "/properties/build_url", 25 | "type": "string" 26 | }, 27 | "canceled": { 28 | "$id": "/properties/canceled", 29 | "type": "boolean" 30 | }, 31 | "committer_email": { 32 | "$id": "/properties/committer_email", 33 | "type": "string" 34 | }, 35 | "committer_name": { 36 | "$id": "/properties/committer_name", 37 | "type": "string" 38 | }, 39 | "dont_build": { 40 | "$id": "/properties/dont_build", 41 | "type": "null" 42 | }, 43 | "lifecycle": { 44 | "$id": "/properties/lifecycle", 45 | "type": "string" 46 | }, 47 | "outcome": { 48 | "$id": "/properties/outcome", 49 | "type": "null" 50 | }, 51 | "previous": { 52 | "$id": "/properties/previous", 53 | "type": "object", 54 | "properties": { 55 | "build_num": { 56 | "$id": "/properties/previous/properties/build_num", 57 | "type": "integer" 58 | }, 59 | "status": { 60 | "$id": "/properties/previous/properties/status", 61 | "type": "string" 62 | } 63 | } 64 | }, 65 | "queued_at": { 66 | "$id": "/properties/queued_at", 67 | "type": "string" 68 | }, 69 | "reponame": { 70 | "$id": "/properties/reponame", 71 | "type": "string" 72 | }, 73 | "retry_of": { 74 | "$id": "/properties/retry_of", 75 | "type": "integer" 76 | }, 77 | "start_time": { 78 | "$id": "/properties/start_time", 79 | "type": "string" 80 | }, 81 | "status": { 82 | "$id": "/properties/status", 83 | "type": "string" 84 | }, 85 | "stop_time": { 86 | "$id": "/properties/stop_time", 87 | "type": "string" 88 | }, 89 | "subject": { 90 | "$id": "/properties/subject", 91 | "type": "string" 92 | }, 93 | "username": { 94 | "$id": "/properties/username", 95 | "type": "string" 96 | }, 97 | "vcs_revision": { 98 | "$id": "/properties/vcs_revision", 99 | "type": "string" 100 | }, 101 | "vcs_url": { 102 | "$id": "/properties/vcs_url", 103 | "type": "string" 104 | }, 105 | "why": { 106 | "$id": "/properties/why", 107 | "type": "string" 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/types/json/followNewProject.sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "followed": true, 3 | "first_build": { 4 | "compare": null, 5 | "previous_successful_build": null, 6 | "build_parameters": null, 7 | "oss": true, 8 | "committer_date": null, 9 | "body": null, 10 | "usage_queued_at": "2016-09-07T13:48:10.825Z", 11 | "fail_reason": null, 12 | "retry_of": null, 13 | "reponame": "testing-circleci", 14 | "ssh_users": [], 15 | "build_url": "https://circleci.com/gh/circleci/mongofinil/1", 16 | "parallel": 1, 17 | "failed": null, 18 | "branch": "master", 19 | "username": "circleci", 20 | "author_date": null, 21 | "why": "first-build", 22 | "user": { 23 | "is_user": true, 24 | "login": "circleci", 25 | "avatar_url": "https://avatars.githubusercontent.com/u/6017470?v=3", 26 | "name": "CircleCI", 27 | "vcs_type": "github", 28 | "id": 10101010 29 | }, 30 | "vcs_revision": "b2b5def65bf54091dde02ebb39ef3c54de3ff049", 31 | "vcs_tag": null, 32 | "build_num": 1, 33 | "infrastructure_fail": false, 34 | "committer_email": null, 35 | "previous": null, 36 | "status": "not_running", 37 | "committer_name": null, 38 | "retries": null, 39 | "subject": null, 40 | "vcs_type": "github", 41 | "timedout": false, 42 | "dont_build": null, 43 | "lifecycle": "not_running", 44 | "no_dependency_cache": false, 45 | "stop_time": null, 46 | "ssh_disabled": false, 47 | "build_time_millis": null, 48 | "circle_yml": null, 49 | "messages": [], 50 | "is_first_green_build": false, 51 | "job_name": null, 52 | "start_time": null, 53 | "canceler": null, 54 | "outcome": null, 55 | "vcs_url": "https://github.com/circleci/mongofinil", 56 | "author_name": null, 57 | "node": null, 58 | "canceled": false, 59 | "author_email": null 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/types/json/followNewProject.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "http://circleci.com/", 3 | "type": "object", 4 | "definitions": {}, 5 | "$schema": "http://json-schema.org/draft-07/schema#", 6 | "properties": { 7 | "followed": { 8 | "$id": "/properties/followed", 9 | "type": "boolean" 10 | }, 11 | "first_build": { 12 | "$id": "/properties/first_build", 13 | "type": "object", 14 | "properties": { 15 | "compare": { 16 | "$id": "/properties/first_build/properties/compare", 17 | "type": "null" 18 | }, 19 | "previous_successful_build": { 20 | "$id": "/properties/first_build/properties/previous_successful_build", 21 | "type": "null" 22 | }, 23 | "build_parameters": { 24 | "$id": "/properties/first_build/properties/build_parameters", 25 | "type": "null" 26 | }, 27 | "oss": { 28 | "$id": "/properties/first_build/properties/oss", 29 | "type": "boolean" 30 | }, 31 | "committer_date": { 32 | "$id": "/properties/first_build/properties/committer_date", 33 | "type": "null" 34 | }, 35 | "body": { 36 | "$id": "/properties/first_build/properties/body", 37 | "type": "null" 38 | }, 39 | "usage_queued_at": { 40 | "$id": "/properties/first_build/properties/usage_queued_at", 41 | "type": "string" 42 | }, 43 | "fail_reason": { 44 | "$id": "/properties/first_build/properties/fail_reason", 45 | "type": "null" 46 | }, 47 | "retry_of": { 48 | "$id": "/properties/first_build/properties/retry_of", 49 | "type": "null" 50 | }, 51 | "reponame": { 52 | "$id": "/properties/first_build/properties/reponame", 53 | "type": "string" 54 | }, 55 | "ssh_users": { 56 | "$id": "/properties/first_build/properties/ssh_users", 57 | "type": "array" 58 | }, 59 | "build_url": { 60 | "$id": "/properties/first_build/properties/build_url", 61 | "type": "string" 62 | }, 63 | "parallel": { 64 | "$id": "/properties/first_build/properties/parallel", 65 | "type": "integer" 66 | }, 67 | "failed": { 68 | "$id": "/properties/first_build/properties/failed", 69 | "type": "null" 70 | }, 71 | "branch": { 72 | "$id": "/properties/first_build/properties/branch", 73 | "type": "string" 74 | }, 75 | "username": { 76 | "$id": "/properties/first_build/properties/username", 77 | "type": "string" 78 | }, 79 | "author_date": { 80 | "$id": "/properties/first_build/properties/author_date", 81 | "type": "null" 82 | }, 83 | "why": { 84 | "$id": "/properties/first_build/properties/why", 85 | "type": "string" 86 | }, 87 | "user": { 88 | "$id": "/properties/first_build/properties/user", 89 | "type": "object", 90 | "properties": { 91 | "is_user": { 92 | "$id": 93 | "/properties/first_build/properties/user/properties/is_user", 94 | "type": "boolean" 95 | }, 96 | "login": { 97 | "$id": "/properties/first_build/properties/user/properties/login", 98 | "type": "string" 99 | }, 100 | "avatar_url": { 101 | "$id": 102 | "/properties/first_build/properties/user/properties/avatar_url", 103 | "type": "string" 104 | }, 105 | "name": { 106 | "$id": "/properties/first_build/properties/user/properties/name", 107 | "type": "string" 108 | }, 109 | "vcs_type": { 110 | "$id": 111 | "/properties/first_build/properties/user/properties/vcs_type", 112 | "type": "string" 113 | }, 114 | "id": { 115 | "$id": "/properties/first_build/properties/user/properties/id", 116 | "type": "integer" 117 | } 118 | } 119 | }, 120 | "vcs_revision": { 121 | "$id": "/properties/first_build/properties/vcs_revision", 122 | "type": "string" 123 | }, 124 | "vcs_tag": { 125 | "$id": "/properties/first_build/properties/vcs_tag", 126 | "type": "null" 127 | }, 128 | "build_num": { 129 | "$id": "/properties/first_build/properties/build_num", 130 | "type": "integer" 131 | }, 132 | "infrastructure_fail": { 133 | "$id": "/properties/first_build/properties/infrastructure_fail", 134 | "type": "boolean" 135 | }, 136 | "committer_email": { 137 | "$id": "/properties/first_build/properties/committer_email", 138 | "type": "null" 139 | }, 140 | "previous": { 141 | "$id": "/properties/first_build/properties/previous", 142 | "type": "null" 143 | }, 144 | "status": { 145 | "$id": "/properties/first_build/properties/status", 146 | "type": "string" 147 | }, 148 | "committer_name": { 149 | "$id": "/properties/first_build/properties/committer_name", 150 | "type": "null" 151 | }, 152 | "retries": { 153 | "$id": "/properties/first_build/properties/retries", 154 | "type": "null" 155 | }, 156 | "subject": { 157 | "$id": "/properties/first_build/properties/subject", 158 | "type": "null" 159 | }, 160 | "vcs_type": { 161 | "$id": "/properties/first_build/properties/vcs_type", 162 | "type": "string" 163 | }, 164 | "timedout": { 165 | "$id": "/properties/first_build/properties/timedout", 166 | "type": "boolean" 167 | }, 168 | "dont_build": { 169 | "$id": "/properties/first_build/properties/dont_build", 170 | "type": "null" 171 | }, 172 | "lifecycle": { 173 | "$id": "/properties/first_build/properties/lifecycle", 174 | "type": "string" 175 | }, 176 | "no_dependency_cache": { 177 | "$id": "/properties/first_build/properties/no_dependency_cache", 178 | "type": "boolean" 179 | }, 180 | "stop_time": { 181 | "$id": "/properties/first_build/properties/stop_time", 182 | "type": "null" 183 | }, 184 | "ssh_disabled": { 185 | "$id": "/properties/first_build/properties/ssh_disabled", 186 | "type": "boolean" 187 | }, 188 | "build_time_millis": { 189 | "$id": "/properties/first_build/properties/build_time_millis", 190 | "type": "null" 191 | }, 192 | "circle_yml": { 193 | "$id": "/properties/first_build/properties/circle_yml", 194 | "type": "null" 195 | }, 196 | "messages": { 197 | "$id": "/properties/first_build/properties/messages", 198 | "type": "array" 199 | }, 200 | "is_first_green_build": { 201 | "$id": "/properties/first_build/properties/is_first_green_build", 202 | "type": "boolean" 203 | }, 204 | "job_name": { 205 | "$id": "/properties/first_build/properties/job_name", 206 | "type": "null" 207 | }, 208 | "start_time": { 209 | "$id": "/properties/first_build/properties/start_time", 210 | "type": "null" 211 | }, 212 | "canceler": { 213 | "$id": "/properties/first_build/properties/canceler", 214 | "type": "null" 215 | }, 216 | "outcome": { 217 | "$id": "/properties/first_build/properties/outcome", 218 | "type": "null" 219 | }, 220 | "vcs_url": { 221 | "$id": "/properties/first_build/properties/vcs_url", 222 | "type": "string" 223 | }, 224 | "author_name": { 225 | "$id": "/properties/first_build/properties/author_name", 226 | "type": "null" 227 | }, 228 | "node": { 229 | "$id": "/properties/first_build/properties/node", 230 | "type": "null" 231 | }, 232 | "canceled": { 233 | "$id": "/properties/first_build/properties/canceled", 234 | "type": "boolean" 235 | }, 236 | "author_email": { 237 | "$id": "/properties/first_build/properties/author_email", 238 | "type": "null" 239 | } 240 | } 241 | } 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /src/types/json/me.sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "enrolled_betas": [""], 3 | "in_beta_program": false, 4 | "selected_email": "", 5 | "avatar_url": "", 6 | "trial_end": "2017-10-19T21:09:35.903Z", 7 | "basic_email_prefs": "smart", 8 | "sign_in_count": 23, 9 | "github_oauth_scopes": ["user:email", "repo"], 10 | "analytics_id": "3a54d29a-dd85-4b65-aad8-f9c3017418d0", 11 | "name": "Jordon de Hoog", 12 | "gravatar_id": "", 13 | "first_vcs_authorized_client_id": "1507237586887", 14 | "days_left_in_trial": -267, 15 | "parallelism": 1, 16 | "student": false, 17 | "bitbucket_authorized": false, 18 | "github_id": 528792, 19 | "bitbucket": "", 20 | "dev_admin": false, 21 | "all_emails": [""], 22 | "created_at": "2017-10-05T21:09:35.903Z", 23 | "plan": "", 24 | "heroku_api_key": "", 25 | "identities": { 26 | "github": { 27 | "avatar_url": "", 28 | "external_id": 528792, 29 | "id": 528792, 30 | "name": "", 31 | "user?": true, 32 | "domain": "", 33 | "type": "github", 34 | "authorized?": true, 35 | "provider_id": "bcc68be8-ef10-34f19e0db930", 36 | "login": "" 37 | } 38 | }, 39 | "projects": { 40 | "https://github.com/test/repo": { 41 | "on_dashboard": true, 42 | "emails": "default" 43 | } 44 | }, 45 | "login": "", 46 | "organization_prefs": { 47 | "github": { 48 | "RoomRoster": { 49 | "email": "" 50 | } 51 | } 52 | }, 53 | "containers": 1, 54 | "pusher_id": "", 55 | "num_projects_followed": 4 56 | } 57 | -------------------------------------------------------------------------------- /src/types/json/me.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "http://circleci.com/api/v1.1", 3 | "type": "object", 4 | "definitions": {}, 5 | "$schema": "http://json-schema.org/draft-07/schema#", 6 | "properties": { 7 | "enrolled_betas": { 8 | "$id": "/properties/enrolled_betas", 9 | "type": "array", 10 | "items": { 11 | "$id": "/properties/enrolled_betas/items", 12 | "type": "string" 13 | } 14 | }, 15 | "in_beta_program": { 16 | "$id": "/properties/in_beta_program", 17 | "type": "boolean" 18 | }, 19 | "selected_email": { 20 | "$id": "/properties/selected_email", 21 | "type": "string" 22 | }, 23 | "avatar_url": { 24 | "$id": "/properties/avatar_url", 25 | "type": "string" 26 | }, 27 | "trial_end": { 28 | "$id": "/properties/trial_end", 29 | "type": "string" 30 | }, 31 | "basic_email_prefs": { 32 | "$id": "/properties/basic_email_prefs", 33 | "type": "string" 34 | }, 35 | "sign_in_count": { 36 | "$id": "/properties/sign_in_count", 37 | "type": "integer" 38 | }, 39 | "github_oauth_scopes": { 40 | "$id": "/properties/github_oauth_scopes", 41 | "type": "array", 42 | "items": { 43 | "$id": "/properties/github_oauth_scopes/items", 44 | "type": "string" 45 | } 46 | }, 47 | "analytics_id": { 48 | "$id": "/properties/analytics_id", 49 | "type": "string" 50 | }, 51 | "name": { 52 | "$id": "/properties/name", 53 | "type": "string" 54 | }, 55 | "gravatar_id": { 56 | "$id": "/properties/gravatar_id", 57 | "type": "string" 58 | }, 59 | "first_vcs_authorized_client_id": { 60 | "$id": "/properties/first_vcs_authorized_client_id", 61 | "type": "string" 62 | }, 63 | "days_left_in_trial": { 64 | "$id": "/properties/days_left_in_trial", 65 | "type": "integer" 66 | }, 67 | "parallelism": { 68 | "$id": "/properties/parallelism", 69 | "type": "integer" 70 | }, 71 | "student": { 72 | "$id": "/properties/student", 73 | "type": "boolean" 74 | }, 75 | "bitbucket_authorized": { 76 | "$id": "/properties/bitbucket_authorized", 77 | "type": "boolean" 78 | }, 79 | "github_id": { 80 | "$id": "/properties/github_id", 81 | "type": "integer" 82 | }, 83 | "bitbucket": { 84 | "$id": "/properties/bitbucket", 85 | "type": "string" 86 | }, 87 | "dev_admin": { 88 | "$id": "/properties/dev_admin", 89 | "type": "boolean" 90 | }, 91 | "all_emails": { 92 | "$id": "/properties/all_emails", 93 | "type": "array", 94 | "items": { 95 | "$id": "/properties/all_emails/items", 96 | "type": "string" 97 | } 98 | }, 99 | "created_at": { 100 | "$id": "/properties/created_at", 101 | "type": "string" 102 | }, 103 | "plan": { 104 | "$id": "/properties/plan", 105 | "type": "string" 106 | }, 107 | "heroku_api_key": { 108 | "$id": "/properties/heroku_api_key", 109 | "type": "string" 110 | }, 111 | "identities": { 112 | "$id": "/properties/identities", 113 | "type": "object", 114 | "properties": { 115 | "github": { 116 | "$id": "/properties/identities/properties/github", 117 | "type": "object", 118 | "properties": { 119 | "avatar_url": { 120 | "$id": 121 | "/properties/identities/properties/github/properties/avatar_url", 122 | "type": "string" 123 | }, 124 | "external_id": { 125 | "$id": 126 | "/properties/identities/properties/github/properties/external_id", 127 | "type": "integer" 128 | }, 129 | "id": { 130 | "$id": "/properties/identities/properties/github/properties/id", 131 | "type": "integer" 132 | }, 133 | "name": { 134 | "$id": "/properties/identities/properties/github/properties/name", 135 | "type": "string" 136 | }, 137 | "user?": { 138 | "$id": 139 | "/properties/identities/properties/github/properties/user?", 140 | "type": "boolean" 141 | }, 142 | "domain": { 143 | "$id": 144 | "/properties/identities/properties/github/properties/domain", 145 | "type": "string" 146 | }, 147 | "type": { 148 | "$id": "/properties/identities/properties/github/properties/type", 149 | "type": "string" 150 | }, 151 | "authorized?": { 152 | "$id": 153 | "/properties/identities/properties/github/properties/authorized?", 154 | "type": "boolean" 155 | }, 156 | "provider_id": { 157 | "$id": 158 | "/properties/identities/properties/github/properties/provider_id", 159 | "type": "string" 160 | }, 161 | "login": { 162 | "$id": 163 | "/properties/identities/properties/github/properties/login", 164 | "type": "string" 165 | } 166 | } 167 | } 168 | } 169 | }, 170 | "projects": { 171 | "$id": "/properties/projects", 172 | "type": "object", 173 | "properties": { 174 | "https://github.com/test/repo": { 175 | "$id": "/properties/projects/properties/https://github.com/test/repo", 176 | "type": "object", 177 | "properties": { 178 | "on_dashboard": { 179 | "$id": 180 | "/properties/projects/properties/https://github.com/test/repo/properties/on_dashboard", 181 | "type": "boolean" 182 | }, 183 | "emails": { 184 | "$id": 185 | "/properties/projects/properties/https://github.com/test/repo/properties/emails", 186 | "type": "string" 187 | } 188 | } 189 | } 190 | } 191 | }, 192 | "login": { 193 | "$id": "/properties/login", 194 | "type": "string" 195 | }, 196 | "organization_prefs": { 197 | "$id": "/properties/organization_prefs", 198 | "type": "object", 199 | "properties": { 200 | "github": { 201 | "$id": "/properties/organization_prefs/properties/github", 202 | "type": "object", 203 | "properties": { 204 | "RoomRoster": { 205 | "$id": 206 | "/properties/organization_prefs/properties/github/properties/RoomRoster", 207 | "type": "object", 208 | "properties": { 209 | "email": { 210 | "$id": 211 | "/properties/organization_prefs/properties/github/properties/RoomRoster/properties/email", 212 | "type": "string" 213 | } 214 | } 215 | } 216 | } 217 | } 218 | } 219 | }, 220 | "containers": { 221 | "$id": "/properties/containers", 222 | "type": "integer" 223 | }, 224 | "pusher_id": { 225 | "$id": "/properties/pusher_id", 226 | "type": "string" 227 | }, 228 | "num_projects_followed": { 229 | "$id": "/properties/num_projects_followed", 230 | "type": "integer" 231 | } 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/types/json/project.sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "irc_server": null, 3 | "ssh_keys": [], 4 | "branches": { 5 | "master": { 6 | "pusher_logins": ["greenkeeper[bot]"], 7 | "running_builds": [], 8 | "recent_builds": [ 9 | { 10 | "outcome": "failed", 11 | "status": "failed", 12 | "build_num": 121, 13 | "vcs_revision": "9a070e6b256ddf99336e8058083a5496637a69fb", 14 | "pushed_at": "2018-06-24T04:21:59.619Z", 15 | "is_workflow_job": false, 16 | "added_at": "2018-06-24T04:23:01.792Z" 17 | } 18 | ], 19 | "last_non_success": { 20 | "outcome": "failed", 21 | "status": "failed", 22 | "build_num": 121, 23 | "vcs_revision": "9a070e6b256ddf99336e8058083a5496637a69fb", 24 | "pushed_at": "2018-06-24T04:21:59.619Z", 25 | "is_workflow_job": false, 26 | "added_at": "2018-06-24T04:23:01.792Z" 27 | } 28 | } 29 | }, 30 | "irc_keyword": null, 31 | "oss": true, 32 | "slack_channel": null, 33 | "hipchat_notify_prefs": null, 34 | "reponame": "", 35 | "dependencies": "", 36 | "aws": { 37 | "keypair": null 38 | }, 39 | "slack_webhook_url": null, 40 | "irc_channel": null, 41 | "parallel": 1, 42 | "campfire_subdomain": null, 43 | "slack_integration_access_token": null, 44 | "username": "", 45 | "campfire_notify_prefs": null, 46 | "slack_integration_team": null, 47 | "slack_integration_channel": null, 48 | "hipchat_notify": null, 49 | "heroku_deploy_user": null, 50 | "irc_username": null, 51 | "slack_notify_prefs": null, 52 | "scopes": [ 53 | "write-settings", 54 | "view-builds", 55 | "read-settings", 56 | "trigger-builds", 57 | "all", 58 | "status", 59 | "none" 60 | ], 61 | "campfire_room": null, 62 | "hipchat_api_token": null, 63 | "campfire_token": null, 64 | "slack_subdomain": null, 65 | "has_usable_key": true, 66 | "setup": "", 67 | "vcs_type": "github", 68 | "feature_flags": { 69 | "trusty-beta": true, 70 | "osx": false, 71 | "set-github-status": true, 72 | "build-prs-only": false, 73 | "forks-receive-secret-env-vars": false, 74 | "fleet": null, 75 | "build-fork-prs": false, 76 | "autocancel-builds": true, 77 | "oss": true 78 | }, 79 | "irc_password": null, 80 | "compile": "", 81 | "slack_integration_notify_prefs": null, 82 | "slack_integration_webhook_url": null, 83 | "irc_notify_prefs": null, 84 | "slack_integration_team_id": null, 85 | "extra": "", 86 | "jira": null, 87 | "slack_integration_channel_id": null, 88 | "language": "TypeScript", 89 | "hipchat_room": null, 90 | "flowdock_api_token": null, 91 | "slack_channel_override": null, 92 | "vcs_url": "", 93 | "following": true, 94 | "default_branch": "develop", 95 | "slack_api_token": null, 96 | "test": "" 97 | } 98 | -------------------------------------------------------------------------------- /src/types/json/project.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "http://circleci.com/", 3 | "type": "object", 4 | "definitions": {}, 5 | "$schema": "http://json-schema.org/draft-07/schema#", 6 | "properties": { 7 | "irc_server": { 8 | "$id": "/properties/irc_server", 9 | "type": "null" 10 | }, 11 | "ssh_keys": { 12 | "$id": "/properties/ssh_keys", 13 | "type": "array" 14 | }, 15 | "branches": { 16 | "$id": "/properties/branches", 17 | "type": "object", 18 | "properties": { 19 | "master": { 20 | "$id": "/properties/branches/properties/master", 21 | "type": "object", 22 | "properties": { 23 | "pusher_logins": { 24 | "$id": 25 | "/properties/branches/properties/master/properties/pusher_logins", 26 | "type": "array", 27 | "items": { 28 | "$id": 29 | "/properties/branches/properties/master/properties/pusher_logins/items", 30 | "type": "string" 31 | } 32 | }, 33 | "running_builds": { 34 | "$id": 35 | "/properties/branches/properties/master/properties/running_builds", 36 | "type": "array" 37 | }, 38 | "recent_builds": { 39 | "$id": 40 | "/properties/branches/properties/master/properties/recent_builds", 41 | "type": "array", 42 | "items": { 43 | "$id": 44 | "/properties/branches/properties/master/properties/recent_builds/items", 45 | "type": "object", 46 | "properties": { 47 | "outcome": { 48 | "$id": 49 | "/properties/branches/properties/master/properties/recent_builds/items/properties/outcome", 50 | "type": "string" 51 | }, 52 | "status": { 53 | "$id": 54 | "/properties/branches/properties/master/properties/recent_builds/items/properties/status", 55 | "type": "string" 56 | }, 57 | "build_num": { 58 | "$id": 59 | "/properties/branches/properties/master/properties/recent_builds/items/properties/build_num", 60 | "type": "integer" 61 | }, 62 | "vcs_revision": { 63 | "$id": 64 | "/properties/branches/properties/master/properties/recent_builds/items/properties/vcs_revision", 65 | "type": "string" 66 | }, 67 | "pushed_at": { 68 | "$id": 69 | "/properties/branches/properties/master/properties/recent_builds/items/properties/pushed_at", 70 | "type": "string" 71 | }, 72 | "is_workflow_job": { 73 | "$id": 74 | "/properties/branches/properties/master/properties/recent_builds/items/properties/is_workflow_job", 75 | "type": "boolean" 76 | }, 77 | "added_at": { 78 | "$id": 79 | "/properties/branches/properties/master/properties/recent_builds/items/properties/added_at", 80 | "type": "string" 81 | } 82 | } 83 | } 84 | }, 85 | "last_non_success": { 86 | "$id": 87 | "/properties/branches/properties/master/properties/last_non_success", 88 | "type": "object", 89 | "properties": { 90 | "outcome": { 91 | "$id": 92 | "/properties/branches/properties/master/properties/last_non_success/properties/outcome", 93 | "type": "string" 94 | }, 95 | "status": { 96 | "$id": 97 | "/properties/branches/properties/master/properties/last_non_success/properties/status", 98 | "type": "string" 99 | }, 100 | "build_num": { 101 | "$id": 102 | "/properties/branches/properties/master/properties/last_non_success/properties/build_num", 103 | "type": "integer" 104 | }, 105 | "vcs_revision": { 106 | "$id": 107 | "/properties/branches/properties/master/properties/last_non_success/properties/vcs_revision", 108 | "type": "string" 109 | }, 110 | "pushed_at": { 111 | "$id": 112 | "/properties/branches/properties/master/properties/last_non_success/properties/pushed_at", 113 | "type": "string" 114 | }, 115 | "is_workflow_job": { 116 | "$id": 117 | "/properties/branches/properties/master/properties/last_non_success/properties/is_workflow_job", 118 | "type": "boolean" 119 | }, 120 | "added_at": { 121 | "$id": 122 | "/properties/branches/properties/master/properties/last_non_success/properties/added_at", 123 | "type": "string" 124 | } 125 | } 126 | } 127 | } 128 | } 129 | } 130 | }, 131 | "irc_keyword": { 132 | "$id": "/properties/irc_keyword", 133 | "type": "null" 134 | }, 135 | "oss": { 136 | "$id": "/properties/oss", 137 | "type": "boolean" 138 | }, 139 | "slack_channel": { 140 | "$id": "/properties/slack_channel", 141 | "type": "null" 142 | }, 143 | "hipchat_notify_prefs": { 144 | "$id": "/properties/hipchat_notify_prefs", 145 | "type": "null" 146 | }, 147 | "reponame": { 148 | "$id": "/properties/reponame", 149 | "type": "string" 150 | }, 151 | "dependencies": { 152 | "$id": "/properties/dependencies", 153 | "type": "string" 154 | }, 155 | "aws": { 156 | "$id": "/properties/aws", 157 | "type": "object", 158 | "properties": { 159 | "keypair": { 160 | "$id": "/properties/aws/properties/keypair", 161 | "type": "null" 162 | } 163 | } 164 | }, 165 | "slack_webhook_url": { 166 | "$id": "/properties/slack_webhook_url", 167 | "type": "null" 168 | }, 169 | "irc_channel": { 170 | "$id": "/properties/irc_channel", 171 | "type": "null" 172 | }, 173 | "parallel": { 174 | "$id": "/properties/parallel", 175 | "type": "integer" 176 | }, 177 | "campfire_subdomain": { 178 | "$id": "/properties/campfire_subdomain", 179 | "type": "null" 180 | }, 181 | "slack_integration_access_token": { 182 | "$id": "/properties/slack_integration_access_token", 183 | "type": "null" 184 | }, 185 | "username": { 186 | "$id": "/properties/username", 187 | "type": "string" 188 | }, 189 | "campfire_notify_prefs": { 190 | "$id": "/properties/campfire_notify_prefs", 191 | "type": "null" 192 | }, 193 | "slack_integration_team": { 194 | "$id": "/properties/slack_integration_team", 195 | "type": "null" 196 | }, 197 | "slack_integration_channel": { 198 | "$id": "/properties/slack_integration_channel", 199 | "type": "null" 200 | }, 201 | "hipchat_notify": { 202 | "$id": "/properties/hipchat_notify", 203 | "type": "null" 204 | }, 205 | "heroku_deploy_user": { 206 | "$id": "/properties/heroku_deploy_user", 207 | "type": "null" 208 | }, 209 | "irc_username": { 210 | "$id": "/properties/irc_username", 211 | "type": "null" 212 | }, 213 | "slack_notify_prefs": { 214 | "$id": "/properties/slack_notify_prefs", 215 | "type": "null" 216 | }, 217 | "scopes": { 218 | "$id": "/properties/scopes", 219 | "type": "array", 220 | "items": { 221 | "$id": "/properties/scopes/items", 222 | "type": "string" 223 | } 224 | }, 225 | "campfire_room": { 226 | "$id": "/properties/campfire_room", 227 | "type": "null" 228 | }, 229 | "hipchat_api_token": { 230 | "$id": "/properties/hipchat_api_token", 231 | "type": "null" 232 | }, 233 | "campfire_token": { 234 | "$id": "/properties/campfire_token", 235 | "type": "null" 236 | }, 237 | "slack_subdomain": { 238 | "$id": "/properties/slack_subdomain", 239 | "type": "null" 240 | }, 241 | "has_usable_key": { 242 | "$id": "/properties/has_usable_key", 243 | "type": "boolean" 244 | }, 245 | "setup": { 246 | "$id": "/properties/setup", 247 | "type": "string" 248 | }, 249 | "vcs_type": { 250 | "$id": "/properties/vcs_type", 251 | "type": "string" 252 | }, 253 | "feature_flags": { 254 | "$id": "/properties/feature_flags", 255 | "type": "object", 256 | "properties": { 257 | "trusty-beta": { 258 | "$id": "/properties/feature_flags/properties/trusty-beta", 259 | "type": "boolean" 260 | }, 261 | "osx": { 262 | "$id": "/properties/feature_flags/properties/osx", 263 | "type": "boolean" 264 | }, 265 | "set-github-status": { 266 | "$id": "/properties/feature_flags/properties/set-github-status", 267 | "type": "boolean" 268 | }, 269 | "build-prs-only": { 270 | "$id": "/properties/feature_flags/properties/build-prs-only", 271 | "type": "boolean" 272 | }, 273 | "forks-receive-secret-env-vars": { 274 | "$id": 275 | "/properties/feature_flags/properties/forks-receive-secret-env-vars", 276 | "type": "boolean" 277 | }, 278 | "fleet": { 279 | "$id": "/properties/feature_flags/properties/fleet", 280 | "type": "null" 281 | }, 282 | "build-fork-prs": { 283 | "$id": "/properties/feature_flags/properties/build-fork-prs", 284 | "type": "boolean" 285 | }, 286 | "autocancel-builds": { 287 | "$id": "/properties/feature_flags/properties/autocancel-builds", 288 | "type": "boolean" 289 | }, 290 | "oss": { 291 | "$id": "/properties/feature_flags/properties/oss", 292 | "type": "boolean" 293 | } 294 | } 295 | }, 296 | "irc_password": { 297 | "$id": "/properties/irc_password", 298 | "type": "null" 299 | }, 300 | "compile": { 301 | "$id": "/properties/compile", 302 | "type": "string" 303 | }, 304 | "slack_integration_notify_prefs": { 305 | "$id": "/properties/slack_integration_notify_prefs", 306 | "type": "null" 307 | }, 308 | "slack_integration_webhook_url": { 309 | "$id": "/properties/slack_integration_webhook_url", 310 | "type": "null" 311 | }, 312 | "irc_notify_prefs": { 313 | "$id": "/properties/irc_notify_prefs", 314 | "type": "null" 315 | }, 316 | "slack_integration_team_id": { 317 | "$id": "/properties/slack_integration_team_id", 318 | "type": "null" 319 | }, 320 | "extra": { 321 | "$id": "/properties/extra", 322 | "type": "string" 323 | }, 324 | "jira": { 325 | "$id": "/properties/jira", 326 | "type": "null" 327 | }, 328 | "slack_integration_channel_id": { 329 | "$id": "/properties/slack_integration_channel_id", 330 | "type": "null" 331 | }, 332 | "language": { 333 | "$id": "/properties/language", 334 | "type": "string" 335 | }, 336 | "hipchat_room": { 337 | "$id": "/properties/hipchat_room", 338 | "type": "null" 339 | }, 340 | "flowdock_api_token": { 341 | "$id": "/properties/flowdock_api_token", 342 | "type": "null" 343 | }, 344 | "slack_channel_override": { 345 | "$id": "/properties/slack_channel_override", 346 | "type": "null" 347 | }, 348 | "vcs_url": { 349 | "$id": "/properties/vcs_url", 350 | "type": "string" 351 | }, 352 | "following": { 353 | "$id": "/properties/following", 354 | "type": "boolean" 355 | }, 356 | "default_branch": { 357 | "$id": "/properties/default_branch", 358 | "type": "string" 359 | }, 360 | "slack_api_token": { 361 | "$id": "/properties/slack_api_token", 362 | "type": "null" 363 | }, 364 | "test": { 365 | "$id": "/properties/test", 366 | "type": "string" 367 | } 368 | } 369 | } 370 | -------------------------------------------------------------------------------- /src/types/lib.ts: -------------------------------------------------------------------------------- 1 | export const API_BASE = "https://circleci.com/api/v1.1"; 2 | export const API_ME = "/me"; 3 | export const API_PROJECT = "/project"; 4 | export const API_ALL_PROJECTS = "/projects"; 5 | export const API_RECENT_BUILDS = "/recent-builds"; 6 | export const API_USER = "/user"; 7 | 8 | /** 9 | * @description Currently supported VCS types 10 | * @see GitInfo 11 | */ 12 | export enum GitType { 13 | GITHUB = "github", 14 | BITBUCKET = "bitbucket" 15 | } 16 | 17 | /** 18 | * Create a base project url 19 | * @param type - Type of version control, default "github" 20 | * @param owner - Owner of the repository 21 | * @param repo - Target repository 22 | */ 23 | export function createVcsUrl({ type = GitType.GITHUB, owner, repo }: GitInfo) { 24 | return `${API_PROJECT}/${type}/${owner}/${repo}`; 25 | } 26 | 27 | export enum BuildAction { 28 | RETRY = "retry", 29 | CANCEL = "cancel" 30 | } 31 | 32 | /** 33 | * @description Settings for git project. Used for endpoints such as builds, and artifacts 34 | * @see https://circleci.com/docs/api/v1-reference/#version-control-system-vcs-type 35 | * @property {GitType} [type] - Type of VCS (github, bitbucket, etc) 36 | * @property {string} [owner] - Owner of the target repository 37 | * @property {repo} [repo] - Target repository name 38 | */ 39 | export interface GitInfo { 40 | type?: GitType; 41 | owner?: string; 42 | repo?: string; 43 | } 44 | 45 | // TODO change to enum 46 | export type Filter = "completed" | "successful" | "failed" | "running"; 47 | 48 | /** 49 | * @description Additional options used as query params 50 | * @property {string} [branch="master"] - Git branch to use with api 51 | * @property {Filter} [filter] - Filters to grab selected builds, such as completed 52 | */ 53 | export interface Options { 54 | branch?: string; 55 | filter?: Filter; 56 | limit?: number; 57 | offset?: number; 58 | newBuildOptions?: NewBuildOptions; 59 | } 60 | 61 | export interface RequestOptions { 62 | readonly limit?: number; 63 | readonly offset?: number; 64 | } 65 | 66 | export interface ArtifactsRequestOptions { 67 | readonly filter?: Filter; 68 | readonly branch?: string; 69 | } 70 | 71 | export interface FilterRequestOptions extends RequestOptions { 72 | readonly filter?: Filter; 73 | } 74 | 75 | /** 76 | * @description Basic information required for a standard CircleCI request 77 | * @property {string} [token] - CircleCI API key 78 | * @property {GitInfo} [vcs] - Git information required for project related endpoints 79 | * @property {Options} [options] - Extra query parameters for build endpoints 80 | * @property {string} circleHost - Override the default host for CircleCI [API_BASE] 81 | */ 82 | export interface CircleRequest { 83 | token?: string; 84 | vcs?: GitInfo; 85 | options?: Options; 86 | circleHost?: string; 87 | } 88 | 89 | /** 90 | * @description Required options for the CircleCI factory 91 | * @property {string} token - CircleCI API key 92 | */ 93 | export interface CircleCIOptions extends CircleRequest { 94 | token: string; 95 | } 96 | 97 | /** 98 | * @description Required settings for requests regarding a git project 99 | * @property {GitInfo} vcs - Git information 100 | */ 101 | export interface GitRequiredRequest extends CircleRequest { 102 | vcs: GitInfo; 103 | } 104 | 105 | /** 106 | * @description Required properties for CircleRequest 107 | * @see {@link CircleRequest} 108 | */ 109 | export interface FullRequest extends CircleRequest { 110 | token: string; 111 | vcs: GitInfo; 112 | } 113 | 114 | export interface CircleOptions { 115 | circleHost?: string; 116 | } 117 | 118 | /** 119 | * Options for triggering a new build 120 | * @property revision - The specific revision to build. Default is null and the head of the branch is used. Cannot be used with tag parameter. 121 | * @property tag - The tag to build. Default is null. Cannot be used with revision parameter. 122 | * @property parallel - The number of containers to use to run the build. Default is null and the project default is used. This parameter is ignored for builds running on our 2.0 infrastructure. 123 | * @property build_parameters - Additional environment variables to inject into the build environment. A map of names to values. 124 | */ 125 | export interface NewBuildOptions { 126 | revision?: string; 127 | tag?: string; 128 | parallel?: number; 129 | build_parameters?: BuildParameters; 130 | } 131 | 132 | /** 133 | * Additional ENV parameters to pass to the build 134 | */ 135 | export interface BuildParameters { 136 | [param: string]: string; 137 | } 138 | -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | import { AxiosRequestConfig } from "axios"; 2 | 3 | import { Options, FullRequest, GitType } from "./types"; 4 | import { name, version, organization } from "../package.json"; 5 | 6 | /** 7 | * Validate a Request object for endpoints that require 8 | * certain information 9 | * @param token - CircleCI API token 10 | * @param type - Git type 11 | * @param owner - Repository owner 12 | * @param repo - Target repository 13 | * @throws If options passed in are not valid 14 | */ 15 | export function validateVCSRequest({ 16 | token, 17 | vcs: { type, owner, repo }, 18 | }: FullRequest) { 19 | if (!token) { 20 | throw new Error("CircleCiApi - No token was provided"); 21 | } 22 | 23 | const missing = []; 24 | if (!type) { 25 | missing.push("type"); 26 | } 27 | 28 | if (!owner) { 29 | missing.push("owner"); 30 | } 31 | 32 | if (!repo) { 33 | missing.push("repo"); 34 | } 35 | 36 | if (missing.length) { 37 | throw new Error(`CircleCiApi - Missing options ${missing}`); 38 | } 39 | } 40 | 41 | /** 42 | * Take a Options object and map it to query parameters 43 | * @example { limit: 5, branch: "develop" } => /builds?branch=develop&limit=5 44 | * @param opts - Query param object, branch is defaulted to master 45 | * @param ignoreBranch - Ignore the 'branch' option 46 | * @returns A string containing url encoded query params 47 | */ 48 | export function queryParams(opts: Options = {}) { 49 | const params = Object.keys(opts) 50 | .filter((key) => typeof opts[key] !== "undefined" && opts[key] !== null) 51 | .reduce( 52 | (prev: string[], key: string) => [ 53 | ...prev, 54 | `${key}=${encodeURIComponent(opts[key])}`, 55 | ], 56 | [] 57 | ) 58 | .join("&"); 59 | 60 | return params.length ? `?${params}` : ""; 61 | } 62 | 63 | /** 64 | * Takes a string and will return the matching type, or 65 | * default to GitType.GITHUB 66 | * @default GitType.GITHUB 67 | * @see GitType 68 | * @param type - Raw string type 69 | */ 70 | export function getGitType(type: string): GitType { 71 | const formatted = type.trim().replace(/ /g, "").toLowerCase(); 72 | if (formatted === GitType.BITBUCKET) { 73 | return formatted as GitType; 74 | } 75 | 76 | return GitType.GITHUB; 77 | } 78 | 79 | /** 80 | * Create JSON headers for Axios 81 | */ 82 | export function createJsonHeader(): AxiosRequestConfig { 83 | return { 84 | headers: { 85 | "Content-Type": "application/json", 86 | Accepts: "application/json", 87 | }, 88 | }; 89 | } 90 | 91 | /** 92 | * Modify an existing AxiosRequestConfig object with the user-agent set to 93 | * the current version of the library 94 | */ 95 | export function addUserAgentHeader({ 96 | headers = {}, 97 | ...config 98 | }: AxiosRequestConfig = {}): AxiosRequestConfig { 99 | return { 100 | ...config, 101 | headers: { 102 | ...headers, 103 | "User-Agent": `${organization}/${name} ${version}`, 104 | }, 105 | }; 106 | } 107 | -------------------------------------------------------------------------------- /test/__mocks__/axios.ts: -------------------------------------------------------------------------------- 1 | let mockError: any; 2 | let mockResponse: any = {}; 3 | 4 | const axiosMock: any = jest.genMockFromModule("axios"); 5 | 6 | function req() { 7 | return new Promise(function(resolve, reject) { 8 | if (mockError) { 9 | reject(mockError); 10 | } else { 11 | resolve(mockResponse); 12 | } 13 | }); 14 | } 15 | 16 | axiosMock.reset = () => { 17 | mockError = null; 18 | mockResponse = { 19 | data: {}, 20 | status: 200, 21 | statusText: "OK", 22 | headers: {}, 23 | config: {} 24 | }; 25 | }; 26 | 27 | axiosMock.get.mockImplementation(req); 28 | 29 | axiosMock.post.mockImplementation(req); 30 | 31 | axiosMock.put.mockImplementation(req); 32 | 33 | axiosMock.delete.mockImplementation(req); 34 | 35 | axiosMock._setMockError = (err: any) => { 36 | mockError = err; 37 | }; 38 | 39 | axiosMock._setMockResponse = (response: any) => { 40 | mockResponse = { ...mockResponse, ...response }; 41 | }; 42 | 43 | export default axiosMock; 44 | -------------------------------------------------------------------------------- /test/api/actions.test.ts: -------------------------------------------------------------------------------- 1 | import { CircleCI, GitType, postTriggerNewBuild } from "../../src"; 2 | import { BuildActionResponse, TriggerBuildResponse } from "../../src/types/api"; 3 | import * as client from "../../src/client"; 4 | 5 | jest.mock("../../src/client"); 6 | 7 | const mock = client as any; 8 | const TOKEN = "test-token"; 9 | 10 | describe("API - Actions", () => { 11 | let circle: CircleCI; 12 | 13 | beforeEach(() => { 14 | mock.__reset(); 15 | 16 | circle = new CircleCI({ 17 | token: TOKEN, 18 | vcs: { owner: "test", repo: "repotest" } 19 | }); 20 | }); 21 | 22 | describe("Retry & Cancel", () => { 23 | const response: BuildActionResponse = { 24 | outcome: "success", 25 | status: "finished", 26 | build_num: 42 27 | }; 28 | 29 | describe("Retry", () => { 30 | it("should use defaults to retry build", async () => { 31 | mock.__setResponse(response); 32 | const result = await circle.retry(5); 33 | 34 | expect(mock.client).toBeCalledWith(TOKEN, undefined); 35 | expect(mock.__postMock).toBeCalledWith( 36 | "/project/github/test/repotest/5/retry" 37 | ); 38 | expect(result).toEqual(response); 39 | }); 40 | 41 | it("should use passed in options", async () => { 42 | mock.__setResponse(response); 43 | const result = await circle.retry(5, { 44 | token: TOKEN, 45 | vcs: { type: GitType.BITBUCKET, repo: "square" } 46 | }); 47 | 48 | expect(mock.client).toBeCalledWith(TOKEN, undefined); 49 | expect(mock.__postMock).toBeCalledWith( 50 | "/project/bitbucket/test/square/5/retry" 51 | ); 52 | expect(result).toEqual(response); 53 | }); 54 | 55 | it("should throw an error if request fails", async () => { 56 | mock.__setError({ code: 404 }); 57 | 58 | const check = circle.retry(5); 59 | await expect(check).rejects.toEqual({ code: 404 }); 60 | }); 61 | }); 62 | 63 | describe("Cancel", () => { 64 | it("should use defaults to cancel build", async () => { 65 | mock.__setResponse(response); 66 | const result = await circle.cancel(5); 67 | 68 | expect(mock.client).toBeCalledWith(TOKEN, undefined); 69 | expect(mock.__postMock).toBeCalledWith( 70 | "/project/github/test/repotest/5/cancel" 71 | ); 72 | expect(result).toEqual(response); 73 | }); 74 | 75 | it("should use passed in options", async () => { 76 | mock.__setResponse(response); 77 | const result = await circle.cancel(5, { 78 | token: TOKEN, 79 | vcs: { type: GitType.BITBUCKET, repo: "square" } 80 | }); 81 | 82 | expect(mock.client).toBeCalledWith(TOKEN, undefined); 83 | expect(mock.__postMock).toBeCalledWith( 84 | "/project/bitbucket/test/square/5/cancel" 85 | ); 86 | expect(result).toEqual(response); 87 | }); 88 | 89 | it("should use the custom CircleCI url", async () => { 90 | mock.__setResponse(response); 91 | await new CircleCI({ 92 | token: TOKEN, 93 | circleHost: "foo.bar/api", 94 | vcs: { type: GitType.BITBUCKET, repo: "square", owner: "bar" } 95 | }).cancel(5); 96 | 97 | expect(mock.client).toBeCalledWith(TOKEN, "foo.bar/api"); 98 | }); 99 | }); 100 | }); 101 | 102 | describe("Trigger Build", () => { 103 | const response: TriggerBuildResponse = { 104 | body: "test", 105 | build_num: 42, 106 | branch: "master" 107 | }; 108 | 109 | it("should use defaults to trigger a build", async () => { 110 | mock.__setResponse(response); 111 | const result = await circle.triggerBuild(); 112 | 113 | expect(mock.client).toBeCalledWith(TOKEN, undefined); 114 | expect(mock.__postMock).toBeCalledWith( 115 | "/project/github/test/repotest", 116 | expect.any(Object) 117 | ); 118 | expect(result).toEqual(response); 119 | }); 120 | 121 | it("should use passed in options", async () => { 122 | mock.__setResponse(response); 123 | const result = await circle.triggerBuild({ 124 | token: TOKEN, 125 | vcs: { type: GitType.BITBUCKET, repo: "square" }, 126 | options: { branch: "develop" } 127 | }); 128 | 129 | expect(mock.client).toBeCalledWith(TOKEN, undefined); 130 | expect(mock.__postMock).toBeCalledWith( 131 | "/project/bitbucket/test/square/tree/develop", 132 | expect.any(Object) 133 | ); 134 | expect(result).toEqual(response); 135 | }); 136 | }); 137 | 138 | describe("Trigger Build For Branch", () => { 139 | const response: TriggerBuildResponse = { 140 | body: "test", 141 | build_num: 42, 142 | branch: "master" 143 | }; 144 | 145 | it("should build default master", async () => { 146 | mock.__setResponse(response); 147 | const result = await circle.triggerBuildFor(); 148 | 149 | expect(mock.client).toBeCalledWith(TOKEN, undefined); 150 | expect(mock.__postMock).toBeCalledWith( 151 | "/project/github/test/repotest/tree/master", 152 | expect.any(Object) 153 | ); 154 | expect(result).toEqual(response); 155 | }); 156 | 157 | it("should use passed in options", async () => { 158 | mock.__setResponse(response); 159 | const result = await circle.triggerBuildFor("feat", { 160 | token: TOKEN, 161 | vcs: { type: GitType.BITBUCKET, repo: "square" }, 162 | options: { newBuildOptions: { tag: "foo" } } 163 | }); 164 | 165 | expect(mock.client).toBeCalledWith(TOKEN, undefined); 166 | expect(mock.__postMock).toBeCalledWith( 167 | "/project/bitbucket/test/square/tree/feat", 168 | expect.objectContaining({ 169 | tag: "foo" 170 | }) 171 | ); 172 | expect(result).toEqual(response); 173 | }); 174 | 175 | it("should handle no options", async () => { 176 | mock.__setResponse(response); 177 | const result = await postTriggerNewBuild(TOKEN, { 178 | vcs: { owner: "foo", repo: "bar" } 179 | }); 180 | expect(mock.__postMock).toBeCalledWith( 181 | "/project/github/foo/bar", 182 | expect.any(Object) 183 | ); 184 | expect(result).toEqual(response); 185 | }); 186 | }); 187 | }); 188 | -------------------------------------------------------------------------------- /test/api/artifacts.test.ts: -------------------------------------------------------------------------------- 1 | import { CircleCI, getLatestArtifacts } from "../../src"; 2 | import { Artifact } from "../../src/types/api"; 3 | import * as client from "../../src/client"; 4 | 5 | jest.mock("../../src/client"); 6 | 7 | const mock = client as any; 8 | 9 | describe("API - Artifacts", () => { 10 | const TOKEN = "test-token"; 11 | const artifact: Artifact = { 12 | url: "http://test.com/1.txt", 13 | path: "docs" 14 | }; 15 | 16 | let circle: CircleCI; 17 | 18 | beforeEach(() => { 19 | mock.__reset(); 20 | circle = new CircleCI({ 21 | token: TOKEN, 22 | vcs: { owner: "test", repo: "proj" } 23 | }); 24 | }); 25 | 26 | describe("Build Artifacts", () => { 27 | it("should get artifacts for specific build", async () => { 28 | mock.__setResponse(artifact); 29 | const result = await circle.artifacts(42); 30 | 31 | expect(mock.__getMock).toBeCalledWith( 32 | expect.stringContaining("test/proj/42/artifacts") 33 | ); 34 | expect(result).toEqual(artifact); 35 | }); 36 | }); 37 | 38 | it("should get artifacts for specific build with options", async () => { 39 | mock.__setResponse(artifact); 40 | const result = await circle.artifacts(42, { vcs: { repo: "baz" } }); 41 | 42 | expect(mock.__getMock).toBeCalledWith( 43 | expect.stringContaining("test/baz/42/artifacts") 44 | ); 45 | expect(result).toEqual(artifact); 46 | }); 47 | 48 | it("should use a custom circleci host", async () => { 49 | mock.__setResponse(artifact); 50 | await new CircleCI({ 51 | token: TOKEN, 52 | vcs: { owner: "test", repo: "proj" }, 53 | circleHost: "foo.bar/api" 54 | }).artifacts(42); 55 | 56 | expect(mock.client).toBeCalledWith(TOKEN, "foo.bar/api"); 57 | }); 58 | 59 | describe("Latest Artifacts", () => { 60 | it("should fetch latest artifact for project", async () => { 61 | mock.__setResponse(artifact); 62 | const result = await getLatestArtifacts(TOKEN, { 63 | vcs: { owner: "t", repo: "r" } 64 | }); 65 | 66 | expect(mock.client).toBeCalledWith(TOKEN, undefined); 67 | expect(mock.__getMock).toBeCalledWith( 68 | expect.stringContaining("t/r/latest/artifacts") 69 | ); 70 | expect(result).toEqual(artifact); 71 | }); 72 | 73 | it("should fetch aritfacts for different project", async () => { 74 | mock.__setResponse(artifact); 75 | const result = await circle.latestArtifacts(undefined, { 76 | vcs: { owner: "test2" } 77 | }); 78 | 79 | expect(mock.client).toBeCalledWith(TOKEN, undefined); 80 | expect(mock.__getMock).toBeCalledWith( 81 | expect.stringContaining("test2/proj/latest/artifacts") 82 | ); 83 | expect(result).toEqual(artifact); 84 | }); 85 | 86 | it("should fetch latest aritfacts specific branch", async () => { 87 | mock.__setResponse(artifact); 88 | const result = await getLatestArtifacts(TOKEN, { 89 | vcs: { owner: "test", repo: "test" }, 90 | options: { branch: "develop" } 91 | }); 92 | 93 | expect(mock.client).toBeCalledWith(TOKEN, undefined); 94 | expect(mock.__getMock).toBeCalledWith( 95 | expect.stringContaining("branch=develop") 96 | ); 97 | expect(result).toEqual(artifact); 98 | }); 99 | 100 | it("should use a custom circleci host", async () => { 101 | mock.__setResponse(artifact); 102 | await new CircleCI({ 103 | token: TOKEN, 104 | vcs: { owner: "test", repo: "proj" }, 105 | circleHost: "foo.bar/api" 106 | }).latestArtifacts(); 107 | 108 | expect(mock.client).toBeCalledWith(TOKEN, "foo.bar/api"); 109 | }); 110 | 111 | it("should throw an error if request fails", async () => { 112 | mock.__setError({ code: 404 }); 113 | 114 | const check = circle.me(); 115 | await expect(check).rejects.toEqual({ code: 404 }); 116 | }); 117 | }); 118 | }); 119 | -------------------------------------------------------------------------------- /test/api/builds.test.ts: -------------------------------------------------------------------------------- 1 | import { CircleCI, getBuildSummaries, getRecentBuilds } from "../../src"; 2 | import { BuildSummary } from "../../src/types/api"; 3 | import * as client from "../../src/client"; 4 | 5 | jest.mock("../../src/client"); 6 | 7 | const mock = client as any; 8 | 9 | describe("API - Builds", () => { 10 | const TOKEN = "test-token"; 11 | const response: BuildSummary = { outcome: "success" }; 12 | 13 | let circle: CircleCI; 14 | 15 | beforeEach(() => { 16 | mock.__reset(); 17 | circle = new CircleCI({ 18 | token: TOKEN, 19 | vcs: { owner: "foo", repo: "bar" }, 20 | }); 21 | }); 22 | 23 | describe("Recent Builds", () => { 24 | it("should get recent builds", async () => { 25 | mock.__setResponse(response); 26 | const result = await circle.recentBuilds(); 27 | 28 | expect(mock.__getMock).toBeCalledWith("/recent-builds"); 29 | expect(result).toEqual(response); 30 | }); 31 | 32 | it("should filter recent builds", async () => { 33 | mock.__setResponse(response); 34 | const result = await circle.recentBuilds({ limit: 5, offset: 5 }); 35 | 36 | expect(mock.__getMock).toBeCalledWith("/recent-builds?limit=5&offset=5"); 37 | expect(result).toEqual(response); 38 | }); 39 | 40 | it("should use provided optional options", async () => { 41 | mock.__setResponse(response); 42 | const result = await circle.recentBuilds( 43 | { limit: 5 }, 44 | { token: "foobar" } 45 | ); 46 | 47 | expect(mock.__getMock).toBeCalledWith("/recent-builds?limit=5&offset=5"); 48 | expect(result).toEqual(response); 49 | }); 50 | 51 | it("should handle no options", async () => { 52 | mock.__setResponse(response); 53 | const result = await getRecentBuilds(TOKEN); 54 | expect(mock.__getMock).toBeCalledWith("/recent-builds"); 55 | expect(result).toEqual(response); 56 | }); 57 | 58 | it("should use a custom circleci host", async () => { 59 | mock.__setResponse(response); 60 | await new CircleCI({ 61 | token: TOKEN, 62 | vcs: { owner: "test", repo: "proj" }, 63 | circleHost: "foo.bar/api", 64 | }).recentBuilds(); 65 | 66 | expect(mock.client).toBeCalledWith(TOKEN, "foo.bar/api"); 67 | }); 68 | }); 69 | 70 | describe("Build Summaries", () => { 71 | it("should fetch latest builds for a project", async () => { 72 | mock.__setResponse(response); 73 | const result = await circle.builds(); 74 | 75 | expect(mock.__getMock).toBeCalledWith("/project/github/foo/bar"); 76 | expect(result).toEqual(response); 77 | }); 78 | 79 | it("should use a custom circleci host", async () => { 80 | mock.__setResponse(response); 81 | await new CircleCI({ 82 | token: TOKEN, 83 | vcs: { owner: "test", repo: "proj" }, 84 | circleHost: "foo.bar/api", 85 | }).builds(); 86 | 87 | expect(mock.client).toBeCalledWith(TOKEN, "foo.bar/api"); 88 | }); 89 | 90 | it("should fetch latest builds with options", async () => { 91 | mock.__setResponse(response); 92 | const result = await circle.builds(undefined, { options: { limit: 5 } }); 93 | 94 | expect(mock.__getMock).toBeCalledWith( 95 | expect.stringContaining("?limit=5") 96 | ); 97 | expect(result).toEqual(response); 98 | }); 99 | 100 | it("should fetch builds for branch", async () => { 101 | mock.__setResponse(response); 102 | const result = await circle.buildsFor(); 103 | 104 | expect(mock.__getMock).toBeCalledWith( 105 | "/project/github/foo/bar/tree/master" 106 | ); 107 | expect(result).toEqual(response); 108 | }); 109 | 110 | it("should fetch builds for branch with options", async () => { 111 | mock.__setResponse(response); 112 | const result = await circle.buildsFor( 113 | "master", 114 | { limit: 5 }, 115 | { 116 | options: { branch: "develop" }, 117 | } 118 | ); 119 | 120 | expect(mock.__getMock).toBeCalledWith( 121 | "/project/github/foo/bar/tree/master?limit=5" 122 | ); 123 | expect(mock.__getMock).toBeCalledWith(expect.stringContaining("limit=5")); 124 | expect(result).toEqual(response); 125 | }); 126 | 127 | it("should use a custom circleci host", async () => { 128 | mock.__setResponse(response); 129 | await new CircleCI({ 130 | token: TOKEN, 131 | vcs: { owner: "test", repo: "proj" }, 132 | circleHost: "foo.bar/api", 133 | }).buildsFor(); 134 | 135 | expect(mock.client).toBeCalledWith(TOKEN, "foo.bar/api"); 136 | }); 137 | 138 | it("should handle no options", async () => { 139 | mock.__setResponse(response); 140 | const result = await getBuildSummaries(TOKEN, { 141 | vcs: { owner: "foo", repo: "bar" }, 142 | }); 143 | 144 | expect(mock.__getMock).toBeCalledWith("/project/github/foo/bar"); 145 | expect(result).toEqual(response); 146 | }); 147 | }); 148 | 149 | describe("Full Builds", () => { 150 | it("should fetch full build", async () => { 151 | mock.__setResponse(response); 152 | const result = await circle.build(42); 153 | 154 | expect(mock.__getMock).toBeCalledWith("/project/github/foo/bar/42"); 155 | expect(result).toEqual(response); 156 | }); 157 | 158 | it("should handle request options", async () => { 159 | mock.__setResponse(response); 160 | const result = await circle.build(42, { vcs: { owner: "test" } }); 161 | expect(mock.__getMock).toBeCalledWith("/project/github/test/bar/42"); 162 | expect(result).toEqual(response); 163 | }); 164 | 165 | it("should use a custom circleci host", async () => { 166 | mock.__setResponse(response); 167 | await new CircleCI({ 168 | token: TOKEN, 169 | vcs: { owner: "test", repo: "proj" }, 170 | circleHost: "foo.bar/api", 171 | }).build(1); 172 | 173 | expect(mock.client).toBeCalledWith(TOKEN, "foo.bar/api"); 174 | }); 175 | }); 176 | }); 177 | -------------------------------------------------------------------------------- /test/api/cache.test.ts: -------------------------------------------------------------------------------- 1 | import * as axios from "axios"; 2 | 3 | import { CircleCI, ClearCacheResponse } from "../../src"; 4 | 5 | jest.mock("axios"); 6 | 7 | const mockAxios = axios.default as any; 8 | 9 | describe("API - Cache", () => { 10 | const TOKEN = "test-token"; 11 | const response: ClearCacheResponse = { status: "BAR" }; 12 | 13 | let circle: CircleCI; 14 | 15 | beforeEach(() => { 16 | mockAxios.reset(); 17 | circle = new CircleCI({ 18 | token: TOKEN, 19 | vcs: { owner: "foo", repo: "bar" } 20 | }); 21 | mockAxios._setMockResponse({ data: response }); 22 | }); 23 | 24 | describe("Clear Cache", () => { 25 | it("should hit clear cache endpoint", async () => { 26 | const result = await circle.clearCache(); 27 | 28 | expect(mockAxios.delete).toBeCalledWith( 29 | `/project/github/foo/bar/build-cache?circle-token=${TOKEN}`, 30 | expect.anything() 31 | ); 32 | expect(result).toEqual(response); 33 | }); 34 | 35 | it("should override client settings with custom token", async () => { 36 | const result = await circle.clearCache({ 37 | token: "BUZZ", 38 | vcs: { repo: "foo" } 39 | }); 40 | expect(mockAxios.delete).toBeCalledWith( 41 | expect.stringContaining("/foo/foo/build-cache?circle-token=BUZZ"), 42 | expect.anything() 43 | ); 44 | expect(result).toEqual(response); 45 | }); 46 | 47 | it("should use a custom circleci host", async () => { 48 | await new CircleCI({ 49 | token: TOKEN, 50 | vcs: { owner: "test", repo: "proj" }, 51 | circleHost: "foo.bar/api" 52 | }).clearCache(); 53 | 54 | expect(mockAxios.delete).toBeCalledWith( 55 | expect.anything(), 56 | expect.objectContaining({ baseURL: "foo.bar/api" }) 57 | ); 58 | }); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /test/api/checkout-keys.test.ts: -------------------------------------------------------------------------------- 1 | import * as axios from "axios"; 2 | 3 | import { 4 | CircleCI, 5 | FullCheckoutKey, 6 | DeleteCheckoutKeyResponse, 7 | } from "../../src"; 8 | import { createJsonHeader, addUserAgentHeader } from "../../src/util"; 9 | 10 | jest.mock("axios"); 11 | 12 | const mockAxios = axios.default as any; 13 | 14 | describe("API - Checkout Keys", () => { 15 | const TOKEN = "token"; 16 | const variable: FullCheckoutKey = { 17 | public_key: "FOO", 18 | fingerprint: "foo", 19 | preferred: true, 20 | time: "bar", 21 | type: "deploy-key", 22 | }; 23 | 24 | let circle: CircleCI; 25 | 26 | beforeEach(() => { 27 | mockAxios.reset(); 28 | circle = new CircleCI({ 29 | token: TOKEN, 30 | vcs: { owner: "foo", repo: "bar" }, 31 | }); 32 | }); 33 | 34 | describe("List Keys", () => { 35 | beforeEach(() => { 36 | mockAxios._setMockResponse({ data: [variable] }); 37 | }); 38 | 39 | it("should get all checkout keys for project", async () => { 40 | const result = await circle.listCheckoutKeys(); 41 | 42 | expect(mockAxios.get).toBeCalledWith( 43 | `/project/github/foo/bar/checkout-key?circle-token=${TOKEN}`, 44 | expect.anything() 45 | ); 46 | expect(result[0]).toEqual(variable); 47 | }); 48 | 49 | it("should override client settings with custom token", async () => { 50 | const result = await circle.listCheckoutKeys({ token: "BUZZ" }); 51 | expect(mockAxios.get).toBeCalledWith( 52 | expect.stringContaining( 53 | "/github/foo/bar/checkout-key?circle-token=BUZZ" 54 | ), 55 | expect.anything() 56 | ); 57 | expect(result[0]).toEqual(variable); 58 | }); 59 | 60 | it("should use a custom circleci host", async () => { 61 | await new CircleCI({ 62 | token: TOKEN, 63 | vcs: { owner: "test", repo: "proj" }, 64 | circleHost: "foo.bar/api", 65 | }).listCheckoutKeys(); 66 | 67 | expect(mockAxios.get).toBeCalledWith( 68 | expect.anything(), 69 | expect.objectContaining({ baseURL: "foo.bar/api" }) 70 | ); 71 | }); 72 | }); 73 | 74 | describe("Create checkout key", () => { 75 | beforeEach(() => { 76 | mockAxios._setMockResponse({ data: variable }); 77 | }); 78 | 79 | it("should hit the create endpoint with JSON headers", async () => { 80 | const result = await circle.addCheckoutKey("deploy-key"); 81 | expect(mockAxios.post).toBeCalledWith( 82 | `/project/github/foo/bar/checkout-key?circle-token=${TOKEN}`, 83 | { type: "deploy-key" }, 84 | expect.objectContaining(addUserAgentHeader(createJsonHeader())) 85 | ); 86 | 87 | expect(result).toEqual(variable); 88 | }); 89 | 90 | it("should override token and project", async () => { 91 | const result = await circle.addCheckoutKey("deploy-key", { 92 | token: "BAR", 93 | vcs: { owner: "buzz" }, 94 | }); 95 | expect(mockAxios.post).toBeCalledWith( 96 | expect.stringContaining("/buzz/bar/checkout-key?circle-token=BAR"), 97 | { type: "deploy-key" }, 98 | expect.any(Object) 99 | ); 100 | 101 | expect(result).toEqual(variable); 102 | }); 103 | 104 | it("should use a custom circleci host", async () => { 105 | await new CircleCI({ 106 | token: TOKEN, 107 | vcs: { owner: "test", repo: "proj" }, 108 | circleHost: "foo.bar/api", 109 | }).addCheckoutKey("deploy-key"); 110 | 111 | expect(mockAxios.post).toBeCalledWith( 112 | expect.anything(), 113 | expect.anything(), 114 | expect.objectContaining({ baseURL: "foo.bar/api" }) 115 | ); 116 | }); 117 | }); 118 | 119 | describe("Get Checkout key", () => { 120 | beforeEach(() => { 121 | mockAxios._setMockResponse({ data: variable }); 122 | }); 123 | 124 | it("should get single checkout key", async () => { 125 | const result = await circle.getCheckoutKey("foo"); 126 | 127 | expect(mockAxios.get).toBeCalledWith( 128 | `/project/github/foo/bar/checkout-key/foo?circle-token=${TOKEN}`, 129 | expect.anything() 130 | ); 131 | expect(result).toEqual(variable); 132 | }); 133 | 134 | it("should override client settings with custom token", async () => { 135 | const result = await circle.getCheckoutKey("foo", { 136 | token: "BUZZ", 137 | vcs: { owner: "bar" }, 138 | }); 139 | expect(mockAxios.get).toBeCalledWith( 140 | expect.stringContaining("/bar/bar/checkout-key/foo?circle-token=BUZZ"), 141 | expect.anything() 142 | ); 143 | expect(result).toEqual(variable); 144 | }); 145 | 146 | it("should use a custom circleci host", async () => { 147 | await new CircleCI({ 148 | token: TOKEN, 149 | vcs: { owner: "test", repo: "proj" }, 150 | circleHost: "foo.bar/api", 151 | }).getCheckoutKey("test"); 152 | 153 | expect(mockAxios.get).toBeCalledWith( 154 | expect.anything(), 155 | expect.objectContaining({ baseURL: "foo.bar/api" }) 156 | ); 157 | }); 158 | }); 159 | 160 | describe("Delete Key", () => { 161 | const response: DeleteCheckoutKeyResponse = { 162 | message: "success", 163 | }; 164 | 165 | beforeEach(() => { 166 | mockAxios._setMockResponse({ data: response }); 167 | }); 168 | 169 | it("should hit delete endpoint", async () => { 170 | const result = await circle.deleteCheckoutKey("foo"); 171 | 172 | expect(mockAxios.delete).toBeCalledWith( 173 | `/project/github/foo/bar/checkout-key/foo?circle-token=${TOKEN}`, 174 | expect.anything() 175 | ); 176 | expect(result).toEqual(response); 177 | }); 178 | 179 | it("should override client settings with custom token", async () => { 180 | const result = await circle.deleteCheckoutKey("foo", { 181 | token: "BUZZ", 182 | vcs: { owner: "bar" }, 183 | }); 184 | expect(mockAxios.delete).toBeCalledWith( 185 | expect.stringContaining("/bar/bar/checkout-key/foo?circle-token=BUZZ"), 186 | expect.anything() 187 | ); 188 | expect(result).toEqual(response); 189 | }); 190 | 191 | it("should use a custom circleci host", async () => { 192 | await new CircleCI({ 193 | token: TOKEN, 194 | vcs: { owner: "test", repo: "proj" }, 195 | circleHost: "foo.bar/api", 196 | }).deleteCheckoutKey("foo"); 197 | 198 | expect(mockAxios.delete).toBeCalledWith( 199 | expect.anything(), 200 | expect.objectContaining({ baseURL: "foo.bar/api" }) 201 | ); 202 | }); 203 | }); 204 | }); 205 | -------------------------------------------------------------------------------- /test/api/env.test.ts: -------------------------------------------------------------------------------- 1 | import * as axios from "axios"; 2 | 3 | import { CircleCI } from "../../src"; 4 | import { EnvVariable, DeleteEnvVarResponse } from "../../src/types/api"; 5 | import { addUserAgentHeader } from "../../src/util"; 6 | 7 | jest.mock("axios"); 8 | 9 | const mockAxios = axios.default as any; 10 | 11 | describe("API - Env", () => { 12 | const TOKEN = "test-token"; 13 | const variable: EnvVariable = { name: "FOO", value: "BAR" }; 14 | 15 | let circle: CircleCI; 16 | 17 | beforeEach(() => { 18 | mockAxios.reset(); 19 | circle = new CircleCI({ 20 | token: TOKEN, 21 | vcs: { owner: "foo", repo: "bar" }, 22 | }); 23 | }); 24 | 25 | describe("List Env", () => { 26 | beforeEach(() => { 27 | mockAxios._setMockResponse({ data: [variable] }); 28 | }); 29 | 30 | it("should get all env variables for project", async () => { 31 | const result = await circle.listEnvVars(); 32 | 33 | expect(mockAxios.get).toBeCalledWith( 34 | `/project/github/foo/bar/envvar?circle-token=${TOKEN}`, 35 | expect.anything() 36 | ); 37 | expect(result[0]).toEqual(variable); 38 | }); 39 | 40 | it("should override client settings with custom token", async () => { 41 | const result = await circle.listEnvVars({ token: "BUZZ" }); 42 | expect(mockAxios.get).toBeCalledWith( 43 | expect.stringContaining("/github/foo/bar/envvar?circle-token=BUZZ"), 44 | expect.anything() 45 | ); 46 | expect(result[0]).toEqual(variable); 47 | }); 48 | }); 49 | 50 | describe("Add Env", () => { 51 | beforeEach(() => { 52 | mockAxios._setMockResponse({ data: variable }); 53 | }); 54 | 55 | it("should hit the add env endpoint with JSON headers", async () => { 56 | const result = await circle.addEnvVar(variable); 57 | expect(mockAxios.post).toBeCalledWith( 58 | `/project/github/foo/bar/envvar?circle-token=${TOKEN}`, 59 | variable, 60 | expect.objectContaining({ 61 | headers: { 62 | "Content-Type": "application/json", 63 | Accepts: "application/json", 64 | ...addUserAgentHeader().headers, 65 | }, 66 | }) 67 | ); 68 | 69 | expect(result).toEqual(variable); 70 | }); 71 | 72 | it("should override token and project", async () => { 73 | const result = await circle.addEnvVar(variable, { 74 | token: "BAR", 75 | vcs: { owner: "buzz" }, 76 | }); 77 | expect(mockAxios.post).toBeCalledWith( 78 | expect.stringContaining("/buzz/bar/envvar?circle-token=BAR"), 79 | variable, 80 | expect.any(Object) 81 | ); 82 | 83 | expect(result).toEqual(variable); 84 | }); 85 | 86 | it("should use a custom circleci host", async () => { 87 | await new CircleCI({ 88 | token: TOKEN, 89 | vcs: { owner: "test", repo: "proj" }, 90 | circleHost: "foo.bar/api", 91 | }).addEnvVar(variable); 92 | 93 | expect(mockAxios.post).toBeCalledWith( 94 | expect.anything(), 95 | expect.anything(), 96 | expect.objectContaining({ baseURL: "foo.bar/api" }) 97 | ); 98 | }); 99 | }); 100 | 101 | describe("Get Env", () => { 102 | beforeEach(() => { 103 | mockAxios._setMockResponse({ data: variable }); 104 | }); 105 | 106 | it("should get single environment variable", async () => { 107 | const result = await circle.getEnvVar("foo"); 108 | 109 | expect(mockAxios.get).toBeCalledWith( 110 | `/project/github/foo/bar/envvar/foo?circle-token=${TOKEN}`, 111 | expect.anything() 112 | ); 113 | expect(result).toEqual(variable); 114 | }); 115 | 116 | it("should override client settings with custom token", async () => { 117 | const result = await circle.getEnvVar("foo", { 118 | token: "BUZZ", 119 | vcs: { owner: "bar" }, 120 | }); 121 | expect(mockAxios.get).toBeCalledWith( 122 | expect.stringContaining("/bar/bar/envvar/foo?circle-token=BUZZ"), 123 | expect.anything() 124 | ); 125 | expect(result).toEqual(variable); 126 | }); 127 | 128 | it("should use a custom circleci host", async () => { 129 | await new CircleCI({ 130 | token: TOKEN, 131 | vcs: { owner: "test", repo: "proj" }, 132 | circleHost: "foo.bar/api", 133 | }).getEnvVar("foo"); 134 | 135 | expect(mockAxios.get).toBeCalledWith( 136 | expect.anything(), 137 | expect.objectContaining({ baseURL: "foo.bar/api" }) 138 | ); 139 | }); 140 | }); 141 | 142 | describe("Delete Env", () => { 143 | const response: DeleteEnvVarResponse = { 144 | message: "success", 145 | }; 146 | 147 | beforeEach(() => { 148 | mockAxios._setMockResponse({ data: response }); 149 | }); 150 | 151 | it("should hit delete endpoint", async () => { 152 | const result = await circle.deleteEnvVar("foo"); 153 | 154 | expect(mockAxios.delete).toBeCalledWith( 155 | `/project/github/foo/bar/envvar/foo?circle-token=${TOKEN}`, 156 | expect.anything() 157 | ); 158 | expect(result).toEqual(response); 159 | }); 160 | 161 | it("should override client settings with custom token", async () => { 162 | const result = await circle.deleteEnvVar("foo", { 163 | token: "BUZZ", 164 | vcs: { owner: "bar" }, 165 | }); 166 | expect(mockAxios.delete).toBeCalledWith( 167 | expect.stringContaining("/bar/bar/envvar/foo?circle-token=BUZZ"), 168 | expect.anything() 169 | ); 170 | expect(result).toEqual(response); 171 | }); 172 | 173 | it("should use a custom circleci host", async () => { 174 | await new CircleCI({ 175 | token: TOKEN, 176 | vcs: { owner: "test", repo: "proj" }, 177 | circleHost: "foo.bar/api", 178 | }).deleteEnvVar("foo"); 179 | 180 | expect(mockAxios.delete).toBeCalledWith( 181 | expect.anything(), 182 | expect.objectContaining({ baseURL: "foo.bar/api" }) 183 | ); 184 | }); 185 | }); 186 | }); 187 | -------------------------------------------------------------------------------- /test/api/metadata.test.ts: -------------------------------------------------------------------------------- 1 | import * as axios from "axios"; 2 | 3 | import { CircleCI, TestMetadataResponse } from "../../src"; 4 | 5 | jest.mock("axios"); 6 | 7 | const mockAxios = axios.default as any; 8 | 9 | describe("API - Test Metadata", () => { 10 | const TOKEN = "token"; 11 | const variable: TestMetadataResponse = { 12 | tests: [ 13 | { 14 | message: "ok" 15 | } 16 | ] 17 | }; 18 | 19 | let circle: CircleCI; 20 | 21 | beforeEach(() => { 22 | mockAxios.reset(); 23 | circle = new CircleCI({ 24 | token: TOKEN, 25 | vcs: { owner: "foo", repo: "bar" } 26 | }); 27 | }); 28 | 29 | describe("Get Metadata", () => { 30 | beforeEach(() => { 31 | mockAxios._setMockResponse({ data: variable }); 32 | }); 33 | 34 | it("should get test metadata for build number", async () => { 35 | const result = await circle.getTestMetadata(42); 36 | 37 | expect(mockAxios.get).toBeCalledWith( 38 | `/project/github/foo/bar/42/tests?circle-token=${TOKEN}`, 39 | expect.anything() 40 | ); 41 | expect(result.tests[0].message).toEqual("ok"); 42 | }); 43 | 44 | it("should override client settings with custom token", async () => { 45 | const result = await circle.getTestMetadata(42, { token: "BUZZ" }); 46 | expect(mockAxios.get).toBeCalledWith( 47 | expect.stringContaining("/github/foo/bar/42/tests?circle-token=BUZZ"), 48 | expect.anything() 49 | ); 50 | expect(result.tests[0].message).toEqual("ok"); 51 | }); 52 | 53 | it("should use a custom circleci host", async () => { 54 | await new CircleCI({ 55 | token: TOKEN, 56 | vcs: { owner: "test", repo: "proj" }, 57 | circleHost: "foo.bar/api" 58 | }).getTestMetadata(42); 59 | 60 | expect(mockAxios.get).toBeCalledWith( 61 | expect.anything(), 62 | expect.objectContaining({ baseURL: "foo.bar/api" }) 63 | ); 64 | }); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /test/api/misc.test.ts: -------------------------------------------------------------------------------- 1 | import * as axios from "axios"; 2 | 3 | import { 4 | CircleCI, 5 | HerokuKey, 6 | addSSHKey, 7 | API_BASE, 8 | addHerokuKey 9 | } from "../../src"; 10 | 11 | jest.mock("axios"); 12 | 13 | const mockAxios = axios.default as any; 14 | 15 | describe("API - Misc", () => { 16 | const TOKEN = "token"; 17 | let circle: CircleCI; 18 | 19 | beforeEach(() => { 20 | mockAxios.reset(); 21 | mockAxios._setMockResponse({ data: {} }); 22 | circle = new CircleCI({ 23 | token: TOKEN, 24 | vcs: { owner: "foo", repo: "bar" } 25 | }); 26 | }); 27 | 28 | describe("Add SSH Key", () => { 29 | const testKey = { 30 | hostname: "foobar", 31 | private_key: "bizz" 32 | }; 33 | 34 | it("should add ssh key to a project", async () => { 35 | await circle.addSSHKey(testKey); 36 | 37 | expect(mockAxios.post).toBeCalledWith( 38 | `/project/github/foo/bar/ssh-key?circle-token=${TOKEN}`, 39 | testKey, 40 | expect.any(Object) 41 | ); 42 | }); 43 | 44 | it("should override client settings with custom token", async () => { 45 | await circle.addSSHKey(testKey, { token: "BUZZ" }); 46 | expect(mockAxios.post).toBeCalledWith( 47 | expect.stringContaining("/github/foo/bar/ssh-key?circle-token=BUZZ"), 48 | testKey, 49 | expect.any(Object) 50 | ); 51 | }); 52 | 53 | it("should use default CircleCI host", async () => { 54 | await addSSHKey(TOKEN, { owner: "test", repo: "proj" }, testKey as any); 55 | 56 | expect(mockAxios.post).toBeCalledWith( 57 | expect.anything(), 58 | expect.anything(), 59 | expect.objectContaining({ baseURL: API_BASE }) 60 | ); 61 | }); 62 | 63 | it("should use a custom circleci host", async () => { 64 | await new CircleCI({ 65 | token: TOKEN, 66 | vcs: { owner: "test", repo: "proj" }, 67 | circleHost: "foo.bar/api" 68 | }).addSSHKey(testKey); 69 | 70 | expect(mockAxios.post).toBeCalledWith( 71 | expect.anything(), 72 | expect.anything(), 73 | expect.objectContaining({ baseURL: "foo.bar/api" }) 74 | ); 75 | }); 76 | }); 77 | 78 | describe("Add Heroku key", () => { 79 | const testKey: HerokuKey = { apikey: "foobar" }; 80 | 81 | it("should add heroku key to project", async () => { 82 | await circle.addHerokuKey(testKey); 83 | expect(mockAxios.post).toBeCalledWith( 84 | `/user/heroku-key?circle-token=${TOKEN}`, 85 | testKey, 86 | expect.any(Object) 87 | ); 88 | }); 89 | 90 | it("should override client settings with custom token", async () => { 91 | await circle.addHerokuKey(testKey, { token: "BUZZ" }); 92 | expect(mockAxios.post).toBeCalledWith( 93 | expect.stringContaining("/user/heroku-key?circle-token=BUZZ"), 94 | testKey, 95 | expect.any(Object) 96 | ); 97 | }); 98 | 99 | it("should use default CircleCI host", async () => { 100 | await addHerokuKey(TOKEN, testKey as any); 101 | 102 | expect(mockAxios.post).toBeCalledWith( 103 | expect.anything(), 104 | expect.anything(), 105 | expect.objectContaining({ baseURL: API_BASE }) 106 | ); 107 | }); 108 | 109 | it("should use a custom circleci host using wrapper", async () => { 110 | await new CircleCI({ 111 | token: TOKEN, 112 | vcs: { owner: "test", repo: "proj" }, 113 | circleHost: "foo.bar/api" 114 | }).addHerokuKey(testKey); 115 | 116 | expect(mockAxios.post).toBeCalledWith( 117 | expect.anything(), 118 | expect.anything(), 119 | expect.objectContaining({ baseURL: "foo.bar/api" }) 120 | ); 121 | }); 122 | }); 123 | }); 124 | -------------------------------------------------------------------------------- /test/api/projects.test.ts: -------------------------------------------------------------------------------- 1 | import { CircleCI, API_ALL_PROJECTS, getAllProjects } from "../../src"; 2 | import { AllProjectsResponse, FollowNewResult } from "../../src/types/api"; 3 | import * as client from "../../src/client"; 4 | 5 | jest.mock("../../src/client"); 6 | 7 | const mock = client as any; 8 | const TOKEN = "test-token"; 9 | 10 | describe("API - Projects", () => { 11 | let circle: CircleCI; 12 | 13 | beforeEach(() => { 14 | mock.__reset(); 15 | 16 | circle = new CircleCI({ token: TOKEN }); 17 | }); 18 | 19 | describe("All Projects", () => { 20 | const response: AllProjectsResponse = [ 21 | { 22 | reponame: "test1", 23 | username: "Tom", 24 | branches: { master: {} } 25 | }, 26 | { 27 | reponame: "test2", 28 | username: "Tom", 29 | branches: { master: {} } 30 | } 31 | ]; 32 | 33 | it('should call the "allProjects" endpoint', async () => { 34 | mock.__setResponse(response); 35 | const result = await circle.projects(); 36 | 37 | expect(mock.client).toBeCalledWith(TOKEN, undefined); 38 | expect(mock.__getMock).toBeCalledWith(API_ALL_PROJECTS); 39 | expect(result).toEqual(response); 40 | }); 41 | 42 | it("should throw an error if request fails", async () => { 43 | mock.__setError({ code: 404 }); 44 | 45 | const check = circle.projects(); 46 | await expect(check).rejects.toEqual({ code: 404 }); 47 | }); 48 | 49 | it("should use the default circleci host", async () => { 50 | mock.__setResponse(response); 51 | await getAllProjects(TOKEN); 52 | 53 | expect(mock.client).toBeCalledWith(TOKEN, undefined); 54 | }); 55 | 56 | it("should use a custom circleci host", async () => { 57 | mock.__setResponse(response); 58 | await new CircleCI({ 59 | token: TOKEN, 60 | vcs: { owner: "test", repo: "proj" }, 61 | circleHost: "foo.bar/api" 62 | }).projects(); 63 | 64 | expect(mock.client).toBeCalledWith(TOKEN, "foo.bar/api"); 65 | }); 66 | }); 67 | 68 | describe("FollowProject", () => { 69 | const response: FollowNewResult = { 70 | followed: true, 71 | first_build: { 72 | branch: "master" 73 | } 74 | }; 75 | 76 | it("should follow a specific repository", async () => { 77 | mock.__setResponse({ data: response }); 78 | await circle.followProject({ 79 | vcs: { 80 | owner: "johnsmith", 81 | repo: "tinker" 82 | } 83 | }); 84 | 85 | expect(mock.client).toBeCalledWith(TOKEN, undefined); 86 | expect(mock.__postMock).toBeCalledWith( 87 | expect.stringContaining("johnsmith/tinker/follow") 88 | ); 89 | }); 90 | 91 | it("should throw an error if request fails", async () => { 92 | mock.__setError({ code: 404 }); 93 | 94 | const check = circle.followProject({ vcs: { owner: "t", repo: "t" } }); 95 | await expect(check).rejects.toEqual({ code: 404 }); 96 | }); 97 | 98 | it("should use a custom circleci host", async () => { 99 | mock.__setResponse(response); 100 | await new CircleCI({ 101 | token: TOKEN, 102 | vcs: { owner: "test", repo: "proj" }, 103 | circleHost: "foo.bar/api" 104 | }).followProject({} as any); 105 | 106 | expect(mock.client).toBeCalledWith(TOKEN, "foo.bar/api"); 107 | }); 108 | }); 109 | }); 110 | -------------------------------------------------------------------------------- /test/api/user.test.ts: -------------------------------------------------------------------------------- 1 | import { CircleCI, API_ME, getMe } from "../../src"; 2 | import { Me } from "../../src/types/api"; 3 | import * as client from "../../src/client"; 4 | 5 | jest.mock("../../src/client"); 6 | 7 | const mock = client as any; 8 | 9 | describe("API - Me", () => { 10 | const TOKEN = "test-token"; 11 | const me: Me = { 12 | name: "Test", 13 | student: false, 14 | login: "test@test.com" 15 | }; 16 | 17 | let circle: CircleCI; 18 | 19 | beforeEach(() => { 20 | mock.__reset(); 21 | circle = new CircleCI({ token: TOKEN }); 22 | }); 23 | 24 | it('should call the "me" endpoint', async () => { 25 | mock.__setResponse(me); 26 | const result = await circle.me(); 27 | 28 | expect(mock.client).toBeCalledWith(TOKEN, undefined); 29 | expect(mock.__getMock).toBeCalledWith(API_ME); 30 | expect(result).toEqual(me); 31 | }); 32 | 33 | it("should throw an error if request fails", async () => { 34 | mock.__setError({ code: 404 }); 35 | 36 | const check = circle.me(); 37 | await expect(check).rejects.toEqual({ code: 404 }); 38 | }); 39 | 40 | it("should use the default circleci host", async () => { 41 | mock.__setResponse(me); 42 | await getMe(TOKEN); 43 | 44 | expect(mock.client).toBeCalledWith(TOKEN, undefined); 45 | }); 46 | 47 | it("should use a custom circleci host", async () => { 48 | mock.__setResponse(me); 49 | await new CircleCI({ 50 | token: TOKEN, 51 | circleHost: "foo.bar/api" 52 | }).projects(); 53 | 54 | expect(mock.client).toBeCalledWith(TOKEN, "foo.bar/api"); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /test/circleci.test.ts: -------------------------------------------------------------------------------- 1 | import { CircleCI } from "../src"; 2 | import * as client from "../src/client"; 3 | import { GitType } from "../src/types/lib"; 4 | 5 | jest.mock("../src/client"); 6 | 7 | const mock = client as any; 8 | const token = "test-token"; 9 | 10 | describe("CircleCI", () => { 11 | describe("Factory", () => { 12 | beforeEach(() => { 13 | mock.__reset(); 14 | }); 15 | 16 | it("should override all options", () => { 17 | const circle = new CircleCI({ 18 | token, 19 | vcs: { 20 | type: GitType.BITBUCKET, 21 | owner: "test", 22 | repo: "test" 23 | }, 24 | options: { 25 | branch: "develop", 26 | filter: "successful" 27 | } 28 | }); 29 | 30 | // Override options, they shouldn't save 31 | circle 32 | .latestArtifacts(undefined, { 33 | vcs: { type: GitType.GITHUB, owner: "foo", repo: "bar" } 34 | }) 35 | .catch(jest.fn()); 36 | 37 | expect(circle.defaults().vcs!.owner).toBe("test"); 38 | }); 39 | 40 | it("should add token to url", () => { 41 | const circle = new CircleCI({ token }); 42 | const expected = `test?circle-token=${token}`; 43 | expect(circle.addToken("test")).toEqual(expected); 44 | }); 45 | 46 | it("should throw credentials error", () => { 47 | const circle = new CircleCI({ token }); 48 | expect(() => circle.latestArtifacts()).toThrow(); 49 | }); 50 | 51 | it("should override request options and not fail", () => { 52 | const circle = new CircleCI({ token }); 53 | mock.__setResponse({ data: [1, 2, 3] }); 54 | 55 | expect(() => 56 | circle.latestArtifacts(undefined, { 57 | token: "new-token", 58 | vcs: { type: GitType.BITBUCKET, owner: "j", repo: "d" } 59 | }) 60 | ).not.toThrow(); 61 | }); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /test/client.test.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | import { client, circleGet, circlePost, circleDelete } from "../src/client"; 4 | import { API_BASE } from "../src"; 5 | import { addUserAgentHeader } from "../src/util"; 6 | 7 | jest.mock("axios"); 8 | 9 | const mockAxios = axios as any; 10 | 11 | /** 12 | * TODO 13 | * Add tests for the auth param adder 14 | */ 15 | 16 | const TOKEN = "test-token"; 17 | const URL = "cats.com"; 18 | const URL_WITH_TOKEN = `${URL}?circle-token=${TOKEN}`; 19 | 20 | describe("Client", () => { 21 | beforeEach(() => { 22 | mockAxios.reset(); 23 | }); 24 | 25 | describe("Factory", () => { 26 | it("should call the default Circle URL", () => { 27 | client(TOKEN).get(URL).catch(jest.fn()); 28 | expect(mockAxios.get).toBeCalledWith(URL_WITH_TOKEN, { 29 | baseURL: API_BASE, 30 | ...addUserAgentHeader(), 31 | }); 32 | }); 33 | 34 | it("should call a custom Circle URL", () => { 35 | client(TOKEN, "foo.bar/api").get(URL).catch(jest.fn()); 36 | expect(mockAxios.get).toBeCalledWith(URL_WITH_TOKEN, { 37 | baseURL: "foo.bar/api", 38 | ...addUserAgentHeader(), 39 | }); 40 | }); 41 | 42 | it("should override the custom url with another url", () => { 43 | client(TOKEN, "foo.bar/api") 44 | .get(URL, { baseURL: "biz.baz/api" }) 45 | .catch(jest.fn()); 46 | expect(mockAxios.get).toBeCalledWith(URL_WITH_TOKEN, { 47 | baseURL: "biz.baz/api", 48 | ...addUserAgentHeader(), 49 | }); 50 | }); 51 | 52 | it("should add token param to url for get", () => { 53 | client(TOKEN).get(URL).catch(jest.fn()); 54 | expect(mockAxios.get).toBeCalledWith(URL_WITH_TOKEN, expect.anything()); 55 | }); 56 | 57 | it("should add token param to url for post", () => { 58 | client(TOKEN).post(URL, null).catch(jest.fn()); 59 | expect(mockAxios.post).toBeCalledWith( 60 | URL_WITH_TOKEN, 61 | null, 62 | expect.anything() 63 | ); 64 | }); 65 | 66 | it("should use options", () => { 67 | client(TOKEN).post(URL, "test", { timeout: 1000 }).catch(jest.fn()); 68 | expect(mockAxios.post).toBeCalledWith( 69 | URL_WITH_TOKEN, 70 | "test", 71 | expect.objectContaining({ 72 | timeout: 1000, 73 | }) 74 | ); 75 | }); 76 | 77 | it("should add the user-agent to the get request", () => { 78 | client(TOKEN).get(URL).catch(jest.fn()); 79 | expect(mockAxios.get).toBeCalledWith( 80 | URL_WITH_TOKEN, 81 | expect.objectContaining(addUserAgentHeader()) 82 | ); 83 | }); 84 | 85 | it("should add the user-agent to the post request", () => { 86 | client(TOKEN).post(URL, "payload").catch(jest.fn()); 87 | expect(mockAxios.post).toBeCalledWith( 88 | URL_WITH_TOKEN, 89 | "payload", 90 | expect.objectContaining(addUserAgentHeader()) 91 | ); 92 | }); 93 | 94 | it("should add the user-agent to the delete request", () => { 95 | client(TOKEN).delete(URL).catch(jest.fn()); 96 | expect(mockAxios.get).toBeCalledWith( 97 | URL_WITH_TOKEN, 98 | expect.objectContaining(addUserAgentHeader()) 99 | ); 100 | }); 101 | }); 102 | 103 | describe("circleGet", () => { 104 | it("should get url with auth token", () => { 105 | /* tslint:disable-next-line:deprecation */ 106 | circleGet(TOKEN, URL).catch(jest.fn()); 107 | expect(mockAxios.get).toBeCalledWith(URL_WITH_TOKEN, expect.anything()); 108 | }); 109 | 110 | it("should get url with options", () => { 111 | /* tslint:disable-next-line:deprecation */ 112 | circleGet(TOKEN, URL, { timeout: 1000 }).catch(jest.fn()); 113 | expect(mockAxios.get).toBeCalledWith( 114 | URL_WITH_TOKEN, 115 | expect.objectContaining({ 116 | timeout: 1000, 117 | }) 118 | ); 119 | }); 120 | 121 | it("should handle null options", () => { 122 | /* tslint:disable-next-line:deprecation */ 123 | circleGet(TOKEN, URL, undefined).catch(jest.fn()); 124 | expect(mockAxios.get).toBeCalledWith(URL_WITH_TOKEN, expect.anything()); 125 | }); 126 | 127 | it("should add circle-token param with ?", () => { 128 | /* tslint:disable-next-line:deprecation */ 129 | circleGet("foo", "bar.com").catch(jest.fn()); 130 | expect(mockAxios.get).toBeCalledWith( 131 | "bar.com?circle-token=foo", 132 | expect.anything() 133 | ); 134 | }); 135 | 136 | it("should add circle-token param with &", () => { 137 | /* tslint:disable-next-line:deprecation */ 138 | circleGet("foo", "bar.com?fizz=buzz").catch(jest.fn()); 139 | expect(mockAxios.get).toBeCalledWith( 140 | "bar.com?fizz=buzz&circle-token=foo", 141 | expect.anything() 142 | ); 143 | }); 144 | 145 | it("should return data after awaiting promise", () => { 146 | const catchFn = jest.fn(); 147 | const thenFn = jest.fn(); 148 | 149 | client(TOKEN).get("/biz/baz").then(thenFn).catch(catchFn); 150 | 151 | expect(mockAxios.get).toHaveBeenCalledWith( 152 | `/biz/baz?circle-token=${TOKEN}`, 153 | expect.anything() 154 | ); 155 | 156 | mockAxios._setMockResponse({ data: "okay" }); 157 | }); 158 | }); 159 | 160 | describe("circlePost", () => { 161 | it("should post url", () => { 162 | /* tslint:disable-next-line:deprecation */ 163 | circlePost(TOKEN, URL, null).catch(jest.fn()); 164 | expect(mockAxios.post).toBeCalledWith( 165 | URL_WITH_TOKEN, 166 | null, 167 | expect.anything() 168 | ); 169 | }); 170 | 171 | it("should post url with options", () => { 172 | /* tslint:disable-next-line:deprecation */ 173 | circlePost(TOKEN, URL, { cat: "meow" }, { timeout: 1 }).catch(jest.fn()); 174 | expect(mockAxios.post).toBeCalledWith( 175 | URL_WITH_TOKEN, 176 | { cat: "meow" }, 177 | expect.objectContaining({ timeout: 1 }) 178 | ); 179 | }); 180 | 181 | it("should return data after awaiting promise", () => { 182 | const catchFn = jest.fn(); 183 | const thenFn = jest.fn(); 184 | 185 | client(TOKEN) 186 | .post("/biz/baz", { foo: "bar" }) 187 | .then(thenFn) 188 | .catch(catchFn); 189 | 190 | expect(mockAxios.post).toHaveBeenCalledWith( 191 | `/biz/baz?circle-token=${TOKEN}`, 192 | { foo: "bar" }, 193 | expect.anything() 194 | ); 195 | 196 | mockAxios._setMockResponse({ data: "okay" }); 197 | }); 198 | }); 199 | 200 | describe("circleDelete", () => { 201 | it("should send delete to url", () => { 202 | /* tslint:disable-next-line:deprecation */ 203 | circleDelete("foo", "bar.com").catch(jest.fn()); 204 | expect(mockAxios.delete).toBeCalledWith( 205 | "bar.com?circle-token=foo", 206 | expect.anything() 207 | ); 208 | }); 209 | 210 | it("should delete url with options", () => { 211 | /* tslint:disable-next-line:deprecation */ 212 | circleDelete(TOKEN, URL, { timeout: 1000 }).catch(jest.fn()); 213 | expect(mockAxios.delete).toBeCalledWith( 214 | URL_WITH_TOKEN, 215 | expect.objectContaining({ 216 | timeout: 1000, 217 | }) 218 | ); 219 | }); 220 | 221 | it("should be able to use the factory to delete", () => { 222 | const catchFn = jest.fn(); 223 | const thenFn = jest.fn(); 224 | 225 | client(TOKEN).delete("/biz/baz").then(thenFn).catch(catchFn); 226 | 227 | expect(mockAxios.delete).toHaveBeenCalledWith( 228 | `/biz/baz?circle-token=${TOKEN}`, 229 | expect.anything() 230 | ); 231 | 232 | mockAxios._setMockResponse({ data: "okay" }); 233 | }); 234 | }); 235 | }); 236 | -------------------------------------------------------------------------------- /test/index.test.ts: -------------------------------------------------------------------------------- 1 | import { CircleCI, CircleCIFactory, getGitType, GitType } from "../src"; 2 | 3 | describe("Lib", () => { 4 | it("should export the circleci wrapper", () => { 5 | const circleci = new CircleCI({ token: "foo" }); 6 | expect(circleci).toBeInstanceOf(CircleCI); 7 | expect(circleci.me).toBeInstanceOf(Function); 8 | }); 9 | 10 | it("should export required components", () => { 11 | expect(getGitType("bitbucket")).toEqual(GitType.BITBUCKET); 12 | expect(CircleCIFactory(" ").get).toBeInstanceOf(Function); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/util.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | queryParams, 3 | validateVCSRequest, 4 | getGitType, 5 | createJsonHeader, 6 | addUserAgentHeader, 7 | } from "../src/util"; 8 | import { FullRequest, GitType } from "../src/types/lib"; 9 | import { AxiosRequestConfig } from "axios"; 10 | 11 | jest.mock( 12 | "../package.json", 13 | () => ({ 14 | organization: "foo", 15 | name: "bar", 16 | version: "42", 17 | }), 18 | { virtual: true } 19 | ); 20 | 21 | describe("Util", () => { 22 | describe("Validate Git required request", () => { 23 | const blank: FullRequest = { 24 | token: "", 25 | vcs: { owner: "", repo: "" }, 26 | }; 27 | 28 | it("should throw with all empty values", () => { 29 | expect(() => validateVCSRequest(blank)).toThrow(Error); 30 | }); 31 | 32 | it("should throw on missing vcs", () => { 33 | expect(() => validateVCSRequest({ ...blank, token: "token" })).toThrow( 34 | /type||owner||repo/ 35 | ); 36 | }); 37 | 38 | it("should successfully validate", () => { 39 | const valid: FullRequest = { 40 | token: "token", 41 | vcs: { type: GitType.GITHUB, owner: "o", repo: "r" }, 42 | }; 43 | expect(() => validateVCSRequest(valid)).not.toThrow(); 44 | }); 45 | }); 46 | 47 | describe("Query Parameters", () => { 48 | it("should set branch", () => { 49 | expect(queryParams({ branch: "develop" })).toContain("branch=develop"); 50 | }); 51 | 52 | it("should set the filter", () => { 53 | expect(queryParams({ filter: "failed" })).toContain("filter=failed"); 54 | }); 55 | 56 | it("should return nothing", () => { 57 | expect(queryParams({})).toBe(""); 58 | expect(queryParams()).toBe(""); 59 | }); 60 | 61 | it("should handle keys with undefined values", () => { 62 | expect(queryParams({ offset: undefined })).toBe(""); 63 | }); 64 | }); 65 | 66 | it("should properly convert git type", () => { 67 | expect(getGitType("")).toEqual("github"); 68 | expect(getGitType("bitbucket")).toEqual("bitbucket"); 69 | expect(getGitType("BitB u ckeT ")).toEqual("bitbucket"); 70 | }); 71 | 72 | it("should create a json header object", () => { 73 | const { headers } = createJsonHeader(); 74 | expect(headers).toBeInstanceOf(Object); 75 | expect(headers["Content-Type"]).toEqual("application/json"); 76 | expect(headers.Accepts).toEqual("application/json"); 77 | }); 78 | 79 | it("should create the appropriate user agent string from the package.json", () => { 80 | const { headers } = addUserAgentHeader(); 81 | expect(headers["User-Agent"]).not.toBeUndefined(); 82 | expect(headers["User-Agent"]).toEqual("foo/bar 42"); 83 | }); 84 | 85 | it("should merge config object and only overwrite the user-agent header", () => { 86 | const config: AxiosRequestConfig = { 87 | baseURL: "foobar.com", 88 | headers: { 89 | foo: "bar", 90 | }, 91 | }; 92 | 93 | const mergedConfig = addUserAgentHeader(config); 94 | expect(mergedConfig).toEqual( 95 | expect.objectContaining({ 96 | baseURL: "foobar.com", 97 | headers: { 98 | foo: "bar", 99 | "User-Agent": "foo/bar 42", 100 | }, 101 | }) 102 | ); 103 | }); 104 | }); 105 | -------------------------------------------------------------------------------- /tools/gh-pages-publish.ts: -------------------------------------------------------------------------------- 1 | const { cd, exec, echo, touch, cp } = require("shelljs"); 2 | const { readFileSync } = require("fs"); 3 | const url = require("url"); 4 | 5 | let repoUrl; 6 | let pkg = JSON.parse(readFileSync("package.json") as any); 7 | if (typeof pkg.repository === "object") { 8 | if (!pkg.repository.hasOwnProperty("url")) { 9 | throw new Error("URL does not exist in repository section"); 10 | } 11 | repoUrl = pkg.repository.url; 12 | } else { 13 | repoUrl = pkg.repository; 14 | } 15 | 16 | let parsedUrl = url.parse(repoUrl); 17 | let repository = (parsedUrl.host || "") + (parsedUrl.path || ""); 18 | let ghToken = process.env.GH_TOKEN; 19 | 20 | echo("Deploying docs!!!"); 21 | cd("docs"); 22 | touch(".nojekyll"); 23 | exec("git init"); 24 | exec("git add ."); 25 | exec('git config user.name "Jordon de Hoog"'); 26 | exec('git config user.email "jordon.dehoog@gmail.com"'); 27 | exec('git commit -m "docs(docs): update gh-pages [skip ci]"'); 28 | exec( 29 | `git push --force --quiet "https://${ghToken}@${repository}" master:gh-pages` 30 | ); 31 | echo("Docs deployed!!"); 32 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "target": "es5", 5 | "lib": ["es2015", "es2016", "es2017", "dom"], 6 | "strict": true, 7 | "sourceMap": true, 8 | "experimentalDecorators": true, 9 | "emitDecoratorMetadata": true, 10 | "suppressImplicitAnyIndexErrors": true, 11 | "resolveJsonModule": true, 12 | "noUnusedLocals": true, 13 | "typeRoots": ["node_modules/@types"] 14 | }, 15 | "include": ["src"] 16 | } 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "es2015", 5 | "declarationDir": "dist/types", 6 | "outDir": "dist/lib", 7 | "declaration": true, 8 | "allowSyntheticDefaultImports": true, 9 | }, 10 | "include": ["src"] 11 | } 12 | -------------------------------------------------------------------------------- /tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "inlineSourceMap": true, 6 | "inlineSources": true 7 | }, 8 | "include": ["src", "test"] 9 | } 10 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint-config-standard", 4 | "tslint-config-prettier" 5 | ] 6 | } --------------------------------------------------------------------------------